// Package cli provides the interface for the command-line. package cli import ( "errors" "fmt" "os" "os/exec" "path" "coopcloud.tech/abra/cli/app" "coopcloud.tech/abra/cli/catalogue" "coopcloud.tech/abra/cli/internal" "coopcloud.tech/abra/cli/recipe" "coopcloud.tech/abra/cli/server" "coopcloud.tech/abra/pkg/autocomplete" cataloguePkg "coopcloud.tech/abra/pkg/catalogue" "coopcloud.tech/abra/pkg/config" "coopcloud.tech/abra/pkg/web" "github.com/sirupsen/logrus" "github.com/urfave/cli" ) // AutoCompleteCommand helps people set up auto-complete in their shells var AutoCompleteCommand = cli.Command{ Name: "autocomplete", Aliases: []string{"ac"}, Usage: "Configure shell autocompletion (recommended)", Description: ` Set up auto-completion in your shell by downloading the relevant files and laying out what additional information must be loaded. Supported shells are as follows: bash, fish, fizsh & zsh. Example: abra autocomplete bash `, ArgsUsage: "", Flags: []cli.Flag{ internal.DebugFlag, }, Action: func(c *cli.Context) error { shellType := c.Args().First() if shellType == "" { internal.ShowSubcommandHelpAndError(c, errors.New("no shell provided")) } supportedShells := map[string]bool{ "bash": true, "zsh": true, "fizsh": true, "fish": true, } if _, ok := supportedShells[shellType]; !ok { logrus.Fatalf("%s is not a supported shell right now, sorry", shellType) } if shellType == "fizsh" { shellType = "zsh" // handled the same on the autocompletion side } autocompletionDir := path.Join(config.ABRA_DIR, "autocompletion") if err := os.Mkdir(autocompletionDir, 0764); err != nil { if !os.IsExist(err) { logrus.Fatal(err) } logrus.Debugf("%s already created", autocompletionDir) } autocompletionFile := path.Join(config.ABRA_DIR, "autocompletion", shellType) if _, err := os.Stat(autocompletionFile); err != nil && os.IsNotExist(err) { url := fmt.Sprintf("https://git.coopcloud.tech/coop-cloud/abra/raw/branch/main/scripts/autocomplete/%s", shellType) logrus.Infof("fetching %s", url) if err := web.GetFile(autocompletionFile, url); err != nil { logrus.Fatal(err) } } switch shellType { case "bash": fmt.Println(fmt.Sprintf(` # Run the following commands to install auto-completion sudo mkdir /etc/bash_completion.d/ sudo cp %s /etc/bash_completion.d/abra echo "source /etc/bash_completion.d/abra" >> ~/.bashrc # To test, run the following: "abra app " - you should see command completion! `, autocompletionFile)) case "zsh": fmt.Println(fmt.Sprintf(` # Run the following commands to install auto-completion sudo mkdir /etc/zsh/completion.d/ sudo cp %s /etc/zsh/completion.d/abra echo "PROG=abra\n_CLI_ZSH_AUTOCOMPLETE_HACK=1\nsource /etc/zsh/completion.d/abra" >> ~/.zshrc # To test, run the following: "abra app " - you should see command completion! `, autocompletionFile)) case "fish": fmt.Println(fmt.Sprintf(` # Run the following commands to install auto-completion sudo mkdir -p /etc/fish/completions sudo cp %s /etc/fish/completions/abra echo "source /etc/fish/completions/abra" >> ~/.config/fish/config.fish # To test, run the following: "abra app " - you should see command completion! `, autocompletionFile)) } return nil }, } // UpgradeCommand upgrades abra in-place. var UpgradeCommand = cli.Command{ Name: "upgrade", Aliases: []string{"u"}, Usage: "Upgrade Abra itself", Description: ` Upgrade Abra in-place with the latest stable or release candidate. Pass "-r/--rc" to install the latest release candidate. Please bear in mind that it may contain catastrophic bugs. Thank you very much for the testing efforts! `, Flags: []cli.Flag{internal.RCFlag}, Action: func(c *cli.Context) error { mainURL := "https://install.abra.coopcloud.tech" cmd := exec.Command("bash", "-c", fmt.Sprintf("wget -q -O- %s | bash", mainURL)) if internal.RC { releaseCandidateURL := "https://git.coopcloud.tech/coop-cloud/abra/raw/branch/main/scripts/installer/installer" cmd = exec.Command("bash", "-c", fmt.Sprintf("wget -q -O- %s | bash -s -- --rc", releaseCandidateURL)) } logrus.Debugf("attempting to run %s", cmd) if err := internal.RunCmd(cmd); err != nil { logrus.Fatal(err) } return nil }, } func newAbraApp(version, commit string) *cli.App { app := &cli.App{ Name: "abra", Usage: `The Co-op Cloud command-line utility belt 🎩🐇 ____ ____ _ _ / ___|___ ___ _ __ / ___| | ___ _ _ __| | | | / _ \ _____ / _ \| '_ \ | | | |/ _ \| | | |/ _' | | |__| (_) |_____| (_) | |_) | | |___| | (_) | |_| | (_| | \____\___/ \___/| .__/ \____|_|\___/ \__,_|\__,_| |_| `, Version: fmt.Sprintf("%s-%s", version, commit[:7]), Commands: []cli.Command{ app.AppCommand, server.ServerCommand, recipe.RecipeCommand, catalogue.CatalogueCommand, UpgradeCommand, AutoCompleteCommand, }, BashComplete: autocomplete.SubcommandComplete, } app.EnableBashCompletion = true app.Before = func(c *cli.Context) error { paths := []string{ config.ABRA_DIR, config.SERVERS_DIR, config.RECIPES_DIR, config.VENDOR_DIR, config.BACKUP_DIR, } for _, path := range paths { if err := os.Mkdir(path, 0764); err != nil { if !os.IsExist(err) { logrus.Fatal(err) } continue } } if err := cataloguePkg.EnsureCatalogue(); err != nil { logrus.Fatal(err) } logrus.Debugf("abra version %s, commit %s", version, commit) return nil } return app } // RunApp runs CLI abra app. func RunApp(version, commit string) { app := newAbraApp(version, commit) if err := app.Run(os.Args); err != nil { logrus.Fatal(err) } }