From aca11f09447e9a7efcd13b59e82c5b56a09c1348 Mon Sep 17 00:00:00 2001 From: Aravind Shankar Date: Wed, 22 Apr 2020 14:45:42 +0530 Subject: [PATCH] cli: spinner bug, add cli_environment in global config (#4459) close #4456 close #4436 close #4496 --- cli/cli.go | 38 ++++++++++++++++++++-------- cli/commands/actions_create.go | 4 +-- cli/commands/plugins_install.go | 6 ++++- cli/global_config.go | 27 +++++++++++++++++--- cli/plugins/plugins.go | 25 ++++++++++-------- cli/util/git.go | 13 +++++++++- scripts/cli-migrations/v2/Dockerfile | 7 +++-- 7 files changed, 89 insertions(+), 31 deletions(-) diff --git a/cli/cli.go b/cli/cli.go index 4a5126d1d69a7..b3eefbc380b51 100644 --- a/cli/cli.go +++ b/cli/cli.go @@ -351,6 +351,10 @@ func (ec *ExecutionContext) setupPlugins() error { } ec.PluginsConfig = plugins.New(base) ec.PluginsConfig.Logger = ec.Logger + ec.PluginsConfig.Repo.Logger = ec.Logger + if ec.GlobalConfig.CLIEnvironment == ServerOnDockerEnvironment { + ec.PluginsConfig.Repo.DisableCloneOrUpdate = true + } return ec.PluginsConfig.Prepare() } @@ -361,6 +365,10 @@ func (ec *ExecutionContext) setupCodegenAssetsRepo() error { return errors.Wrap(err, "cannot get absolute path") } ec.CodegenAssetsRepo = util.NewGitUtil(util.ActionsCodegenRepoURI, base, "") + ec.CodegenAssetsRepo.Logger = ec.Logger + if ec.GlobalConfig.CLIEnvironment == ServerOnDockerEnvironment { + ec.CodegenAssetsRepo.DisableCloneOrUpdate = true + } return nil } @@ -371,6 +379,10 @@ func (ec *ExecutionContext) setupInitTemplatesRepo() error { return errors.Wrap(err, "cannot get absolute path") } ec.InitTemplatesRepo = util.NewGitUtil(util.InitTemplatesRepoURI, base, "") + ec.InitTemplatesRepo.Logger = ec.Logger + if ec.GlobalConfig.CLIEnvironment == ServerOnDockerEnvironment { + ec.InitTemplatesRepo.DisableCloneOrUpdate = true + } return nil } @@ -613,10 +625,6 @@ func (ec *ExecutionContext) setVersion() { // If forceCLIVersion is set, it uses ec.Version.CLISemver version for the plugin to be installed. // Else, it installs the latest version of the plugin func (ec ExecutionContext) InstallPlugin(name string, forceCLIVersion bool) error { - prevPrefix := ec.Spinner.Prefix - ec.Spin(fmt.Sprintf("Installing plugin %s...", name)) - defer ec.Spin(prevPrefix) - var version *semver.Version if forceCLIVersion { err := ec.PluginsConfig.Repo.EnsureUpdated() @@ -625,19 +633,29 @@ func (ec ExecutionContext) InstallPlugin(name string, forceCLIVersion bool) erro } version = ec.Version.CLISemver } - err := ec.PluginsConfig.Install(name, plugins.InstallOpts{ + plugin, err := ec.PluginsConfig.GetPlugin(name, plugins.FetchOpts{ Version: version, }) if err != nil { if err != plugins.ErrIsAlreadyInstalled { - msg := fmt.Sprintf(`unable to install %s plugin. execute the following commands to continue: + return errors.Wrapf(err, "cannot fetch plugin manfiest %s", name) + } + return nil + } + if ec.Spinner.Active() { + prevPrefix := ec.Spinner.Prefix + defer ec.Spin(prevPrefix) + } + ec.Spin(fmt.Sprintf("Installing plugin %s...", name)) + defer ec.Spinner.Stop() + err = ec.PluginsConfig.Install(plugin) + if err != nil { + msg := fmt.Sprintf(`unable to install %s plugin. execute the following commands to continue: hasura plugins install %s `, name, name) - ec.Logger.Info(msg) - return errors.Wrapf(err, "cannot install plugin %s", name) - } - return nil + ec.Logger.Info(msg) + return errors.Wrapf(err, "cannot install plugin %s", name) } ec.Logger.WithField("name", name).Infoln("plugin installed") return nil diff --git a/cli/commands/actions_create.go b/cli/commands/actions_create.go index 09ab325db11d0..43c03880995db 100644 --- a/cli/commands/actions_create.go +++ b/cli/commands/actions_create.go @@ -83,8 +83,8 @@ func (o *actionsCreateOptions) run() error { // create new action o.EC.Spin("Creating the action...") - o.EC.Spinner.Stop() actionCfg := actions.New(o.EC, o.EC.MetadataDir) + o.EC.Spinner.Stop() err = actionCfg.Create(o.name, introSchema, o.deriveFrom) if err != nil { return errors.Wrap(err, "error in creating action") @@ -93,8 +93,6 @@ func (o *actionsCreateOptions) run() error { if err != nil { return errors.Wrap(err, "error in applying metadata") } - - o.EC.Spinner.Stop() o.EC.Logger.WithField("name", o.name).Infoln("action created") // if codegen config not present, skip codegen diff --git a/cli/commands/plugins_install.go b/cli/commands/plugins_install.go index 3f8c3eae75f97..45676dcb70046 100644 --- a/cli/commands/plugins_install.go +++ b/cli/commands/plugins_install.go @@ -77,8 +77,12 @@ type PluginInstallOptions struct { } func (o *PluginInstallOptions) Run() error { - return o.EC.PluginsConfig.Install(o.Name, plugins.InstallOpts{ + plugin, err := ec.PluginsConfig.GetPlugin(o.Name, plugins.FetchOpts{ ManifestFile: o.ManifestFile, Version: o.Version.Version, }) + if err != nil { + return err + } + return o.EC.PluginsConfig.Install(plugin) } diff --git a/cli/global_config.go b/cli/global_config.go index d88327975da90..5ca72ae9f7738 100644 --- a/cli/global_config.go +++ b/cli/global_config.go @@ -14,6 +14,16 @@ import ( "github.com/spf13/viper" ) +// Environment defines the environment the CLI is running +type Environment string + +const ( + // DefaultEnvironment - CLI running in default mode + DefaultEnvironment Environment = "default" + // ServerOnDockerEnvironment - CLI running in cli-migrations image + ServerOnDockerEnvironment = "server-on-docker" +) + // GlobalConfig is the configuration object stored in the GlobalConfigFile. type GlobalConfig struct { // UUID used for telemetry, generated on first run. @@ -24,12 +34,16 @@ type GlobalConfig struct { // Indicates whether update notifications should be shown or not ShowUpdateNotification bool `json:"show_update_notification"` + + // CLIEnvironment defines the environment the CLI is running + CLIEnvironment Environment `json:"cli_environment"` } type rawGlobalConfig struct { - UUID *string `json:"uuid"` - EnableTelemetry *bool `json:"enable_telemetry"` - ShowUpdateNotification *bool `json:"show_update_notification"` + UUID *string `json:"uuid"` + EnableTelemetry *bool `json:"enable_telemetry"` + ShowUpdateNotification *bool `json:"show_update_notification"` + CLIEnvironment Environment `json:"cli_environment"` logger *logrus.Logger shoudlWrite bool @@ -73,6 +87,10 @@ func (c *rawGlobalConfig) validateKeys() error { c.shoudlWrite = true } + if c.CLIEnvironment == "" { + c.CLIEnvironment = DefaultEnvironment + } + return nil } @@ -181,6 +199,7 @@ func (ec *ExecutionContext) readGlobalConfig() error { v.AutomaticEnv() v.SetConfigName("config") v.AddConfigPath(ec.GlobalConfigDir) + v.SetDefault("cli_environment", DefaultEnvironment) err := v.ReadInConfig() if err != nil { return errors.Wrap(err, "cannot read global config from file/env") @@ -191,6 +210,7 @@ func (ec *ExecutionContext) readGlobalConfig() error { UUID: v.GetString("uuid"), EnableTelemetry: v.GetBool("enable_telemetry"), ShowUpdateNotification: v.GetBool("show_update_notification"), + CLIEnvironment: Environment(v.GetString("cli_environment")), } } else { ec.Logger.Debugf("global config is pre-set to %#v", ec.GlobalConfig) @@ -198,6 +218,7 @@ func (ec *ExecutionContext) readGlobalConfig() error { ec.Logger.Debugf("global config: uuid: %v", ec.GlobalConfig.UUID) ec.Logger.Debugf("global config: enableTelemetry: %v", ec.GlobalConfig.EnableTelemetry) ec.Logger.Debugf("global config: showUpdateNotification: %v", ec.GlobalConfig.ShowUpdateNotification) + ec.Logger.Debugf("global config: cliEnvironment: %v", ec.GlobalConfig.CLIEnvironment) // set if telemetry can be beamed or not ec.Telemetry.CanBeam = ec.GlobalConfig.EnableTelemetry diff --git a/cli/plugins/plugins.go b/cli/plugins/plugins.go index 409929e5dc76d..8112b7377e6f2 100644 --- a/cli/plugins/plugins.go +++ b/cli/plugins/plugins.go @@ -48,8 +48,8 @@ type Config struct { Logger *logrus.Logger } -// InstallOpts - options available during plugin install -type InstallOpts struct { +// FetchOpts - options available during fetching plugin manifest +type FetchOpts struct { ManifestFile string Version *semver.Version @@ -96,7 +96,7 @@ func (c *Config) ListPlugins() (Plugins, error) { return c.LoadPluginListFromFS(c.Paths.IndexPluginsPath()) } -func (c *Config) Install(pluginName string, opts InstallOpts) error { +func (c *Config) GetPlugin(pluginName string, opts FetchOpts) (Plugin, error) { var plugin Plugin var err error if opts.ManifestFile == "" { @@ -104,31 +104,31 @@ func (c *Config) Install(pluginName string, opts InstallOpts) error { ps, err := c.LoadPluginByName(pluginName) if err != nil { if os.IsNotExist(err) { - return errors.Errorf("plugin %q does not exist in the plugin index", pluginName) + return plugin, errors.Errorf("plugin %q does not exist in the plugin index", pluginName) } - return errors.Wrapf(err, "failed to load plugin %q from the index", pluginName) + return plugin, errors.Wrapf(err, "failed to load plugin %q from the index", pluginName) } // Load the installed manifest pluginReceipt, err := c.LoadManifest(c.Paths.PluginInstallReceiptPath(pluginName)) if err != nil && !os.IsNotExist(err) { - return errors.Wrap(err, "failed to look up plugin receipt") + return plugin, errors.Wrap(err, "failed to look up plugin receipt") } if opts.Version != nil { if pluginReceipt.Version == opts.Version.Original() { - return ErrIsAlreadyInstalled + return plugin, ErrIsAlreadyInstalled } // check if version is available ver := ps.Index.Search(opts.Version) if ver != nil { plugin = ps.Versions[ver] } else { - return ErrVersionNotAvailable + return plugin, ErrVersionNotAvailable } } else { if err == nil { - return ErrIsAlreadyInstalled + return plugin, ErrIsAlreadyInstalled } // get the latest version latestVersion := ps.Index[len(ps.Index)-1] @@ -137,13 +137,16 @@ func (c *Config) Install(pluginName string, opts InstallOpts) error { } else { plugin, err = c.ReadPluginFromFile(opts.ManifestFile) if err != nil { - return errors.Wrap(err, "failed to load plugin manifest from file") + return plugin, errors.Wrap(err, "failed to load plugin manifest from file") } if plugin.Name != pluginName { - return fmt.Errorf("plugin name %s doesn't match with plugin in the manifest file %s", pluginName, opts.ManifestFile) + return plugin, fmt.Errorf("plugin name %s doesn't match with plugin in the manifest file %s", pluginName, opts.ManifestFile) } } + return plugin, nil +} +func (c *Config) Install(plugin Plugin) error { // Find available installation platform platform, ok, err := MatchPlatform(plugin.Platforms) if err != nil { diff --git a/cli/util/git.go b/cli/util/git.go index c805db8ca91ab..7fa6b557ca866 100644 --- a/cli/util/git.go +++ b/cli/util/git.go @@ -4,6 +4,7 @@ import ( "os" "path/filepath" + "github.com/sirupsen/logrus" "gopkg.in/src-d/go-git.v4" "gopkg.in/src-d/go-git.v4/config" "gopkg.in/src-d/go-git.v4/plumbing" @@ -27,7 +28,9 @@ type GitUtil struct { Path string // Optional - ReferenceName plumbing.ReferenceName + ReferenceName plumbing.ReferenceName + DisableCloneOrUpdate bool + Logger *logrus.Logger } func NewGitUtil(uri string, path string, refName string) *GitUtil { @@ -43,6 +46,10 @@ func NewGitUtil(uri string, path string, refName string) *GitUtil { } func (g *GitUtil) EnsureCloned() error { + if g.DisableCloneOrUpdate { + g.Logger.Debugf("skipping clone/update for %s", g.URI) + return nil + } if ok, err := g.IsGitCloned(); err != nil { return err } else if !ok { @@ -67,6 +74,10 @@ func (g *GitUtil) IsGitCloned() (bool, error) { // EnsureUpdated will ensure the destination path exists and is up to date. func (g *GitUtil) EnsureUpdated() error { + if g.DisableCloneOrUpdate { + g.Logger.Debugf("skipping clone/update for %s", g.URI) + return nil + } if err := g.EnsureCloned(); err != nil { return err } diff --git a/scripts/cli-migrations/v2/Dockerfile b/scripts/cli-migrations/v2/Dockerfile index 163758a7b14bf..cd29bbeee66f9 100644 --- a/scripts/cli-migrations/v2/Dockerfile +++ b/scripts/cli-migrations/v2/Dockerfile @@ -10,8 +10,7 @@ RUN busybox dpkg-deb -x libstdc++6*.deb / \ && rm libstdc++6*.deb # set an env var to let the cli know that -# it is running in server environment -ENV HASURA_GRAPHQL_CLI_ENVIRONMENT=server-on-docker +# update notification is disabled ENV HASURA_GRAPHQL_SHOW_UPDATE_NOTIFICATION=false COPY docker-entrypoint.sh /bin/ @@ -21,6 +20,10 @@ RUN chmod +x /bin/hasura-cli \ && hasura-cli plugins install cli-ext --manifest-file /tmp/manifest.yaml \ && rm /tmp/manifest.yaml +# set an env var to let the cli know that +# it is running in server environment +ENV HASURA_GRAPHQL_CLI_ENVIRONMENT=server-on-docker + ENTRYPOINT ["docker-entrypoint.sh"] CMD ["graphql-engine", "serve"]