Skip to content

Commit

Permalink
feat(extensions): add execute command (#225)
Browse files Browse the repository at this point in the history
* feat(extensions): add execute command

this adds Extensions to the global cmd.go resulting in another section showing up when doing a shuttle --help

To execute an extension simply shuttle myExtension where myExtension is the name of the downloaded extension. All args are passed to the child as well

Signed-off-by: Kasper J. Hermansen <[email protected]>

* feat: remember to add error

Signed-off-by: Kasper J. Hermansen <[email protected]>

* feat: without empty line

Signed-off-by: Kasper J. Hermansen <[email protected]>

* feat: adds github remote registry index (#226)

* feat: with github registry

Signed-off-by: Kasper J. Hermansen <[email protected]>

* fix: shuttle extensions

Signed-off-by: Kasper J. Hermansen <[email protected]>

* feat: can download private files

Signed-off-by: Kasper J. Hermansen <[email protected]>

* feat: remove fluff

Signed-off-by: Kasper J. Hermansen <[email protected]>

* feat: fix review comments

Signed-off-by: Kasper J. Hermansen <[email protected]>

* feat: it needs to implement the functions

Signed-off-by: Kasper J. Hermansen <[email protected]>

---------

Signed-off-by: Kasper J. Hermansen <[email protected]>

---------

Signed-off-by: Kasper J. Hermansen <[email protected]>
  • Loading branch information
kjuulh authored Jun 13, 2024
1 parent 974e250 commit 42ee36f
Show file tree
Hide file tree
Showing 13 changed files with 592 additions and 16 deletions.
3 changes: 3 additions & 0 deletions cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,9 @@ func initializedRootFromArgs(stdout, stderr io.Writer, args []string) (*cobra.Co
rootCmd.ParseFlags(args)

rootCmd.AddCommand(newExtCmd())
if err := addExtensions(rootCmd); err != nil {
uii.Verboseln("failed to register extensions: %s", err.Error())
}

if isInRepoContext() {
runCmd, err := newRun(uii, ctxProvider)
Expand Down
74 changes: 74 additions & 0 deletions cmd/ext.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ package cmd
import (
"errors"
"os"
"os/exec"

stdcontext "context"

"github.com/lunarway/shuttle/internal/extensions"
"github.com/lunarway/shuttle/internal/global"
Expand All @@ -25,6 +28,52 @@ func (c *extGlobalConfig) getRegistry() (string, bool) {
return "", false
}

func addExtensions(rootCmd *cobra.Command) error {
extManager := extensions.NewExtensionsManager(global.NewGlobalStore())

extensions, err := extManager.GetAll(stdcontext.Background())
if err != nil {
return err
}
grp := &cobra.Group{
ID: "extensions",
Title: "Extensions",
}
rootCmd.AddGroup(grp)
for _, extension := range extensions {
extension := extension

rootCmd.AddCommand(
&cobra.Command{
Use: extension.Name(),
Short: extension.Description(),
Version: extension.Version(),
GroupID: "extensions",
DisableFlagParsing: true,
RunE: func(cmd *cobra.Command, args []string) error {
extCmd := exec.CommandContext(cmd.Context(), extension.FullPath(), args...)

extCmd.Stdout = os.Stdout
extCmd.Stderr = os.Stderr
extCmd.Stdin = os.Stdin

if err := extCmd.Start(); err != nil {
return err
}

if err := extCmd.Wait(); err != nil {
return err
}

return nil
},
},
)
}

return nil
}

func newExtCmd() *cobra.Command {
globalConfig := &extGlobalConfig{}

Expand All @@ -37,6 +86,7 @@ func newExtCmd() *cobra.Command {
newExtInstallCmd(globalConfig),
newExtUpdateCmd(globalConfig),
newExtInitCmd(globalConfig),
newExtPublishCmd(globalConfig),
)

cmd.PersistentFlags().StringVar(&globalConfig.registry, "registry", "", "the given registry, if not set will default to SHUTTLE_EXTENSIONS_REGISTRY")
Expand Down Expand Up @@ -96,3 +146,27 @@ func newExtInitCmd(globalConfig *extGlobalConfig) *cobra.Command {

return cmd
}

func newExtPublishCmd(globalConfig *extGlobalConfig) *cobra.Command {
var version string

// Publish can either be called by a user to rollback an extension, or by CI to automatically publish an extension.
cmd := &cobra.Command{
Use: "publish",
Short: "Publishes the current extension to a registry",
RunE: func(cmd *cobra.Command, args []string) error {
extManager := extensions.NewExtensionsManager(global.NewGlobalStore())

if err := extManager.Publish(cmd.Context(), version); err != nil {
return err
}

return nil
},
}

cmd.Flags().StringVar(&version, "version", "", "the version to publish")
cmd.MarkFlagRequired("version")

return cmd
}
21 changes: 11 additions & 10 deletions internal/extensions/downloader.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ package extensions

import (
"context"
"errors"
"fmt"
"io"
"log"
"net/http"
"os"
"time"
Expand Down Expand Up @@ -42,29 +42,30 @@ func (d *gitHubReleaseDownloader) Download(ctx context.Context, dest string) err
return err
}

var bearer string
if accessToken := os.Getenv("SHUTTLE_EXTENSIONS_GITHUB_ACCESS_TOKEN"); accessToken != "" {
bearer = accessToken
} else if accessToken := os.Getenv("GITHUB_ACCESS_TOKEN"); accessToken != "" {
bearer = accessToken
bearer, err := getGithubToken()
if err != nil {
return err
}

if bearer == "" {
return errors.New("failed to find a valid authorization token for github. Please make sure you're logged into github-cli (gh), or have followed the setup documentation")
}
req.Header.Add("Authorization", fmt.Sprintf("token %s", bearer))
req.Header.Add("Accept", "application/octet-stream")

req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", bearer))
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()

if err := os.RemoveAll(dest); err != nil {
log.Printf("failed to remove extension before downloading new: %s, please try again", err.Error())
}

extensionBinary, err := os.Create(dest)
if err != nil {
return err
}
defer extensionBinary.Close()
extensionBinary.Chmod(0o755)

if _, err := io.Copy(extensionBinary, resp.Body); err != nil {
return err
Expand Down
19 changes: 18 additions & 1 deletion internal/extensions/extension.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ func (e *Extension) Ensure(ctx context.Context) error {

binaryPath := path.Join(extensionsCachePath, binaryName)
if exists(binaryPath) {
return nil
// TODO: do a checksum chck
//return nil
}

downloadLink := e.getRemoteBinaryDownloadLink()
Expand All @@ -55,10 +56,26 @@ func (e *Extension) Ensure(ctx context.Context) error {
return nil
}

func (e *Extension) Name() string {
return e.remote.Name
}

func (e *Extension) Version() string {
return e.remote.Version
}

func (e *Extension) Description() string {
return e.remote.Description
}

func (e *Extension) getExtensionBinaryName() string {
return e.remote.Name
}

func (e *Extension) FullPath() string {
return path.Join(getExtensionsCachePath(e.globalStore), e.Name())
}

func (e *Extension) getRemoteBinaryDownloadLink() *registryExtensionDownloadLink {
for _, download := range e.remote.DownloadUrls {
if download.Os == e.os &&
Expand Down
44 changes: 44 additions & 0 deletions internal/extensions/extension_source.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package extensions

import (
"context"
"fmt"
"os"

"gopkg.in/yaml.v2"
)

type shuttleExtensionsRegistry struct {
GitHub *string `json:"github" yaml:"github"`
}

type shuttleExtensionProviderGitHubRelease struct {
Owner string `json:"owner" yaml:"owner"`
Repo string `json:"repo" yaml:"repo"`
}

type shuttleExtensionsProvider struct {
GitHubRelease *shuttleExtensionProviderGitHubRelease `json:"github-release" yaml:"github-release"`
}

type shuttleExtensionsFile struct {
Name string `json:"name" yaml:"name"`
Description string `json:"description" yaml:"description"`

Provider shuttleExtensionsProvider `json:"provider" yaml:"provider"`
Registry shuttleExtensionsRegistry `json:"registry" yaml:"registry"`
}

func getExtensionsFile(_ context.Context) (*shuttleExtensionsFile, error) {
templateFileContent, err := os.ReadFile("shuttle.template.yaml")
if err != nil {
return nil, fmt.Errorf("failed to find shuttle.template.yaml: %w", err)
}

var templateFile shuttleExtensionsFile
if err := yaml.Unmarshal(templateFileContent, &templateFile); err != nil {
return nil, fmt.Errorf("failed to parse shuttle.template.yaml: %w", err)
}

return &templateFile, nil
}
45 changes: 43 additions & 2 deletions internal/extensions/extensions.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,30 @@ func (e *ExtensionsManager) Init(ctx context.Context) error {

// GetAll will return all known and installed extensions
func (e *ExtensionsManager) GetAll(ctx context.Context) ([]Extension, error) {
return nil, nil
registry := getRegistryPath(e.globalStore)

index := newRegistryIndex(registry)

registryExtensions, err := index.getExtensions(ctx)
if err != nil {
return nil, fmt.Errorf("failed to install extensions, could not get extensions from index: %w", err)
}

extensions := make([]Extension, 0)
for _, registryExtension := range registryExtensions {
registryExtension := registryExtension

extension, err := newExtensionFromRegistry(e.globalStore, &registryExtension)
if err != nil {
return nil, err
}

if extension != nil {
extensions = append(extensions, *extension)
}
}

return extensions, nil
}

// Install will ensure that all known extensions are installed and ready for use
Expand Down Expand Up @@ -53,7 +76,7 @@ func (e *ExtensionsManager) Install(ctx context.Context) error {

// Update will fetch the latest extensions from a registry and install them afterwards so that they're ready for use
func (e *ExtensionsManager) Update(ctx context.Context, registry string) error {
reg, err := NewRegistry(registry, e.globalStore)
reg, err := NewRegistryFromCombined(registry, e.globalStore)
if err != nil {
return fmt.Errorf("failed to update extensions: %w", err)
}
Expand All @@ -68,3 +91,21 @@ func (e *ExtensionsManager) Update(ctx context.Context, registry string) error {

return nil
}

func (e *ExtensionsManager) Publish(ctx context.Context, version string) error {
extensionsFile, err := getExtensionsFile(ctx)
if err != nil {
return err
}

registry, err := newGitHubRegistry()
if err != nil {
return err
}

if err := registry.Publish(ctx, extensionsFile, version); err != nil {
return err
}

return nil
}
5 changes: 5 additions & 0 deletions internal/extensions/git_registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ type gitRegistry struct {
globalStore *global.GlobalStore
}

// Publish isn't implemented yet for gitRegistry
func (*gitRegistry) Publish(ctx context.Context, extFile *shuttleExtensionsFile, version string) error {
panic("unimplemented")
}

func (*gitRegistry) Get(ctx context.Context) error {
panic("unimplemented")
}
Expand Down
Loading

0 comments on commit 42ee36f

Please sign in to comment.