diff --git a/cli/app/backup.go b/cli/app/backup.go index d784c372..df49a145 100644 --- a/cli/app/backup.go +++ b/cli/app/backup.go @@ -78,7 +78,7 @@ var AppBackupDownloadCommand = &cobra.Command{ Run: func(cmd *cobra.Command, args []string) { app := internal.ValidateApp(args) - if err := app.Recipe.Ensure(internal.Chaos, internal.Offline); err != nil { + if err := app.Recipe.Ensure(internal.GetEnsureContext()); err != nil { log.Fatal(err) } @@ -143,7 +143,7 @@ var AppBackupCreateCommand = &cobra.Command{ Run: func(cmd *cobra.Command, args []string) { app := internal.ValidateApp(args) - if err := app.Recipe.Ensure(internal.Chaos, internal.Offline); err != nil { + if err := app.Recipe.Ensure(internal.GetEnsureContext()); err != nil { log.Fatal(err) } diff --git a/cli/app/check.go b/cli/app/check.go index ac042f0a..1c42c626 100644 --- a/cli/app/check.go +++ b/cli/app/check.go @@ -36,7 +36,7 @@ ${FOO:} syntax). "check" does not confirm or deny this for you.`, Run: func(cmd *cobra.Command, args []string) { app := internal.ValidateApp(args) - if err := app.Recipe.Ensure(internal.Chaos, internal.Offline); err != nil { + if err := app.Recipe.Ensure(internal.GetEnsureContext()); err != nil { log.Fatal(err) } diff --git a/cli/app/cmd.go b/cli/app/cmd.go index 1cb0d663..53714548 100644 --- a/cli/app/cmd.go +++ b/cli/app/cmd.go @@ -92,7 +92,7 @@ does not).`, Run: func(cmd *cobra.Command, args []string) { app := internal.ValidateApp(args) - if err := app.Recipe.Ensure(internal.Chaos, internal.Offline); err != nil { + if err := app.Recipe.Ensure(internal.GetEnsureContext()); err != nil { log.Fatal(err) } @@ -199,7 +199,7 @@ var AppCmdListCommand = &cobra.Command{ Run: func(cmd *cobra.Command, args []string) { app := internal.ValidateApp(args) - if err := app.Recipe.Ensure(internal.Chaos, internal.Offline); err != nil { + if err := app.Recipe.Ensure(internal.GetEnsureContext()); err != nil { log.Fatal(err) } diff --git a/cli/app/cp.go b/cli/app/cp.go index 4304e63b..f8614f54 100644 --- a/cli/app/cp.go +++ b/cli/app/cp.go @@ -49,7 +49,7 @@ var AppCpCommand = &cobra.Command{ Run: func(cmd *cobra.Command, args []string) { app := internal.ValidateApp(args) - if err := app.Recipe.Ensure(internal.Chaos, internal.Offline); err != nil { + if err := app.Recipe.Ensure(internal.GetEnsureContext()); err != nil { log.Fatal(err) } diff --git a/cli/app/deploy.go b/cli/app/deploy.go index c9e4e9a3..c9ba68b9 100644 --- a/cli/app/deploy.go +++ b/cli/app/deploy.go @@ -2,10 +2,10 @@ package app import ( "context" - "errors" "fmt" "coopcloud.tech/abra/cli/internal" + "coopcloud.tech/abra/pkg/app" "coopcloud.tech/abra/pkg/autocomplete" "coopcloud.tech/abra/pkg/config" "coopcloud.tech/abra/pkg/envfile" @@ -18,6 +18,7 @@ import ( "coopcloud.tech/abra/pkg/lint" "coopcloud.tech/abra/pkg/log" "coopcloud.tech/abra/pkg/upstream/stack" + dockerClient "github.com/docker/docker/client" "github.com/spf13/cobra" ) @@ -27,9 +28,9 @@ var AppDeployCommand = &cobra.Command{ Short: "Deploy an app", Long: `Deploy an app. -This command supports chaos operations. Use "--chaos/-c" to deploy your recipe -checkout as-is. Recipe commit hashes are also supported values for "[version]". -Please note, "upgrade"/"rollback" do not support chaos operations.`, +This command supports chaos operations. Use "--chaos/-C" to deploy your recipe +checkout as-is. Recipe commit hashes are also supported as values for +"[version]". Please note, "upgrade"/"rollback" do not support chaos operations.`, Example: ` # standard deployment abra app deploy 1312.net @@ -61,29 +62,20 @@ Please note, "upgrade"/"rollback" do not support chaos operations.`, } }, Run: func(cmd *cobra.Command, args []string) { - var warnMessages []string + var ( + deployWarnMessages []string + toDeployVersion string + isChaosCommit bool + toDeployChaosVersion = config.CHAOS_DEFAULT + ) app := internal.ValidateApp(args) - stackName := app.StackName() - ok, err := validateChaosXORVersion(args) - if !ok { - log.Fatalf(err.Error()) + if err := validateArgsAndFlags(args); err != nil { + log.Fatal(err) } - specificVersion := getSpecifiedVersion(args) - - if specificVersion != "" { - log.Debugf("overriding env file version (%s) with %s", app.Recipe.Version, specificVersion) - app.Recipe.Version = specificVersion - } - - if specificVersion == "" && app.Recipe.Version != "" && !internal.Chaos { - log.Debugf("retrieved %s as version from env file", app.Recipe.Version) - specificVersion = app.Recipe.Version - } - - if err := app.Recipe.Ensure(internal.Chaos, internal.Offline); err != nil { + if err := app.Recipe.Ensure(internal.GetEnsureContext()); err != nil { log.Fatal(err) } @@ -91,96 +83,66 @@ Please note, "upgrade"/"rollback" do not support chaos operations.`, log.Fatal(err) } - log.Debugf("checking whether %s is already deployed", stackName) - cl, err := client.New(app.Server) if err != nil { log.Fatal(err) } - deployMeta, err := stack.IsDeployed(context.Background(), cl, stackName) + log.Debugf("checking whether %s is already deployed", app.StackName()) + + deployMeta, err := stack.IsDeployed(context.Background(), cl, app.StackName()) if err != nil { log.Fatal(err) } - // NOTE(d1): handles "[version] as git hash" use case - var isChaosCommit bool - - // NOTE(d1): check out specific version before dealing with secrets. This - // is because we need to deal with GetComposeFiles under the hood and these - // files change from version to version which therefore affects which - // secrets might be generated - toDeployVersion := deployMeta.Version - if specificVersion != "" { - toDeployVersion = specificVersion - log.Debugf("choosing %s as version to deploy", toDeployVersion) - - var err error - isChaosCommit, err = app.Recipe.EnsureVersion(toDeployVersion) - if err != nil { - log.Fatal(err) - } - - if isChaosCommit { - log.Debugf("assuming '%s' is a chaos commit", toDeployVersion) - internal.Chaos = true - } - } - - secStats, err := secret.PollSecretsStatus(cl, app) - if err != nil { - log.Fatal(err) - } - - for _, secStat := range secStats { - if !secStat.CreatedOnRemote { - log.Fatalf("unable to deploy, secrets not generated (%s)?", secStat.LocalName) - } - } - if deployMeta.IsDeployed && !(internal.Force || internal.Chaos) { log.Fatalf("%s is already deployed", app.Name) } - if !internal.Chaos && specificVersion == "" { - versions, err := app.Recipe.Tags() - if err != nil { - log.Fatal(err) - } + if len(args) == 2 && args[1] != "" { + toDeployVersion = args[1] + } - if len(versions) > 0 && !internal.Chaos { - toDeployVersion = versions[len(versions)-1] - log.Debugf("choosing %s as version to deploy", toDeployVersion) - if _, err := app.Recipe.EnsureVersion(toDeployVersion); err != nil { - log.Fatal(err) - } - } else { - head, err := app.Recipe.Head() - if err != nil { - log.Fatal(err) - } - toDeployVersion = formatter.SmallSHA(head.String()) + if !deployMeta.IsDeployed && + toDeployVersion == "" && + app.Recipe.EnvVersion != "" && !internal.IgnoreEnvVersion { + log.Debugf("new deployment, choosing .env version: %s", app.Recipe.EnvVersion) + toDeployVersion = app.Recipe.EnvVersion + } + + if !internal.Chaos && toDeployVersion == "" { + if err := getLatestVersionOrCommit(app, &toDeployVersion); err != nil { + log.Fatal(err) } } - toDeployChaosVersion := config.CHAOS_DEFAULT if internal.Chaos { - if isChaosCommit { - toDeployChaosVersion = specificVersion - versionLabelLocal, err := app.Recipe.GetVersionLabelLocal() - if err != nil { - log.Fatal(err) - } - toDeployVersion = versionLabelLocal - } else { - var err error - toDeployChaosVersion, err = app.Recipe.ChaosVersion() - if err != nil { - log.Fatal(err) - } + if err := getChaosVersion(app, &toDeployVersion, &toDeployChaosVersion); err != nil { + log.Fatal(err) } } + isChaosCommit, err = app.Recipe.EnsureVersion(toDeployVersion) + if err != nil { + log.Fatal(err) + } + + if isChaosCommit { + log.Debugf("assuming chaos commit: %s", toDeployVersion) + + internal.Chaos = true + toDeployChaosVersion = toDeployVersion + + toDeployVersion, err = app.Recipe.GetVersionLabelLocal() + if err != nil { + log.Fatal(err) + } + } + + if err := validateSecrets(cl, app); err != nil { + log.Fatal(err) + } + abraShEnv, err := envfile.ReadAbraShEnvVars(app.Recipe.AbraShPath) if err != nil { log.Fatal(err) @@ -194,6 +156,7 @@ Please note, "upgrade"/"rollback" do not support chaos operations.`, log.Fatal(err) } + stackName := app.StackName() deployOpts := stack.Deploy{ Composefiles: composeFiles, Namespace: stackName, @@ -224,23 +187,22 @@ Please note, "upgrade"/"rollback" do not support chaos operations.`, for _, envVar := range envVars { if !envVar.Present { - warnMessages = append(warnMessages, - fmt.Sprintf("env var %s missing from %s.env, present in recipe .env.sample", envVar.Name, app.Domain), + deployWarnMessages = append(deployWarnMessages, + fmt.Sprintf("%s missing from %s.env", envVar.Name, app.Domain), ) } } if !internal.NoDomainChecks { - domainName, ok := app.Env["DOMAIN"] - if ok { + if domainName, ok := app.Env["DOMAIN"]; ok { if _, err = dns.EnsureDomainsResolveSameIPv4(domainName, app.Server); err != nil { log.Fatal(err) } } else { - log.Debug("skipping domain checks as no DOMAIN=... configured for app") + log.Debug("skipping domain checks, no DOMAIN=... configured") } } else { - log.Debug("skipping domain checks as requested") + log.Debug("skipping domain checks") } deployedVersion := config.NO_VERSION_DEFAULT @@ -248,13 +210,20 @@ Please note, "upgrade"/"rollback" do not support chaos operations.`, deployedVersion = deployMeta.Version } + toWriteVersion := toDeployVersion + if internal.Chaos || isChaosCommit { + toWriteVersion = toDeployChaosVersion + } + if err := internal.DeployOverview( app, - warnMessages, + deployWarnMessages, deployedVersion, deployMeta.ChaosVersion, toDeployVersion, - toDeployChaosVersion); err != nil { + toDeployChaosVersion, + toWriteVersion, + ); err != nil { log.Fatal(err) } @@ -262,7 +231,8 @@ Please note, "upgrade"/"rollback" do not support chaos operations.`, if err != nil { log.Fatal(err) } - log.Debugf("set waiting timeout to %d s", stack.WaitTimeout) + + log.Debugf("set waiting timeout to %d second(s)", stack.WaitTimeout) if err := stack.RunDeploy(cl, deployOpts, compose, app.Name, internal.DontWaitConverge); err != nil { log.Fatal(err) @@ -276,30 +246,77 @@ Please note, "upgrade"/"rollback" do not support chaos operations.`, } } - app.Recipe.Version = toDeployVersion - if toDeployChaosVersion != config.CHAOS_DEFAULT { - app.Recipe.Version = toDeployChaosVersion - } - if err := app.WriteRecipeVersion(app.Recipe.Version, false); err != nil { - log.Fatalf("writing new recipe version in env file: %s", err) + if err := app.WriteRecipeVersion(toWriteVersion, false); err != nil { + log.Fatalf("writing recipe version failed: %s", err) } }, } -// validateChaosXORVersion xor checks version/chaos mode -func validateChaosXORVersion(args []string) (bool, error) { - if getSpecifiedVersion(args) != "" && internal.Chaos { - return false, errors.New("cannot use [version] and --chaos together") +func getChaosVersion(app app.App, toDeployVersion, toDeployChaosVersion *string) error { + var err error + *toDeployChaosVersion, err = app.Recipe.ChaosVersion() + if err != nil { + return err } - return true, nil + + *toDeployVersion, err = app.Recipe.GetVersionLabelLocal() + if err != nil { + return err + } + + return nil } -// getSpecifiedVersion retrieves the specific version if available -func getSpecifiedVersion(args []string) string { - if len(args) >= 2 { - return args[1] +func getLatestVersionOrCommit(app app.App, toDeployVersion *string) error { + versions, err := app.Recipe.Tags() + if err != nil { + return err } - return "" + + if len(versions) > 0 && !internal.Chaos { + *toDeployVersion = versions[len(versions)-1] + + log.Debugf("choosing %s as version to deploy", *toDeployVersion) + + if _, err := app.Recipe.EnsureVersion(*toDeployVersion); err != nil { + return err + } + + return nil + } + + head, err := app.Recipe.Head() + if err != nil { + return err + } + + *toDeployVersion = formatter.SmallSHA(head.String()) + + return nil +} + +// validateArgsAndFlags ensures compatible args/flags. +func validateArgsAndFlags(args []string) error { + if len(args) == 2 && args[1] != "" && internal.Chaos { + return fmt.Errorf("cannot use [version] and --chaos together") + } + + return nil +} + +func validateSecrets(cl *dockerClient.Client, app app.App) error { + secStats, err := secret.PollSecretsStatus(cl, app) + if err != nil { + return err + } + + for _, secStat := range secStats { + if !secStat.CreatedOnRemote { + return fmt.Errorf("secret not generated: %s", secStat.LocalName) + } + } + + return nil } func init() { @@ -332,6 +349,6 @@ func init() { "no-converge-checks", "c", false, - "do not wait for converge logic checks", + "disable converge logic checks", ) } diff --git a/cli/app/deploy_test.go b/cli/app/deploy_test.go deleted file mode 100644 index 25039a3d..00000000 --- a/cli/app/deploy_test.go +++ /dev/null @@ -1,57 +0,0 @@ -package app - -import ( - "testing" - - "coopcloud.tech/abra/cli/internal" -) - -func TestGetSpecificVersion(t *testing.T) { - tests := []struct { - input []string - expectedOutput string - }{ - // No specified version when command has one or less args - {[]string{}, ""}, - {[]string{"arg0"}, ""}, - // Second in arg (index-1) is the specified result when command has more than 1 args - {[]string{"arg0", "arg1"}, "arg1"}, - {[]string{"arg0", "arg1", "arg2"}, "arg1"}, - } - - for _, test := range tests { - if test.expectedOutput != getSpecifiedVersion(test.input) { - t.Fatalf("result for %s should be %s", test.input, test.expectedOutput) - } - } -} - -func TestValidateChaosXORVersion(t *testing.T) { - tests := []struct { - input []string - isChaos bool - expectedResult bool - }{ - // Chaos = true, Specified Version absent - {[]string{}, true, true}, - // Chaos = false, Specified Version absent - {[]string{}, false, true}, - // Chaos = true, Specified Version present - {[]string{"arg0", "arg1"}, true, false}, - // Chaos = false, Specified Version present - {[]string{"arg0", "arg1", "arg2"}, false, true}, - } - - for _, test := range tests { - internal.Chaos = test.isChaos - res, _ := validateChaosXORVersion(test.input) - if res != test.expectedResult { - t.Fatalf( - "When args are %s and Chaos mode is %t result needs to be %t", - test.input, - test.isChaos, - test.expectedResult, - ) - } - } -} diff --git a/cli/app/labels.go b/cli/app/labels.go index e452b73d..6b1d110d 100644 --- a/cli/app/labels.go +++ b/cli/app/labels.go @@ -34,7 +34,7 @@ var AppLabelsCommand = &cobra.Command{ Run: func(cmd *cobra.Command, args []string) { app := internal.ValidateApp(args) - if err := app.Recipe.Ensure(internal.Chaos, internal.Offline); err != nil { + if err := app.Recipe.Ensure(internal.GetEnsureContext()); err != nil { log.Fatal(err) } diff --git a/cli/app/new.go b/cli/app/new.go index ceadc32c..2d9c2566 100644 --- a/cli/app/new.go +++ b/cli/app/new.go @@ -212,7 +212,7 @@ var AppNewCommand = &cobra.Command{ } if err := app.WriteRecipeVersion(recipeVersion, false); err != nil { - log.Fatalf("writing new recipe version in env file: %s", err) + log.Fatalf("writing recipe version failed: %s", err) } }, } diff --git a/cli/app/ps.go b/cli/app/ps.go index 06816518..ec28ebe5 100644 --- a/cli/app/ps.go +++ b/cli/app/ps.go @@ -36,7 +36,7 @@ var AppPsCommand = &cobra.Command{ Run: func(cmd *cobra.Command, args []string) { app := internal.ValidateApp(args) - if err := app.Recipe.Ensure(internal.Chaos, internal.Offline); err != nil { + if err := app.Recipe.Ensure(internal.GetEnsureContext()); err != nil { log.Fatal(err) } diff --git a/cli/app/restart.go b/cli/app/restart.go index c6b20f03..d951ea67 100644 --- a/cli/app/restart.go +++ b/cli/app/restart.go @@ -48,7 +48,7 @@ Pass "--all-services/-a" to restart all services.`, Run: func(cmd *cobra.Command, args []string) { app := internal.ValidateApp(args) - if err := app.Recipe.Ensure(false, false); err != nil { + if err := app.Recipe.Ensure(internal.GetEnsureContext()); err != nil { log.Fatal(err) } diff --git a/cli/app/restore.go b/cli/app/restore.go index da5bfebf..bb584af9 100644 --- a/cli/app/restore.go +++ b/cli/app/restore.go @@ -28,7 +28,7 @@ Some restore scenarios may require service / app restarts.`, Run: func(cmd *cobra.Command, args []string) { app := internal.ValidateApp(args) - if err := app.Recipe.Ensure(internal.Chaos, internal.Offline); err != nil { + if err := app.Recipe.Ensure(internal.GetEnsureContext()); err != nil { log.Fatal(err) } diff --git a/cli/app/rollback.go b/cli/app/rollback.go index 2b223610..78b1d6f4 100644 --- a/cli/app/rollback.go +++ b/cli/app/rollback.go @@ -1,13 +1,14 @@ package app import ( - "context" "fmt" + "coopcloud.tech/abra/pkg/app" appPkg "coopcloud.tech/abra/pkg/app" "coopcloud.tech/abra/pkg/autocomplete" "coopcloud.tech/abra/pkg/config" "coopcloud.tech/abra/pkg/envfile" + "coopcloud.tech/abra/pkg/formatter" "coopcloud.tech/abra/pkg/lint" stack "coopcloud.tech/abra/pkg/upstream/stack" "coopcloud.tech/tagcmp" @@ -25,11 +26,18 @@ var AppRollbackCommand = &cobra.Command{ Short: "Roll an app back to a previous version", Long: `This command rolls an app back to a previous version. -Unlike "deploy", chaos operations are not supported here. Only recipe versions -are supported values for "[]". +Unlike "abra app deploy", chaos operations are not supported here. Only recipe +versions are supported values for "[version]". -A rollback can be destructive, please ensure you have a copy of your app data -beforehand.`, +It is possible to "--force/-f" an downgrade if you want to re-deploy a specific +version. + +Only the deployed version is consulted when trying to determine what downgrades +are available. The live deployment version is the "source of truth" in this +case. The stored .env version is not consulted. + +A downgrade can be destructive, please ensure you have a copy of your app data +beforehand. See "abra app backup" for more.`, Example: ` # standard rollback abra app rollback 1312.net @@ -55,26 +63,15 @@ beforehand.`, } }, Run: func(cmd *cobra.Command, args []string) { - var warnMessages []string + var ( + downgradeWarnMessages []string + chosenDowngrade string + availableDowngrades []string + ) app := internal.ValidateApp(args) - stackName := app.StackName() - var specificVersion string - if len(args) == 2 { - specificVersion = args[1] - } - - if specificVersion != "" { - log.Debugf("overriding env file version (%s) with %s", app.Recipe.Version, specificVersion) - app.Recipe.Version = specificVersion - } - - if err := app.Recipe.Ensure(internal.Chaos, internal.Offline); err != nil { - log.Fatal(err) - } - - if err := lint.LintForErrors(app.Recipe); err != nil { + if err := app.Recipe.Ensure(internal.GetEnsureContext()); err != nil { log.Fatal(err) } @@ -83,15 +80,13 @@ beforehand.`, log.Fatal(err) } - log.Debugf("checking whether %s is already deployed", stackName) - - deployMeta, err := stack.IsDeployed(context.Background(), cl, stackName) + deployMeta, err := ensureDeployed(cl, app) if err != nil { log.Fatal(err) } - if !deployMeta.IsDeployed { - log.Fatalf("%s is not deployed?", app.Name) + if err := lint.LintForErrors(app.Recipe); err != nil { + log.Fatal(err) } versions, err := app.Recipe.Tags() @@ -99,84 +94,56 @@ beforehand.`, log.Fatal(err) } - var availableDowngrades []string - if deployMeta.Version == "unknown" { + // NOTE(d1): we've no idea what the live deployment version is, so every + // possible downgrade can be shown. it's up to the user to make the choice + if deployMeta.Version == config.UNKNOWN_DEFAULT { availableDowngrades = versions - warnMessages = append(warnMessages, fmt.Sprintf("failed to determine deployed version of %s", app.Name)) } - if specificVersion != "" { - parsedDeployedVersion, err := tagcmp.Parse(deployMeta.Version) - if err != nil { - log.Fatalf("'%s' is not a known version for %s", deployMeta.Version, app.Recipe.Name) + if len(args) == 2 && args[1] != "" { + chosenDowngrade = args[1] + + if err := validateDowngradeVersionArg(chosenDowngrade, app, deployMeta); err != nil { + log.Fatal(err) } - parsedSpecificVersion, err := tagcmp.Parse(specificVersion) - if err != nil { - log.Fatalf("'%s' is not a known version for %s", specificVersion, app.Recipe.Name) - } - - if parsedSpecificVersion.IsGreaterThan(parsedDeployedVersion) && !parsedSpecificVersion.Equals(parsedDeployedVersion) { - log.Fatalf("%s is not a downgrade for %s?", deployMeta.Version, specificVersion) - } - - if parsedSpecificVersion.Equals(parsedDeployedVersion) && !internal.Force { - log.Fatalf("%s is not a downgrade for %s?", deployMeta.Version, specificVersion) - } - - availableDowngrades = append(availableDowngrades, specificVersion) + availableDowngrades = append(availableDowngrades, chosenDowngrade) } - if deployMeta.Version != "unknown" && specificVersion == "" { - if deployMeta.IsChaos { - warnMessages = append(warnMessages, fmt.Sprintf("attempting to rollback a chaos deployment")) + if deployMeta.Version != config.UNKNOWN_DEFAULT && chosenDowngrade == "" { + downgradeAvailable, err := ensureDowngradesAvailable(versions, &availableDowngrades, deployMeta) + if err != nil { + log.Fatal(err) } - for _, version := range versions { - parsedDeployedVersion, err := tagcmp.Parse(deployMeta.Version) - if err != nil { - log.Fatal(err) - } - - parsedVersion, err := tagcmp.Parse(version) - if err != nil { - log.Fatal(err) - } - - if parsedVersion.IsLessThan(parsedDeployedVersion) && !(parsedVersion.Equals(parsedDeployedVersion)) { - availableDowngrades = append(availableDowngrades, version) - } - } - - if len(availableDowngrades) == 0 && !internal.Force { + if !downgradeAvailable { log.Info("no available downgrades") return } } - var chosenDowngrade string - if len(availableDowngrades) > 0 { - if internal.Force || internal.NoInput || specificVersion != "" { + if internal.Force || internal.NoInput || chosenDowngrade != "" { + if len(availableDowngrades) > 0 { chosenDowngrade = availableDowngrades[len(availableDowngrades)-1] - log.Debugf("choosing %s as version to downgrade to (--force/--no-input)", chosenDowngrade) - } else { - msg := fmt.Sprintf("please select a downgrade (version: %s):", deployMeta.Version) - if deployMeta.IsChaos { - msg = fmt.Sprintf("please select a downgrade (version: %s, chaosVersion: %s):", deployMeta.Version, deployMeta.ChaosVersion) - } - - prompt := &survey.Select{ - Message: msg, - Options: internal.SortVersionsDesc(availableDowngrades), - } - - if err := survey.AskOne(prompt, &chosenDowngrade); err != nil { - return - } + } + } else { + if err := chooseDowngrade(availableDowngrades, deployMeta, &chosenDowngrade); err != nil { + log.Fatal(err) } } + if internal.Force && + chosenDowngrade == "" && + deployMeta.Version != config.UNKNOWN_DEFAULT { + chosenDowngrade = deployMeta.Version + } + + if chosenDowngrade == "" { + log.Fatal("unknown deployed version, unable to downgrade") + } + log.Debugf("choosing %s as version to rollback", chosenDowngrade) + if _, err := app.Recipe.EnsureVersion(chosenDowngrade); err != nil { log.Fatal(err) } @@ -194,6 +161,7 @@ beforehand.`, log.Fatal(err) } + stackName := app.StackName() deployOpts := stack.Deploy{ Composefiles: composeFiles, Namespace: stackName, @@ -221,12 +189,13 @@ beforehand.`, // NOTE(d1): no release notes implemeneted for rolling back if err := internal.NewVersionOverview( app, - warnMessages, + downgradeWarnMessages, "rollback", deployMeta.Version, chaosVersion, chosenDowngrade, - ""); err != nil { + "", + ); err != nil { log.Fatal(err) } @@ -234,13 +203,100 @@ beforehand.`, log.Fatal(err) } - app.Recipe.Version = chosenDowngrade - if err := app.WriteRecipeVersion(app.Recipe.Version, false); err != nil { - log.Fatalf("writing new recipe version in env file: %s", err) + if err := app.WriteRecipeVersion(chosenDowngrade, false); err != nil { + log.Fatalf("writing recipe version failed: %s", err) } }, } +// chooseDowngrade prompts the user to choose an downgrade interactively. +func chooseDowngrade( + availableDowngrades []string, + deployMeta stack.DeployMeta, + chosenDowngrade *string, +) error { + msg := fmt.Sprintf("please select a downgrade (version: %s):", deployMeta.Version) + + if deployMeta.IsChaos { + chaosVersion := formatter.BoldDirtyDefault(deployMeta.ChaosVersion) + + msg = fmt.Sprintf( + "please select a downgrade (version: %s, chaos: %s):", + deployMeta.Version, + chaosVersion, + ) + } + + prompt := &survey.Select{ + Message: msg, + Options: internal.SortVersionsDesc(availableDowngrades), + } + + if err := survey.AskOne(prompt, &chosenDowngrade); err != nil { + return err + } + + return nil +} + +// validateDownpgradeVersionArg validates the specific version. +func validateDowngradeVersionArg( + specificVersion string, + app app.App, + deployMeta stack.DeployMeta, +) error { + parsedDeployedVersion, err := tagcmp.Parse(deployMeta.Version) + if err != nil { + return fmt.Errorf("'%s' is not a known version for %s", deployMeta.Version, app.Recipe.Name) + } + + parsedSpecificVersion, err := tagcmp.Parse(specificVersion) + if err != nil { + return fmt.Errorf("'%s' is not a known version for %s", specificVersion, app.Recipe.Name) + } + + if parsedSpecificVersion.IsGreaterThan(parsedDeployedVersion) && + !parsedSpecificVersion.Equals(parsedDeployedVersion) { + return fmt.Errorf("%s is not a downgrade for %s?", deployMeta.Version, specificVersion) + } + + if parsedSpecificVersion.Equals(parsedDeployedVersion) && !internal.Force { + return fmt.Errorf("%s is not a downgrade for %s?", deployMeta.Version, specificVersion) + } + + return nil +} + +// ensureDowngradesAvailable ensures that there are available downgrades. +func ensureDowngradesAvailable( + versions []string, + availableDowngrades *[]string, + deployMeta stack.DeployMeta, +) (bool, error) { + parsedDeployedVersion, err := tagcmp.Parse(deployMeta.Version) + if err != nil { + return false, err + } + + for _, version := range versions { + parsedVersion, err := tagcmp.Parse(version) + if err != nil { + return false, err + } + + if parsedVersion.IsLessThan(parsedDeployedVersion) && + !(parsedVersion.Equals(parsedDeployedVersion)) { + *availableDowngrades = append(*availableDowngrades, version) + } + } + + if len(*availableDowngrades) == 0 && !internal.Force { + return false, nil + } + + return true, nil +} + func init() { AppRollbackCommand.Flags().BoolVarP( &internal.Force, @@ -262,6 +318,6 @@ func init() { &internal.DontWaitConverge, "no-converge-checks", "c", false, - "do not wait for converge logic checks", + "disable converge logic checks", ) } diff --git a/cli/app/secret.go b/cli/app/secret.go index 9364a6da..2018e14f 100644 --- a/cli/app/secret.go +++ b/cli/app/secret.go @@ -45,7 +45,7 @@ var AppSecretGenerateCommand = &cobra.Command{ Run: func(cmd *cobra.Command, args []string) { app := internal.ValidateApp(args) - if err := app.Recipe.Ensure(internal.Chaos, internal.Offline); err != nil { + if err := app.Recipe.Ensure(internal.GetEnsureContext()); err != nil { log.Fatal(err) } @@ -170,7 +170,7 @@ environment. Typically, you can let Abra generate them for you on app creation Run: func(cmd *cobra.Command, args []string) { app := internal.ValidateApp(args) - if err := app.Recipe.Ensure(internal.Chaos, internal.Offline); err != nil { + if err := app.Recipe.Ensure(internal.GetEnsureContext()); err != nil { log.Fatal(err) } @@ -258,7 +258,7 @@ var AppSecretRmCommand = &cobra.Command{ Run: func(cmd *cobra.Command, args []string) { app := internal.ValidateApp(args) - if err := app.Recipe.Ensure(internal.Chaos, internal.Offline); err != nil { + if err := app.Recipe.Ensure(internal.GetEnsureContext()); err != nil { log.Fatal(err) } @@ -351,7 +351,7 @@ var AppSecretLsCommand = &cobra.Command{ Run: func(cmd *cobra.Command, args []string) { app := internal.ValidateApp(args) - if err := app.Recipe.Ensure(internal.Chaos, internal.Offline); err != nil { + if err := app.Recipe.Ensure(internal.GetEnsureContext()); err != nil { log.Fatal(err) } diff --git a/cli/app/services.go b/cli/app/services.go index b3313374..f356d020 100644 --- a/cli/app/services.go +++ b/cli/app/services.go @@ -30,7 +30,7 @@ var AppServicesCommand = &cobra.Command{ Run: func(cmd *cobra.Command, args []string) { app := internal.ValidateApp(args) - if err := app.Recipe.Ensure(internal.Chaos, internal.Offline); err != nil { + if err := app.Recipe.Ensure(internal.GetEnsureContext()); err != nil { log.Fatal(err) } diff --git a/cli/app/undeploy.go b/cli/app/undeploy.go index f476cfa8..54016294 100644 --- a/cli/app/undeploy.go +++ b/cli/app/undeploy.go @@ -62,12 +62,13 @@ Passing "--prune/-p" does not remove those volumes.`, if err := internal.UndeployOverview( app, deployMeta.Version, - chaosVersion); err != nil { + chaosVersion, + ); err != nil { log.Fatal(err) } rmOpts := stack.Remove{ - Namespaces: []string{app.StackName()}, + Namespaces: []string{stackName}, Detach: false, } if err := stack.RunRemove(context.Background(), cl, rmOpts); err != nil { @@ -81,7 +82,7 @@ Passing "--prune/-p" does not remove those volumes.`, } if err := app.WriteRecipeVersion(deployMeta.Version, false); err != nil { - log.Fatalf("writing undeployed recipe version in env file: %s", err) + log.Fatalf("writing recipe version failed: %s", err) } }, } diff --git a/cli/app/upgrade.go b/cli/app/upgrade.go index 617c8830..f3e3b85f 100644 --- a/cli/app/upgrade.go +++ b/cli/app/upgrade.go @@ -5,16 +5,19 @@ import ( "fmt" "coopcloud.tech/abra/cli/internal" + "coopcloud.tech/abra/pkg/app" appPkg "coopcloud.tech/abra/pkg/app" "coopcloud.tech/abra/pkg/autocomplete" "coopcloud.tech/abra/pkg/client" "coopcloud.tech/abra/pkg/config" "coopcloud.tech/abra/pkg/envfile" + "coopcloud.tech/abra/pkg/formatter" "coopcloud.tech/abra/pkg/lint" "coopcloud.tech/abra/pkg/log" stack "coopcloud.tech/abra/pkg/upstream/stack" "coopcloud.tech/tagcmp" "github.com/AlecAivazis/survey/v2" + dockerClient "github.com/docker/docker/client" "github.com/spf13/cobra" ) @@ -24,11 +27,18 @@ var AppUpgradeCommand = &cobra.Command{ Short: "Upgrade an app", Long: `Upgrade an app. -Unlike "deploy", chaos operations are not supported here. Only recipe versions -are supported values for "[version]". +Unlike "abra app deploy", chaos operations are not supported here. Only recipe +versions are supported values for "[version]". + +It is possible to "--force/-f" an upgrade if you want to re-deploy a specific +version. + +Only the deployed version is consulted when trying to determine what upgrades +are available. The live deployment version is the "source of truth" in this +case. The stored .env version is not consulted. An upgrade can be destructive, please ensure you have a copy of your app data -beforehand.`, +beforehand. See "abra app backup" for more.`, Args: cobra.RangeArgs(1, 2), ValidArgsFunction: func( cmd *cobra.Command, @@ -49,22 +59,26 @@ beforehand.`, } }, Run: func(cmd *cobra.Command, args []string) { - var warnMessages []string + var ( + upgradeWarnMessages []string + chosenUpgrade string + availableUpgrades []string + upgradeReleaseNotes string + ) app := internal.ValidateApp(args) - stackName := app.StackName() - var specificVersion string - if len(args) == 2 { - specificVersion = args[1] + if err := app.Recipe.Ensure(internal.GetEnsureContext()); err != nil { + log.Fatal(err) } - if specificVersion != "" { - log.Debugf("overriding env file version (%s) with %s", app.Recipe.Version, specificVersion) - app.Recipe.Version = specificVersion + cl, err := client.New(app.Server) + if err != nil { + log.Fatal(err) } - if err := app.Recipe.Ensure(internal.Chaos, internal.Offline); err != nil { + deployMeta, err := ensureDeployed(cl, app) + if err != nil { log.Fatal(err) } @@ -72,134 +86,69 @@ beforehand.`, log.Fatal(err) } - log.Debugf("checking whether %s is already deployed", stackName) - - cl, err := client.New(app.Server) - if err != nil { - log.Fatal(err) - } - - deployMeta, err := stack.IsDeployed(context.Background(), cl, stackName) - if err != nil { - log.Fatal(err) - } - - if !deployMeta.IsDeployed { - log.Fatalf("%s is not deployed?", app.Name) - } - versions, err := app.Recipe.Tags() if err != nil { log.Fatal(err) } - var availableUpgrades []string - if deployMeta.Version == "unknown" { + // NOTE(d1): we've no idea what the live deployment version is, so every + // possible upgrade can be shown. it's up to the user to make the choice + if deployMeta.Version == config.UNKNOWN_DEFAULT { availableUpgrades = versions - warnMessages = append(warnMessages, fmt.Sprintf("failed to determine deployed version of %s", app.Name)) } - if specificVersion != "" { - parsedDeployedVersion, err := tagcmp.Parse(deployMeta.Version) + if len(args) == 2 && args[1] != "" { + chosenUpgrade = args[1] + + if err := validateUpgradeVersionArg(chosenUpgrade, app, deployMeta); err != nil { + log.Fatal(err) + } + + availableUpgrades = append(availableUpgrades, chosenUpgrade) + } + + if deployMeta.Version != config.UNKNOWN_DEFAULT && chosenUpgrade == "" { + upgradeAvailable, err := ensureUpgradesAvailable(versions, &availableUpgrades, deployMeta) if err != nil { - log.Fatalf("'%s' is not a known version for %s", deployMeta.Version, app.Recipe.Name) - } - parsedSpecificVersion, err := tagcmp.Parse(specificVersion) - if err != nil { - log.Fatalf("'%s' is not a known version for %s", specificVersion, app.Recipe.Name) + log.Fatal(err) } - if parsedSpecificVersion.IsLessThan(parsedDeployedVersion) && !parsedSpecificVersion.Equals(parsedDeployedVersion) { - log.Fatalf("%s is not an upgrade for %s?", deployMeta.Version, specificVersion) - } - - if parsedSpecificVersion.Equals(parsedDeployedVersion) && !internal.Force { - log.Fatalf("%s is not an upgrade for %s?", deployMeta.Version, specificVersion) - } - - availableUpgrades = append(availableUpgrades, specificVersion) - } - - parsedDeployedVersion, err := tagcmp.Parse(deployMeta.Version) - if err != nil { - log.Fatal(err) - } - - if deployMeta.Version != "unknown" && specificVersion == "" { - if deployMeta.IsChaos { - warnMessages = append(warnMessages, fmt.Sprintf("attempting to upgrade a chaos deployment")) - } - - for _, version := range versions { - parsedVersion, err := tagcmp.Parse(version) - if err != nil { - log.Fatal(err) - } - if parsedVersion.IsGreaterThan(parsedDeployedVersion) && !(parsedVersion.Equals(parsedDeployedVersion)) { - availableUpgrades = append(availableUpgrades, version) - } - } - - if len(availableUpgrades) == 0 && !internal.Force { + if !upgradeAvailable { log.Info("no available upgrades") return } } - var chosenUpgrade string - if len(availableUpgrades) > 0 { - if internal.Force || internal.NoInput || specificVersion != "" { + if internal.Force || internal.NoInput || chosenUpgrade != "" { + if len(availableUpgrades) > 0 { chosenUpgrade = availableUpgrades[len(availableUpgrades)-1] - log.Debugf("choosing %s as version to upgrade to", chosenUpgrade) - } else { - msg := fmt.Sprintf("please select an upgrade (version: %s):", deployMeta.Version) - if deployMeta.IsChaos { - msg = fmt.Sprintf("please select an upgrade (version: %s, chaosVersion: %s):", deployMeta.Version, deployMeta.ChaosVersion) - } - - prompt := &survey.Select{ - Message: msg, - Options: internal.SortVersionsDesc(availableUpgrades), - } - - if err := survey.AskOne(prompt, &chosenUpgrade); err != nil { - return - } + } + } else { + if err := chooseUpgrade(availableUpgrades, deployMeta, &chosenUpgrade); err != nil { + log.Fatal(err) } } - if internal.Force && chosenUpgrade == "" { - warnMessages = append(warnMessages, fmt.Sprintf("%s is already upgraded to latest", app.Name)) + if internal.Force && + chosenUpgrade == "" && + deployMeta.Version != config.UNKNOWN_DEFAULT { chosenUpgrade = deployMeta.Version } - // if release notes written after git tag published, read them before we - // check out the tag and then they'll appear to be missing. this covers - // when we obviously will forget to write release notes before publishing - var releaseNotes string - if chosenUpgrade != "" { - parsedChosenUpgrade, err := tagcmp.Parse(chosenUpgrade) - if err != nil { - log.Fatal(err) - } - for _, version := range internal.SortVersionsDesc(versions) { - parsedVersion, err := tagcmp.Parse(version) - if err != nil { - log.Fatal(err) - } - if parsedVersion.IsGreaterThan(parsedDeployedVersion) && parsedVersion.IsLessThan(parsedChosenUpgrade) { - note, err := app.Recipe.GetReleaseNotes(version) - if err != nil { - log.Fatal(err) - } - if note != "" { - releaseNotes += fmt.Sprintf("%s\n", note) - } - } - } + if chosenUpgrade == "" { + log.Fatal("unknown deployed version, unable to upgrade") } log.Debugf("choosing %s as version to upgrade", chosenUpgrade) + + // NOTE(d1): if release notes written after git tag published, read them + // before we check out the tag and then they'll appear to be missing. this + // covers when we obviously will forget to write release notes before + // publishing + if err := getReleaseNotes(app, versions, chosenUpgrade, deployMeta, &upgradeReleaseNotes); err != nil { + log.Fatal(err) + } + if _, err := app.Recipe.EnsureVersion(chosenUpgrade); err != nil { log.Fatal(err) } @@ -217,6 +166,7 @@ beforehand.`, log.Fatal(err) } + stackName := app.StackName() deployOpts := stack.Deploy{ Composefiles: composeFiles, Namespace: stackName, @@ -243,30 +193,35 @@ beforehand.`, for _, envVar := range envVars { if !envVar.Present { - warnMessages = append(warnMessages, - fmt.Sprintf("env var %s missing from %s.env, present in recipe .env.sample", envVar.Name, app.Domain), + upgradeWarnMessages = append(upgradeWarnMessages, + fmt.Sprintf("%s missing from %s.env", envVar.Name, app.Domain), ) } } if showReleaseNotes { - fmt.Print(releaseNotes) + fmt.Print(upgradeReleaseNotes) return } chaosVersion := config.CHAOS_DEFAULT if deployMeta.IsChaos { chaosVersion = deployMeta.ChaosVersion + + if deployMeta.ChaosVersion == "" { + chaosVersion = config.UNKNOWN_DEFAULT + } } if err := internal.NewVersionOverview( app, - warnMessages, + upgradeWarnMessages, "upgrade", deployMeta.Version, chaosVersion, chosenUpgrade, - releaseNotes); err != nil { + upgradeReleaseNotes, + ); err != nil { log.Fatal(err) } @@ -274,7 +229,8 @@ beforehand.`, if err != nil { log.Fatal(err) } - log.Debugf("set waiting timeout to %d s", stack.WaitTimeout) + + log.Debugf("set waiting timeout to %d second(s)", stack.WaitTimeout) if err := stack.RunDeploy(cl, deployOpts, compose, stackName, internal.DontWaitConverge); err != nil { log.Fatal(err) @@ -283,18 +239,162 @@ beforehand.`, postDeployCmds, ok := app.Env["POST_UPGRADE_CMDS"] if ok && !internal.DontWaitConverge { log.Debugf("run the following post-deploy commands: %s", postDeployCmds) + if err := internal.PostCmds(cl, app, postDeployCmds); err != nil { log.Fatalf("attempting to run post deploy commands, saw: %s", err) } } - app.Recipe.Version = chosenUpgrade - if err := app.WriteRecipeVersion(app.Recipe.Version, false); err != nil { - log.Fatalf("writing new recipe version in env file: %s", err) + if err := app.WriteRecipeVersion(chosenUpgrade, false); err != nil { + log.Fatalf("writing recipe version failed: %s", err) } }, } +// chooseUpgrade prompts the user to choose an upgrade interactively. +func chooseUpgrade( + availableUpgrades []string, + deployMeta stack.DeployMeta, + chosenUpgrade *string, +) error { + msg := fmt.Sprintf("please select an upgrade (version: %s):", deployMeta.Version) + + if deployMeta.IsChaos { + chaosVersion := formatter.BoldDirtyDefault(deployMeta.ChaosVersion) + + msg = fmt.Sprintf( + "please select an upgrade (version: %s, chaos: %s):", + deployMeta.Version, + chaosVersion, + ) + } + + prompt := &survey.Select{ + Message: msg, + Options: internal.SortVersionsDesc(availableUpgrades), + } + + if err := survey.AskOne(prompt, &chosenUpgrade); err != nil { + return err + } + + return nil +} + +func getReleaseNotes( + app app.App, + versions []string, + chosenUpgrade string, + deployMeta stack.DeployMeta, + upgradeReleaseNotes *string, +) error { + parsedChosenUpgrade, err := tagcmp.Parse(chosenUpgrade) + if err != nil { + return err + } + + parsedDeployedVersion, err := tagcmp.Parse(deployMeta.Version) + if err != nil { + return err + } + + for _, version := range internal.SortVersionsDesc(versions) { + parsedVersion, err := tagcmp.Parse(version) + if err != nil { + return err + } + + if parsedVersion.IsGreaterThan(parsedDeployedVersion) && + parsedVersion.IsLessThan(parsedChosenUpgrade) { + note, err := app.Recipe.GetReleaseNotes(version) + if err != nil { + return err + } + + if note != "" { + *upgradeReleaseNotes += fmt.Sprintf("%s\n", note) + } + } + } + + return nil +} + +// ensureUpgradesAvailable ensures that there are available upgrades. +func ensureUpgradesAvailable( + versions []string, + availableUpgrades *[]string, + deployMeta stack.DeployMeta, +) (bool, error) { + parsedDeployedVersion, err := tagcmp.Parse(deployMeta.Version) + if err != nil { + return false, err + } + + for _, version := range versions { + parsedVersion, err := tagcmp.Parse(version) + if err != nil { + return false, err + } + + if parsedVersion.IsGreaterThan(parsedDeployedVersion) && + !(parsedVersion.Equals(parsedDeployedVersion)) { + *availableUpgrades = append(*availableUpgrades, version) + } + } + + if len(*availableUpgrades) == 0 && !internal.Force { + return false, nil + } + + return true, nil +} + +// validateUpgradeVersionArg validates the specific version. +func validateUpgradeVersionArg( + specificVersion string, + app app.App, + deployMeta stack.DeployMeta, +) error { + parsedSpecificVersion, err := tagcmp.Parse(specificVersion) + if err != nil { + return fmt.Errorf("'%s' is not a known version for %s", specificVersion, app.Recipe.Name) + } + + parsedDeployedVersion, err := tagcmp.Parse(deployMeta.Version) + if err != nil { + return err + } + + if parsedSpecificVersion.IsLessThan(parsedDeployedVersion) && + !parsedSpecificVersion.Equals(parsedDeployedVersion) { + return fmt.Errorf("%s is not an upgrade for %s?", deployMeta.Version, specificVersion) + } + + if parsedSpecificVersion.Equals(parsedDeployedVersion) && !internal.Force { + return fmt.Errorf("%s is not an upgrade for %s?", deployMeta.Version, specificVersion) + } + + return nil +} + +// ensureDeployed ensures the app is deployed and if so, returns deployment +// meta info. +func ensureDeployed(cl *dockerClient.Client, app app.App) (stack.DeployMeta, error) { + log.Debugf("checking whether %s is already deployed", app.StackName()) + + deployMeta, err := stack.IsDeployed(context.Background(), cl, app.StackName()) + if err != nil { + return stack.DeployMeta{}, err + } + + if !deployMeta.IsDeployed { + return stack.DeployMeta{}, fmt.Errorf("%s is not deployed?", app.Name) + } + + return deployMeta, nil +} + var ( showReleaseNotes bool ) @@ -320,7 +420,7 @@ func init() { &internal.DontWaitConverge, "no-converge-checks", "c", false, - "do not wait for converge logic checks", + "disable converge logic checks", ) AppUpgradeCommand.Flags().BoolVarP( diff --git a/cli/internal/cli.go b/cli/internal/cli.go index 13ae6313..7c4825ad 100644 --- a/cli/internal/cli.go +++ b/cli/internal/cli.go @@ -2,9 +2,10 @@ package internal var ( // NOTE(d1): global - Debug bool - NoInput bool - Offline bool + Debug bool + NoInput bool + Offline bool + IgnoreEnvVersion bool // NOTE(d1): sub-command specific Chaos bool diff --git a/cli/internal/deploy.go b/cli/internal/deploy.go index 151c7b48..72dc6f05 100644 --- a/cli/internal/deploy.go +++ b/cli/internal/deploy.go @@ -68,11 +68,17 @@ func NewVersionOverview( {"RECIPE", app.Recipe.Name}, {"SERVER", server}, {"CONFIG", deployConfig}, + {"CURRENT DEPLOYMENT", "---"}, {"VERSION", deployedVersion}, {"CHAOS ", deployedChaosVersion}, + {upperKind, "---"}, {"VERSION", toDeployVersion}, + + {fmt.Sprintf("%s.ENV", strings.ToUpper(app.Domain)), "---"}, + {"OLD", app.Recipe.EnvVersion}, + {"NEW", toDeployVersion}, } overview := formatter.CreateOverview( @@ -119,7 +125,9 @@ func DeployOverview( deployedVersion string, deployedChaosVersion string, toDeployVersion, - toDeployChaosVersion string) error { + toDeployChaosVersion string, + toWriteVersion string, +) error { deployConfig := "compose.yml" if composeFiles, ok := app.Env["COMPOSE_FILE"]; ok { deployConfig = composeFiles @@ -154,6 +162,10 @@ func DeployOverview( {"NEW DEPLOYMENT", "---"}, {"VERSION", toDeployVersion}, {"CHAOS", toDeployChaosVersion}, + + {fmt.Sprintf("%s.ENV", strings.ToUpper(app.Name)), "---"}, + {"CURRENT VERSION", app.Recipe.EnvVersion}, + {"NEW VERSION", toWriteVersion}, } overview := formatter.CreateOverview("DEPLOY OVERVIEW", rows) @@ -210,9 +222,14 @@ func UndeployOverview( {"RECIPE", app.Recipe.Name}, {"SERVER", server}, {"CONFIG", deployConfig}, + {"CURRENT DEPLOYMENT", "---"}, {"DEPLOYED", version}, {"CHAOS", chaosVersion}, + + {fmt.Sprintf("%s.ENV", strings.ToUpper(app.Name)), "---"}, + {"CURRENT VERSION", app.Recipe.EnvVersion}, + {"NEW VERSION", version}, } overview := formatter.CreateOverview("UNDEPLOY OVERVIEW", rows) diff --git a/cli/internal/ensure.go b/cli/internal/ensure.go new file mode 100644 index 00000000..e0b09924 --- /dev/null +++ b/cli/internal/ensure.go @@ -0,0 +1,11 @@ +package internal + +import "coopcloud.tech/abra/pkg/recipe" + +func GetEnsureContext() recipe.EnsureContext { + return recipe.EnsureContext{ + Chaos, + Offline, + IgnoreEnvVersion, + } +} diff --git a/cli/internal/errors.go b/cli/internal/errors.go deleted file mode 100644 index 45a19752..00000000 --- a/cli/internal/errors.go +++ /dev/null @@ -1,18 +0,0 @@ -package internal - -import ( - "os" - - "coopcloud.tech/abra/pkg/log" - "github.com/urfave/cli/v3" -) - -// ShowSubcommandHelpAndError exits the program on error, logs the error to the -// terminal, and shows the help command. -func ShowSubcommandHelpAndError(cmd *cli.Command, err interface{}) { - if err2 := cli.ShowSubcommandHelp(cmd); err2 != nil { - log.Error(err2) - } - log.Error(err) - os.Exit(1) -} diff --git a/cli/recipe/fetch.go b/cli/recipe/fetch.go index 7840dc0f..b1eeb5d2 100644 --- a/cli/recipe/fetch.go +++ b/cli/recipe/fetch.go @@ -34,9 +34,10 @@ var RecipeFetchCommand = &cobra.Command{ log.Fatal("cannot use [recipe] and --all/-a together") } + ensureCtx := internal.GetEnsureContext() if recipeName != "" { r := internal.ValidateRecipe(args, cmd.Name()) - if err := r.Ensure(false, false); err != nil { + if err := r.Ensure(ensureCtx); err != nil { log.Fatal(err) } return @@ -50,7 +51,7 @@ var RecipeFetchCommand = &cobra.Command{ catlBar := formatter.CreateProgressbar(len(catalogue), "fetching latest recipes...") for recipeName := range catalogue { r := recipe.Get(recipeName) - if err := r.Ensure(false, false); err != nil { + if err := r.Ensure(ensureCtx); err != nil { log.Error(err) } catlBar.Add(1) diff --git a/cli/recipe/lint.go b/cli/recipe/lint.go index 030816de..d885fe04 100644 --- a/cli/recipe/lint.go +++ b/cli/recipe/lint.go @@ -23,7 +23,7 @@ var RecipeLintCommand = &cobra.Command{ Run: func(cmd *cobra.Command, args []string) { recipe := internal.ValidateRecipe(args, cmd.Name()) - if err := recipe.Ensure(internal.Chaos, internal.Offline); err != nil { + if err := recipe.Ensure(internal.GetEnsureContext()); err != nil { log.Fatal(err) } diff --git a/cli/recipe/upgrade.go b/cli/recipe/upgrade.go index de75048e..9eb262c8 100644 --- a/cli/recipe/upgrade.go +++ b/cli/recipe/upgrade.go @@ -63,7 +63,7 @@ You may invoke this command in "wizard" mode and be prompted for input.`, Run: func(cmd *cobra.Command, args []string) { recipe := internal.ValidateRecipe(args, cmd.Name()) - if err := recipe.Ensure(internal.Chaos, internal.Offline); err != nil { + if err := recipe.Ensure(internal.GetEnsureContext()); err != nil { log.Fatal(err) } diff --git a/cli/run.go b/cli/run.go index bb0797c2..dc6e2e73 100644 --- a/cli/run.go +++ b/cli/run.go @@ -95,20 +95,37 @@ func Run(version, commit string) { } rootCmd.PersistentFlags().BoolVarP( - &internal.Debug, "debug", "d", false, + &internal.Debug, + "debug", + "d", + false, "show debug messages", ) rootCmd.PersistentFlags().BoolVarP( - &internal.NoInput, "no-input", "n", false, + &internal.NoInput, + "no-input", + "n", + false, "toggle non-interactive mode", ) rootCmd.PersistentFlags().BoolVarP( - &internal.Offline, "offline", "o", false, + &internal.Offline, + "offline", + "o", + false, "prefer offline & filesystem access", ) + rootCmd.PersistentFlags().BoolVarP( + &internal.IgnoreEnvVersion, + "ignore-env-version", + "i", + false, + "ignore .env version checkout", + ) + catalogue.CatalogueCommand.AddCommand( catalogue.CatalogueGenerateCommand, ) diff --git a/go.mod b/go.mod index fa84abae..640c168f 100644 --- a/go.mod +++ b/go.mod @@ -22,7 +22,6 @@ require ( github.com/moby/term v0.5.0 github.com/pkg/errors v0.9.1 github.com/schollz/progressbar/v3 v3.17.1 - github.com/urfave/cli/v3 v3.0.0-alpha9 golang.org/x/term v0.27.0 gopkg.in/yaml.v3 v3.0.1 gotest.tools/v3 v3.5.1 diff --git a/go.sum b/go.sum index 618690e1..383548bc 100644 --- a/go.sum +++ b/go.sum @@ -131,7 +131,6 @@ github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInq github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= @@ -140,8 +139,6 @@ github.com/charmbracelet/lipgloss v1.0.0 h1:O7VkGDvqEdGi93X+DeqsQ7PKHDgtQfF8j8/O github.com/charmbracelet/lipgloss v1.0.0/go.mod h1:U5fy9Z+C38obMs+T+tJqst9VGzlOYGj4ri9reL3qUlo= github.com/charmbracelet/log v0.4.0 h1:G9bQAcx8rWA2T3pWvx7YtPTPwgqpk7D68BX21IRW8ZM= github.com/charmbracelet/log v0.4.0/go.mod h1:63bXt/djrizTec0l11H20t8FDSvA4CRZJ1KH22MdptM= -github.com/charmbracelet/x/ansi v0.5.2 h1:dEa1x2qdOZXD/6439s+wF7xjV+kZLu/iN00GuXXrU9E= -github.com/charmbracelet/x/ansi v0.5.2/go.mod h1:KBUFw1la39nl0dLl10l5ORDAqGXaeurTQmwyyVKse/Q= github.com/charmbracelet/x/ansi v0.6.0 h1:qOznutrb93gx9oMiGf7caF7bqqubh6YIM0SWKyA08pA= github.com/charmbracelet/x/ansi v0.6.0/go.mod h1:KBUFw1la39nl0dLl10l5ORDAqGXaeurTQmwyyVKse/Q= github.com/charmbracelet/x/exp/golden v0.0.0-20240806155701-69247e0abc2a h1:G99klV19u0QnhiizODirwVksQB91TJKV/UaTnACcG30= @@ -278,7 +275,6 @@ github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfc github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= @@ -289,8 +285,6 @@ github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= -github.com/cyphar/filepath-securejoin v0.3.4 h1:VBWugsJh2ZxJmLFSM06/0qzQyiQX2Qs0ViKrUAcqdZ8= -github.com/cyphar/filepath-securejoin v0.3.4/go.mod h1:8s/MCNJREmFK0H02MF6Ihv1nakJe4L/w3WZLHNkvlYM= github.com/cyphar/filepath-securejoin v0.3.6 h1:4d9N5ykBnSp5Xn2JkhocYDkOpURL/18CYMpo6xB9uWM= github.com/cyphar/filepath-securejoin v0.3.6/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI= github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ= @@ -311,8 +305,6 @@ github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5Qvfr github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= github.com/docker/cli v0.0.0-20191017083524-a8ff7f821017/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= -github.com/docker/cli v27.3.1+incompatible h1:qEGdFBF3Xu6SCvCYhc7CzaQTlBmqDuzxPDpigSyeKQQ= -github.com/docker/cli v27.3.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/cli v27.4.1+incompatible h1:VzPiUlRJ/xh+otB75gva3r05isHMo5wXDfPRi5/b4hI= github.com/docker/cli v27.4.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v0.0.0-20190905152932-14b96e55d84c/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY= @@ -321,8 +313,6 @@ github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4Kfc github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v1.4.2-0.20190924003213-a8608b5b67c7/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker v27.3.1+incompatible h1:KttF0XoteNTicmUtBO0L2tP+J7FGRFTjaEF4k6WdhfI= -github.com/docker/docker v27.3.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v27.4.1+incompatible h1:ZJvcY7gfwHn1JF48PfbyXg7Jyt9ZCWDW+GGXOIxEwp4= github.com/docker/docker v27.4.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y= @@ -529,10 +519,7 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0 h1:TmHmbvxPmaegwhDubVz0lICL0J5Ka2vwTzhoePEXsGE= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0/go.mod h1:qztMSjm835F2bXf+5HKAPIS5qsmQDqZna/PgVt4rWtI= github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 h1:VNqngBF40hVlDloBruUehVYC3ArSgIyScOAyMRqBxRg= github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1/go.mod h1:RBRO7fro65R6tjKzYgLAFo0t1QEXY1Dp+i/bvpRiqiQ= github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8= @@ -793,8 +780,6 @@ github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8 github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.60.1 h1:FUas6GcOw66yB/73KC+BOZoFJmbo/1pojoILArPAaSc= -github.com/prometheus/common v0.60.1/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw= github.com/prometheus/common v0.61.0 h1:3gv/GThfX0cV2lpO7gkTUwZru38mxevy90Bj8YFSRQQ= github.com/prometheus/common v0.61.0/go.mod h1:zr29OCN/2BsJRaFwG8QOBr41D6kkchKbpeNH7pAjb/s= github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= @@ -819,7 +804,6 @@ github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6L github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= -github.com/russross/blackfriday v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3VRLtww= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -905,8 +889,6 @@ github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijb github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/urfave/cli/v3 v3.0.0-alpha9.1.0.20241019193437-5053ec708a44 h1:BeSTAZEDkDVNv9EOrycIGCkEg+6EhRRgSsbdc93Q3OM= -github.com/urfave/cli/v3 v3.0.0-alpha9.1.0.20241019193437-5053ec708a44/go.mod h1:FnIeEMYu+ko8zP1F9Ypr3xkZMIDqW3DR92yUtY39q1Y= github.com/vbatts/tar-split v0.11.2/go.mod h1:vV3ZuO2yWSVsz+pfFzDG/upWH1JhjOiEaWq6kXyQ3VI= github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= @@ -947,47 +929,27 @@ go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.57.0 h1:DheMAlT6POBP+gh8RUH19EOTnQIor5QE0uSRPtzCpSw= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.57.0/go.mod h1:wZcGmeVO9nzP67aYSLDqXNWK87EZWhi7JWj1v7ZXf94= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 h1:yd02MEjBdJkG3uabWP9apV+OuWRIXGDuJEUJbOHmCFU= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0/go.mod h1:umTcuxiv1n/s/S6/c2AT/g2CQ7u5C59sHDNmfSwgz7Q= -go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U= -go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg= go.opentelemetry.io/otel v1.33.0 h1:/FerN9bax5LoK51X/sI0SVYrjSE0/yUL7DpxW4K3FWw= go.opentelemetry.io/otel v1.33.0/go.mod h1:SUUkR6csvUQl+yjReHu5uM3EtVV7MBm5FHKRlNx4I8I= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.32.0 h1:j7ZSD+5yn+lo3sGV69nW04rRR0jhYnBwjuX3r0HvnK0= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.32.0/go.mod h1:WXbYJTUaZXAbYd8lbgGuvih0yuCfOFC5RJoYnoLcGz8= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.33.0 h1:7F29RDmnlqk6B5d+sUqemt8TBfDqxryYW5gX6L74RFA= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.33.0/go.mod h1:ZiGDq7xwDMKmWDrN1XsXAj0iC7hns+2DhxBFSncNHSE= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.32.0 h1:IJFEoHiytixx8cMiVAO+GmHR6Frwu+u5Ur8njpFO6Ac= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.32.0/go.mod h1:3rHrKNtLIoS0oZwkY2vxi+oJcwFRWdtUyRII+so45p8= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 h1:Vh5HayB/0HHfOQA7Ctx69E/Y/DcQSMPpKANYVMQ7fBA= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0/go.mod h1:cpgtDBaqD/6ok/UG0jT15/uKjAY8mRA53diogHBg3UI= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.32.0 h1:9kV11HXBHZAvuPUZxmMWrH8hZn/6UnHX4K0mu36vNsU= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.32.0/go.mod h1:JyA0FHXe22E1NeNiHmVp7kFHglnexDQ7uRWDiiJ1hKQ= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0 h1:5pojmb1U1AogINhN3SurB+zm/nIcusopeBNp42f45QM= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0/go.mod h1:57gTHJSE5S1tqg+EKsLPlTWhpHMsWlVmer+LA926XiA= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 h1:IeMeyr1aBvBiPVYihXIaeIZba6b8E1bYp7lbdxK8CQg= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0/go.mod h1:oVdCUtjq9MK9BlS7TtucsQwUcXcymNiEDjgDD2jMtZU= -go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZktzNa0M= -go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8= go.opentelemetry.io/otel/metric v1.33.0 h1:r+JOocAyeRVXD8lZpjdQjzMadVZp2M4WmQ+5WtEnklQ= go.opentelemetry.io/otel/metric v1.33.0/go.mod h1:L9+Fyctbp6HFTddIxClbQkjtubW6O9QS3Ann/M82u6M= -go.opentelemetry.io/otel/sdk v1.32.0 h1:RNxepc9vK59A8XsgZQouW8ue8Gkb4jpWtJm9ge5lEG4= -go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU= go.opentelemetry.io/otel/sdk v1.33.0 h1:iax7M131HuAm9QkZotNHEfstof92xM+N8sr3uHXc2IM= go.opentelemetry.io/otel/sdk v1.33.0/go.mod h1:A1Q5oi7/9XaMlIWzPSxLRWOI8nG3FnzHJNbiENQuihM= -go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU= -go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ= go.opentelemetry.io/otel/sdk/metric v1.33.0 h1:Gs5VK9/WUJhNXZgn8MR6ITatvAmKeIuCtNbsP3JkNqU= go.opentelemetry.io/otel/sdk/metric v1.33.0/go.mod h1:dL5ykHZmm1B1nVRk9dDjChwDmt81MjVp3gLkQRwKf/Q= -go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM= -go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8= go.opentelemetry.io/otel/trace v1.33.0 h1:cCJuF7LRjUFso9LPnEAHJDB2pqzp+hbO8eu1qqW2d/s= go.opentelemetry.io/otel/trace v1.33.0/go.mod h1:uIcdVUZMpTAmz0tI1z04GoVSezK37CbGV4fr1f2nBck= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= -go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= go.opentelemetry.io/proto/otlp v1.4.0 h1:TA9WRvW6zMwP+Ssb6fLoUIuirti1gGbP28GcKG1jgeg= go.opentelemetry.io/proto/otlp v1.4.0/go.mod h1:PPBWZIP98o2ElSqI35IHfu7hIhSwvc5N38Jw8pXuGFY= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= @@ -1014,8 +976,6 @@ golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9/go.mod h1:jdWPYTVW3xRLrWP golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ= -golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg= golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1028,8 +988,6 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f h1:XdNn9LlyWAhLVp6P/i8QYBW+hlyhrhei9uErw2B5GJo= -golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:D5SMRVC3C2/4+F/DB1wZsLRnSNimn2Sp/NPsCrsv8ak= golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67 h1:1UoZQm6f0P/ZO0w1Ri+f+ifG/gXhegadRdwBIXEFWDo= golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= @@ -1095,8 +1053,6 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo= -golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM= golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -1116,8 +1072,6 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= -golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1199,15 +1153,11 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= -golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU= -golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E= golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1219,8 +1169,6 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= -golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1324,12 +1272,8 @@ google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20200527145253-8367513e4ece/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto/googleapis/api v0.0.0-20241118233622-e639e219e697 h1:pgr/4QbFyktUv9CtQ/Fq4gzEE6/Xs7iCXbktaGzLHbQ= -google.golang.org/genproto/googleapis/api v0.0.0-20241118233622-e639e219e697/go.mod h1:+D9ySVjN8nY8YCVjc5O7PZDIdZporIDY3KaGfJunh88= google.golang.org/genproto/googleapis/api v0.0.0-20241223144023-3abc09e42ca8 h1:st3LcW/BPi75W4q1jJTEor/QWwbNlPlDG0JTn6XhZu0= google.golang.org/genproto/googleapis/api v0.0.0-20241223144023-3abc09e42ca8/go.mod h1:klhJGKFyG8Tn50enBn7gizg4nXGXJ+jqEREdCWaPcV4= -google.golang.org/genproto/googleapis/rpc v0.0.0-20241118233622-e639e219e697 h1:LWZqQOEjDyONlF1H6afSWpAL/znlREo2tHfLoe+8LMA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20241118233622-e639e219e697/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU= google.golang.org/genproto/googleapis/rpc v0.0.0-20241223144023-3abc09e42ca8 h1:TqExAhdPaB60Ux47Cn0oLV07rGnxZzIsaRhQaqS666A= google.golang.org/genproto/googleapis/rpc v0.0.0-20241223144023-3abc09e42ca8/go.mod h1:lcTa1sDdWEIHMWlITnIczmw5w60CF9ffkb8Z+DVmmjA= google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= @@ -1351,8 +1295,6 @@ google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTp google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.68.0 h1:aHQeeJbo8zAkAa3pRzrVjZlbz6uSfeOXlJNQM0RAbz0= -google.golang.org/grpc v1.68.0/go.mod h1:fmSPC5AsjSBCK54MyHRx48kpOti1/jRfOlwEWywNjWA= google.golang.org/grpc v1.69.2 h1:U3S9QEtbXC0bYNvRtcoklF3xGtLViumSYxWykJS+7AU= google.golang.org/grpc v1.69.2/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= @@ -1368,8 +1310,6 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io= -google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk= google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= @@ -1411,7 +1351,6 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= diff --git a/pkg/app/app_test.go b/pkg/app/app_test.go index 0a0f377a..3ed6abfe 100644 --- a/pkg/app/app_test.go +++ b/pkg/app/app_test.go @@ -211,7 +211,7 @@ func TestWriteRecipeVersionOverwrite(t *testing.T) { } }) - assert.Equal(t, "", app.Recipe.Version) + assert.Equal(t, "", app.Recipe.EnvVersion) if err := app.WriteRecipeVersion("foo", false); err != nil { t.Fatal(err) @@ -222,7 +222,7 @@ func TestWriteRecipeVersionOverwrite(t *testing.T) { t.Fatal(err) } - assert.Equal(t, "foo", app.Recipe.Version) + assert.Equal(t, "foo", app.Recipe.EnvVersion) app.Recipe.Dirty = true if err := app.WriteRecipeVersion("foo+U", false); err != nil { @@ -234,5 +234,5 @@ func TestWriteRecipeVersionOverwrite(t *testing.T) { t.Fatal(err) } - assert.Equal(t, "foo+U", app.Recipe.Version) + assert.Equal(t, "foo+U", app.Recipe.EnvVersion) } diff --git a/pkg/config/abra.go b/pkg/config/abra.go index 55358115..07ff04cf 100644 --- a/pkg/config/abra.go +++ b/pkg/config/abra.go @@ -118,4 +118,6 @@ var ( NO_DOMAIN_DEFAULT = "N/A" NO_VERSION_DEFAULT = "N/A" + + UNKNOWN_DEFAULT = "unknown" ) diff --git a/pkg/formatter/formatter.go b/pkg/formatter/formatter.go index a5a8ab1e..ff6a685d 100644 --- a/pkg/formatter/formatter.go +++ b/pkg/formatter/formatter.go @@ -125,6 +125,10 @@ func CreateOverview(header string, rows [][]string) string { var renderedRows []string for _, row := range rows { + if len(row) < 2 { + continue + } + if len(row) > 2 { panic("CreateOverview: only accepts rows of len == 2") } @@ -140,6 +144,7 @@ func CreateOverview(header string, rows [][]string) string { } rendered := horizontal(leftStyle.Render(row[0]), offset, rightStyle.Render(row[1])) + if row[1] == "---" { rendered = horizontal( leftStyle. diff --git a/pkg/recipe/git.go b/pkg/recipe/git.go index a304495d..d45ebe4a 100644 --- a/pkg/recipe/git.go +++ b/pkg/recipe/git.go @@ -13,13 +13,20 @@ import ( "github.com/go-git/go-git/v5/plumbing" ) -// Ensure makes sure the recipe exists, is up to date and has the latest version checked out. -func (r Recipe) Ensure(chaos bool, offline bool) error { +type EnsureContext struct { + Chaos bool + Offline bool + IgnoreEnv bool +} + +// Ensure makes sure the recipe exists, is up to date and has the specific +// version checked out. +func (r Recipe) Ensure(ctx EnsureContext) error { if err := r.EnsureExists(); err != nil { return err } - if chaos { + if ctx.Chaos { return nil } @@ -27,15 +34,16 @@ func (r Recipe) Ensure(chaos bool, offline bool) error { return err } - if !offline { + if !ctx.Offline { if err := r.EnsureUpToDate(); err != nil { log.Fatal(err) } } - if r.Version != "" { - log.Debugf("ensuring version %s", r.Version) - if _, err := r.EnsureVersion(r.Version); err != nil { + if r.EnvVersion != "" && !ctx.IgnoreEnv { + log.Debugf("ensuring env version %s", r.EnvVersion) + + if _, err := r.EnsureVersion(r.EnvVersion); err != nil { return err } @@ -65,6 +73,41 @@ func (r Recipe) EnsureExists() error { return nil } +// IsChaosCommit determines if a version sttring is a chaos commit or not. +func (r Recipe) IsChaosCommit(version string) (bool, error) { + isChaosCommit := false + + if err := gitPkg.EnsureGitRepo(r.Dir); err != nil { + return isChaosCommit, err + } + + repo, err := git.PlainOpen(r.Dir) + if err != nil { + return isChaosCommit, err + } + + tags, err := repo.Tags() + if err != nil { + return isChaosCommit, err + } + + var tagRef plumbing.ReferenceName + if err := tags.ForEach(func(ref *plumbing.Reference) (err error) { + if ref.Name().Short() == version { + tagRef = ref.Name() + } + return nil + }); err != nil { + return isChaosCommit, err + } + + if tagRef.String() == "" { + isChaosCommit = true + } + + return true, nil +} + // EnsureVersion checks whether a specific version exists for a recipe. func (r Recipe) EnsureVersion(version string) (bool, error) { isChaosCommit := false diff --git a/pkg/recipe/recipe.go b/pkg/recipe/recipe.go index 84b54624..aed00ce1 100644 --- a/pkg/recipe/recipe.go +++ b/pkg/recipe/recipe.go @@ -159,11 +159,11 @@ func Get(name string) Recipe { dir := path.Join(config.RECIPES_DIR, escapeRecipeName(name)) r := Recipe{ - Name: name, - Version: version, - Dir: dir, - GitURL: gitURL, - SSHURL: sshURL, + Name: name, + EnvVersion: version, + Dir: dir, + GitURL: gitURL, + SSHURL: sshURL, ComposePath: path.Join(dir, "compose.yml"), ReadmePath: path.Join(dir, "README.md"), @@ -179,12 +179,12 @@ func Get(name string) Recipe { } type Recipe struct { - Name string - Version string - Dirty bool // NOTE(d1): git terminology for unstaged changes - Dir string - GitURL string - SSHURL string + Name string + EnvVersion string + Dirty bool // NOTE(d1): git terminology for unstaged changes + Dir string + GitURL string + SSHURL string ComposePath string ReadmePath string @@ -195,7 +195,7 @@ type Recipe struct { // String outputs a human-friendly string representation. func (r Recipe) String() string { out := fmt.Sprintf("{name: %s, ", r.Name) - out += fmt.Sprintf("version : %s, ", r.Version) + out += fmt.Sprintf("version : %s, ", r.EnvVersion) out += fmt.Sprintf("dirty: %v, ", r.Dirty) out += fmt.Sprintf("dir: %s, ", r.Dir) out += fmt.Sprintf("git url: %s, ", r.GitURL) diff --git a/pkg/recipe/recipe_test.go b/pkg/recipe/recipe_test.go index a2abcf11..a94bba06 100644 --- a/pkg/recipe/recipe_test.go +++ b/pkg/recipe/recipe_test.go @@ -33,7 +33,7 @@ func TestGet(t *testing.T) { name: "foo:1.2.3", recipe: Recipe{ Name: "foo", - Version: "1.2.3", + EnvVersion: "1.2.3", Dir: path.Join(cfg.GetAbraDir(), "/recipes/foo"), GitURL: "https://git.coopcloud.tech/coop-cloud/foo.git", SSHURL: "ssh://git@git.coopcloud.tech:2222/coop-cloud/foo.git", @@ -60,7 +60,7 @@ func TestGet(t *testing.T) { name: "mygit.org/myorg/cool-recipe:1.2.4", recipe: Recipe{ Name: "mygit.org/myorg/cool-recipe", - Version: "1.2.4", + EnvVersion: "1.2.4", Dir: path.Join(cfg.GetAbraDir(), "/recipes/mygit_org_myorg_cool-recipe"), GitURL: "https://mygit.org/myorg/cool-recipe.git", SSHURL: "ssh://git@mygit.org/myorg/cool-recipe.git", @@ -108,5 +108,5 @@ func TestGetVersionLabelLocalDoesNotUseTimeoutLabel(t *testing.T) { func TestDirtyMarkerRemoved(t *testing.T) { r := Get("abra-test-recipe:1e83340e+U") - assert.Equal(t, "1e83340e", r.Version) + assert.Equal(t, "1e83340e", r.EnvVersion) } diff --git a/tests/integration/app_deploy.bats b/tests/integration/app_deploy.bats index cdd413b0..2b0016f0 100644 --- a/tests/integration/app_deploy.bats +++ b/tests/integration/app_deploy.bats @@ -161,6 +161,32 @@ teardown(){ assert_output --partial 'already deployed' } +@test "no re-deploy after chaos deploy without --force/--chaos" { + run $ABRA app deploy "$TEST_APP_DOMAIN" \ + --no-input --no-converge-checks --chaos + assert_success + + run $ABRA app deploy "$TEST_APP_DOMAIN" \ + --no-input --no-converge-checks + assert_failure + + run $ABRA app deploy "$TEST_APP_DOMAIN" \ + --no-input --no-converge-checks --chaos + assert_success + + run $ABRA app deploy "$TEST_APP_DOMAIN" \ + --no-input --no-converge-checks --force + assert_success +} + +@test "no re-deploy without --force" { + _deploy_app + + run $ABRA app deploy "$TEST_APP_DOMAIN" \ + --no-input --no-converge-checks + assert_failure +} + # bats test_tags=slow @test "re-deploy deployed app if --force/--chaos" { _deploy_app @@ -302,7 +328,6 @@ teardown(){ run $ABRA app deploy "$TEST_APP_DOMAIN" --no-input --no-converge-checks assert_failure - assert_output --partial 'unable to deploy, secrets not generated' } # bats test_tags=slow @@ -338,7 +363,6 @@ teardown(){ run $ABRA app deploy "$TEST_APP_DOMAIN" "0.1.1+1.20.2" \ --no-input --no-converge-checks assert_success - refute_output --partial 'no such file or directory' _undeploy_app @@ -360,3 +384,42 @@ teardown(){ assert_success assert_output --regexp 'chaos-version.*+U' } + +# bats test_tags=slow +@test "ignore env version checkout after deploy" { + tagHash=$(_get_tag_hash "0.1.0+1.20.0") + + run $ABRA app deploy "$TEST_APP_DOMAIN" "0.1.0+1.20.0" \ + --no-input --no-converge-checks + assert_success + + assert_equal $(_get_current_hash) "$tagHash" + + run $ABRA app check --ignore-env-version "$TEST_APP_DOMAIN" + assert_success + + assert_equal $(_get_current_hash) "$(_get_head_hash)" + + run $ABRA app check "$TEST_APP_DOMAIN" + assert_success + + assert_equal $(_get_current_hash) "$tagHash" +} + +# bats test_tags=slow +@test "ignore env version on new deploy" { + tagHash=$(_get_tag_hash "0.1.0+1.20.0") + + run $ABRA app deploy "$TEST_APP_DOMAIN" "0.1.0+1.20.0" \ + --no-input --no-converge-checks + assert_success + + _undeploy_app + + latestRelease=$(_latest_release) + + run $ABRA app deploy "$TEST_APP_DOMAIN" \ + --no-input --no-converge-checks --ignore-env-version + assert_success + assert_output --partial "$latestRelease" +} diff --git a/tests/integration/app_deploy_env_version.bats b/tests/integration/app_deploy_env_version.bats index 0522ea05..013e0a68 100644 --- a/tests/integration/app_deploy_env_version.bats +++ b/tests/integration/app_deploy_env_version.bats @@ -91,9 +91,27 @@ teardown(){ run $ABRA app deploy "$TEST_APP_DOMAIN" "0.2.0+1.21.0" \ --no-input --no-converge-checks --force --debug assert_success - assert_output --partial "overriding env file version" run grep -q "TYPE=$TEST_RECIPE:0.2.0+1.21.0" \ "$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env" assert_success } + +# bats test_tags=slow +@test "deploy overwrites chaos deploy" { + run $ABRA app deploy "$TEST_APP_DOMAIN" "1e83340e" \ + --no-input --no-converge-checks + assert_success + + run grep -q "TYPE=$TEST_RECIPE:1e83340e" \ + "$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env" + assert_success + + run $ABRA app deploy "$TEST_APP_DOMAIN" \ + --force --no-input --no-converge-checks + assert_success + + run grep -q "TYPE=$TEST_RECIPE:1e83340e" \ + "$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env" + assert_failure +} diff --git a/tests/integration/app_rollback.bats b/tests/integration/app_rollback.bats index d7c63241..b4d30b20 100644 --- a/tests/integration/app_rollback.bats +++ b/tests/integration/app_rollback.bats @@ -129,6 +129,17 @@ teardown(){ assert_output --partial "0.1.0+1.20.0" } +@test "force rollback with no available downgrades" { + run $ABRA app deploy "$TEST_APP_DOMAIN" "0.1.0+1.20.0" \ + --no-input --no-converge-checks + assert_success + + run $ABRA app rollback "$TEST_APP_DOMAIN" \ + --force --no-input --no-converge-checks + assert_success + assert_output --partial "0.1.0+1.20.0" +} + # bats test_tags=slow @test "rollback to a version 2 tags behind" { run $ABRA app deploy "$TEST_APP_DOMAIN" "0.2.0+1.21.0" --no-input --no-converge-checks diff --git a/tests/integration/app_rollback_env_version.bats b/tests/integration/app_rollback_env_version.bats index bdb3ced3..21d5c78c 100644 --- a/tests/integration/app_rollback_env_version.bats +++ b/tests/integration/app_rollback_env_version.bats @@ -24,9 +24,9 @@ teardown(){ } @test "rollback writes version to env file" { - run $ABRA app deploy "$TEST_APP_DOMAIN" "0.2.0+1.21.0" --no-input --no-converge-checks + run $ABRA app deploy "$TEST_APP_DOMAIN" "0.2.0+1.21.0" \ + --no-input --no-converge-checks assert_success - assert_output --partial "0.2.0+1.21.0" run grep -q "TYPE=abra-test-recipe:0.2.0+1.21.0" \ "$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env" @@ -35,8 +35,6 @@ teardown(){ run $ABRA app rollback "$TEST_APP_DOMAIN" "0.1.0+1.20.0" \ --no-input --no-converge-checks --debug assert_success - assert_output --partial "0.1.0+1.20.0" - assert_output --partial "overriding env file version" run grep -q "TYPE=abra-test-recipe:0.1.0+1.20.0" \ "$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env" diff --git a/tests/integration/app_upgrade.bats b/tests/integration/app_upgrade.bats index 3599add4..8e3c0b2e 100644 --- a/tests/integration/app_upgrade.bats +++ b/tests/integration/app_upgrade.bats @@ -33,6 +33,16 @@ teardown(){ assert_failure } +@test "retrieve recipe if missing" { + run rm -rf "$ABRA_DIR/recipes/$TEST_RECIPE" + assert_success + assert_not_exists "$ABRA_DIR/recipes/$TEST_RECIPE" + + run $ABRA app upgrade "$TEST_APP_DOMAIN" --no-input --no-converge-checks + assert_failure + assert_exists "$ABRA_DIR/recipes/$TEST_RECIPE" +} + # bats test_tags=slow @test "error if specific version is not an upgrade" { run $ABRA app deploy "$TEST_APP_DOMAIN" "0.1.0+1.20.0" --no-input --no-converge-checks @@ -118,6 +128,19 @@ teardown(){ assert_output --partial '0.2.0+1.21.0' } +@test "force upgrade with no available upgrades" { + run $ABRA app deploy "$TEST_APP_DOMAIN" \ + --no-input --no-converge-checks + assert_success + + latestRelease=$(_latest_release) + + run $ABRA app upgrade "$TEST_APP_DOMAIN" \ + --force --no-input --no-converge-checks + assert_success + assert_output --partial "$latestRelease" +} + # bats test_tags=slow @test "upgrade to latest" { run $ABRA app deploy "$TEST_APP_DOMAIN" "0.1.0+1.20.0" --no-input --no-converge-checks diff --git a/tests/integration/app_upgrade_env_version.bats b/tests/integration/app_upgrade_env_version.bats index 292a121a..e6d9754b 100644 --- a/tests/integration/app_upgrade_env_version.bats +++ b/tests/integration/app_upgrade_env_version.bats @@ -22,20 +22,19 @@ teardown(){ _reset_recipe } +# bats test_tags=slow @test "upgrade writes version to env file" { - run $ABRA app deploy "$TEST_APP_DOMAIN" "0.1.0+1.20.0" --no-input --no-converge-checks + run $ABRA app deploy "$TEST_APP_DOMAIN" "0.1.0+1.20.0" \ + --no-input --no-converge-checks assert_success - assert_output --partial '0.1.0+1.20.0' run grep -q "TYPE=abra-test-recipe:0.1.0+1.20.0" \ "$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env" assert_success run $ABRA app upgrade "$TEST_APP_DOMAIN" "0.2.0+1.21.0" \ - --no-input --no-converge-checks --debug + --no-input --no-converge-checks assert_success - assert_output --partial "0.2.0+1.21.0" - assert_output --partial "overriding env file version" run grep -q "TYPE=abra-test-recipe:0.2.0+1.21.0" \ "$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env" diff --git a/vendor/github.com/urfave/cli/v3/.gitignore b/vendor/github.com/urfave/cli/v3/.gitignore deleted file mode 100644 index cbccea21..00000000 --- a/vendor/github.com/urfave/cli/v3/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -*.coverprofile -*.exe -*.orig -.*envrc -.envrc -.idea -/.local/ -/site/ -coverage.txt -internal/*/built-example -vendor diff --git a/vendor/github.com/urfave/cli/v3/.golangci.yaml b/vendor/github.com/urfave/cli/v3/.golangci.yaml deleted file mode 100644 index a6e8c7e9..00000000 --- a/vendor/github.com/urfave/cli/v3/.golangci.yaml +++ /dev/null @@ -1,5 +0,0 @@ -# https://golangci-lint.run/usage/configuration/ -linters: - enable: - - makezero - - misspell diff --git a/vendor/github.com/urfave/cli/v3/CODE_OF_CONDUCT.md b/vendor/github.com/urfave/cli/v3/CODE_OF_CONDUCT.md deleted file mode 100644 index 9fee1480..00000000 --- a/vendor/github.com/urfave/cli/v3/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,75 +0,0 @@ -# Contributor Covenant Code of Conduct - -## Our Pledge - -In the interest of fostering an open and welcoming environment, we as -contributors and maintainers pledge to making participation in our project and -our community a harassment-free experience for everyone, regardless of age, body -size, disability, ethnicity, gender identity and expression, level of experience, -education, socio-economic status, nationality, personal appearance, race, -religion, or sexual identity and orientation. - -## Our Standards - -Examples of behavior that contributes to creating a positive environment -include: - -* Using welcoming and inclusive language -* Being respectful of differing viewpoints and experiences -* Gracefully accepting constructive criticism -* Focusing on what is best for the community -* Showing empathy towards other community members - -Examples of unacceptable behavior by participants include: - -* The use of sexualized language or imagery and unwelcome sexual attention or - advances -* Trolling, insulting/derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or electronic - address, without explicit permission -* Other conduct which could reasonably be considered inappropriate in a - professional setting - -## Our Responsibilities - -Project maintainers are responsible for clarifying the standards of acceptable -behavior and are expected to take appropriate and fair corrective action in -response to any instances of unacceptable behavior. - -Project maintainers have the right and responsibility to remove, edit, or -reject comments, commits, code, wiki edits, issues, and other contributions -that are not aligned to this Code of Conduct, or to ban temporarily or -permanently any contributor for other behaviors that they deem inappropriate, -threatening, offensive, or harmful. - -## Scope - -This Code of Conduct applies both within project spaces and in public spaces -when an individual is representing the project or its community. Examples of -representing a project or community include using an official project e-mail -address, posting via an official social media account, or acting as an appointed -representative at an online or offline event. Representation of a project may be -further defined and clarified by project maintainers. - -## Enforcement - -Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported by contacting urfave-governance@googlegroups.com, a members-only group -that is world-postable. All complaints will be reviewed and investigated and -will result in a response that is deemed necessary and appropriate to the -circumstances. The project team is obligated to maintain confidentiality with -regard to the reporter of an incident. Further details of specific enforcement -policies may be posted separately. - -Project maintainers who do not follow or enforce the Code of Conduct in good -faith may face temporary or permanent repercussions as determined by other -members of the project's leadership. - -## Attribution - -This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, -available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html - -[homepage]: https://www.contributor-covenant.org - diff --git a/vendor/github.com/urfave/cli/v3/LICENSE b/vendor/github.com/urfave/cli/v3/LICENSE deleted file mode 100644 index a23fc53d..00000000 --- a/vendor/github.com/urfave/cli/v3/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2023 urfave/cli maintainers - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/vendor/github.com/urfave/cli/v3/Makefile b/vendor/github.com/urfave/cli/v3/Makefile deleted file mode 100644 index e9e13ff5..00000000 --- a/vendor/github.com/urfave/cli/v3/Makefile +++ /dev/null @@ -1,26 +0,0 @@ -# NOTE: this Makefile is meant to provide a simplified entry point for humans to -# run all of the critical steps to verify one's changes are harmonious in -# nature. Keeping target bodies to one line each and abstaining from make magic -# are very important so that maintainers and contributors can focus their -# attention on files that are primarily Go. - -GO_RUN_BUILD := go run internal/build/build.go - -.PHONY: all -all: generate vet test check-binary-size gfmrun - -# NOTE: this is a special catch-all rule to run any of the commands -# defined in internal/build/build.go with optional arguments passed -# via GFLAGS (global flags) and FLAGS (command-specific flags), e.g.: -# -# $ make test GFLAGS='--packages cli' -%: - $(GO_RUN_BUILD) $(GFLAGS) $* $(FLAGS) - -.PHONY: docs -docs: - mkdocs build - -.PHONY: serve-docs -serve-docs: - mkdocs serve diff --git a/vendor/github.com/urfave/cli/v3/README.md b/vendor/github.com/urfave/cli/v3/README.md deleted file mode 100644 index e04c7295..00000000 --- a/vendor/github.com/urfave/cli/v3/README.md +++ /dev/null @@ -1,56 +0,0 @@ -# Welcome to urfave/cli - -[![Go Reference][goreference_badge]][goreference_link] -[![Go Report Card][goreportcard_badge]][goreportcard_link] -[![codecov][codecov_badge]][codecov_link] -[![Tests status][test_badge]][test_link] - -urfave/cli is a **declarative**, simple, fast, and fun package for building -command line tools in Go featuring: - -- commands and subcommands with alias and prefix match support -- flexible and permissive help system -- dynamic shell completion for `bash`, `zsh`, `fish`, and `powershell` -- no dependencies except Go standard library -- input flags for simple types, slices of simple types, time, duration, and - others -- compound short flag support (`-a` `-b` `-c` can be shortened to `-abc`) -- documentation generation in `man` and Markdown (supported via the - [`urfave/cli-docs`][urfave/cli-docs] module) -- input lookup from: - - environment variables - - plain text files - - structured file formats (supported via the - [`urfave/cli-altsrc`][urfave/cli-altsrc] module) - -## Documentation - -See the hosted documentation website at . Contents of -this website are built from the [`./docs`](./docs) directory. - -## Support - -Check the [Q&A discussions]. If you don't find answer to your question, [create -a new discussion]. - -If you found a bug or have a feature request, [create a new issue]. - -Please keep in mind that this project is run by unpaid volunteers. - -### License - -See [`LICENSE`](./LICENSE). - -[test_badge]: https://github.com/urfave/cli/actions/workflows/test.yml/badge.svg -[test_link]: https://github.com/urfave/cli/actions/workflows/test.yml -[goreference_badge]: https://pkg.go.dev/badge/github.com/urfave/cli/v3.svg -[goreference_link]: https://pkg.go.dev/github.com/urfave/cli/v3 -[goreportcard_badge]: https://goreportcard.com/badge/github.com/urfave/cli/v3 -[goreportcard_link]: https://goreportcard.com/report/github.com/urfave/cli/v3 -[codecov_badge]: https://codecov.io/gh/urfave/cli/branch/main/graph/badge.svg?token=t9YGWLh05g -[codecov_link]: https://codecov.io/gh/urfave/cli -[Q&A discussions]: https://github.com/urfave/cli/discussions/categories/q-a -[create a new discussion]: https://github.com/urfave/cli/discussions/new?category=q-a -[urfave/cli-docs]: https://github.com/urfave/cli-docs -[urfave/cli-altsrc]: https://github.com/urfave/cli-altsrc -[create a new issue]: https://github.com/urfave/cli/issues/new/choose diff --git a/vendor/github.com/urfave/cli/v3/args.go b/vendor/github.com/urfave/cli/v3/args.go deleted file mode 100644 index 682eb497..00000000 --- a/vendor/github.com/urfave/cli/v3/args.go +++ /dev/null @@ -1,153 +0,0 @@ -package cli - -import ( - "fmt" - "time" -) - -type Args interface { - // Get returns the nth argument, or else a blank string - Get(n int) string - // First returns the first argument, or else a blank string - First() string - // Tail returns the rest of the arguments (not the first one) - // or else an empty string slice - Tail() []string - // Len returns the length of the wrapped slice - Len() int - // Present checks if there are any arguments present - Present() bool - // Slice returns a copy of the internal slice - Slice() []string -} - -type stringSliceArgs struct { - v []string -} - -func (a *stringSliceArgs) Get(n int) string { - if len(a.v) > n { - return a.v[n] - } - return "" -} - -func (a *stringSliceArgs) First() string { - return a.Get(0) -} - -func (a *stringSliceArgs) Tail() []string { - if a.Len() >= 2 { - tail := a.v[1:] - ret := make([]string, len(tail)) - copy(ret, tail) - return ret - } - - return []string{} -} - -func (a *stringSliceArgs) Len() int { - return len(a.v) -} - -func (a *stringSliceArgs) Present() bool { - return a.Len() != 0 -} - -func (a *stringSliceArgs) Slice() []string { - ret := make([]string, len(a.v)) - copy(ret, a.v) - return ret -} - -type Argument interface { - Parse([]string) ([]string, error) - Usage() string -} - -type ArgumentBase[T any, C any, VC ValueCreator[T, C]] struct { - Name string `json:"name"` // the name of this argument - Value T `json:"value"` // the default value of this argument - Destination *T `json:"-"` // the destination point for this argument - Values *[]T `json:"-"` // all the values of this argument, only if multiple are supported - UsageText string `json:"usageText"` // the usage text to show - Min int `json:"minTimes"` // the min num of occurrences of this argument - Max int `json:"maxTimes"` // the max num of occurrences of this argument, set to -1 for unlimited - Config C `json:"config"` // config for this argument similar to Flag Config -} - -func (a *ArgumentBase[T, C, VC]) Usage() string { - if a.UsageText != "" { - return a.UsageText - } - - usageFormat := "" - if a.Min == 0 { - if a.Max == 1 { - usageFormat = "[%[1]s]" - } else { - usageFormat = "[%[1]s ...]" - } - } else { - usageFormat = "%[1]s [%[1]s ...]" - } - return fmt.Sprintf(usageFormat, a.Name) -} - -func (a *ArgumentBase[T, C, VC]) Parse(s []string) ([]string, error) { - tracef("calling arg%[1] parse with args %[2]", &a.Name, s) - if a.Max == 0 { - fmt.Printf("WARNING args %s has max 0, not parsing argument", a.Name) - return s, nil - } - if a.Max != -1 && a.Min > a.Max { - fmt.Printf("WARNING args %s has min[%d] > max[%d], not parsing argument", a.Name, a.Min, a.Max) - return s, nil - } - - count := 0 - var vc VC - var t T - value := vc.Create(a.Value, &t, a.Config) - values := []T{} - - for _, arg := range s { - if err := value.Set(arg); err != nil { - return s, err - } - values = append(values, value.Get().(T)) - count++ - if count >= a.Max && a.Max > -1 { - break - } - } - if count < a.Min { - return s, fmt.Errorf("sufficient count of arg %s not provided, given %d expected %d", a.Name, count, a.Min) - } - - if a.Values == nil { - a.Values = &values - } else if count > 0 { - *a.Values = values - } - - if a.Max == 1 && a.Destination != nil { - if len(values) > 0 { - *a.Destination = values[0] - } else { - *a.Destination = t - } - } - - return s[count:], nil -} - -type ( - FloatArg = ArgumentBase[float64, NoConfig, floatValue] - IntArg = ArgumentBase[int64, IntegerConfig, intValue] - StringArg = ArgumentBase[string, StringConfig, stringValue] - StringMapArg = ArgumentBase[map[string]string, StringConfig, StringMap] - TimestampArg = ArgumentBase[time.Time, TimestampConfig, timestampValue] - UintArg = ArgumentBase[uint64, IntegerConfig, uintValue] -) diff --git a/vendor/github.com/urfave/cli/v3/autocomplete/bash_autocomplete b/vendor/github.com/urfave/cli/v3/autocomplete/bash_autocomplete deleted file mode 100644 index 7120a0d2..00000000 --- a/vendor/github.com/urfave/cli/v3/autocomplete/bash_autocomplete +++ /dev/null @@ -1,35 +0,0 @@ -#! /bin/bash - -: ${PROG:=$(basename ${BASH_SOURCE})} - -# Macs have bash3 for which the bash-completion package doesn't include -# _init_completion. This is a minimal version of that function. -_cli_init_completion() { - COMPREPLY=() - _get_comp_words_by_ref "$@" cur prev words cword -} - -_cli_bash_autocomplete() { - if [[ "${COMP_WORDS[0]}" != "source" ]]; then - local cur opts base words - COMPREPLY=() - cur="${COMP_WORDS[COMP_CWORD]}" - if declare -F _init_completion >/dev/null 2>&1; then - _init_completion -n "=:" || return - else - _cli_init_completion -n "=:" || return - fi - words=("${words[@]:0:$cword}") - if [[ "$cur" == "-"* ]]; then - requestComp="${words[*]} ${cur} --generate-shell-completion" - else - requestComp="${words[*]} --generate-shell-completion" - fi - opts=$(eval "${requestComp}" 2>/dev/null) - COMPREPLY=($(compgen -W "${opts}" -- ${cur})) - return 0 - fi -} - -complete -o bashdefault -o default -o nospace -F _cli_bash_autocomplete $PROG -unset PROG diff --git a/vendor/github.com/urfave/cli/v3/autocomplete/powershell_autocomplete.ps1 b/vendor/github.com/urfave/cli/v3/autocomplete/powershell_autocomplete.ps1 deleted file mode 100644 index cbf76942..00000000 --- a/vendor/github.com/urfave/cli/v3/autocomplete/powershell_autocomplete.ps1 +++ /dev/null @@ -1,9 +0,0 @@ -$fn = $($MyInvocation.MyCommand.Name) -$name = $fn -replace "(.*)\.ps1$", '$1' -Register-ArgumentCompleter -Native -CommandName $name -ScriptBlock { - param($commandName, $wordToComplete, $cursorPosition) - $other = "$wordToComplete --generate-shell-completion" - Invoke-Expression $other | ForEach-Object { - [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) - } - } \ No newline at end of file diff --git a/vendor/github.com/urfave/cli/v3/autocomplete/zsh_autocomplete b/vendor/github.com/urfave/cli/v3/autocomplete/zsh_autocomplete deleted file mode 100644 index 943cf60b..00000000 --- a/vendor/github.com/urfave/cli/v3/autocomplete/zsh_autocomplete +++ /dev/null @@ -1,30 +0,0 @@ -#compdef program -compdef _program program - -# Replace all occurrences of "program" in this file with the actual name of your -# CLI program. We recommend using Find+Replace feature of your editor. Let's say -# your CLI program is called "acme", then replace like so: -# * program => acme -# * _program => _acme - -_program() { - local -a opts - local cur - cur=${words[-1]} - if [[ "$cur" == "-"* ]]; then - opts=("${(@f)$(${words[@]:0:#words[@]-1} ${cur} --generate-shell-completion)}") - else - opts=("${(@f)$(${words[@]:0:#words[@]-1} --generate-shell-completion)}") - fi - - if [[ "${opts[1]}" != "" ]]; then - _describe 'values' opts - else - _files - fi -} - -# don't run the completion function when being source-ed or eval-ed -if [ "$funcstack[1]" = "_program" ]; then - _program -fi diff --git a/vendor/github.com/urfave/cli/v3/category.go b/vendor/github.com/urfave/cli/v3/category.go deleted file mode 100644 index 14e3649c..00000000 --- a/vendor/github.com/urfave/cli/v3/category.go +++ /dev/null @@ -1,195 +0,0 @@ -package cli - -import "sort" - -// CommandCategories interface allows for category manipulation -type CommandCategories interface { - // AddCommand adds a command to a category, creating a new category if necessary. - AddCommand(category string, command *Command) - // Categories returns a slice of categories sorted by name - Categories() []CommandCategory -} - -type commandCategories []*commandCategory - -func newCommandCategories() CommandCategories { - ret := commandCategories([]*commandCategory{}) - return &ret -} - -func (c *commandCategories) Less(i, j int) bool { - return lexicographicLess((*c)[i].Name(), (*c)[j].Name()) -} - -func (c *commandCategories) Len() int { - return len(*c) -} - -func (c *commandCategories) Swap(i, j int) { - (*c)[i], (*c)[j] = (*c)[j], (*c)[i] -} - -func (c *commandCategories) AddCommand(category string, command *Command) { - for _, commandCategory := range []*commandCategory(*c) { - if commandCategory.name == category { - commandCategory.commands = append(commandCategory.commands, command) - return - } - } - newVal := append(*c, - &commandCategory{name: category, commands: []*Command{command}}) - *c = newVal -} - -func (c *commandCategories) Categories() []CommandCategory { - ret := make([]CommandCategory, len(*c)) - for i, cat := range *c { - ret[i] = cat - } - return ret -} - -// CommandCategory is a category containing commands. -type CommandCategory interface { - // Name returns the category name string - Name() string - // VisibleCommands returns a slice of the Commands with Hidden=false - VisibleCommands() []*Command -} - -type commandCategory struct { - name string - commands []*Command -} - -func (c *commandCategory) Name() string { - return c.name -} - -func (c *commandCategory) VisibleCommands() []*Command { - if c.commands == nil { - c.commands = []*Command{} - } - - var ret []*Command - for _, command := range c.commands { - if !command.Hidden { - ret = append(ret, command) - } - } - return ret -} - -// FlagCategories interface allows for category manipulation -type FlagCategories interface { - // AddFlags adds a flag to a category, creating a new category if necessary. - AddFlag(category string, fl Flag) - // VisibleCategories returns a slice of visible flag categories sorted by name - VisibleCategories() []VisibleFlagCategory -} - -type defaultFlagCategories struct { - m map[string]*defaultVisibleFlagCategory -} - -func newFlagCategories() FlagCategories { - return &defaultFlagCategories{ - m: map[string]*defaultVisibleFlagCategory{}, - } -} - -func newFlagCategoriesFromFlags(fs []Flag) FlagCategories { - fc := newFlagCategories() - - var categorized bool - - for _, fl := range fs { - if cf, ok := fl.(CategorizableFlag); ok { - visible := false - if vf, ok := fl.(VisibleFlag); ok { - visible = vf.IsVisible() - } - if cat := cf.GetCategory(); cat != "" && visible { - fc.AddFlag(cat, fl) - categorized = true - } - } - } - - if categorized { - for _, fl := range fs { - if cf, ok := fl.(CategorizableFlag); ok { - visible := false - if vf, ok := fl.(VisibleFlag); ok { - visible = vf.IsVisible() - } - if cf.GetCategory() == "" && visible { - fc.AddFlag("", fl) - } - } - } - } - - return fc -} - -func (f *defaultFlagCategories) AddFlag(category string, fl Flag) { - if _, ok := f.m[category]; !ok { - f.m[category] = &defaultVisibleFlagCategory{name: category, m: map[string]Flag{}} - } - - f.m[category].m[fl.String()] = fl -} - -func (f *defaultFlagCategories) VisibleCategories() []VisibleFlagCategory { - catNames := []string{} - for name := range f.m { - catNames = append(catNames, name) - } - - sort.Strings(catNames) - - ret := make([]VisibleFlagCategory, len(catNames)) - for i, name := range catNames { - ret[i] = f.m[name] - } - - return ret -} - -// VisibleFlagCategory is a category containing flags. -type VisibleFlagCategory interface { - // Name returns the category name string - Name() string - // Flags returns a slice of VisibleFlag sorted by name - Flags() []Flag -} - -type defaultVisibleFlagCategory struct { - name string - m map[string]Flag -} - -func (fc *defaultVisibleFlagCategory) Name() string { - return fc.name -} - -func (fc *defaultVisibleFlagCategory) Flags() []Flag { - vfNames := []string{} - for flName, fl := range fc.m { - if vf, ok := fl.(VisibleFlag); ok { - if vf.IsVisible() { - vfNames = append(vfNames, flName) - } - } - } - - sort.Strings(vfNames) - - ret := make([]Flag, len(vfNames)) - for i, flName := range vfNames { - ret[i] = fc.m[flName] - } - - return ret -} diff --git a/vendor/github.com/urfave/cli/v3/cli.go b/vendor/github.com/urfave/cli/v3/cli.go deleted file mode 100644 index d833aff5..00000000 --- a/vendor/github.com/urfave/cli/v3/cli.go +++ /dev/null @@ -1,60 +0,0 @@ -// Package cli provides a minimal framework for creating and organizing command line -// Go applications. cli is designed to be easy to understand and write, the most simple -// cli application can be written as follows: -// -// func main() { -// (&cli.Command{}).Run(context.Background(), os.Args) -// } -// -// Of course this application does not do much, so let's make this an actual application: -// -// func main() { -// cmd := &cli.Command{ -// Name: "greet", -// Usage: "say a greeting", -// Action: func(c *cli.Context) error { -// fmt.Println("Greetings") -// return nil -// }, -// } -// -// cmd.Run(context.Background(), os.Args) -// } -package cli - -import ( - "fmt" - "os" - "runtime" - "strings" -) - -var isTracingOn = os.Getenv("URFAVE_CLI_TRACING") == "on" - -func tracef(format string, a ...any) { - if !isTracingOn { - return - } - - if !strings.HasSuffix(format, "\n") { - format = format + "\n" - } - - pc, file, line, _ := runtime.Caller(1) - cf := runtime.FuncForPC(pc) - - fmt.Fprintf( - os.Stderr, - strings.Join([]string{ - "## URFAVE CLI TRACE ", - file, - ":", - fmt.Sprintf("%v", line), - " ", - fmt.Sprintf("(%s)", cf.Name()), - " ", - format, - }, ""), - a..., - ) -} diff --git a/vendor/github.com/urfave/cli/v3/command.go b/vendor/github.com/urfave/cli/v3/command.go deleted file mode 100644 index 9fa8f9dc..00000000 --- a/vendor/github.com/urfave/cli/v3/command.go +++ /dev/null @@ -1,1296 +0,0 @@ -package cli - -import ( - "bufio" - "context" - "flag" - "fmt" - "io" - "os" - "path/filepath" - "reflect" - "sort" - "strings" - "unicode" -) - -const ( - // ignoreFlagPrefix is to ignore test flags when adding flags from other packages - ignoreFlagPrefix = "test." - - commandContextKey = contextKey("cli.context") -) - -type contextKey string - -// Command contains everything needed to run an application that -// accepts a string slice of arguments such as os.Args. A given -// Command may contain Flags and sub-commands in Commands. -type Command struct { - // The name of the command - Name string `json:"name"` - // A list of aliases for the command - Aliases []string `json:"aliases"` - // A short description of the usage of this command - Usage string `json:"usage"` - // Text to override the USAGE section of help - UsageText string `json:"usageText"` - // A short description of the arguments of this command - ArgsUsage string `json:"argsUsage"` - // Version of the command - Version string `json:"version"` - // Longer explanation of how the command works - Description string `json:"description"` - // DefaultCommand is the (optional) name of a command - // to run if no command names are passed as CLI arguments. - DefaultCommand string `json:"defaultCommand"` - // The category the command is part of - Category string `json:"category"` - // List of child commands - Commands []*Command `json:"commands"` - // List of flags to parse - Flags []Flag `json:"flags"` - // Boolean to hide built-in help command and help flag - HideHelp bool `json:"hideHelp"` - // Ignored if HideHelp is true. - HideHelpCommand bool `json:"hideHelpCommand"` - // Boolean to hide built-in version flag and the VERSION section of help - HideVersion bool `json:"hideVersion"` - // Boolean to enable shell completion commands - EnableShellCompletion bool `json:"-"` - // Shell Completion generation command name - ShellCompletionCommandName string `json:"-"` - // The function to call when checking for shell command completions - ShellComplete ShellCompleteFunc `json:"-"` - // An action to execute before any subcommands are run, but after the context is ready - // If a non-nil error is returned, no subcommands are run - Before BeforeFunc `json:"-"` - // An action to execute after any subcommands are run, but after the subcommand has finished - // It is run even if Action() panics - After AfterFunc `json:"-"` - // The function to call when this command is invoked - Action ActionFunc `json:"-"` - // Execute this function if the proper command cannot be found - CommandNotFound CommandNotFoundFunc `json:"-"` - // Execute this function if a usage error occurs. - OnUsageError OnUsageErrorFunc `json:"-"` - // Execute this function when an invalid flag is accessed from the context - InvalidFlagAccessHandler InvalidFlagAccessFunc `json:"-"` - // Boolean to hide this command from help or completion - Hidden bool `json:"hidden"` - // List of all authors who contributed (string or fmt.Stringer) - // TODO: ~string | fmt.Stringer when interface unions are available - Authors []any `json:"authors"` - // Copyright of the binary if any - Copyright string `json:"copyright"` - // Reader reader to write input to (useful for tests) - Reader io.Reader `json:"-"` - // Writer writer to write output to - Writer io.Writer `json:"-"` - // ErrWriter writes error output - ErrWriter io.Writer `json:"-"` - // ExitErrHandler processes any error encountered while running an App before - // it is returned to the caller. If no function is provided, HandleExitCoder - // is used as the default behavior. - ExitErrHandler ExitErrHandlerFunc `json:"-"` - // Other custom info - Metadata map[string]interface{} `json:"metadata"` - // Carries a function which returns app specific info. - ExtraInfo func() map[string]string `json:"-"` - // CustomRootCommandHelpTemplate the text template for app help topic. - // cli.go uses text/template to render templates. You can - // render custom help text by setting this variable. - CustomRootCommandHelpTemplate string `json:"-"` - // SliceFlagSeparator is used to customize the separator for SliceFlag, the default is "," - SliceFlagSeparator string `json:"sliceFlagSeparator"` - // DisableSliceFlagSeparator is used to disable SliceFlagSeparator, the default is false - DisableSliceFlagSeparator bool `json:"disableSliceFlagSeparator"` - // Boolean to enable short-option handling so user can combine several - // single-character bool arguments into one - // i.e. foobar -o -v -> foobar -ov - UseShortOptionHandling bool `json:"useShortOptionHandling"` - // Enable suggestions for commands and flags - Suggest bool `json:"suggest"` - // Allows global flags set by libraries which use flag.XXXVar(...) directly - // to be parsed through this library - AllowExtFlags bool `json:"allowExtFlags"` - // Treat all flags as normal arguments if true - SkipFlagParsing bool `json:"skipFlagParsing"` - // CustomHelpTemplate the text template for the command help topic. - // cli.go uses text/template to render templates. You can - // render custom help text by setting this variable. - CustomHelpTemplate string `json:"-"` - // Use longest prefix match for commands - PrefixMatchCommands bool `json:"prefixMatchCommands"` - // Custom suggest command for matching - SuggestCommandFunc SuggestCommandFunc `json:"-"` - // Flag exclusion group - MutuallyExclusiveFlags []MutuallyExclusiveFlags `json:"mutuallyExclusiveFlags"` - // Arguments to parse for this command - Arguments []Argument `json:"arguments"` - // Whether to read arguments from stdin - // applicable to root command only - ReadArgsFromStdin bool `json:"readArgsFromStdin"` - - // categories contains the categorized commands and is populated on app startup - categories CommandCategories - // flagCategories contains the categorized flags and is populated on app startup - flagCategories FlagCategories - // flags that have been applied in current parse - appliedFlags []Flag - // The parent of this command. This value will be nil for the - // command at the root of the graph. - parent *Command - // the flag.FlagSet for this command - flagSet *flag.FlagSet - // parsed args - parsedArgs Args - // track state of error handling - isInError bool - // track state of defaults - didSetupDefaults bool - // whether in shell completion mode - shellCompletion bool -} - -// FullName returns the full name of the command. -// For commands with parents this ensures that the parent commands -// are part of the command path. -func (cmd *Command) FullName() string { - namePath := []string{} - - if cmd.parent != nil { - namePath = append(namePath, cmd.parent.FullName()) - } - - return strings.Join(append(namePath, cmd.Name), " ") -} - -func (cmd *Command) Command(name string) *Command { - for _, subCmd := range cmd.Commands { - if subCmd.HasName(name) { - return subCmd - } - } - - return nil -} - -func (cmd *Command) setupDefaults(osArgs []string) { - if cmd.didSetupDefaults { - tracef("already did setup (cmd=%[1]q)", cmd.Name) - return - } - - cmd.didSetupDefaults = true - - isRoot := cmd.parent == nil - tracef("isRoot? %[1]v (cmd=%[2]q)", isRoot, cmd.Name) - - if cmd.ShellComplete == nil { - tracef("setting default ShellComplete (cmd=%[1]q)", cmd.Name) - cmd.ShellComplete = DefaultCompleteWithFlags - } - - if cmd.Name == "" && isRoot { - name := filepath.Base(osArgs[0]) - tracef("setting cmd.Name from first arg basename (cmd=%[1]q)", name) - cmd.Name = name - } - - if cmd.Usage == "" && isRoot { - tracef("setting default Usage (cmd=%[1]q)", cmd.Name) - cmd.Usage = "A new cli application" - } - - if cmd.Version == "" { - tracef("setting HideVersion=true due to empty Version (cmd=%[1]q)", cmd.Name) - cmd.HideVersion = true - } - - if cmd.Action == nil { - tracef("setting default Action as help command action (cmd=%[1]q)", cmd.Name) - cmd.Action = helpCommandAction - } - - if cmd.Reader == nil { - tracef("setting default Reader as os.Stdin (cmd=%[1]q)", cmd.Name) - cmd.Reader = os.Stdin - } - - if cmd.Writer == nil { - tracef("setting default Writer as os.Stdout (cmd=%[1]q)", cmd.Name) - cmd.Writer = os.Stdout - } - - if cmd.ErrWriter == nil { - tracef("setting default ErrWriter as os.Stderr (cmd=%[1]q)", cmd.Name) - cmd.ErrWriter = os.Stderr - } - - if cmd.AllowExtFlags { - tracef("visiting all flags given AllowExtFlags=true (cmd=%[1]q)", cmd.Name) - // add global flags added by other packages - flag.VisitAll(func(f *flag.Flag) { - // skip test flags - if !strings.HasPrefix(f.Name, ignoreFlagPrefix) { - cmd.Flags = append(cmd.Flags, &extFlag{f}) - } - }) - } - - for _, subCmd := range cmd.Commands { - tracef("setting sub-command (cmd=%[1]q) parent as self (cmd=%[2]q)", subCmd.Name, cmd.Name) - subCmd.parent = cmd - } - - cmd.ensureHelp() - - if !cmd.HideVersion && isRoot { - tracef("appending version flag (cmd=%[1]q)", cmd.Name) - cmd.appendFlag(VersionFlag) - } - - if cmd.PrefixMatchCommands && cmd.SuggestCommandFunc == nil { - tracef("setting default SuggestCommandFunc (cmd=%[1]q)", cmd.Name) - cmd.SuggestCommandFunc = suggestCommand - } - - if cmd.EnableShellCompletion || cmd.Root().shellCompletion { - completionCommand := buildCompletionCommand() - - if cmd.ShellCompletionCommandName != "" { - tracef( - "setting completion command name (%[1]q) from "+ - "cmd.ShellCompletionCommandName (cmd=%[2]q)", - cmd.ShellCompletionCommandName, cmd.Name, - ) - completionCommand.Name = cmd.ShellCompletionCommandName - } - - tracef("appending completionCommand (cmd=%[1]q)", cmd.Name) - cmd.appendCommand(completionCommand) - } - - tracef("setting command categories (cmd=%[1]q)", cmd.Name) - cmd.categories = newCommandCategories() - - for _, subCmd := range cmd.Commands { - cmd.categories.AddCommand(subCmd.Category, subCmd) - } - - tracef("sorting command categories (cmd=%[1]q)", cmd.Name) - sort.Sort(cmd.categories.(*commandCategories)) - - tracef("setting category on mutually exclusive flags (cmd=%[1]q)", cmd.Name) - for _, grp := range cmd.MutuallyExclusiveFlags { - grp.propagateCategory() - } - - tracef("setting flag categories (cmd=%[1]q)", cmd.Name) - cmd.flagCategories = newFlagCategoriesFromFlags(cmd.allFlags()) - - if cmd.Metadata == nil { - tracef("setting default Metadata (cmd=%[1]q)", cmd.Name) - cmd.Metadata = map[string]any{} - } - - if len(cmd.SliceFlagSeparator) != 0 { - tracef("setting defaultSliceFlagSeparator from cmd.SliceFlagSeparator (cmd=%[1]q)", cmd.Name) - defaultSliceFlagSeparator = cmd.SliceFlagSeparator - } - - tracef("setting disableSliceFlagSeparator from cmd.DisableSliceFlagSeparator (cmd=%[1]q)", cmd.Name) - disableSliceFlagSeparator = cmd.DisableSliceFlagSeparator -} - -func (cmd *Command) setupCommandGraph() { - tracef("setting up command graph (cmd=%[1]q)", cmd.Name) - - for _, subCmd := range cmd.Commands { - subCmd.parent = cmd - subCmd.setupSubcommand() - subCmd.setupCommandGraph() - } -} - -func (cmd *Command) setupSubcommand() { - tracef("setting up self as sub-command (cmd=%[1]q)", cmd.Name) - - cmd.ensureHelp() - - tracef("setting command categories (cmd=%[1]q)", cmd.Name) - cmd.categories = newCommandCategories() - - for _, subCmd := range cmd.Commands { - cmd.categories.AddCommand(subCmd.Category, subCmd) - } - - tracef("sorting command categories (cmd=%[1]q)", cmd.Name) - sort.Sort(cmd.categories.(*commandCategories)) - - tracef("setting category on mutually exclusive flags (cmd=%[1]q)", cmd.Name) - for _, grp := range cmd.MutuallyExclusiveFlags { - grp.propagateCategory() - } - - tracef("setting flag categories (cmd=%[1]q)", cmd.Name) - cmd.flagCategories = newFlagCategoriesFromFlags(cmd.allFlags()) -} - -func (cmd *Command) hideHelp() bool { - tracef("hide help (cmd=%[1]q)", cmd.Name) - for c := cmd; c != nil; c = c.parent { - if c.HideHelp { - return true - } - } - - return false -} - -func (cmd *Command) ensureHelp() { - tracef("ensuring help (cmd=%[1]q)", cmd.Name) - - helpCommand := buildHelpCommand(true) - - if !cmd.hideHelp() { - if cmd.Command(helpCommand.Name) == nil { - if !cmd.HideHelpCommand { - tracef("appending helpCommand (cmd=%[1]q)", cmd.Name) - cmd.appendCommand(helpCommand) - } - } - - if HelpFlag != nil { - tracef("appending HelpFlag (cmd=%[1]q)", cmd.Name) - cmd.appendFlag(HelpFlag) - } - } -} - -func (cmd *Command) parseArgsFromStdin() ([]string, error) { - type state int - const ( - stateSearchForToken state = -1 - stateSearchForString state = 0 - ) - - st := stateSearchForToken - linenum := 1 - token := "" - args := []string{} - - breader := bufio.NewReader(cmd.Reader) - -outer: - for { - ch, _, err := breader.ReadRune() - if err == io.EOF { - switch st { - case stateSearchForToken: - if token != "--" { - args = append(args, token) - } - case stateSearchForString: - // make sure string is not empty - for _, t := range token { - if !unicode.IsSpace(t) { - args = append(args, token) - } - } - } - break outer - } - if err != nil { - return nil, err - } - switch st { - case stateSearchForToken: - if unicode.IsSpace(ch) || ch == '"' { - if ch == '\n' { - linenum++ - } - if token != "" { - // end the processing here - if token == "--" { - break outer - } - args = append(args, token) - token = "" - } - if ch == '"' { - st = stateSearchForString - } - continue - } - token += string(ch) - case stateSearchForString: - if ch != '"' { - token += string(ch) - } else { - if token != "" { - args = append(args, token) - token = "" - } - /*else { - //TODO. Should we pass in empty strings ? - }*/ - st = stateSearchForToken - } - } - } - - tracef("parsed stdin args as %v (cmd=%[2]q)", args, cmd.Name) - - return args, nil -} - -// Run is the entry point to the command graph. The positional -// arguments are parsed according to the Flag and Command -// definitions and the matching Action functions are run. -func (cmd *Command) Run(ctx context.Context, osArgs []string) (deferErr error) { - tracef("running with arguments %[1]q (cmd=%[2]q)", osArgs, cmd.Name) - cmd.setupDefaults(osArgs) - - if v, ok := ctx.Value(commandContextKey).(*Command); ok { - tracef("setting parent (cmd=%[1]q) command from context.Context value (cmd=%[2]q)", v.Name, cmd.Name) - cmd.parent = v - } - - if cmd.parent == nil { - if cmd.ReadArgsFromStdin { - if args, err := cmd.parseArgsFromStdin(); err != nil { - return err - } else { - osArgs = append(osArgs, args...) - } - } - // handle the completion flag separately from the flagset since - // completion could be attempted after a flag, but before its value was put - // on the command line. this causes the flagset to interpret the completion - // flag name as the value of the flag before it which is undesirable - // note that we can only do this because the shell autocomplete function - // always appends the completion flag at the end of the command - tracef("checking osArgs %v (cmd=%[2]q)", osArgs, cmd.Name) - cmd.shellCompletion, osArgs = checkShellCompleteFlag(cmd, osArgs) - - tracef("setting cmd.shellCompletion=%[1]v from checkShellCompleteFlag (cmd=%[2]q)", cmd.shellCompletion && cmd.EnableShellCompletion, cmd.Name) - cmd.shellCompletion = cmd.EnableShellCompletion && cmd.shellCompletion - } - - tracef("using post-checkShellCompleteFlag arguments %[1]q (cmd=%[2]q)", osArgs, cmd.Name) - - tracef("setting self as cmd in context (cmd=%[1]q)", cmd.Name) - ctx = context.WithValue(ctx, commandContextKey, cmd) - - if cmd.parent == nil { - cmd.setupCommandGraph() - } - - args, err := cmd.parseFlags(&stringSliceArgs{v: osArgs}) - - tracef("using post-parse arguments %[1]q (cmd=%[2]q)", args, cmd.Name) - - if checkCompletions(ctx, cmd) { - return nil - } - - if err != nil { - tracef("setting deferErr from %[1]q (cmd=%[2]q)", err, cmd.Name) - deferErr = err - - cmd.isInError = true - if cmd.OnUsageError != nil { - err = cmd.OnUsageError(ctx, cmd, err, cmd.parent != nil) - err = cmd.handleExitCoder(ctx, err) - return err - } - fmt.Fprintf(cmd.Root().ErrWriter, "Incorrect Usage: %s\n\n", err.Error()) - if cmd.Suggest { - if suggestion, err := cmd.suggestFlagFromError(err, ""); err == nil { - fmt.Fprintf(cmd.Root().ErrWriter, "%s", suggestion) - } - } - if !cmd.hideHelp() { - if cmd.parent == nil { - tracef("running ShowAppHelp") - if err := ShowAppHelp(cmd); err != nil { - tracef("SILENTLY IGNORING ERROR running ShowAppHelp %[1]v (cmd=%[2]q)", err, cmd.Name) - } - } else { - tracef("running ShowCommandHelp with %[1]q", cmd.Name) - if err := ShowCommandHelp(ctx, cmd, cmd.Name); err != nil { - tracef("SILENTLY IGNORING ERROR running ShowCommandHelp with %[1]q %[2]v", cmd.Name, err) - } - } - } - - return err - } - - if cmd.checkHelp() { - return helpCommandAction(ctx, cmd) - } else { - tracef("no help is wanted (cmd=%[1]q)", cmd.Name) - } - - if cmd.parent == nil && !cmd.HideVersion && checkVersion(cmd) { - ShowVersion(cmd) - return nil - } - - if cmd.After != nil && !cmd.Root().shellCompletion { - defer func() { - if err := cmd.After(ctx, cmd); err != nil { - err = cmd.handleExitCoder(ctx, err) - - if deferErr != nil { - deferErr = newMultiError(deferErr, err) - } else { - deferErr = err - } - } - }() - } - - for _, grp := range cmd.MutuallyExclusiveFlags { - if err := grp.check(cmd); err != nil { - _ = ShowSubcommandHelp(cmd) - return err - } - } - - if cmd.Before != nil && !cmd.Root().shellCompletion { - if err := cmd.Before(ctx, cmd); err != nil { - deferErr = cmd.handleExitCoder(ctx, err) - return deferErr - } - } - - tracef("running flag actions (cmd=%[1]q)", cmd.Name) - - if err := cmd.runFlagActions(ctx); err != nil { - return err - } - - var subCmd *Command - - if args.Present() { - tracef("checking positional args %[1]q (cmd=%[2]q)", args, cmd.Name) - - name := args.First() - - tracef("using first positional argument as sub-command name=%[1]q (cmd=%[2]q)", name, cmd.Name) - - if cmd.SuggestCommandFunc != nil { - name = cmd.SuggestCommandFunc(cmd.Commands, name) - } - subCmd = cmd.Command(name) - if subCmd == nil { - hasDefault := cmd.DefaultCommand != "" - isFlagName := checkStringSliceIncludes(name, cmd.FlagNames()) - - if hasDefault { - tracef("using default command=%[1]q (cmd=%[2]q)", cmd.DefaultCommand, cmd.Name) - } - - if isFlagName || hasDefault { - argsWithDefault := cmd.argsWithDefaultCommand(args) - tracef("using default command args=%[1]q (cmd=%[2]q)", argsWithDefault, cmd.Name) - if !reflect.DeepEqual(args, argsWithDefault) { - subCmd = cmd.Command(argsWithDefault.First()) - } - } - } - } else if cmd.parent == nil && cmd.DefaultCommand != "" { - tracef("no positional args present; checking default command %[1]q (cmd=%[2]q)", cmd.DefaultCommand, cmd.Name) - - if dc := cmd.Command(cmd.DefaultCommand); dc != cmd { - subCmd = dc - } - } - - if subCmd != nil { - tracef("running sub-command %[1]q with arguments %[2]q (cmd=%[3]q)", subCmd.Name, cmd.Args(), cmd.Name) - return subCmd.Run(ctx, cmd.Args().Slice()) - } - - if cmd.Action == nil { - cmd.Action = helpCommandAction - } else { - if err := cmd.checkAllRequiredFlags(); err != nil { - cmd.isInError = true - _ = ShowSubcommandHelp(cmd) - return err - } - - if len(cmd.Arguments) > 0 { - rargs := cmd.Args().Slice() - tracef("calling argparse with %[1]v", rargs) - for _, arg := range cmd.Arguments { - var err error - rargs, err = arg.Parse(rargs) - if err != nil { - tracef("calling with %[1]v (cmd=%[2]q)", err, cmd.Name) - return err - } - } - cmd.parsedArgs = &stringSliceArgs{v: rargs} - } - } - - if err := cmd.Action(ctx, cmd); err != nil { - tracef("calling handleExitCoder with %[1]v (cmd=%[2]q)", err, cmd.Name) - deferErr = cmd.handleExitCoder(ctx, err) - } - - tracef("returning deferErr (cmd=%[1]q) %[2]q", cmd.Name, deferErr) - return deferErr -} - -func (cmd *Command) checkHelp() bool { - tracef("checking if help is wanted (cmd=%[1]q)", cmd.Name) - - if HelpFlag == nil { - return false - } - - for _, name := range HelpFlag.Names() { - if cmd.Bool(name) { - return true - } - } - - return false -} - -func (cmd *Command) newFlagSet() (*flag.FlagSet, error) { - allFlags := cmd.allFlags() - - cmd.appliedFlags = append(cmd.appliedFlags, allFlags...) - - tracef("making new flag set (cmd=%[1]q)", cmd.Name) - - return newFlagSet(cmd.Name, allFlags) -} - -func (cmd *Command) allFlags() []Flag { - var flags []Flag - flags = append(flags, cmd.Flags...) - for _, grpf := range cmd.MutuallyExclusiveFlags { - for _, f1 := range grpf.Flags { - flags = append(flags, f1...) - } - } - return flags -} - -// useShortOptionHandling traverses Lineage() for *any* ancestors -// with UseShortOptionHandling -func (cmd *Command) useShortOptionHandling() bool { - for _, pCmd := range cmd.Lineage() { - if pCmd.UseShortOptionHandling { - return true - } - } - - return false -} - -func (cmd *Command) suggestFlagFromError(err error, commandName string) (string, error) { - fl, parseErr := flagFromError(err) - if parseErr != nil { - return "", err - } - - flags := cmd.Flags - hideHelp := cmd.hideHelp() - - if commandName != "" { - subCmd := cmd.Command(commandName) - if subCmd == nil { - return "", err - } - flags = subCmd.Flags - hideHelp = hideHelp || subCmd.HideHelp - } - - suggestion := SuggestFlag(flags, fl, hideHelp) - if len(suggestion) == 0 { - return "", err - } - - return fmt.Sprintf(SuggestDidYouMeanTemplate, suggestion) + "\n\n", nil -} - -func (cmd *Command) parseFlags(args Args) (Args, error) { - tracef("parsing flags from arguments %[1]q (cmd=%[2]q)", args, cmd.Name) - - cmd.parsedArgs = nil - if v, err := cmd.newFlagSet(); err != nil { - return args, err - } else { - cmd.flagSet = v - } - - if cmd.SkipFlagParsing { - tracef("skipping flag parsing (cmd=%[1]q)", cmd.Name) - - return cmd.Args(), cmd.flagSet.Parse(append([]string{"--"}, args.Tail()...)) - } - - tracef("walking command lineage for persistent flags (cmd=%[1]q)", cmd.Name) - - for pCmd := cmd.parent; pCmd != nil; pCmd = pCmd.parent { - tracef( - "checking ancestor command=%[1]q for persistent flags (cmd=%[2]q)", - pCmd.Name, cmd.Name, - ) - - for _, fl := range pCmd.Flags { - flNames := fl.Names() - - pfl, ok := fl.(LocalFlag) - if !ok || pfl.IsLocal() { - tracef("skipping non-persistent flag %[1]q (cmd=%[2]q)", flNames, cmd.Name) - continue - } - - tracef( - "checking for applying persistent flag=%[1]q pCmd=%[2]q (cmd=%[3]q)", - flNames, pCmd.Name, cmd.Name, - ) - - applyPersistentFlag := true - - cmd.flagSet.VisitAll(func(f *flag.Flag) { - for _, name := range flNames { - if name == f.Name { - applyPersistentFlag = false - break - } - } - }) - - if !applyPersistentFlag { - tracef("not applying as persistent flag=%[1]q (cmd=%[2]q)", flNames, cmd.Name) - - continue - } - - tracef("applying as persistent flag=%[1]q (cmd=%[2]q)", flNames, cmd.Name) - - if err := fl.Apply(cmd.flagSet); err != nil { - return cmd.Args(), err - } - - tracef("appending to applied flags flag=%[1]q (cmd=%[2]q)", flNames, cmd.Name) - cmd.appliedFlags = append(cmd.appliedFlags, fl) - } - } - - tracef("parsing flags iteratively tail=%[1]q (cmd=%[2]q)", args.Tail(), cmd.Name) - defer tracef("done parsing flags (cmd=%[1]q)", cmd.Name) - - rargs := args.Tail() - posArgs := []string{} - for { - tracef("rearrange:1 (cmd=%[1]q) %[2]q", cmd.Name, rargs) - for { - tracef("rearrange:2 (cmd=%[1]q) %[2]q %[3]q", cmd.Name, posArgs, rargs) - - // no more args to parse. Break out of inner loop - if len(rargs) == 0 { - break - } - - if strings.TrimSpace(rargs[0]) == "" { - break - } - - // stop parsing once we see a "--" - if rargs[0] == "--" { - posArgs = append(posArgs, rargs...) - cmd.parsedArgs = &stringSliceArgs{posArgs} - return cmd.parsedArgs, nil - } - - // let flagset parse this - if rargs[0][0] == '-' { - break - } - - tracef("rearrange-3 (cmd=%[1]q) check %[2]q", cmd.Name, rargs[0]) - - // if there is a command by that name let the command handle the - // rest of the parsing - if cmd.Command(rargs[0]) != nil { - posArgs = append(posArgs, rargs...) - cmd.parsedArgs = &stringSliceArgs{posArgs} - return cmd.parsedArgs, nil - } - - posArgs = append(posArgs, rargs[0]) - - // if this is the sole argument then - // break from inner loop - if len(rargs) == 1 { - rargs = []string{} - break - } - - rargs = rargs[1:] - } - if err := parseIter(cmd.flagSet, cmd, rargs, cmd.Root().shellCompletion); err != nil { - posArgs = append(posArgs, cmd.flagSet.Args()...) - tracef("returning-1 (cmd=%[1]q) args %[2]q", cmd.Name, posArgs) - cmd.parsedArgs = &stringSliceArgs{posArgs} - return cmd.parsedArgs, err - } - tracef("rearrange-4 (cmd=%[1]q) check %[2]q", cmd.Name, cmd.flagSet.Args()) - rargs = cmd.flagSet.Args() - if len(rargs) == 0 || strings.TrimSpace(rargs[0]) == "" || rargs[0] == "-" { - break - } - } - - posArgs = append(posArgs, cmd.flagSet.Args()...) - tracef("returning-2 (cmd=%[1]q) args %[2]q", cmd.Name, posArgs) - cmd.parsedArgs = &stringSliceArgs{posArgs} - return cmd.parsedArgs, nil -} - -// Names returns the names including short names and aliases. -func (cmd *Command) Names() []string { - return append([]string{cmd.Name}, cmd.Aliases...) -} - -// HasName returns true if Command.Name matches given name -func (cmd *Command) HasName(name string) bool { - for _, n := range cmd.Names() { - if n == name { - return true - } - } - - return false -} - -// VisibleCategories returns a slice of categories and commands that are -// Hidden=false -func (cmd *Command) VisibleCategories() []CommandCategory { - ret := []CommandCategory{} - for _, category := range cmd.categories.Categories() { - if visible := func() CommandCategory { - if len(category.VisibleCommands()) > 0 { - return category - } - return nil - }(); visible != nil { - ret = append(ret, visible) - } - } - return ret -} - -// VisibleCommands returns a slice of the Commands with Hidden=false -func (cmd *Command) VisibleCommands() []*Command { - var ret []*Command - for _, command := range cmd.Commands { - if !command.Hidden { - ret = append(ret, command) - } - } - return ret -} - -// VisibleFlagCategories returns a slice containing all the visible flag categories with the flags they contain -func (cmd *Command) VisibleFlagCategories() []VisibleFlagCategory { - if cmd.flagCategories == nil { - cmd.flagCategories = newFlagCategoriesFromFlags(cmd.allFlags()) - } - return cmd.flagCategories.VisibleCategories() -} - -// VisibleFlags returns a slice of the Flags with Hidden=false -func (cmd *Command) VisibleFlags() []Flag { - return visibleFlags(cmd.allFlags()) -} - -func (cmd *Command) appendFlag(fl Flag) { - if !hasFlag(cmd.Flags, fl) { - cmd.Flags = append(cmd.Flags, fl) - } -} - -// VisiblePersistentFlags returns a slice of [LocalFlag] with Persistent=true and Hidden=false. -func (cmd *Command) VisiblePersistentFlags() []Flag { - var flags []Flag - for _, fl := range cmd.Root().Flags { - pfl, ok := fl.(LocalFlag) - if !ok || pfl.IsLocal() { - continue - } - flags = append(flags, fl) - } - return visibleFlags(flags) -} - -func (cmd *Command) appendCommand(aCmd *Command) { - if !hasCommand(cmd.Commands, aCmd) { - aCmd.parent = cmd - cmd.Commands = append(cmd.Commands, aCmd) - } -} - -func (cmd *Command) handleExitCoder(ctx context.Context, err error) error { - if cmd.parent != nil { - return cmd.parent.handleExitCoder(ctx, err) - } - - if cmd.ExitErrHandler != nil { - cmd.ExitErrHandler(ctx, cmd, err) - return err - } - - HandleExitCoder(err) - return err -} - -func (cmd *Command) argsWithDefaultCommand(oldArgs Args) Args { - if cmd.DefaultCommand != "" { - rawArgs := append([]string{cmd.DefaultCommand}, oldArgs.Slice()...) - newArgs := &stringSliceArgs{v: rawArgs} - - return newArgs - } - - return oldArgs -} - -// Root returns the Command at the root of the graph -func (cmd *Command) Root() *Command { - if cmd.parent == nil { - return cmd - } - - return cmd.parent.Root() -} - -func (cmd *Command) lookupFlag(name string) Flag { - for _, pCmd := range cmd.Lineage() { - for _, f := range pCmd.Flags { - for _, n := range f.Names() { - if n == name { - tracef("flag found for name %[1]q (cmd=%[2]q)", name, cmd.Name) - return f - } - } - } - } - - tracef("flag NOT found for name %[1]q (cmd=%[2]q)", name, cmd.Name) - return nil -} - -func (cmd *Command) lookupFlagSet(name string) *flag.FlagSet { - for _, pCmd := range cmd.Lineage() { - if pCmd.flagSet == nil { - continue - } - - if f := pCmd.flagSet.Lookup(name); f != nil { - tracef("matching flag set found for name %[1]q (cmd=%[2]q)", name, cmd.Name) - return pCmd.flagSet - } - } - - tracef("matching flag set NOT found for name %[1]q (cmd=%[2]q)", name, cmd.Name) - cmd.onInvalidFlag(context.TODO(), name) - return nil -} - -func (cmd *Command) checkRequiredFlag(f Flag) (bool, string) { - if rf, ok := f.(RequiredFlag); ok && rf.IsRequired() { - flagPresent := false - flagName := "" - - for _, key := range f.Names() { - // use the first name to return since that is the - // primary flag name - if flagName == "" { - flagName = key - } - - if cmd.IsSet(strings.TrimSpace(key)) { - flagPresent = true - break - } - } - - if !flagPresent && flagName != "" { - return false, flagName - } - } - return true, "" -} - -func (cmd *Command) checkAllRequiredFlags() requiredFlagsErr { - for pCmd := cmd; pCmd != nil; pCmd = pCmd.parent { - if err := pCmd.checkRequiredFlags(); err != nil { - return err - } - } - return nil -} - -func (cmd *Command) checkRequiredFlags() requiredFlagsErr { - tracef("checking for required flags (cmd=%[1]q)", cmd.Name) - - missingFlags := []string{} - - for _, f := range cmd.appliedFlags { - if ok, name := cmd.checkRequiredFlag(f); !ok { - missingFlags = append(missingFlags, name) - } - } - - if len(missingFlags) != 0 { - tracef("found missing required flags %[1]q (cmd=%[2]q)", missingFlags, cmd.Name) - - return &errRequiredFlags{missingFlags: missingFlags} - } - - tracef("all required flags set (cmd=%[1]q)", cmd.Name) - - return nil -} - -func (cmd *Command) onInvalidFlag(ctx context.Context, name string) { - for cmd != nil { - if cmd.InvalidFlagAccessHandler != nil { - cmd.InvalidFlagAccessHandler(ctx, cmd, name) - break - } - cmd = cmd.parent - } -} - -// NumFlags returns the number of flags set -func (cmd *Command) NumFlags() int { - return cmd.flagSet.NFlag() -} - -// Set sets a context flag to a value. -func (cmd *Command) Set(name, value string) error { - if fs := cmd.lookupFlagSet(name); fs != nil { - return fs.Set(name, value) - } - - return fmt.Errorf("no such flag -%s", name) -} - -// IsSet determines if the flag was actually set -func (cmd *Command) IsSet(name string) bool { - flSet := cmd.lookupFlagSet(name) - - if flSet == nil { - return false - } - - isSet := false - - flSet.Visit(func(f *flag.Flag) { - if f.Name == name { - isSet = true - } - }) - - if isSet { - tracef("flag with name %[1]q found via flag set lookup (cmd=%[2]q)", name, cmd.Name) - return true - } - - fl := cmd.lookupFlag(name) - if fl == nil { - tracef("flag with name %[1]q NOT found; assuming not set (cmd=%[2]q)", name, cmd.Name) - return false - } - - isSet = fl.IsSet() - if isSet { - tracef("flag with name %[1]q is set (cmd=%[2]q)", name, cmd.Name) - } else { - tracef("flag with name %[1]q is NOT set (cmd=%[2]q)", name, cmd.Name) - } - - return isSet -} - -// LocalFlagNames returns a slice of flag names used in this -// command. -func (cmd *Command) LocalFlagNames() []string { - names := []string{} - - cmd.flagSet.Visit(makeFlagNameVisitor(&names)) - - // Check the flags which have been set via env or file - for _, f := range cmd.Flags { - if f.IsSet() { - names = append(names, f.Names()...) - } - } - - // Sort out the duplicates since flag could be set via multiple - // paths - m := map[string]struct{}{} - uniqNames := []string{} - - for _, name := range names { - if _, ok := m[name]; !ok { - m[name] = struct{}{} - uniqNames = append(uniqNames, name) - } - } - - return uniqNames -} - -// FlagNames returns a slice of flag names used by the this command -// and all of its parent commands. -func (cmd *Command) FlagNames() []string { - names := cmd.LocalFlagNames() - - if cmd.parent != nil { - names = append(cmd.parent.FlagNames(), names...) - } - - return names -} - -// Lineage returns *this* command and all of its ancestor commands -// in order from child to parent -func (cmd *Command) Lineage() []*Command { - lineage := []*Command{cmd} - - if cmd.parent != nil { - lineage = append(lineage, cmd.parent.Lineage()...) - } - - return lineage -} - -// Count returns the num of occurrences of this flag -func (cmd *Command) Count(name string) int { - if fs := cmd.lookupFlagSet(name); fs != nil { - if cf, ok := fs.Lookup(name).Value.(Countable); ok { - return cf.Count() - } - } - return 0 -} - -// Value returns the value of the flag corresponding to `name` -func (cmd *Command) Value(name string) interface{} { - if fs := cmd.lookupFlagSet(name); fs != nil { - tracef("value found for name %[1]q (cmd=%[2]q)", name, cmd.Name) - return fs.Lookup(name).Value.(flag.Getter).Get() - } - - tracef("value NOT found for name %[1]q (cmd=%[2]q)", name, cmd.Name) - return nil -} - -// Args returns the command line arguments associated with the -// command. -func (cmd *Command) Args() Args { - if cmd.parsedArgs != nil { - return cmd.parsedArgs - } - return &stringSliceArgs{v: cmd.flagSet.Args()} -} - -// NArg returns the number of the command line arguments. -func (cmd *Command) NArg() int { - return cmd.Args().Len() -} - -func hasCommand(commands []*Command, command *Command) bool { - for _, existing := range commands { - if command == existing { - return true - } - } - - return false -} - -func (cmd *Command) runFlagActions(ctx context.Context) error { - for _, fl := range cmd.appliedFlags { - isSet := false - - // check only local flagset for running local flag actions - for _, name := range fl.Names() { - cmd.flagSet.Visit(func(f *flag.Flag) { - if f.Name == name { - isSet = true - } - }) - if isSet { - break - } - } - - // If the flag hasnt been set on cmd line then we need to further - // check if it has been set via other means. If however it has - // been set by other means but it is persistent(and not set via current cmd) - // do not run the flag action - if !isSet { - if !fl.IsSet() { - continue - } - if pf, ok := fl.(LocalFlag); ok && !pf.IsLocal() { - continue - } - } - - if af, ok := fl.(ActionableFlag); ok { - if err := af.RunAction(ctx, cmd); err != nil { - return err - } - } - } - - return nil -} - -func checkStringSliceIncludes(want string, sSlice []string) bool { - found := false - for _, s := range sSlice { - if want == s { - found = true - break - } - } - - return found -} - -func makeFlagNameVisitor(names *[]string) func(*flag.Flag) { - return func(f *flag.Flag) { - nameParts := strings.Split(f.Name, ",") - name := strings.TrimSpace(nameParts[0]) - - for _, part := range nameParts { - part = strings.TrimSpace(part) - if len(part) > len(name) { - name = part - } - } - - if name != "" { - *names = append(*names, name) - } - } -} diff --git a/vendor/github.com/urfave/cli/v3/completion.go b/vendor/github.com/urfave/cli/v3/completion.go deleted file mode 100644 index 2319a815..00000000 --- a/vendor/github.com/urfave/cli/v3/completion.go +++ /dev/null @@ -1,68 +0,0 @@ -package cli - -import ( - "context" - "embed" - "fmt" - "sort" -) - -const ( - completionCommandName = "generate-completion" -) - -var ( - //go:embed autocomplete - autoCompleteFS embed.FS - - shellCompletions = map[string]renderCompletion{ - "bash": getCompletion("autocomplete/bash_autocomplete"), - "ps": getCompletion("autocomplete/powershell_autocomplete.ps1"), - "zsh": getCompletion("autocomplete/zsh_autocomplete"), - "fish": func(c *Command) (string, error) { - return c.ToFishCompletion() - }, - } -) - -type renderCompletion func(*Command) (string, error) - -func getCompletion(s string) renderCompletion { - return func(c *Command) (string, error) { - b, err := autoCompleteFS.ReadFile(s) - return string(b), err - } -} - -func buildCompletionCommand() *Command { - return &Command{ - Name: completionCommandName, - Hidden: true, - Action: completionCommandAction, - } -} - -func completionCommandAction(ctx context.Context, cmd *Command) error { - var shells []string - for k := range shellCompletions { - shells = append(shells, k) - } - - sort.Strings(shells) - - if cmd.Args().Len() == 0 { - return Exit(fmt.Sprintf("no shell provided for completion command. available shells are %+v", shells), 1) - } - s := cmd.Args().First() - - if rc, ok := shellCompletions[s]; !ok { - return Exit(fmt.Sprintf("unknown shell %s, available shells are %+v", s, shells), 1) - } else if c, err := rc(cmd); err != nil { - return Exit(err, 1) - } else { - if _, err = cmd.Writer.Write([]byte(c)); err != nil { - return Exit(err, 1) - } - } - return nil -} diff --git a/vendor/github.com/urfave/cli/v3/errors.go b/vendor/github.com/urfave/cli/v3/errors.go deleted file mode 100644 index 18938e4d..00000000 --- a/vendor/github.com/urfave/cli/v3/errors.go +++ /dev/null @@ -1,209 +0,0 @@ -package cli - -import ( - "fmt" - "io" - "os" - "strings" -) - -// OsExiter is the function used when the app exits. If not set defaults to os.Exit. -var OsExiter = os.Exit - -// ErrWriter is used to write errors to the user. This can be anything -// implementing the io.Writer interface and defaults to os.Stderr. -var ErrWriter io.Writer = os.Stderr - -// MultiError is an error that wraps multiple errors. -type MultiError interface { - error - Errors() []error -} - -// newMultiError creates a new MultiError. Pass in one or more errors. -func newMultiError(err ...error) MultiError { - ret := multiError(err) - return &ret -} - -type multiError []error - -// Error implements the error interface. -func (m *multiError) Error() string { - errs := make([]string, len(*m)) - for i, err := range *m { - errs[i] = err.Error() - } - - return strings.Join(errs, "\n") -} - -// Errors returns a copy of the errors slice -func (m *multiError) Errors() []error { - errs := make([]error, len(*m)) - copy(errs, *m) - return errs -} - -type requiredFlagsErr interface { - error - getMissingFlags() []string -} - -type errRequiredFlags struct { - missingFlags []string -} - -func (e *errRequiredFlags) Error() string { - if len(e.missingFlags) == 1 { - return fmt.Sprintf("Required flag %q not set", e.missingFlags[0]) - } - joinedMissingFlags := strings.Join(e.missingFlags, ", ") - return fmt.Sprintf("Required flags %q not set", joinedMissingFlags) -} - -func (e *errRequiredFlags) getMissingFlags() []string { - return e.missingFlags -} - -type mutuallyExclusiveGroup struct { - flag1Name string - flag2Name string -} - -func (e *mutuallyExclusiveGroup) Error() string { - return fmt.Sprintf("option %s cannot be set along with option %s", e.flag1Name, e.flag2Name) -} - -type mutuallyExclusiveGroupRequiredFlag struct { - flags *MutuallyExclusiveFlags -} - -func (e *mutuallyExclusiveGroupRequiredFlag) Error() string { - var missingFlags []string - for _, grpf := range e.flags.Flags { - var grpString []string - for _, f := range grpf { - grpString = append(grpString, f.Names()...) - } - if len(e.flags.Flags) == 1 { - err := errRequiredFlags{ - missingFlags: grpString, - } - return err.Error() - } - missingFlags = append(missingFlags, strings.Join(grpString, " ")) - } - - return fmt.Sprintf("one of these flags needs to be provided: %s", strings.Join(missingFlags, ", ")) -} - -// ErrorFormatter is the interface that will suitably format the error output -type ErrorFormatter interface { - Format(s fmt.State, verb rune) -} - -// ExitCoder is the interface checked by `App` and `Command` for a custom exit -// code -type ExitCoder interface { - error - ExitCode() int -} - -type exitError struct { - exitCode int - err error -} - -// Exit wraps a message and exit code into an error, which by default is -// handled with a call to os.Exit during default error handling. -// -// This is the simplest way to trigger a non-zero exit code for an App without -// having to call os.Exit manually. During testing, this behavior can be avoided -// by overriding the ExitErrHandler function on an App or the package-global -// OsExiter function. -func Exit(message interface{}, exitCode int) ExitCoder { - var err error - - switch e := message.(type) { - case ErrorFormatter: - err = fmt.Errorf("%+v", message) - case error: - err = e - default: - err = fmt.Errorf("%+v", message) - } - - return &exitError{ - err: err, - exitCode: exitCode, - } -} - -func (ee *exitError) Error() string { - return ee.err.Error() -} - -func (ee *exitError) ExitCode() int { - return ee.exitCode -} - -func (ee *exitError) Unwrap() error { - return ee.err -} - -// HandleExitCoder handles errors implementing ExitCoder by printing their -// message and calling OsExiter with the given exit code. -// -// If the given error instead implements MultiError, each error will be checked -// for the ExitCoder interface, and OsExiter will be called with the last exit -// code found, or exit code 1 if no ExitCoder is found. -// -// This function is the default error-handling behavior for an App. -func HandleExitCoder(err error) { - if err == nil { - return - } - - if exitErr, ok := err.(ExitCoder); ok { - if err.Error() != "" { - if _, ok := exitErr.(ErrorFormatter); ok { - _, _ = fmt.Fprintf(ErrWriter, "%+v\n", err) - } else { - _, _ = fmt.Fprintln(ErrWriter, err) - } - } - OsExiter(exitErr.ExitCode()) - return - } - - if multiErr, ok := err.(MultiError); ok { - code := handleMultiError(multiErr) - OsExiter(code) - return - } -} - -func handleMultiError(multiErr MultiError) int { - code := 1 - for _, merr := range multiErr.Errors() { - if multiErr2, ok := merr.(MultiError); ok { - code = handleMultiError(multiErr2) - } else if merr != nil { - fmt.Fprintln(ErrWriter, merr) - if exitErr, ok := merr.(ExitCoder); ok { - code = exitErr.ExitCode() - } - } - } - return code -} - -type typeError[T any] struct { - other any -} - -func (te *typeError[T]) Error() string { - var t T - return fmt.Sprintf("Expected type %T got instead %T", t, te.other) -} diff --git a/vendor/github.com/urfave/cli/v3/fish.go b/vendor/github.com/urfave/cli/v3/fish.go deleted file mode 100644 index b2b6ec93..00000000 --- a/vendor/github.com/urfave/cli/v3/fish.go +++ /dev/null @@ -1,183 +0,0 @@ -package cli - -import ( - "bytes" - "fmt" - "io" - "strings" - "text/template" -) - -// ToFishCompletion creates a fish completion string for the `*App` -// The function errors if either parsing or writing of the string fails. -func (cmd *Command) ToFishCompletion() (string, error) { - var w bytes.Buffer - if err := cmd.writeFishCompletionTemplate(&w); err != nil { - return "", err - } - return w.String(), nil -} - -type fishCommandCompletionTemplate struct { - Command *Command - Completions []string - AllCommands []string -} - -func (cmd *Command) writeFishCompletionTemplate(w io.Writer) error { - const name = "cli" - t, err := template.New(name).Parse(FishCompletionTemplate) - if err != nil { - return err - } - allCommands := []string{} - - // Add global flags - completions := cmd.prepareFishFlags(cmd.VisibleFlags(), allCommands) - - // Add help flag - if !cmd.HideHelp { - completions = append( - completions, - cmd.prepareFishFlags([]Flag{HelpFlag}, allCommands)..., - ) - } - - // Add version flag - if !cmd.HideVersion { - completions = append( - completions, - cmd.prepareFishFlags([]Flag{VersionFlag}, allCommands)..., - ) - } - - // Add commands and their flags - completions = append( - completions, - cmd.prepareFishCommands(cmd.VisibleCommands(), &allCommands, []string{})..., - ) - - return t.ExecuteTemplate(w, name, &fishCommandCompletionTemplate{ - Command: cmd, - Completions: completions, - AllCommands: allCommands, - }) -} - -func (cmd *Command) prepareFishCommands(commands []*Command, allCommands *[]string, previousCommands []string) []string { - completions := []string{} - for _, command := range commands { - if command.Hidden { - continue - } - - var completion strings.Builder - completion.WriteString(fmt.Sprintf( - "complete -r -c %s -n '%s' -a '%s'", - cmd.Name, - cmd.fishSubcommandHelper(previousCommands), - strings.Join(command.Names(), " "), - )) - - if command.Usage != "" { - completion.WriteString(fmt.Sprintf(" -d '%s'", - escapeSingleQuotes(command.Usage))) - } - - if !command.HideHelp { - completions = append( - completions, - cmd.prepareFishFlags([]Flag{HelpFlag}, command.Names())..., - ) - } - - *allCommands = append(*allCommands, command.Names()...) - completions = append(completions, completion.String()) - completions = append( - completions, - cmd.prepareFishFlags(command.VisibleFlags(), command.Names())..., - ) - - // recursively iterate subcommands - if len(command.Commands) > 0 { - completions = append( - completions, - cmd.prepareFishCommands( - command.Commands, allCommands, command.Names(), - )..., - ) - } - } - - return completions -} - -func (cmd *Command) prepareFishFlags(flags []Flag, previousCommands []string) []string { - completions := []string{} - for _, f := range flags { - completion := &strings.Builder{} - completion.WriteString(fmt.Sprintf( - "complete -c %s -n '%s'", - cmd.Name, - cmd.fishSubcommandHelper(previousCommands), - )) - - fishAddFileFlag(f, completion) - - for idx, opt := range f.Names() { - if idx == 0 { - completion.WriteString(fmt.Sprintf( - " -l %s", strings.TrimSpace(opt), - )) - } else { - completion.WriteString(fmt.Sprintf( - " -s %s", strings.TrimSpace(opt), - )) - } - } - - if flag, ok := f.(DocGenerationFlag); ok { - if flag.TakesValue() { - completion.WriteString(" -r") - } - - if flag.GetUsage() != "" { - completion.WriteString(fmt.Sprintf(" -d '%s'", - escapeSingleQuotes(flag.GetUsage()))) - } - } - - completions = append(completions, completion.String()) - } - - return completions -} - -func fishAddFileFlag(flag Flag, completion *strings.Builder) { - switch f := flag.(type) { - case *StringFlag: - if f.TakesFile { - return - } - case *StringSliceFlag: - if f.TakesFile { - return - } - } - completion.WriteString(" -f") -} - -func (cmd *Command) fishSubcommandHelper(allCommands []string) string { - fishHelper := fmt.Sprintf("__fish_%s_no_subcommand", cmd.Name) - if len(allCommands) > 0 { - fishHelper = fmt.Sprintf( - "__fish_seen_subcommand_from %s", - strings.Join(allCommands, " "), - ) - } - return fishHelper -} - -func escapeSingleQuotes(input string) string { - return strings.Replace(input, `'`, `\'`, -1) -} diff --git a/vendor/github.com/urfave/cli/v3/flag.go b/vendor/github.com/urfave/cli/v3/flag.go deleted file mode 100644 index 420ea5e9..00000000 --- a/vendor/github.com/urfave/cli/v3/flag.go +++ /dev/null @@ -1,355 +0,0 @@ -package cli - -import ( - "context" - "flag" - "fmt" - "io" - "os" - "regexp" - "runtime" - "strings" - "time" -) - -const defaultPlaceholder = "value" - -var ( - defaultSliceFlagSeparator = "," - defaultMapFlagKeyValueSeparator = "=" - disableSliceFlagSeparator = false -) - -var ( - slPfx = fmt.Sprintf("sl:::%d:::", time.Now().UTC().UnixNano()) - - commaWhitespace = regexp.MustCompile("[, ]+.*") -) - -// GenerateShellCompletionFlag enables shell completion -var GenerateShellCompletionFlag Flag = &BoolFlag{ - Name: "generate-shell-completion", - Hidden: true, -} - -// VersionFlag prints the version for the application -var VersionFlag Flag = &BoolFlag{ - Name: "version", - Aliases: []string{"v"}, - Usage: "print the version", - HideDefault: true, - Local: true, -} - -// HelpFlag prints the help for all commands and subcommands. -// Set to nil to disable the flag. The subcommand -// will still be added unless HideHelp or HideHelpCommand is set to true. -var HelpFlag Flag = &BoolFlag{ - Name: "help", - Aliases: []string{"h"}, - Usage: "show help", - HideDefault: true, - Local: true, -} - -// FlagStringer converts a flag definition to a string. This is used by help -// to display a flag. -var FlagStringer FlagStringFunc = stringifyFlag - -// Serializer is used to circumvent the limitations of flag.FlagSet.Set -type Serializer interface { - Serialize() string -} - -// FlagNamePrefixer converts a full flag name and its placeholder into the help -// message flag prefix. This is used by the default FlagStringer. -var FlagNamePrefixer FlagNamePrefixFunc = prefixedNames - -// FlagEnvHinter annotates flag help message with the environment variable -// details. This is used by the default FlagStringer. -var FlagEnvHinter FlagEnvHintFunc = withEnvHint - -// FlagFileHinter annotates flag help message with the environment variable -// details. This is used by the default FlagStringer. -var FlagFileHinter FlagFileHintFunc = withFileHint - -// FlagsByName is a slice of Flag. -type FlagsByName []Flag - -func (f FlagsByName) Len() int { - return len(f) -} - -func (f FlagsByName) Less(i, j int) bool { - if len(f[j].Names()) == 0 { - return false - } else if len(f[i].Names()) == 0 { - return true - } - return lexicographicLess(f[i].Names()[0], f[j].Names()[0]) -} - -func (f FlagsByName) Swap(i, j int) { - f[i], f[j] = f[j], f[i] -} - -// ActionableFlag is an interface that wraps Flag interface and RunAction operation. -type ActionableFlag interface { - RunAction(context.Context, *Command) error -} - -// Flag is a common interface related to parsing flags in cli. -// For more advanced flag parsing techniques, it is recommended that -// this interface be implemented. -type Flag interface { - fmt.Stringer - - // Apply Flag settings to the given flag set - Apply(*flag.FlagSet) error - - // All possible names for this flag - Names() []string - - // Whether the flag has been set or not - IsSet() bool -} - -// RequiredFlag is an interface that allows us to mark flags as required -// it allows flags required flags to be backwards compatible with the Flag interface -type RequiredFlag interface { - // whether the flag is a required flag or not - IsRequired() bool -} - -// DocGenerationFlag is an interface that allows documentation generation for the flag -type DocGenerationFlag interface { - // TakesValue returns true if the flag takes a value, otherwise false - TakesValue() bool - - // GetUsage returns the usage string for the flag - GetUsage() string - - // GetValue returns the flags value as string representation and an empty - // string if the flag takes no value at all. - GetValue() string - - // GetDefaultText returns the default text for this flag - GetDefaultText() string - - // GetEnvVars returns the env vars for this flag - GetEnvVars() []string - - // IsDefaultVisible returns whether the default value should be shown in - // help text - IsDefaultVisible() bool -} - -// DocGenerationMultiValueFlag extends DocGenerationFlag for slice/map based flags. -type DocGenerationMultiValueFlag interface { - DocGenerationFlag - - // IsMultiValueFlag returns true for flags that can be given multiple times. - IsMultiValueFlag() bool -} - -// Countable is an interface to enable detection of flag values which support -// repetitive flags -type Countable interface { - Count() int -} - -// VisibleFlag is an interface that allows to check if a flag is visible -type VisibleFlag interface { - // IsVisible returns true if the flag is not hidden, otherwise false - IsVisible() bool -} - -// CategorizableFlag is an interface that allows us to potentially -// use a flag in a categorized representation. -type CategorizableFlag interface { - // Returns the category of the flag - GetCategory() string - - // Sets the category of the flag - SetCategory(string) -} - -// LocalFlag is an interface to enable detection of flags which are local -// to current command -type LocalFlag interface { - IsLocal() bool -} - -// IsDefaultVisible returns true if the flag is not hidden, otherwise false -func (f *FlagBase[T, C, V]) IsDefaultVisible() bool { - return !f.HideDefault -} - -func newFlagSet(name string, flags []Flag) (*flag.FlagSet, error) { - set := flag.NewFlagSet(name, flag.ContinueOnError) - - for _, f := range flags { - if err := f.Apply(set); err != nil { - return nil, err - } - } - - set.SetOutput(io.Discard) - - return set, nil -} - -func visibleFlags(fl []Flag) []Flag { - var visible []Flag - for _, f := range fl { - if vf, ok := f.(VisibleFlag); ok && vf.IsVisible() { - visible = append(visible, f) - } - } - return visible -} - -func prefixFor(name string) (prefix string) { - if len(name) == 1 { - prefix = "-" - } else { - prefix = "--" - } - - return -} - -// Returns the placeholder, if any, and the unquoted usage string. -func unquoteUsage(usage string) (string, string) { - for i := 0; i < len(usage); i++ { - if usage[i] == '`' { - for j := i + 1; j < len(usage); j++ { - if usage[j] == '`' { - name := usage[i+1 : j] - usage = usage[:i] + name + usage[j+1:] - return name, usage - } - } - break - } - } - return "", usage -} - -func prefixedNames(names []string, placeholder string) string { - var prefixed string - for i, name := range names { - if name == "" { - continue - } - - prefixed += prefixFor(name) + name - if placeholder != "" { - prefixed += " " + placeholder - } - if i < len(names)-1 { - prefixed += ", " - } - } - return prefixed -} - -func envFormat(envVars []string, prefix, sep, suffix string) string { - if len(envVars) > 0 { - return fmt.Sprintf(" [%s%s%s]", prefix, strings.Join(envVars, sep), suffix) - } - return "" -} - -func defaultEnvFormat(envVars []string) string { - return envFormat(envVars, "$", ", $", "") -} - -func withEnvHint(envVars []string, str string) string { - envText := "" - if runtime.GOOS != "windows" || os.Getenv("PSHOME") != "" { - envText = defaultEnvFormat(envVars) - } else { - envText = envFormat(envVars, "%", "%, %", "%") - } - return str + envText -} - -func FlagNames(name string, aliases []string) []string { - var ret []string - - for _, part := range append([]string{name}, aliases...) { - // v1 -> v2 migration warning zone: - // Strip off anything after the first found comma or space, which - // *hopefully* makes it a tiny bit more obvious that unexpected behavior is - // caused by using the v1 form of stringly typed "Name". - ret = append(ret, commaWhitespace.ReplaceAllString(part, "")) - } - - return ret -} - -func withFileHint(filePath, str string) string { - fileText := "" - if filePath != "" { - fileText = fmt.Sprintf(" [%s]", filePath) - } - return str + fileText -} - -func formatDefault(format string) string { - return " (default: " + format + ")" -} - -func stringifyFlag(f Flag) string { - // enforce DocGeneration interface on flags to avoid reflection - df, ok := f.(DocGenerationFlag) - if !ok { - return "" - } - - placeholder, usage := unquoteUsage(df.GetUsage()) - needsPlaceholder := df.TakesValue() - - if needsPlaceholder && placeholder == "" { - placeholder = defaultPlaceholder - } - - defaultValueString := "" - - // don't print default text for required flags - if rf, ok := f.(RequiredFlag); !ok || !rf.IsRequired() { - isVisible := df.IsDefaultVisible() - if s := df.GetDefaultText(); isVisible && s != "" { - defaultValueString = fmt.Sprintf(formatDefault("%s"), s) - } - } - - usageWithDefault := strings.TrimSpace(usage + defaultValueString) - - pn := prefixedNames(f.Names(), placeholder) - sliceFlag, ok := f.(DocGenerationMultiValueFlag) - if ok && sliceFlag.IsMultiValueFlag() { - pn = pn + " [ " + pn + " ]" - } - - return withEnvHint(df.GetEnvVars(), fmt.Sprintf("%s\t%s", pn, usageWithDefault)) -} - -func hasFlag(flags []Flag, fl Flag) bool { - for _, existing := range flags { - if fl == existing { - return true - } - } - - return false -} - -func flagSplitMultiValues(val string) []string { - if disableSliceFlagSeparator { - return []string{val} - } - - return strings.Split(val, defaultSliceFlagSeparator) -} diff --git a/vendor/github.com/urfave/cli/v3/flag_bool.go b/vendor/github.com/urfave/cli/v3/flag_bool.go deleted file mode 100644 index 4ad81d58..00000000 --- a/vendor/github.com/urfave/cli/v3/flag_bool.go +++ /dev/null @@ -1,87 +0,0 @@ -package cli - -import ( - "errors" - "strconv" -) - -type BoolFlag = FlagBase[bool, BoolConfig, boolValue] - -// BoolConfig defines the configuration for bool flags -type BoolConfig struct { - Count *int -} - -// boolValue needs to implement the boolFlag internal interface in flag -// to be able to capture bool fields and values -// -// type boolFlag interface { -// Value -// IsBoolFlag() bool -// } -type boolValue struct { - destination *bool - count *int -} - -func (cmd *Command) Bool(name string) bool { - if v, ok := cmd.Value(name).(bool); ok { - tracef("bool available for flag name %[1]q with value=%[2]v (cmd=%[3]q)", name, v, cmd.Name) - return v - } - - tracef("bool NOT available for flag name %[1]q (cmd=%[2]q)", name, cmd.Name) - return false -} - -// Below functions are to satisfy the ValueCreator interface - -// Create creates the bool value -func (b boolValue) Create(val bool, p *bool, c BoolConfig) Value { - *p = val - if c.Count == nil { - c.Count = new(int) - } - return &boolValue{ - destination: p, - count: c.Count, - } -} - -// ToString formats the bool value -func (b boolValue) ToString(value bool) string { - return strconv.FormatBool(value) -} - -// Below functions are to satisfy the flag.Value interface - -func (b *boolValue) Set(s string) error { - v, err := strconv.ParseBool(s) - if err != nil { - err = errors.New("parse error") - return err - } - *b.destination = v - if b.count != nil { - *b.count = *b.count + 1 - } - return err -} - -func (b *boolValue) Get() interface{} { return *b.destination } - -func (b *boolValue) String() string { - if b.destination != nil { - return strconv.FormatBool(*b.destination) - } - return strconv.FormatBool(false) -} - -func (b *boolValue) IsBoolFlag() bool { return true } - -func (b *boolValue) Count() int { - if b.count != nil { - return *b.count - } - return 0 -} diff --git a/vendor/github.com/urfave/cli/v3/flag_bool_with_inverse.go b/vendor/github.com/urfave/cli/v3/flag_bool_with_inverse.go deleted file mode 100644 index 00871d61..00000000 --- a/vendor/github.com/urfave/cli/v3/flag_bool_with_inverse.go +++ /dev/null @@ -1,194 +0,0 @@ -package cli - -import ( - "context" - "flag" - "fmt" - "strings" -) - -var DefaultInverseBoolPrefix = "no-" - -type BoolWithInverseFlag struct { - // The BoolFlag which the positive and negative flags are generated from - *BoolFlag - - // The prefix used to indicate a negative value - // Default: `env` becomes `no-env` - InversePrefix string - - positiveFlag *BoolFlag - negativeFlag *BoolFlag - - // pointers obtained from the embedded bool flag - posDest *bool - posCount *int - - negDest *bool -} - -func (parent *BoolWithInverseFlag) Flags() []Flag { - return []Flag{parent.positiveFlag, parent.negativeFlag} -} - -func (parent *BoolWithInverseFlag) IsSet() bool { - return (*parent.posCount > 0) || (parent.positiveFlag.IsSet() || parent.negativeFlag.IsSet()) -} - -func (parent *BoolWithInverseFlag) Value() bool { - return *parent.posDest -} - -func (parent *BoolWithInverseFlag) RunAction(ctx context.Context, cmd *Command) error { - if *parent.negDest && *parent.posDest { - return fmt.Errorf("cannot set both flags `--%s` and `--%s`", parent.positiveFlag.Name, parent.negativeFlag.Name) - } - - if *parent.negDest { - err := cmd.Set(parent.positiveFlag.Name, "false") - if err != nil { - return err - } - } - - if parent.BoolFlag.Action != nil { - return parent.BoolFlag.Action(ctx, cmd, parent.Value()) - } - - return nil -} - -// Initialize creates a new BoolFlag that has an inverse flag -// -// consider a bool flag `--env`, there is no way to set it to false -// this function allows you to set `--env` or `--no-env` and in the command action -// it can be determined that BoolWithInverseFlag.IsSet(). -func (parent *BoolWithInverseFlag) initialize() { - child := parent.BoolFlag - - parent.negDest = new(bool) - if child.Destination != nil { - parent.posDest = child.Destination - } else { - parent.posDest = new(bool) - } - - if child.Config.Count != nil { - parent.posCount = child.Config.Count - } else { - parent.posCount = new(int) - } - - parent.positiveFlag = child - parent.positiveFlag.Destination = parent.posDest - parent.positiveFlag.Config.Count = parent.posCount - - parent.negativeFlag = &BoolFlag{ - Category: child.Category, - DefaultText: child.DefaultText, - Sources: NewValueSourceChain(child.Sources.Chain...), - Usage: child.Usage, - Required: child.Required, - Hidden: child.Hidden, - Local: child.Local, - Value: child.Value, - Destination: parent.negDest, - TakesFile: child.TakesFile, - OnlyOnce: child.OnlyOnce, - hasBeenSet: child.hasBeenSet, - applied: child.applied, - creator: boolValue{}, - value: child.value, - } - - // Set inverse names ex: --env => --no-env - parent.negativeFlag.Name = parent.inverseName() - parent.negativeFlag.Aliases = parent.inverseAliases() - - if len(child.Sources.EnvKeys()) > 0 { - sources := []ValueSource{} - - for _, envVar := range child.GetEnvVars() { - sources = append(sources, &envVarValueSource{Key: strings.ToUpper(parent.InversePrefix) + envVar}) - } - parent.negativeFlag.Sources = NewValueSourceChain(sources...) - } -} - -func (parent *BoolWithInverseFlag) inverseName() string { - if parent.InversePrefix == "" { - parent.InversePrefix = DefaultInverseBoolPrefix - } - - return parent.InversePrefix + parent.BoolFlag.Name -} - -func (parent *BoolWithInverseFlag) inversePrefix() string { - if parent.InversePrefix == "" { - return DefaultInverseBoolPrefix - } - - return parent.InversePrefix -} - -func (parent *BoolWithInverseFlag) inverseAliases() (aliases []string) { - if len(parent.BoolFlag.Aliases) > 0 { - aliases = make([]string, len(parent.BoolFlag.Aliases)) - for idx, alias := range parent.BoolFlag.Aliases { - aliases[idx] = parent.InversePrefix + alias - } - } - - return -} - -func (parent *BoolWithInverseFlag) Apply(set *flag.FlagSet) error { - if parent.positiveFlag == nil { - parent.initialize() - } - - if err := parent.positiveFlag.Apply(set); err != nil { - return err - } - - if err := parent.negativeFlag.Apply(set); err != nil { - return err - } - - return nil -} - -func (parent *BoolWithInverseFlag) Names() []string { - // Get Names when flag has not been initialized - if parent.positiveFlag == nil { - return append(parent.BoolFlag.Names(), FlagNames(parent.inverseName(), parent.inverseAliases())...) - } - - if *parent.negDest { - return parent.negativeFlag.Names() - } - - if *parent.posDest { - return parent.positiveFlag.Names() - } - - return append(parent.negativeFlag.Names(), parent.positiveFlag.Names()...) -} - -// String implements the standard Stringer interface. -// -// Example for BoolFlag{Name: "env"} -// --[no-]env (default: false) -func (parent *BoolWithInverseFlag) String() string { - out := FlagStringer(parent) - i := strings.Index(out, "\t") - - prefix := "--" - - // single character flags are prefixed with `-` instead of `--` - if len(parent.Name) == 1 { - prefix = "-" - } - - return fmt.Sprintf("%s[%s]%s%s", prefix, parent.inversePrefix(), parent.Name, out[i:]) -} diff --git a/vendor/github.com/urfave/cli/v3/flag_duration.go b/vendor/github.com/urfave/cli/v3/flag_duration.go deleted file mode 100644 index 37b4cb64..00000000 --- a/vendor/github.com/urfave/cli/v3/flag_duration.go +++ /dev/null @@ -1,47 +0,0 @@ -package cli - -import ( - "fmt" - "time" -) - -type DurationFlag = FlagBase[time.Duration, NoConfig, durationValue] - -// -- time.Duration Value -type durationValue time.Duration - -// Below functions are to satisfy the ValueCreator interface - -func (d durationValue) Create(val time.Duration, p *time.Duration, c NoConfig) Value { - *p = val - return (*durationValue)(p) -} - -func (d durationValue) ToString(val time.Duration) string { - return fmt.Sprintf("%v", val) -} - -// Below functions are to satisfy the flag.Value interface - -func (d *durationValue) Set(s string) error { - v, err := time.ParseDuration(s) - if err != nil { - return err - } - *d = durationValue(v) - return err -} - -func (d *durationValue) Get() any { return time.Duration(*d) } - -func (d *durationValue) String() string { return (*time.Duration)(d).String() } - -func (cmd *Command) Duration(name string) time.Duration { - if v, ok := cmd.Value(name).(time.Duration); ok { - tracef("duration available for flag name %[1]q with value=%[2]v (cmd=%[3]q)", name, v, cmd.Name) - return v - } - - tracef("bool NOT available for flag name %[1]q (cmd=%[2]q)", name, cmd.Name) - return 0 -} diff --git a/vendor/github.com/urfave/cli/v3/flag_ext.go b/vendor/github.com/urfave/cli/v3/flag_ext.go deleted file mode 100644 index 64da59ea..00000000 --- a/vendor/github.com/urfave/cli/v3/flag_ext.go +++ /dev/null @@ -1,48 +0,0 @@ -package cli - -import "flag" - -type extFlag struct { - f *flag.Flag -} - -func (e *extFlag) Apply(fs *flag.FlagSet) error { - fs.Var(e.f.Value, e.f.Name, e.f.Usage) - return nil -} - -func (e *extFlag) Names() []string { - return []string{e.f.Name} -} - -func (e *extFlag) IsSet() bool { - return false -} - -func (e *extFlag) String() string { - return FlagStringer(e) -} - -func (e *extFlag) IsVisible() bool { - return true -} - -func (e *extFlag) TakesValue() bool { - return false -} - -func (e *extFlag) GetUsage() string { - return e.f.Usage -} - -func (e *extFlag) GetValue() string { - return e.f.Value.String() -} - -func (e *extFlag) GetDefaultText() string { - return e.f.DefValue -} - -func (e *extFlag) GetEnvVars() []string { - return nil -} diff --git a/vendor/github.com/urfave/cli/v3/flag_float.go b/vendor/github.com/urfave/cli/v3/flag_float.go deleted file mode 100644 index 406b55b6..00000000 --- a/vendor/github.com/urfave/cli/v3/flag_float.go +++ /dev/null @@ -1,48 +0,0 @@ -package cli - -import ( - "strconv" -) - -type FloatFlag = FlagBase[float64, NoConfig, floatValue] - -// -- float64 Value -type floatValue float64 - -// Below functions are to satisfy the ValueCreator interface - -func (f floatValue) Create(val float64, p *float64, c NoConfig) Value { - *p = val - return (*floatValue)(p) -} - -func (f floatValue) ToString(b float64) string { - return strconv.FormatFloat(b, 'g', -1, 64) -} - -// Below functions are to satisfy the flag.Value interface - -func (f *floatValue) Set(s string) error { - v, err := strconv.ParseFloat(s, 64) - if err != nil { - return err - } - *f = floatValue(v) - return err -} - -func (f *floatValue) Get() any { return float64(*f) } - -func (f *floatValue) String() string { return strconv.FormatFloat(float64(*f), 'g', -1, 64) } - -// Float looks up the value of a local FloatFlag, returns -// 0 if not found -func (cmd *Command) Float(name string) float64 { - if v, ok := cmd.Value(name).(float64); ok { - tracef("float available for flag name %[1]q with value=%[2]v (cmd=%[3]q)", name, v, cmd.Name) - return v - } - - tracef("float NOT available for flag name %[1]q (cmd=%[2]q)", name, cmd.Name) - return 0 -} diff --git a/vendor/github.com/urfave/cli/v3/flag_float_slice.go b/vendor/github.com/urfave/cli/v3/flag_float_slice.go deleted file mode 100644 index 390e466e..00000000 --- a/vendor/github.com/urfave/cli/v3/flag_float_slice.go +++ /dev/null @@ -1,35 +0,0 @@ -package cli - -import ( - "flag" -) - -type ( - FloatSlice = SliceBase[float64, NoConfig, floatValue] - FloatSliceFlag = FlagBase[[]float64, NoConfig, FloatSlice] -) - -var NewFloatSlice = NewSliceBase[float64, NoConfig, floatValue] - -// FloatSlice looks up the value of a local FloatSliceFlag, returns -// nil if not found -func (cmd *Command) FloatSlice(name string) []float64 { - if flSet := cmd.lookupFlagSet(name); flSet != nil { - return lookupFloatSlice(name, flSet, cmd.Name) - } - - return nil -} - -func lookupFloatSlice(name string, set *flag.FlagSet, cmdName string) []float64 { - fl := set.Lookup(name) - if fl != nil { - if v, ok := fl.Value.(flag.Getter).Get().([]float64); ok { - tracef("float slice available for flag name %[1]q with value=%[2]v (cmd=%[3]q)", name, v, cmdName) - return v - } - } - - tracef("float slice NOT available for flag name %[1]q (cmd=%[2]q)", name, cmdName) - return nil -} diff --git a/vendor/github.com/urfave/cli/v3/flag_impl.go b/vendor/github.com/urfave/cli/v3/flag_impl.go deleted file mode 100644 index 77bc5918..00000000 --- a/vendor/github.com/urfave/cli/v3/flag_impl.go +++ /dev/null @@ -1,286 +0,0 @@ -package cli - -import ( - "context" - "flag" - "fmt" - "reflect" -) - -// Value represents a value as used by cli. -// For now it implements the golang flag.Value interface -type Value interface { - flag.Value - flag.Getter -} - -type boolFlag interface { - IsBoolFlag() bool -} - -type fnValue struct { - fn func(string) error - isBool bool - v Value -} - -func (f *fnValue) Get() any { return f.v.Get() } -func (f *fnValue) Set(s string) error { return f.fn(s) } -func (f *fnValue) String() string { - if f.v == nil { - return "" - } - return f.v.String() -} - -func (f *fnValue) Serialize() string { - if s, ok := f.v.(Serializer); ok { - return s.Serialize() - } - return f.v.String() -} - -func (f *fnValue) IsBoolFlag() bool { return f.isBool } -func (f *fnValue) Count() int { - if s, ok := f.v.(Countable); ok { - return s.Count() - } - return 0 -} - -// ValueCreator is responsible for creating a flag.Value emulation -// as well as custom formatting -// -// T specifies the type -// C specifies the config for the type -type ValueCreator[T any, C any] interface { - Create(T, *T, C) Value - ToString(T) string -} - -// NoConfig is for flags which dont need a custom configuration -type NoConfig struct{} - -// FlagBase [T,C,VC] is a generic flag base which can be used -// as a boilerplate to implement the most common interfaces -// used by urfave/cli. -// -// T specifies the type -// C specifies the configuration required(if any for that flag type) -// VC specifies the value creator which creates the flag.Value emulation -type FlagBase[T any, C any, VC ValueCreator[T, C]] struct { - Name string `json:"name"` // name of the flag - Category string `json:"category"` // category of the flag, if any - DefaultText string `json:"defaultText"` // default text of the flag for usage purposes - HideDefault bool `json:"hideDefault"` // whether to hide the default value in output - Usage string `json:"usage"` // usage string for help output - Sources ValueSourceChain `json:"-"` // sources to load flag value from - Required bool `json:"required"` // whether the flag is required or not - Hidden bool `json:"hidden"` // whether to hide the flag in help output - Local bool `json:"local"` // whether the flag needs to be applied to subcommands as well - Value T `json:"defaultValue"` // default value for this flag if not set by from any source - Destination *T `json:"-"` // destination pointer for value when set - Aliases []string `json:"aliases"` // Aliases that are allowed for this flag - TakesFile bool `json:"takesFileArg"` // whether this flag takes a file argument, mainly for shell completion purposes - Action func(context.Context, *Command, T) error `json:"-"` // Action callback to be called when flag is set - Config C `json:"config"` // Additional/Custom configuration associated with this flag type - OnlyOnce bool `json:"onlyOnce"` // whether this flag can be duplicated on the command line - Validator func(T) error `json:"-"` // custom function to validate this flag value - ValidateDefaults bool `json:"validateDefaults"` // whether to validate defaults or not - - // unexported fields for internal use - count int // number of times the flag has been set - hasBeenSet bool // whether the flag has been set from env or file - applied bool // whether the flag has been applied to a flag set already - creator VC // value creator for this flag type - value Value // value representing this flag's value -} - -// GetValue returns the flags value as string representation and an empty -// string if the flag takes no value at all. -func (f *FlagBase[T, C, V]) GetValue() string { - if reflect.TypeOf(f.Value).Kind() == reflect.Bool { - return "" - } - return fmt.Sprintf("%v", f.Value) -} - -// Apply populates the flag given the flag set and environment -func (f *FlagBase[T, C, V]) Apply(set *flag.FlagSet) error { - tracef("apply (flag=%[1]q)", f.Name) - - // TODO move this phase into a separate flag initialization function - // if flag has been applied previously then it would have already been set - // from env or file. So no need to apply the env set again. However - // lots of units tests prior to persistent flags assumed that the - // flag can be applied to different flag sets multiple times while still - // keeping the env set. - if !f.applied || f.Local { - newVal := f.Value - - if val, source, found := f.Sources.LookupWithSource(); found { - tmpVal := f.creator.Create(f.Value, new(T), f.Config) - if val != "" || reflect.TypeOf(f.Value).Kind() == reflect.String { - if err := tmpVal.Set(val); err != nil { - return fmt.Errorf( - "could not parse %[1]q as %[2]T value from %[3]s for flag %[4]s: %[5]s", - val, f.Value, source, f.Name, err, - ) - } - } else if val == "" && reflect.TypeOf(f.Value).Kind() == reflect.Bool { - val = "false" - if err := tmpVal.Set(val); err != nil { - return fmt.Errorf( - "could not parse %[1]q as %[2]T value from %[3]s for flag %[4]s: %[5]s", - val, f.Value, source, f.Name, err, - ) - } - } - - newVal = tmpVal.Get().(T) - f.hasBeenSet = true - } - - if f.Destination == nil { - f.value = f.creator.Create(newVal, new(T), f.Config) - } else { - f.value = f.creator.Create(newVal, f.Destination, f.Config) - } - - // Validate the given default or values set from external sources as well - if f.Validator != nil && f.ValidateDefaults { - if v, ok := f.value.Get().(T); !ok { - return &typeError[T]{ - other: f.value.Get(), - } - } else if err := f.Validator(v); err != nil { - return err - } - } - } - - isBool := false - if b, ok := f.value.(boolFlag); ok && b.IsBoolFlag() { - isBool = true - } - - for _, name := range f.Names() { - set.Var(&fnValue{ - fn: func(val string) error { - if f.count == 1 && f.OnlyOnce { - return fmt.Errorf("cant duplicate this flag") - } - f.count++ - if err := f.value.Set(val); err != nil { - return err - } - f.hasBeenSet = true - if f.Validator != nil { - if v, ok := f.value.Get().(T); !ok { - return &typeError[T]{ - other: f.value.Get(), - } - } else if err := f.Validator(v); err != nil { - return err - } - } - return nil - }, - isBool: isBool, - v: f.value, - }, name, f.Usage) - } - - f.applied = true - return nil -} - -// String returns a readable representation of this value (for usage defaults) -func (f *FlagBase[T, C, V]) String() string { - return FlagStringer(f) -} - -// IsSet returns whether or not the flag has been set through env or file -func (f *FlagBase[T, C, V]) IsSet() bool { - return f.hasBeenSet -} - -// Names returns the names of the flag -func (f *FlagBase[T, C, V]) Names() []string { - return FlagNames(f.Name, f.Aliases) -} - -// IsRequired returns whether or not the flag is required -func (f *FlagBase[T, C, V]) IsRequired() bool { - return f.Required -} - -// IsVisible returns true if the flag is not hidden, otherwise false -func (f *FlagBase[T, C, V]) IsVisible() bool { - return !f.Hidden -} - -// GetCategory returns the category of the flag -func (f *FlagBase[T, C, V]) GetCategory() string { - return f.Category -} - -func (f *FlagBase[T, C, V]) SetCategory(c string) { - f.Category = c -} - -// GetUsage returns the usage string for the flag -func (f *FlagBase[T, C, V]) GetUsage() string { - return f.Usage -} - -// GetEnvVars returns the env vars for this flag -func (f *FlagBase[T, C, V]) GetEnvVars() []string { - return f.Sources.EnvKeys() -} - -// TakesValue returns true if the flag takes a value, otherwise false -func (f *FlagBase[T, C, V]) TakesValue() bool { - var t T - return reflect.TypeOf(t).Kind() != reflect.Bool -} - -// GetDefaultText returns the default text for this flag -func (f *FlagBase[T, C, V]) GetDefaultText() string { - if f.DefaultText != "" { - return f.DefaultText - } - var v V - return v.ToString(f.Value) -} - -// Get returns the flag’s value in the given Command. -func (f *FlagBase[T, C, V]) Get(cmd *Command) T { - if v, ok := cmd.Value(f.Name).(T); ok { - return v - } - var t T - return t -} - -// RunAction executes flag action if set -func (f *FlagBase[T, C, V]) RunAction(ctx context.Context, cmd *Command) error { - if f.Action != nil { - return f.Action(ctx, cmd, f.Get(cmd)) - } - - return nil -} - -// IsMultiValueFlag returns true if the value type T can take multiple -// values from cmd line. This is true for slice and map type flags -func (f *FlagBase[T, C, VC]) IsMultiValueFlag() bool { - // TBD how to specify - kind := reflect.TypeOf(f.Value).Kind() - return kind == reflect.Slice || kind == reflect.Map -} - -// IsLocal returns false if flag needs to be persistent across subcommands -func (f *FlagBase[T, C, VC]) IsLocal() bool { - return f.Local -} diff --git a/vendor/github.com/urfave/cli/v3/flag_int.go b/vendor/github.com/urfave/cli/v3/flag_int.go deleted file mode 100644 index f8e3640b..00000000 --- a/vendor/github.com/urfave/cli/v3/flag_int.go +++ /dev/null @@ -1,59 +0,0 @@ -package cli - -import ( - "strconv" -) - -type IntFlag = FlagBase[int64, IntegerConfig, intValue] - -// IntegerConfig is the configuration for all integer type flags -type IntegerConfig struct { - Base int -} - -// -- int64 Value -type intValue struct { - val *int64 - base int -} - -// Below functions are to satisfy the ValueCreator interface - -func (i intValue) Create(val int64, p *int64, c IntegerConfig) Value { - *p = val - return &intValue{ - val: p, - base: c.Base, - } -} - -func (i intValue) ToString(b int64) string { - return strconv.FormatInt(b, 10) -} - -// Below functions are to satisfy the flag.Value interface - -func (i *intValue) Set(s string) error { - v, err := strconv.ParseInt(s, i.base, 64) - if err != nil { - return err - } - *i.val = v - return err -} - -func (i *intValue) Get() any { return int64(*i.val) } - -func (i *intValue) String() string { return strconv.FormatInt(int64(*i.val), 10) } - -// Int looks up the value of a local Int64Flag, returns -// 0 if not found -func (cmd *Command) Int(name string) int64 { - if v, ok := cmd.Value(name).(int64); ok { - tracef("int available for flag name %[1]q with value=%[2]v (cmd=%[3]q)", name, v, cmd.Name) - return v - } - - tracef("int NOT available for flag name %[1]q (cmd=%[2]q)", name, cmd.Name) - return 0 -} diff --git a/vendor/github.com/urfave/cli/v3/flag_int_slice.go b/vendor/github.com/urfave/cli/v3/flag_int_slice.go deleted file mode 100644 index 94a9b78d..00000000 --- a/vendor/github.com/urfave/cli/v3/flag_int_slice.go +++ /dev/null @@ -1,20 +0,0 @@ -package cli - -type ( - IntSlice = SliceBase[int64, IntegerConfig, intValue] - IntSliceFlag = FlagBase[[]int64, IntegerConfig, IntSlice] -) - -var NewIntSlice = NewSliceBase[int64, IntegerConfig, intValue] - -// IntSlice looks up the value of a local IntSliceFlag, returns -// nil if not found -func (cmd *Command) IntSlice(name string) []int64 { - if v, ok := cmd.Value(name).([]int64); ok { - tracef("int slice available for flag name %[1]q with value=%[2]v (cmd=%[3]q)", name, v, cmd.Name) - return v - } - - tracef("int slice NOT available for flag name %[1]q (cmd=%[2]q)", name, cmd.Name) - return nil -} diff --git a/vendor/github.com/urfave/cli/v3/flag_map_impl.go b/vendor/github.com/urfave/cli/v3/flag_map_impl.go deleted file mode 100644 index e0ba88be..00000000 --- a/vendor/github.com/urfave/cli/v3/flag_map_impl.go +++ /dev/null @@ -1,116 +0,0 @@ -package cli - -import ( - "encoding/json" - "fmt" - "reflect" - "sort" - "strings" -) - -// MapBase wraps map[string]T to satisfy flag.Value -type MapBase[T any, C any, VC ValueCreator[T, C]] struct { - dict *map[string]T - hasBeenSet bool - value Value -} - -func (i MapBase[T, C, VC]) Create(val map[string]T, p *map[string]T, c C) Value { - *p = map[string]T{} - for k, v := range val { - (*p)[k] = v - } - var t T - np := new(T) - var vc VC - return &MapBase[T, C, VC]{ - dict: p, - value: vc.Create(t, np, c), - } -} - -// NewMapBase makes a *MapBase with default values -func NewMapBase[T any, C any, VC ValueCreator[T, C]](defaults map[string]T) *MapBase[T, C, VC] { - return &MapBase[T, C, VC]{ - dict: &defaults, - } -} - -// Set parses the value and appends it to the list of values -func (i *MapBase[T, C, VC]) Set(value string) error { - if !i.hasBeenSet { - *i.dict = map[string]T{} - i.hasBeenSet = true - } - - if strings.HasPrefix(value, slPfx) { - // Deserializing assumes overwrite - _ = json.Unmarshal([]byte(strings.Replace(value, slPfx, "", 1)), &i.dict) - i.hasBeenSet = true - return nil - } - - for _, item := range flagSplitMultiValues(value) { - key, value, ok := strings.Cut(item, defaultMapFlagKeyValueSeparator) - if !ok { - return fmt.Errorf("item %q is missing separator %q", item, defaultMapFlagKeyValueSeparator) - } - if err := i.value.Set(value); err != nil { - return err - } - tmp, ok := i.value.Get().(T) - if !ok { - return fmt.Errorf("unable to cast %v", i.value) - } - (*i.dict)[key] = tmp - } - - return nil -} - -// String returns a readable representation of this value (for usage defaults) -func (i *MapBase[T, C, VC]) String() string { - v := i.Value() - var t T - if reflect.TypeOf(t).Kind() == reflect.String { - return fmt.Sprintf("%v", v) - } - return fmt.Sprintf("%T{%s}", v, i.ToString(v)) -} - -// Serialize allows MapBase to fulfill Serializer -func (i *MapBase[T, C, VC]) Serialize() string { - jsonBytes, _ := json.Marshal(i.dict) - return fmt.Sprintf("%s%s", slPfx, string(jsonBytes)) -} - -// Value returns the mapping of values set by this flag -func (i *MapBase[T, C, VC]) Value() map[string]T { - if i.dict == nil { - return map[string]T{} - } - return *i.dict -} - -// Get returns the mapping of values set by this flag -func (i *MapBase[T, C, VC]) Get() interface{} { - return *i.dict -} - -func (i MapBase[T, C, VC]) ToString(t map[string]T) string { - var defaultVals []string - var vc VC - for _, k := range sortedKeys(t) { - defaultVals = append(defaultVals, k+defaultMapFlagKeyValueSeparator+vc.ToString(t[k])) - } - return strings.Join(defaultVals, ", ") -} - -func sortedKeys[T any](dict map[string]T) []string { - keys := make([]string, 0, len(dict)) - for k := range dict { - keys = append(keys, k) - } - sort.Strings(keys) - return keys -} diff --git a/vendor/github.com/urfave/cli/v3/flag_mutex.go b/vendor/github.com/urfave/cli/v3/flag_mutex.go deleted file mode 100644 index e03de8da..00000000 --- a/vendor/github.com/urfave/cli/v3/flag_mutex.go +++ /dev/null @@ -1,56 +0,0 @@ -package cli - -// MutuallyExclusiveFlags defines a mutually exclusive flag group -// Multiple option paths can be provided out of which -// only one can be defined on cmdline -// So for example -// [ --foo | [ --bar something --darth somethingelse ] ] -type MutuallyExclusiveFlags struct { - // Flag list - Flags [][]Flag - - // whether this group is required - Required bool - - // Category to apply to all flags within group - Category string -} - -func (grp MutuallyExclusiveFlags) check(cmd *Command) error { - oneSet := false - e := &mutuallyExclusiveGroup{} - - for _, grpf := range grp.Flags { - for _, f := range grpf { - for _, name := range f.Names() { - if cmd.IsSet(name) { - if oneSet { - e.flag2Name = name - return e - } - e.flag1Name = name - oneSet = true - break - } - } - if oneSet { - break - } - } - } - - if !oneSet && grp.Required { - return &mutuallyExclusiveGroupRequiredFlag{flags: &grp} - } - return nil -} - -func (grp MutuallyExclusiveFlags) propagateCategory() { - for _, grpf := range grp.Flags { - for _, f := range grpf { - if cf, ok := f.(CategorizableFlag); ok { - cf.SetCategory(grp.Category) - } - } - } -} diff --git a/vendor/github.com/urfave/cli/v3/flag_slice_base.go b/vendor/github.com/urfave/cli/v3/flag_slice_base.go deleted file mode 100644 index 7278e3ed..00000000 --- a/vendor/github.com/urfave/cli/v3/flag_slice_base.go +++ /dev/null @@ -1,110 +0,0 @@ -package cli - -import ( - "encoding/json" - "fmt" - "reflect" - "strings" -) - -// SliceBase wraps []T to satisfy flag.Value -type SliceBase[T any, C any, VC ValueCreator[T, C]] struct { - slice *[]T - hasBeenSet bool - value Value -} - -func (i SliceBase[T, C, VC]) Create(val []T, p *[]T, c C) Value { - *p = []T{} - *p = append(*p, val...) - var t T - np := new(T) - var vc VC - return &SliceBase[T, C, VC]{ - slice: p, - value: vc.Create(t, np, c), - } -} - -// NewSliceBase makes a *SliceBase with default values -func NewSliceBase[T any, C any, VC ValueCreator[T, C]](defaults ...T) *SliceBase[T, C, VC] { - return &SliceBase[T, C, VC]{ - slice: &defaults, - } -} - -// SetOne directly adds a value to the list of values -func (i *SliceBase[T, C, VC]) SetOne(value T) { - if !i.hasBeenSet { - *i.slice = []T{} - i.hasBeenSet = true - } - - *i.slice = append(*i.slice, value) -} - -// Set parses the value and appends it to the list of values -func (i *SliceBase[T, C, VC]) Set(value string) error { - if !i.hasBeenSet { - *i.slice = []T{} - i.hasBeenSet = true - } - - if strings.HasPrefix(value, slPfx) { - // Deserializing assumes overwrite - _ = json.Unmarshal([]byte(strings.Replace(value, slPfx, "", 1)), &i.slice) - i.hasBeenSet = true - return nil - } - - for _, s := range flagSplitMultiValues(value) { - if err := i.value.Set(strings.TrimSpace(s)); err != nil { - return err - } - tmp, ok := i.value.Get().(T) - if !ok { - return fmt.Errorf("unable to cast %v", i.value) - } - *i.slice = append(*i.slice, tmp) - } - - return nil -} - -// String returns a readable representation of this value (for usage defaults) -func (i *SliceBase[T, C, VC]) String() string { - v := i.Value() - var t T - if reflect.TypeOf(t).Kind() == reflect.String { - return fmt.Sprintf("%v", v) - } - return fmt.Sprintf("%T{%s}", v, i.ToString(v)) -} - -// Serialize allows SliceBase to fulfill Serializer -func (i *SliceBase[T, C, VC]) Serialize() string { - jsonBytes, _ := json.Marshal(i.slice) - return fmt.Sprintf("%s%s", slPfx, string(jsonBytes)) -} - -// Value returns the slice of values set by this flag -func (i *SliceBase[T, C, VC]) Value() []T { - if i.slice == nil { - return []T{} - } - return *i.slice -} - -// Get returns the slice of values set by this flag -func (i *SliceBase[T, C, VC]) Get() interface{} { - return *i.slice -} - -func (i SliceBase[T, C, VC]) ToString(t []T) string { - var defaultVals []string - var v VC - for _, s := range t { - defaultVals = append(defaultVals, v.ToString(s)) - } - return strings.Join(defaultVals, ", ") -} diff --git a/vendor/github.com/urfave/cli/v3/flag_string.go b/vendor/github.com/urfave/cli/v3/flag_string.go deleted file mode 100644 index bdc1ec65..00000000 --- a/vendor/github.com/urfave/cli/v3/flag_string.go +++ /dev/null @@ -1,66 +0,0 @@ -package cli - -import ( - "fmt" - "strings" -) - -type StringFlag = FlagBase[string, StringConfig, stringValue] - -// StringConfig defines the configuration for string flags -type StringConfig struct { - // Whether to trim whitespace of parsed value - TrimSpace bool -} - -// -- string Value -type stringValue struct { - destination *string - trimSpace bool -} - -// Below functions are to satisfy the ValueCreator interface - -func (s stringValue) Create(val string, p *string, c StringConfig) Value { - *p = val - return &stringValue{ - destination: p, - trimSpace: c.TrimSpace, - } -} - -func (s stringValue) ToString(val string) string { - if val == "" { - return val - } - return fmt.Sprintf("%q", val) -} - -// Below functions are to satisfy the flag.Value interface - -func (s *stringValue) Set(val string) error { - if s.trimSpace { - val = strings.TrimSpace(val) - } - *s.destination = val - return nil -} - -func (s *stringValue) Get() any { return *s.destination } - -func (s *stringValue) String() string { - if s.destination != nil { - return *s.destination - } - return "" -} - -func (cmd *Command) String(name string) string { - if v, ok := cmd.Value(name).(string); ok { - tracef("string available for flag name %[1]q with value=%[2]v (cmd=%[3]q)", name, v, cmd.Name) - return v - } - - tracef("string NOT available for flag name %[1]q (cmd=%[2]q)", name, cmd.Name) - return "" -} diff --git a/vendor/github.com/urfave/cli/v3/flag_string_map.go b/vendor/github.com/urfave/cli/v3/flag_string_map.go deleted file mode 100644 index 52fd7362..00000000 --- a/vendor/github.com/urfave/cli/v3/flag_string_map.go +++ /dev/null @@ -1,20 +0,0 @@ -package cli - -type ( - StringMap = MapBase[string, StringConfig, stringValue] - StringMapFlag = FlagBase[map[string]string, StringConfig, StringMap] -) - -var NewStringMap = NewMapBase[string, StringConfig, stringValue] - -// StringMap looks up the value of a local StringMapFlag, returns -// nil if not found -func (cmd *Command) StringMap(name string) map[string]string { - if v, ok := cmd.Value(name).(map[string]string); ok { - tracef("string map available for flag name %[1]q with value=%[2]v (cmd=%[3]q)", name, v, cmd.Name) - return v - } - - tracef("string map NOT available for flag name %[1]q (cmd=%[2]q)", name, cmd.Name) - return nil -} diff --git a/vendor/github.com/urfave/cli/v3/flag_string_slice.go b/vendor/github.com/urfave/cli/v3/flag_string_slice.go deleted file mode 100644 index 4cb6c5a0..00000000 --- a/vendor/github.com/urfave/cli/v3/flag_string_slice.go +++ /dev/null @@ -1,20 +0,0 @@ -package cli - -type ( - StringSlice = SliceBase[string, StringConfig, stringValue] - StringSliceFlag = FlagBase[[]string, StringConfig, StringSlice] -) - -var NewStringSlice = NewSliceBase[string, StringConfig, stringValue] - -// StringSlice looks up the value of a local StringSliceFlag, returns -// nil if not found -func (cmd *Command) StringSlice(name string) []string { - if v, ok := cmd.Value(name).([]string); ok { - tracef("string slice available for flag name %[1]q with value=%[2]v (cmd=%[3]q)", name, v, cmd.Name) - return v - } - - tracef("string slice NOT available for flag name %[1]q (cmd=%[2]q)", name, cmd.Name) - return nil -} diff --git a/vendor/github.com/urfave/cli/v3/flag_timestamp.go b/vendor/github.com/urfave/cli/v3/flag_timestamp.go deleted file mode 100644 index 8b9dd16a..00000000 --- a/vendor/github.com/urfave/cli/v3/flag_timestamp.go +++ /dev/null @@ -1,152 +0,0 @@ -package cli - -import ( - "errors" - "fmt" - "time" -) - -type TimestampFlag = FlagBase[time.Time, TimestampConfig, timestampValue] - -// TimestampConfig defines the config for timestamp flags -type TimestampConfig struct { - Timezone *time.Location - // Available layouts for flag value. - // - // Note that value for formats with missing year/date will be interpreted as current year/date respectively. - // - // Read more about time layouts: https://pkg.go.dev/time#pkg-constants - Layouts []string -} - -// timestampValue wrap to satisfy golang's flag interface. -type timestampValue struct { - timestamp *time.Time - hasBeenSet bool - layouts []string - location *time.Location -} - -var _ ValueCreator[time.Time, TimestampConfig] = timestampValue{} - -// Below functions are to satisfy the ValueCreator interface - -func (t timestampValue) Create(val time.Time, p *time.Time, c TimestampConfig) Value { - *p = val - return ×tampValue{ - timestamp: p, - layouts: c.Layouts, - location: c.Timezone, - } -} - -func (t timestampValue) ToString(b time.Time) string { - if b.IsZero() { - return "" - } - return fmt.Sprintf("%v", b) -} - -// Timestamp constructor(for internal testing only) -func newTimestamp(timestamp time.Time) *timestampValue { - return ×tampValue{timestamp: ×tamp} -} - -// Below functions are to satisfy the flag.Value interface - -// Parses the string value to timestamp -func (t *timestampValue) Set(value string) error { - var timestamp time.Time - var err error - - if t.location == nil { - t.location = time.UTC - } - - if len(t.layouts) == 0 { - return errors.New("got nil/empty layouts slice") - } - - for _, layout := range t.layouts { - var locErr error - - timestamp, locErr = time.ParseInLocation(layout, value, t.location) - if locErr != nil { - if err == nil { - err = locErr - continue - } - - err = newMultiError(err, locErr) - continue - } - - err = nil - break - } - - if err != nil { - return err - } - - defaultTS, _ := time.ParseInLocation(time.TimeOnly, time.TimeOnly, timestamp.Location()) - - n := time.Now() - - // If format is missing date (or year only), set it explicitly to current - if timestamp.Truncate(time.Hour*24).UnixNano() == defaultTS.Truncate(time.Hour*24).UnixNano() { - timestamp = time.Date( - n.Year(), - n.Month(), - n.Day(), - timestamp.Hour(), - timestamp.Minute(), - timestamp.Second(), - timestamp.Nanosecond(), - timestamp.Location(), - ) - } else if timestamp.Year() == 0 { - timestamp = time.Date( - n.Year(), - timestamp.Month(), - timestamp.Day(), - timestamp.Hour(), - timestamp.Minute(), - timestamp.Second(), - timestamp.Nanosecond(), - timestamp.Location(), - ) - } - - if t.timestamp != nil { - *t.timestamp = timestamp - } - t.hasBeenSet = true - return nil -} - -// String returns a readable representation of this value (for usage defaults) -func (t *timestampValue) String() string { - return fmt.Sprintf("%#v", t.timestamp) -} - -// Value returns the timestamp value stored in the flag -func (t *timestampValue) Value() *time.Time { - return t.timestamp -} - -// Get returns the flag structure -func (t *timestampValue) Get() any { - return *t.timestamp -} - -// Timestamp gets the timestamp from a flag name -func (cmd *Command) Timestamp(name string) time.Time { - if v, ok := cmd.Value(name).(time.Time); ok { - tracef("time.Time available for flag name %[1]q with value=%[2]v (cmd=%[3]q)", name, v, cmd.Name) - return v - } - - tracef("time.Time NOT available for flag name %[1]q (cmd=%[2]q)", name, cmd.Name) - return time.Time{} -} diff --git a/vendor/github.com/urfave/cli/v3/flag_uint.go b/vendor/github.com/urfave/cli/v3/flag_uint.go deleted file mode 100644 index 1f3c82da..00000000 --- a/vendor/github.com/urfave/cli/v3/flag_uint.go +++ /dev/null @@ -1,54 +0,0 @@ -package cli - -import ( - "strconv" -) - -type UintFlag = FlagBase[uint64, IntegerConfig, uintValue] - -// -- uint64 Value -type uintValue struct { - val *uint64 - base int -} - -// Below functions are to satisfy the ValueCreator interface - -func (i uintValue) Create(val uint64, p *uint64, c IntegerConfig) Value { - *p = val - return &uintValue{ - val: p, - base: c.Base, - } -} - -func (i uintValue) ToString(b uint64) string { - return strconv.FormatUint(b, 10) -} - -// Below functions are to satisfy the flag.Value interface - -func (i *uintValue) Set(s string) error { - v, err := strconv.ParseUint(s, i.base, 64) - if err != nil { - return err - } - *i.val = v - return err -} - -func (i *uintValue) Get() any { return uint64(*i.val) } - -func (i *uintValue) String() string { return strconv.FormatUint(uint64(*i.val), 10) } - -// Uint looks up the value of a local Uint64Flag, returns -// 0 if not found -func (cmd *Command) Uint(name string) uint64 { - if v, ok := cmd.Value(name).(uint64); ok { - tracef("uint available for flag name %[1]q with value=%[2]v (cmd=%[3]q)", name, v, cmd.Name) - return v - } - - tracef("uint NOT available for flag name %[1]q (cmd=%[2]q)", name, cmd.Name) - return 0 -} diff --git a/vendor/github.com/urfave/cli/v3/flag_uint_slice.go b/vendor/github.com/urfave/cli/v3/flag_uint_slice.go deleted file mode 100644 index ef7d4db4..00000000 --- a/vendor/github.com/urfave/cli/v3/flag_uint_slice.go +++ /dev/null @@ -1,20 +0,0 @@ -package cli - -type ( - UintSlice = SliceBase[uint64, IntegerConfig, uintValue] - UintSliceFlag = FlagBase[[]uint64, IntegerConfig, UintSlice] -) - -var NewUintSlice = NewSliceBase[uint64, IntegerConfig, uintValue] - -// UintSlice looks up the value of a local UintSliceFlag, returns -// nil if not found -func (cmd *Command) UintSlice(name string) []uint64 { - if v, ok := cmd.Value(name).([]uint64); ok { - tracef("uint slice available for flag name %[1]q with value=%[2]v (cmd=%[3]q)", name, v, cmd.Name) - return v - } - - tracef("uint slice NOT available for flag name %[1]q (cmd=%[2]q)", name, cmd.Name) - return nil -} diff --git a/vendor/github.com/urfave/cli/v3/funcs.go b/vendor/github.com/urfave/cli/v3/funcs.go deleted file mode 100644 index 9a7361d8..00000000 --- a/vendor/github.com/urfave/cli/v3/funcs.go +++ /dev/null @@ -1,50 +0,0 @@ -package cli - -import "context" - -// ShellCompleteFunc is an action to execute when the shell completion flag is set -type ShellCompleteFunc func(context.Context, *Command) - -// BeforeFunc is an action that executes prior to any subcommands being run once -// the context is ready. If a non-nil error is returned, no subcommands are -// run. -type BeforeFunc func(context.Context, *Command) error - -// AfterFunc is an action that executes after any subcommands are run and have -// finished. The AfterFunc is run even if Action() panics. -type AfterFunc func(context.Context, *Command) error - -// ActionFunc is the action to execute when no subcommands are specified -type ActionFunc func(context.Context, *Command) error - -// CommandNotFoundFunc is executed if the proper command cannot be found -type CommandNotFoundFunc func(context.Context, *Command, string) - -// OnUsageErrorFunc is executed if a usage error occurs. This is useful for displaying -// customized usage error messages. This function is able to replace the -// original error messages. If this function is not set, the "Incorrect usage" -// is displayed and the execution is interrupted. -type OnUsageErrorFunc func(ctx context.Context, cmd *Command, err error, isSubcommand bool) error - -// InvalidFlagAccessFunc is executed when an invalid flag is accessed from the context. -type InvalidFlagAccessFunc func(context.Context, *Command, string) - -// ExitErrHandlerFunc is executed if provided in order to handle exitError values -// returned by Actions and Before/After functions. -type ExitErrHandlerFunc func(context.Context, *Command, error) - -// FlagStringFunc is used by the help generation to display a flag, which is -// expected to be a single line. -type FlagStringFunc func(Flag) string - -// FlagNamePrefixFunc is used by the default FlagStringFunc to create prefix -// text for a flag's full name. -type FlagNamePrefixFunc func(fullName []string, placeholder string) string - -// FlagEnvHintFunc is used by the default FlagStringFunc to annotate flag help -// with the environment variable details. -type FlagEnvHintFunc func(envVars []string, str string) string - -// FlagFileHintFunc is used by the default FlagStringFunc to annotate flag help -// with the file path details. -type FlagFileHintFunc func(filePath, str string) string diff --git a/vendor/github.com/urfave/cli/v3/godoc-current.txt b/vendor/github.com/urfave/cli/v3/godoc-current.txt deleted file mode 100644 index 2d120038..00000000 --- a/vendor/github.com/urfave/cli/v3/godoc-current.txt +++ /dev/null @@ -1,1038 +0,0 @@ -package cli // import "github.com/urfave/cli/v3" - -Package cli provides a minimal framework for creating and organizing command -line Go applications. cli is designed to be easy to understand and write, -the most simple cli application can be written as follows: - - func main() { - (&cli.Command{}).Run(context.Background(), os.Args) - } - -Of course this application does not do much, so let's make this an actual -application: - - func main() { - cmd := &cli.Command{ - Name: "greet", - Usage: "say a greeting", - Action: func(c *cli.Context) error { - fmt.Println("Greetings") - return nil - }, - } - - cmd.Run(context.Background(), os.Args) - } - -VARIABLES - -var ( - SuggestFlag SuggestFlagFunc = suggestFlag - SuggestCommand SuggestCommandFunc = suggestCommand - SuggestDidYouMeanTemplate string = suggestDidYouMeanTemplate -) -var CommandHelpTemplate = `NAME: - {{template "helpNameTemplate" .}} - -USAGE: - {{template "usageTemplate" .}}{{if .Category}} - -CATEGORY: - {{.Category}}{{end}}{{if .Description}} - -DESCRIPTION: - {{template "descriptionTemplate" .}}{{end}}{{if .VisibleFlagCategories}} - -OPTIONS:{{template "visibleFlagCategoryTemplate" .}}{{else if .VisibleFlags}} - -OPTIONS:{{template "visibleFlagTemplate" .}}{{end}}{{if .VisiblePersistentFlags}} - -GLOBAL OPTIONS:{{template "visiblePersistentFlagTemplate" .}}{{end}} -` - CommandHelpTemplate is the text template for the command help topic. cli.go - uses text/template to render templates. You can render custom help text by - setting this variable. - -var DefaultInverseBoolPrefix = "no-" -var ErrWriter io.Writer = os.Stderr - ErrWriter is used to write errors to the user. This can be anything - implementing the io.Writer interface and defaults to os.Stderr. - -var FishCompletionTemplate = `# {{ .Command.Name }} fish shell completion - -function __fish_{{ .Command.Name }}_no_subcommand --description 'Test if there has been any subcommand yet' - for i in (commandline -opc) - if contains -- $i{{ range $v := .AllCommands }} {{ $v }}{{ end }} - return 1 - end - end - return 0 -end - -{{ range $v := .Completions }}{{ $v }} -{{ end }}` -var NewFloatSlice = NewSliceBase[float64, NoConfig, floatValue] -var NewIntSlice = NewSliceBase[int64, IntegerConfig, intValue] -var NewStringMap = NewMapBase[string, StringConfig, stringValue] -var NewStringSlice = NewSliceBase[string, StringConfig, stringValue] -var NewUintSlice = NewSliceBase[uint64, IntegerConfig, uintValue] -var OsExiter = os.Exit - OsExiter is the function used when the app exits. If not set defaults to - os.Exit. - -var RootCommandHelpTemplate = `NAME: - {{template "helpNameTemplate" .}} - -USAGE: - {{if .UsageText}}{{wrap .UsageText 3}}{{else}}{{.FullName}} {{if .VisibleFlags}}[global options]{{end}}{{if .VisibleCommands}} [command [command options]]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Version}}{{if not .HideVersion}} - -VERSION: - {{.Version}}{{end}}{{end}}{{if .Description}} - -DESCRIPTION: - {{template "descriptionTemplate" .}}{{end}} -{{- if len .Authors}} - -AUTHOR{{template "authorsTemplate" .}}{{end}}{{if .VisibleCommands}} - -COMMANDS:{{template "visibleCommandCategoryTemplate" .}}{{end}}{{if .VisibleFlagCategories}} - -GLOBAL OPTIONS:{{template "visibleFlagCategoryTemplate" .}}{{else if .VisibleFlags}} - -GLOBAL OPTIONS:{{template "visibleFlagTemplate" .}}{{end}}{{if .Copyright}} - -COPYRIGHT: - {{template "copyrightTemplate" .}}{{end}} -` - RootCommandHelpTemplate is the text template for the Default help topic. - cli.go uses text/template to render templates. You can render custom help - text by setting this variable. - -var SubcommandHelpTemplate = `NAME: - {{template "helpNameTemplate" .}} - -USAGE: - {{if .UsageText}}{{wrap .UsageText 3}}{{else}}{{.FullName}} {{if .VisibleCommands}}[command [command options]] {{end}}{{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Category}} - -CATEGORY: - {{.Category}}{{end}}{{if .Description}} - -DESCRIPTION: - {{template "descriptionTemplate" .}}{{end}}{{if .VisibleCommands}} - -COMMANDS:{{template "visibleCommandTemplate" .}}{{end}}{{if .VisibleFlagCategories}} - -OPTIONS:{{template "visibleFlagCategoryTemplate" .}}{{else if .VisibleFlags}} - -OPTIONS:{{template "visibleFlagTemplate" .}}{{end}} -` - SubcommandHelpTemplate is the text template for the subcommand help topic. - cli.go uses text/template to render templates. You can render custom help - text by setting this variable. - -var VersionPrinter = printVersion - VersionPrinter prints the version for the App - -var HelpPrinter helpPrinter = printHelp - HelpPrinter is a function that writes the help output. If not set - explicitly, this calls HelpPrinterCustom using only the default template - functions. - - If custom logic for printing help is required, this function can be - overridden. If the ExtraInfo field is defined on an App, this function - should not be modified, as HelpPrinterCustom will be used directly in order - to capture the extra information. - -var HelpPrinterCustom helpPrinterCustom = printHelpCustom - HelpPrinterCustom is a function that writes the help output. It is used as - the default implementation of HelpPrinter, and may be called directly if the - ExtraInfo field is set on an App. - - In the default implementation, if the customFuncs argument contains a - "wrapAt" key, which is a function which takes no arguments and returns an - int, this int value will be used to produce a "wrap" function used by the - default template to wrap long lines. - - -FUNCTIONS - -func DefaultAppComplete(ctx context.Context, cmd *Command) - DefaultAppComplete prints the list of subcommands as the default app - completion method - -func DefaultCompleteWithFlags(ctx context.Context, cmd *Command) -func FlagNames(name string, aliases []string) []string -func HandleExitCoder(err error) - HandleExitCoder handles errors implementing ExitCoder by printing their - message and calling OsExiter with the given exit code. - - If the given error instead implements MultiError, each error will be checked - for the ExitCoder interface, and OsExiter will be called with the last exit - code found, or exit code 1 if no ExitCoder is found. - - This function is the default error-handling behavior for an App. - -func ShowAppHelp(cmd *Command) error - ShowAppHelp is an action that displays the help. - -func ShowAppHelpAndExit(cmd *Command, exitCode int) - ShowAppHelpAndExit - Prints the list of subcommands for the app and exits - with exit code. - -func ShowCommandHelp(ctx context.Context, cmd *Command, commandName string) error - ShowCommandHelp prints help for the given command - -func ShowCommandHelpAndExit(ctx context.Context, cmd *Command, command string, code int) - ShowCommandHelpAndExit - exits with code after showing help - -func ShowSubcommandHelp(cmd *Command) error - ShowSubcommandHelp prints help for the given subcommand - -func ShowSubcommandHelpAndExit(cmd *Command, exitCode int) - ShowSubcommandHelpAndExit - Prints help for the given subcommand and exits - with exit code. - -func ShowVersion(cmd *Command) - ShowVersion prints the version number of the App - - -TYPES - -type ActionFunc func(context.Context, *Command) error - ActionFunc is the action to execute when no subcommands are specified - -type ActionableFlag interface { - RunAction(context.Context, *Command) error -} - ActionableFlag is an interface that wraps Flag interface and RunAction - operation. - -type AfterFunc func(context.Context, *Command) error - AfterFunc is an action that executes after any subcommands are run and have - finished. The AfterFunc is run even if Action() panics. - -type Args interface { - // Get returns the nth argument, or else a blank string - Get(n int) string - // First returns the first argument, or else a blank string - First() string - // Tail returns the rest of the arguments (not the first one) - // or else an empty string slice - Tail() []string - // Len returns the length of the wrapped slice - Len() int - // Present checks if there are any arguments present - Present() bool - // Slice returns a copy of the internal slice - Slice() []string -} - -type Argument interface { - Parse([]string) ([]string, error) - Usage() string -} - -type ArgumentBase[T any, C any, VC ValueCreator[T, C]] struct { - Name string `json:"name"` // the name of this argument - Value T `json:"value"` // the default value of this argument - Destination *T `json:"-"` // the destination point for this argument - Values *[]T `json:"-"` // all the values of this argument, only if multiple are supported - UsageText string `json:"usageText"` // the usage text to show - Min int `json:"minTimes"` // the min num of occurrences of this argument - Max int `json:"maxTimes"` // the max num of occurrences of this argument, set to -1 for unlimited - Config C `json:"config"` // config for this argument similar to Flag Config -} - -func (a *ArgumentBase[T, C, VC]) Parse(s []string) ([]string, error) - -func (a *ArgumentBase[T, C, VC]) Usage() string - -type BeforeFunc func(context.Context, *Command) error - BeforeFunc is an action that executes prior to any subcommands being run - once the context is ready. If a non-nil error is returned, no subcommands - are run. - -type BoolConfig struct { - Count *int -} - BoolConfig defines the configuration for bool flags - -type BoolFlag = FlagBase[bool, BoolConfig, boolValue] - -type BoolWithInverseFlag struct { - // The BoolFlag which the positive and negative flags are generated from - *BoolFlag - - // The prefix used to indicate a negative value - // Default: `env` becomes `no-env` - InversePrefix string - - // Has unexported fields. -} - -func (parent *BoolWithInverseFlag) Apply(set *flag.FlagSet) error - -func (parent *BoolWithInverseFlag) Flags() []Flag - -func (parent *BoolWithInverseFlag) IsSet() bool - -func (parent *BoolWithInverseFlag) Names() []string - -func (parent *BoolWithInverseFlag) RunAction(ctx context.Context, cmd *Command) error - -func (parent *BoolWithInverseFlag) String() string - String implements the standard Stringer interface. - - Example for BoolFlag{Name: "env"} --[no-]env (default: false) - -func (parent *BoolWithInverseFlag) Value() bool - -type CategorizableFlag interface { - // Returns the category of the flag - GetCategory() string - - // Sets the category of the flag - SetCategory(string) -} - CategorizableFlag is an interface that allows us to potentially use a flag - in a categorized representation. - -type Command struct { - // The name of the command - Name string `json:"name"` - // A list of aliases for the command - Aliases []string `json:"aliases"` - // A short description of the usage of this command - Usage string `json:"usage"` - // Text to override the USAGE section of help - UsageText string `json:"usageText"` - // A short description of the arguments of this command - ArgsUsage string `json:"argsUsage"` - // Version of the command - Version string `json:"version"` - // Longer explanation of how the command works - Description string `json:"description"` - // DefaultCommand is the (optional) name of a command - // to run if no command names are passed as CLI arguments. - DefaultCommand string `json:"defaultCommand"` - // The category the command is part of - Category string `json:"category"` - // List of child commands - Commands []*Command `json:"commands"` - // List of flags to parse - Flags []Flag `json:"flags"` - // Boolean to hide built-in help command and help flag - HideHelp bool `json:"hideHelp"` - // Ignored if HideHelp is true. - HideHelpCommand bool `json:"hideHelpCommand"` - // Boolean to hide built-in version flag and the VERSION section of help - HideVersion bool `json:"hideVersion"` - // Boolean to enable shell completion commands - EnableShellCompletion bool `json:"-"` - // Shell Completion generation command name - ShellCompletionCommandName string `json:"-"` - // The function to call when checking for shell command completions - ShellComplete ShellCompleteFunc `json:"-"` - // An action to execute before any subcommands are run, but after the context is ready - // If a non-nil error is returned, no subcommands are run - Before BeforeFunc `json:"-"` - // An action to execute after any subcommands are run, but after the subcommand has finished - // It is run even if Action() panics - After AfterFunc `json:"-"` - // The function to call when this command is invoked - Action ActionFunc `json:"-"` - // Execute this function if the proper command cannot be found - CommandNotFound CommandNotFoundFunc `json:"-"` - // Execute this function if a usage error occurs. - OnUsageError OnUsageErrorFunc `json:"-"` - // Execute this function when an invalid flag is accessed from the context - InvalidFlagAccessHandler InvalidFlagAccessFunc `json:"-"` - // Boolean to hide this command from help or completion - Hidden bool `json:"hidden"` - // List of all authors who contributed (string or fmt.Stringer) - // TODO: ~string | fmt.Stringer when interface unions are available - Authors []any `json:"authors"` - // Copyright of the binary if any - Copyright string `json:"copyright"` - // Reader reader to write input to (useful for tests) - Reader io.Reader `json:"-"` - // Writer writer to write output to - Writer io.Writer `json:"-"` - // ErrWriter writes error output - ErrWriter io.Writer `json:"-"` - // ExitErrHandler processes any error encountered while running an App before - // it is returned to the caller. If no function is provided, HandleExitCoder - // is used as the default behavior. - ExitErrHandler ExitErrHandlerFunc `json:"-"` - // Other custom info - Metadata map[string]interface{} `json:"metadata"` - // Carries a function which returns app specific info. - ExtraInfo func() map[string]string `json:"-"` - // CustomRootCommandHelpTemplate the text template for app help topic. - // cli.go uses text/template to render templates. You can - // render custom help text by setting this variable. - CustomRootCommandHelpTemplate string `json:"-"` - // SliceFlagSeparator is used to customize the separator for SliceFlag, the default is "," - SliceFlagSeparator string `json:"sliceFlagSeparator"` - // DisableSliceFlagSeparator is used to disable SliceFlagSeparator, the default is false - DisableSliceFlagSeparator bool `json:"disableSliceFlagSeparator"` - // Boolean to enable short-option handling so user can combine several - // single-character bool arguments into one - // i.e. foobar -o -v -> foobar -ov - UseShortOptionHandling bool `json:"useShortOptionHandling"` - // Enable suggestions for commands and flags - Suggest bool `json:"suggest"` - // Allows global flags set by libraries which use flag.XXXVar(...) directly - // to be parsed through this library - AllowExtFlags bool `json:"allowExtFlags"` - // Treat all flags as normal arguments if true - SkipFlagParsing bool `json:"skipFlagParsing"` - // CustomHelpTemplate the text template for the command help topic. - // cli.go uses text/template to render templates. You can - // render custom help text by setting this variable. - CustomHelpTemplate string `json:"-"` - // Use longest prefix match for commands - PrefixMatchCommands bool `json:"prefixMatchCommands"` - // Custom suggest command for matching - SuggestCommandFunc SuggestCommandFunc `json:"-"` - // Flag exclusion group - MutuallyExclusiveFlags []MutuallyExclusiveFlags `json:"mutuallyExclusiveFlags"` - // Arguments to parse for this command - Arguments []Argument `json:"arguments"` - // Whether to read arguments from stdin - // applicable to root command only - ReadArgsFromStdin bool `json:"readArgsFromStdin"` - - // Has unexported fields. -} - Command contains everything needed to run an application that accepts a - string slice of arguments such as os.Args. A given Command may contain Flags - and sub-commands in Commands. - -func (cmd *Command) Args() Args - Args returns the command line arguments associated with the command. - -func (cmd *Command) Bool(name string) bool - -func (cmd *Command) Command(name string) *Command - -func (cmd *Command) Count(name string) int - Count returns the num of occurrences of this flag - -func (cmd *Command) Duration(name string) time.Duration - -func (cmd *Command) FlagNames() []string - FlagNames returns a slice of flag names used by the this command and all of - its parent commands. - -func (cmd *Command) Float(name string) float64 - Float looks up the value of a local FloatFlag, returns 0 if not found - -func (cmd *Command) FloatSlice(name string) []float64 - FloatSlice looks up the value of a local FloatSliceFlag, returns nil if not - found - -func (cmd *Command) FullName() string - FullName returns the full name of the command. For commands with parents - this ensures that the parent commands are part of the command path. - -func (cmd *Command) HasName(name string) bool - HasName returns true if Command.Name matches given name - -func (cmd *Command) Int(name string) int64 - Int looks up the value of a local Int64Flag, returns 0 if not found - -func (cmd *Command) IntSlice(name string) []int64 - IntSlice looks up the value of a local IntSliceFlag, returns nil if not - found - -func (cmd *Command) IsSet(name string) bool - IsSet determines if the flag was actually set - -func (cmd *Command) Lineage() []*Command - Lineage returns *this* command and all of its ancestor commands in order - from child to parent - -func (cmd *Command) LocalFlagNames() []string - LocalFlagNames returns a slice of flag names used in this command. - -func (cmd *Command) NArg() int - NArg returns the number of the command line arguments. - -func (cmd *Command) Names() []string - Names returns the names including short names and aliases. - -func (cmd *Command) NumFlags() int - NumFlags returns the number of flags set - -func (cmd *Command) Root() *Command - Root returns the Command at the root of the graph - -func (cmd *Command) Run(ctx context.Context, osArgs []string) (deferErr error) - Run is the entry point to the command graph. The positional arguments are - parsed according to the Flag and Command definitions and the matching Action - functions are run. - -func (cmd *Command) Set(name, value string) error - Set sets a context flag to a value. - -func (cmd *Command) String(name string) string - -func (cmd *Command) StringMap(name string) map[string]string - StringMap looks up the value of a local StringMapFlag, returns nil if not - found - -func (cmd *Command) StringSlice(name string) []string - StringSlice looks up the value of a local StringSliceFlag, returns nil if - not found - -func (cmd *Command) Timestamp(name string) time.Time - Timestamp gets the timestamp from a flag name - -func (cmd *Command) ToFishCompletion() (string, error) - ToFishCompletion creates a fish completion string for the `*App` The - function errors if either parsing or writing of the string fails. - -func (cmd *Command) Uint(name string) uint64 - Uint looks up the value of a local Uint64Flag, returns 0 if not found - -func (cmd *Command) UintSlice(name string) []uint64 - UintSlice looks up the value of a local UintSliceFlag, returns nil if not - found - -func (cmd *Command) Value(name string) interface{} - Value returns the value of the flag corresponding to `name` - -func (cmd *Command) VisibleCategories() []CommandCategory - VisibleCategories returns a slice of categories and commands that are - Hidden=false - -func (cmd *Command) VisibleCommands() []*Command - VisibleCommands returns a slice of the Commands with Hidden=false - -func (cmd *Command) VisibleFlagCategories() []VisibleFlagCategory - VisibleFlagCategories returns a slice containing all the visible flag - categories with the flags they contain - -func (cmd *Command) VisibleFlags() []Flag - VisibleFlags returns a slice of the Flags with Hidden=false - -func (cmd *Command) VisiblePersistentFlags() []Flag - VisiblePersistentFlags returns a slice of LocalFlag with Persistent=true and - Hidden=false. - -type CommandCategories interface { - // AddCommand adds a command to a category, creating a new category if necessary. - AddCommand(category string, command *Command) - // Categories returns a slice of categories sorted by name - Categories() []CommandCategory -} - CommandCategories interface allows for category manipulation - -type CommandCategory interface { - // Name returns the category name string - Name() string - // VisibleCommands returns a slice of the Commands with Hidden=false - VisibleCommands() []*Command -} - CommandCategory is a category containing commands. - -type CommandNotFoundFunc func(context.Context, *Command, string) - CommandNotFoundFunc is executed if the proper command cannot be found - -type Countable interface { - Count() int -} - Countable is an interface to enable detection of flag values which support - repetitive flags - -type DocGenerationFlag interface { - // TakesValue returns true if the flag takes a value, otherwise false - TakesValue() bool - - // GetUsage returns the usage string for the flag - GetUsage() string - - // GetValue returns the flags value as string representation and an empty - // string if the flag takes no value at all. - GetValue() string - - // GetDefaultText returns the default text for this flag - GetDefaultText() string - - // GetEnvVars returns the env vars for this flag - GetEnvVars() []string - - // IsDefaultVisible returns whether the default value should be shown in - // help text - IsDefaultVisible() bool -} - DocGenerationFlag is an interface that allows documentation generation for - the flag - -type DocGenerationMultiValueFlag interface { - DocGenerationFlag - - // IsMultiValueFlag returns true for flags that can be given multiple times. - IsMultiValueFlag() bool -} - DocGenerationMultiValueFlag extends DocGenerationFlag for slice/map based - flags. - -type DurationFlag = FlagBase[time.Duration, NoConfig, durationValue] - -type ErrorFormatter interface { - Format(s fmt.State, verb rune) -} - ErrorFormatter is the interface that will suitably format the error output - -type ExitCoder interface { - error - ExitCode() int -} - ExitCoder is the interface checked by `App` and `Command` for a custom exit - code - -func Exit(message interface{}, exitCode int) ExitCoder - Exit wraps a message and exit code into an error, which by default is - handled with a call to os.Exit during default error handling. - - This is the simplest way to trigger a non-zero exit code for an App - without having to call os.Exit manually. During testing, this behavior - can be avoided by overriding the ExitErrHandler function on an App or the - package-global OsExiter function. - -type ExitErrHandlerFunc func(context.Context, *Command, error) - ExitErrHandlerFunc is executed if provided in order to handle exitError - values returned by Actions and Before/After functions. - -type Flag interface { - fmt.Stringer - - // Apply Flag settings to the given flag set - Apply(*flag.FlagSet) error - - // All possible names for this flag - Names() []string - - // Whether the flag has been set or not - IsSet() bool -} - Flag is a common interface related to parsing flags in cli. For more - advanced flag parsing techniques, it is recommended that this interface be - implemented. - -var GenerateShellCompletionFlag Flag = &BoolFlag{ - Name: "generate-shell-completion", - Hidden: true, -} - GenerateShellCompletionFlag enables shell completion - -var HelpFlag Flag = &BoolFlag{ - Name: "help", - Aliases: []string{"h"}, - Usage: "show help", - HideDefault: true, - Local: true, -} - HelpFlag prints the help for all commands and subcommands. Set to nil to - disable the flag. The subcommand will still be added unless HideHelp or - HideHelpCommand is set to true. - -var VersionFlag Flag = &BoolFlag{ - Name: "version", - Aliases: []string{"v"}, - Usage: "print the version", - HideDefault: true, - Local: true, -} - VersionFlag prints the version for the application - -type FlagBase[T any, C any, VC ValueCreator[T, C]] struct { - Name string `json:"name"` // name of the flag - Category string `json:"category"` // category of the flag, if any - DefaultText string `json:"defaultText"` // default text of the flag for usage purposes - HideDefault bool `json:"hideDefault"` // whether to hide the default value in output - Usage string `json:"usage"` // usage string for help output - Sources ValueSourceChain `json:"-"` // sources to load flag value from - Required bool `json:"required"` // whether the flag is required or not - Hidden bool `json:"hidden"` // whether to hide the flag in help output - Local bool `json:"local"` // whether the flag needs to be applied to subcommands as well - Value T `json:"defaultValue"` // default value for this flag if not set by from any source - Destination *T `json:"-"` // destination pointer for value when set - Aliases []string `json:"aliases"` // Aliases that are allowed for this flag - TakesFile bool `json:"takesFileArg"` // whether this flag takes a file argument, mainly for shell completion purposes - Action func(context.Context, *Command, T) error `json:"-"` // Action callback to be called when flag is set - Config C `json:"config"` // Additional/Custom configuration associated with this flag type - OnlyOnce bool `json:"onlyOnce"` // whether this flag can be duplicated on the command line - Validator func(T) error `json:"-"` // custom function to validate this flag value - ValidateDefaults bool `json:"validateDefaults"` // whether to validate defaults or not - - // Has unexported fields. -} - FlagBase [T,C,VC] is a generic flag base which can be used as a boilerplate - to implement the most common interfaces used by urfave/cli. - - T specifies the type - C specifies the configuration required(if any for that flag type) - VC specifies the value creator which creates the flag.Value emulation - -func (f *FlagBase[T, C, V]) Apply(set *flag.FlagSet) error - Apply populates the flag given the flag set and environment - -func (f *FlagBase[T, C, V]) Get(cmd *Command) T - Get returns the flag’s value in the given Command. - -func (f *FlagBase[T, C, V]) GetCategory() string - GetCategory returns the category of the flag - -func (f *FlagBase[T, C, V]) GetDefaultText() string - GetDefaultText returns the default text for this flag - -func (f *FlagBase[T, C, V]) GetEnvVars() []string - GetEnvVars returns the env vars for this flag - -func (f *FlagBase[T, C, V]) GetUsage() string - GetUsage returns the usage string for the flag - -func (f *FlagBase[T, C, V]) GetValue() string - GetValue returns the flags value as string representation and an empty - string if the flag takes no value at all. - -func (f *FlagBase[T, C, V]) IsDefaultVisible() bool - IsDefaultVisible returns true if the flag is not hidden, otherwise false - -func (f *FlagBase[T, C, VC]) IsLocal() bool - IsLocal returns false if flag needs to be persistent across subcommands - -func (f *FlagBase[T, C, VC]) IsMultiValueFlag() bool - IsMultiValueFlag returns true if the value type T can take multiple values - from cmd line. This is true for slice and map type flags - -func (f *FlagBase[T, C, V]) IsRequired() bool - IsRequired returns whether or not the flag is required - -func (f *FlagBase[T, C, V]) IsSet() bool - IsSet returns whether or not the flag has been set through env or file - -func (f *FlagBase[T, C, V]) IsVisible() bool - IsVisible returns true if the flag is not hidden, otherwise false - -func (f *FlagBase[T, C, V]) Names() []string - Names returns the names of the flag - -func (f *FlagBase[T, C, V]) RunAction(ctx context.Context, cmd *Command) error - RunAction executes flag action if set - -func (f *FlagBase[T, C, V]) SetCategory(c string) - -func (f *FlagBase[T, C, V]) String() string - String returns a readable representation of this value (for usage defaults) - -func (f *FlagBase[T, C, V]) TakesValue() bool - TakesValue returns true if the flag takes a value, otherwise false - -type FlagCategories interface { - // AddFlags adds a flag to a category, creating a new category if necessary. - AddFlag(category string, fl Flag) - // VisibleCategories returns a slice of visible flag categories sorted by name - VisibleCategories() []VisibleFlagCategory -} - FlagCategories interface allows for category manipulation - -type FlagEnvHintFunc func(envVars []string, str string) string - FlagEnvHintFunc is used by the default FlagStringFunc to annotate flag help - with the environment variable details. - -var FlagEnvHinter FlagEnvHintFunc = withEnvHint - FlagEnvHinter annotates flag help message with the environment variable - details. This is used by the default FlagStringer. - -type FlagFileHintFunc func(filePath, str string) string - FlagFileHintFunc is used by the default FlagStringFunc to annotate flag help - with the file path details. - -var FlagFileHinter FlagFileHintFunc = withFileHint - FlagFileHinter annotates flag help message with the environment variable - details. This is used by the default FlagStringer. - -type FlagNamePrefixFunc func(fullName []string, placeholder string) string - FlagNamePrefixFunc is used by the default FlagStringFunc to create prefix - text for a flag's full name. - -var FlagNamePrefixer FlagNamePrefixFunc = prefixedNames - FlagNamePrefixer converts a full flag name and its placeholder into the help - message flag prefix. This is used by the default FlagStringer. - -type FlagStringFunc func(Flag) string - FlagStringFunc is used by the help generation to display a flag, which is - expected to be a single line. - -var FlagStringer FlagStringFunc = stringifyFlag - FlagStringer converts a flag definition to a string. This is used by help to - display a flag. - -type FlagsByName []Flag - FlagsByName is a slice of Flag. - -func (f FlagsByName) Len() int - -func (f FlagsByName) Less(i, j int) bool - -func (f FlagsByName) Swap(i, j int) - -type FloatArg = ArgumentBase[float64, NoConfig, floatValue] - -type FloatFlag = FlagBase[float64, NoConfig, floatValue] - -type FloatSlice = SliceBase[float64, NoConfig, floatValue] - -type FloatSliceFlag = FlagBase[[]float64, NoConfig, FloatSlice] - -type IntArg = ArgumentBase[int64, IntegerConfig, intValue] - -type IntFlag = FlagBase[int64, IntegerConfig, intValue] - -type IntSlice = SliceBase[int64, IntegerConfig, intValue] - -type IntSliceFlag = FlagBase[[]int64, IntegerConfig, IntSlice] - -type IntegerConfig struct { - Base int -} - IntegerConfig is the configuration for all integer type flags - -type InvalidFlagAccessFunc func(context.Context, *Command, string) - InvalidFlagAccessFunc is executed when an invalid flag is accessed from the - context. - -type LocalFlag interface { - IsLocal() bool -} - LocalFlag is an interface to enable detection of flags which are local to - current command - -type MapBase[T any, C any, VC ValueCreator[T, C]] struct { - // Has unexported fields. -} - MapBase wraps map[string]T to satisfy flag.Value - -func NewMapBase[T any, C any, VC ValueCreator[T, C]](defaults map[string]T) *MapBase[T, C, VC] - NewMapBase makes a *MapBase with default values - -func (i MapBase[T, C, VC]) Create(val map[string]T, p *map[string]T, c C) Value - -func (i *MapBase[T, C, VC]) Get() interface{} - Get returns the mapping of values set by this flag - -func (i *MapBase[T, C, VC]) Serialize() string - Serialize allows MapBase to fulfill Serializer - -func (i *MapBase[T, C, VC]) Set(value string) error - Set parses the value and appends it to the list of values - -func (i *MapBase[T, C, VC]) String() string - String returns a readable representation of this value (for usage defaults) - -func (i MapBase[T, C, VC]) ToString(t map[string]T) string - -func (i *MapBase[T, C, VC]) Value() map[string]T - Value returns the mapping of values set by this flag - -type MultiError interface { - error - Errors() []error -} - MultiError is an error that wraps multiple errors. - -type MutuallyExclusiveFlags struct { - // Flag list - Flags [][]Flag - - // whether this group is required - Required bool - - // Category to apply to all flags within group - Category string -} - MutuallyExclusiveFlags defines a mutually exclusive flag group Multiple - option paths can be provided out of which only one can be defined on cmdline - So for example [ --foo | [ --bar something --darth somethingelse ] ] - -type NoConfig struct{} - NoConfig is for flags which dont need a custom configuration - -type OnUsageErrorFunc func(ctx context.Context, cmd *Command, err error, isSubcommand bool) error - OnUsageErrorFunc is executed if a usage error occurs. This is useful for - displaying customized usage error messages. This function is able to replace - the original error messages. If this function is not set, the "Incorrect - usage" is displayed and the execution is interrupted. - -type RequiredFlag interface { - // whether the flag is a required flag or not - IsRequired() bool -} - RequiredFlag is an interface that allows us to mark flags as required - it allows flags required flags to be backwards compatible with the Flag - interface - -type Serializer interface { - Serialize() string -} - Serializer is used to circumvent the limitations of flag.FlagSet.Set - -type ShellCompleteFunc func(context.Context, *Command) - ShellCompleteFunc is an action to execute when the shell completion flag is - set - -type SliceBase[T any, C any, VC ValueCreator[T, C]] struct { - // Has unexported fields. -} - SliceBase wraps []T to satisfy flag.Value - -func NewSliceBase[T any, C any, VC ValueCreator[T, C]](defaults ...T) *SliceBase[T, C, VC] - NewSliceBase makes a *SliceBase with default values - -func (i SliceBase[T, C, VC]) Create(val []T, p *[]T, c C) Value - -func (i *SliceBase[T, C, VC]) Get() interface{} - Get returns the slice of values set by this flag - -func (i *SliceBase[T, C, VC]) Serialize() string - Serialize allows SliceBase to fulfill Serializer - -func (i *SliceBase[T, C, VC]) Set(value string) error - Set parses the value and appends it to the list of values - -func (i *SliceBase[T, C, VC]) SetOne(value T) - SetOne directly adds a value to the list of values - -func (i *SliceBase[T, C, VC]) String() string - String returns a readable representation of this value (for usage defaults) - -func (i SliceBase[T, C, VC]) ToString(t []T) string - -func (i *SliceBase[T, C, VC]) Value() []T - Value returns the slice of values set by this flag - -type StringArg = ArgumentBase[string, StringConfig, stringValue] - -type StringConfig struct { - // Whether to trim whitespace of parsed value - TrimSpace bool -} - StringConfig defines the configuration for string flags - -type StringFlag = FlagBase[string, StringConfig, stringValue] - -type StringMap = MapBase[string, StringConfig, stringValue] - -type StringMapArg = ArgumentBase[map[string]string, StringConfig, StringMap] - -type StringMapFlag = FlagBase[map[string]string, StringConfig, StringMap] - -type StringSlice = SliceBase[string, StringConfig, stringValue] - -type StringSliceFlag = FlagBase[[]string, StringConfig, StringSlice] - -type SuggestCommandFunc func(commands []*Command, provided string) string - -type SuggestFlagFunc func(flags []Flag, provided string, hideHelp bool) string - -type TimestampArg = ArgumentBase[time.Time, TimestampConfig, timestampValue] - -type TimestampConfig struct { - Timezone *time.Location - // Available layouts for flag value. - // - // Note that value for formats with missing year/date will be interpreted as current year/date respectively. - // - // Read more about time layouts: https://pkg.go.dev/time#pkg-constants - Layouts []string -} - TimestampConfig defines the config for timestamp flags - -type TimestampFlag = FlagBase[time.Time, TimestampConfig, timestampValue] - -type UintArg = ArgumentBase[uint64, IntegerConfig, uintValue] - -type UintFlag = FlagBase[uint64, IntegerConfig, uintValue] - -type UintSlice = SliceBase[uint64, IntegerConfig, uintValue] - -type UintSliceFlag = FlagBase[[]uint64, IntegerConfig, UintSlice] - -type Value interface { - flag.Value - flag.Getter -} - Value represents a value as used by cli. For now it implements the golang - flag.Value interface - -type ValueCreator[T any, C any] interface { - Create(T, *T, C) Value - ToString(T) string -} - ValueCreator is responsible for creating a flag.Value emulation as well as - custom formatting - - T specifies the type - C specifies the config for the type - -type ValueSource interface { - fmt.Stringer - fmt.GoStringer - - // Lookup returns the value from the source and if it was found - // or returns an empty string and false - Lookup() (string, bool) -} - ValueSource is a source which can be used to look up a value, typically for - use with a cli.Flag - -func EnvVar(key string) ValueSource - -func File(path string) ValueSource - -type ValueSourceChain struct { - Chain []ValueSource -} - ValueSourceChain contains an ordered series of ValueSource that allows for - lookup where the first ValueSource to resolve is returned - -func EnvVars(keys ...string) ValueSourceChain - EnvVars is a helper function to encapsulate a number of envVarValueSource - together as a ValueSourceChain - -func Files(paths ...string) ValueSourceChain - Files is a helper function to encapsulate a number of fileValueSource - together as a ValueSourceChain - -func NewValueSourceChain(src ...ValueSource) ValueSourceChain - -func (vsc *ValueSourceChain) Append(other ValueSourceChain) - -func (vsc *ValueSourceChain) EnvKeys() []string - -func (vsc *ValueSourceChain) GoString() string - -func (vsc *ValueSourceChain) Lookup() (string, bool) - -func (vsc *ValueSourceChain) LookupWithSource() (string, ValueSource, bool) - -func (vsc *ValueSourceChain) String() string - -type VisibleFlag interface { - // IsVisible returns true if the flag is not hidden, otherwise false - IsVisible() bool -} - VisibleFlag is an interface that allows to check if a flag is visible - -type VisibleFlagCategory interface { - // Name returns the category name string - Name() string - // Flags returns a slice of VisibleFlag sorted by name - Flags() []Flag -} - VisibleFlagCategory is a category containing flags. - diff --git a/vendor/github.com/urfave/cli/v3/help.go b/vendor/github.com/urfave/cli/v3/help.go deleted file mode 100644 index 4a31a49e..00000000 --- a/vendor/github.com/urfave/cli/v3/help.go +++ /dev/null @@ -1,588 +0,0 @@ -package cli - -import ( - "context" - "fmt" - "io" - "os" - "strings" - "text/tabwriter" - "text/template" - "unicode/utf8" -) - -const ( - helpName = "help" - helpAlias = "h" -) - -// Prints help for the App or Command -type helpPrinter func(w io.Writer, templ string, data interface{}) - -// Prints help for the App or Command with custom template function. -type helpPrinterCustom func(w io.Writer, templ string, data interface{}, customFunc map[string]interface{}) - -// HelpPrinter is a function that writes the help output. If not set explicitly, -// this calls HelpPrinterCustom using only the default template functions. -// -// If custom logic for printing help is required, this function can be -// overridden. If the ExtraInfo field is defined on an App, this function -// should not be modified, as HelpPrinterCustom will be used directly in order -// to capture the extra information. -var HelpPrinter helpPrinter = printHelp - -// HelpPrinterCustom is a function that writes the help output. It is used as -// the default implementation of HelpPrinter, and may be called directly if -// the ExtraInfo field is set on an App. -// -// In the default implementation, if the customFuncs argument contains a -// "wrapAt" key, which is a function which takes no arguments and returns -// an int, this int value will be used to produce a "wrap" function used -// by the default template to wrap long lines. -var HelpPrinterCustom helpPrinterCustom = printHelpCustom - -// VersionPrinter prints the version for the App -var VersionPrinter = printVersion - -func buildHelpCommand(withAction bool) *Command { - cmd := &Command{ - Name: helpName, - Aliases: []string{helpAlias}, - Usage: "Shows a list of commands or help for one command", - ArgsUsage: "[command]", - HideHelp: true, - } - - if withAction { - cmd.Action = helpCommandAction - } - - return cmd -} - -func helpCommandAction(ctx context.Context, cmd *Command) error { - args := cmd.Args() - firstArg := args.First() - - tracef("doing help for cmd %[1]q with args %[2]q", cmd, args) - - // This action can be triggered by a "default" action of a command - // or via cmd.Run when cmd == helpCmd. So we have following possibilities - // - // 1 $ app - // 2 $ app help - // 3 $ app foo - // 4 $ app help foo - // 5 $ app foo help - - // Case 4. when executing a help command set the context to parent - // to allow resolution of subsequent args. This will transform - // $ app help foo - // to - // $ app foo - // which will then be handled as case 3 - if cmd.parent != nil && (cmd.HasName(helpName) || cmd.HasName(helpAlias)) { - tracef("setting cmd to cmd.parent") - cmd = cmd.parent - } - - // Case 4. $ app help foo - // foo is the command for which help needs to be shown - if firstArg != "" { - tracef("returning ShowCommandHelp with %[1]q", firstArg) - return ShowCommandHelp(ctx, cmd, firstArg) - } - - // Case 1 & 2 - // Special case when running help on main app itself as opposed to individual - // commands/subcommands - if cmd.parent == nil { - tracef("returning ShowAppHelp") - _ = ShowAppHelp(cmd) - return nil - } - - // Case 3, 5 - if (len(cmd.Commands) == 1 && !cmd.HideHelp) || - (len(cmd.Commands) == 0 && cmd.HideHelp) { - - tmpl := cmd.CustomHelpTemplate - if tmpl == "" { - tmpl = CommandHelpTemplate - } - - tracef("running HelpPrinter with command %[1]q", cmd.Name) - HelpPrinter(cmd.Root().Writer, tmpl, cmd) - - return nil - } - - tracef("running ShowSubcommandHelp") - return ShowSubcommandHelp(cmd) -} - -// ShowAppHelpAndExit - Prints the list of subcommands for the app and exits with exit code. -func ShowAppHelpAndExit(cmd *Command, exitCode int) { - _ = ShowAppHelp(cmd) - os.Exit(exitCode) -} - -// ShowAppHelp is an action that displays the help. -func ShowAppHelp(cmd *Command) error { - tmpl := cmd.CustomRootCommandHelpTemplate - if tmpl == "" { - tracef("using RootCommandHelpTemplate") - tmpl = RootCommandHelpTemplate - } - - if cmd.ExtraInfo == nil { - HelpPrinter(cmd.Root().Writer, tmpl, cmd.Root()) - return nil - } - - tracef("setting ExtraInfo in customAppData") - customAppData := func() map[string]any { - return map[string]any{ - "ExtraInfo": cmd.ExtraInfo, - } - } - HelpPrinterCustom(cmd.Root().Writer, tmpl, cmd.Root(), customAppData()) - - return nil -} - -// DefaultAppComplete prints the list of subcommands as the default app completion method -func DefaultAppComplete(ctx context.Context, cmd *Command) { - DefaultCompleteWithFlags(ctx, cmd) -} - -func printCommandSuggestions(commands []*Command, writer io.Writer) { - for _, command := range commands { - if command.Hidden { - continue - } - if strings.HasSuffix(os.Getenv("0"), "zsh") { - _, _ = fmt.Fprintf(writer, "%s:%s\n", command.Name, command.Usage) - } else { - _, _ = fmt.Fprintf(writer, "%s\n", command.Name) - } - } -} - -func cliArgContains(flagName string) bool { - for _, name := range strings.Split(flagName, ",") { - name = strings.TrimSpace(name) - count := utf8.RuneCountInString(name) - if count > 2 { - count = 2 - } - flag := fmt.Sprintf("%s%s", strings.Repeat("-", count), name) - for _, a := range os.Args { - if a == flag { - return true - } - } - } - return false -} - -func printFlagSuggestions(lastArg string, flags []Flag, writer io.Writer) { - cur := strings.TrimPrefix(lastArg, "-") - cur = strings.TrimPrefix(cur, "-") - for _, flag := range flags { - if bflag, ok := flag.(*BoolFlag); ok && bflag.Hidden { - continue - } - - usage := "" - if docFlag, ok := flag.(DocGenerationFlag); ok { - usage = docFlag.GetUsage() - } - - name := strings.TrimSpace(flag.Names()[0]) - // this will get total count utf8 letters in flag name - count := utf8.RuneCountInString(name) - if count > 2 { - count = 2 // reuse this count to generate single - or -- in flag completion - } - // if flag name has more than one utf8 letter and last argument in cli has -- prefix then - // skip flag completion for short flags example -v or -x - if strings.HasPrefix(lastArg, "--") && count == 1 { - continue - } - // match if last argument matches this flag and it is not repeated - if strings.HasPrefix(name, cur) && cur != name && !cliArgContains(name) { - flagCompletion := fmt.Sprintf("%s%s", strings.Repeat("-", count), name) - if usage != "" && strings.HasSuffix(os.Getenv("SHELL"), "zsh") { - flagCompletion = fmt.Sprintf("%s:%s", flagCompletion, usage) - } - fmt.Fprintln(writer, flagCompletion) - } - } -} - -func DefaultCompleteWithFlags(ctx context.Context, cmd *Command) { - args := os.Args - if cmd != nil && cmd.flagSet != nil && cmd.parent != nil { - args = cmd.Args().Slice() - tracef("running default complete with flags[%v] on command %[2]q", args, cmd.Name) - } else { - tracef("running default complete with os.Args flags[%v]", args) - } - argsLen := len(args) - lastArg := "" - // parent command will have --generate-shell-completion so we need - // to account for that - if argsLen > 1 { - lastArg = args[argsLen-2] - } else if argsLen > 0 { - lastArg = args[argsLen-1] - } - - if strings.HasPrefix(lastArg, "-") { - tracef("printing flag suggestion for flag[%v] on command %[1]q", lastArg, cmd.Name) - printFlagSuggestions(lastArg, cmd.Flags, cmd.Root().Writer) - return - } - - if cmd != nil { - tracef("printing command suggestions on command %[1]q", cmd.Name) - printCommandSuggestions(cmd.Commands, cmd.Root().Writer) - return - } -} - -// ShowCommandHelpAndExit - exits with code after showing help -func ShowCommandHelpAndExit(ctx context.Context, cmd *Command, command string, code int) { - _ = ShowCommandHelp(ctx, cmd, command) - os.Exit(code) -} - -// ShowCommandHelp prints help for the given command -func ShowCommandHelp(ctx context.Context, cmd *Command, commandName string) error { - for _, subCmd := range cmd.Commands { - if !subCmd.HasName(commandName) { - continue - } - - tmpl := subCmd.CustomHelpTemplate - if tmpl == "" { - if len(subCmd.Commands) == 0 { - tracef("using CommandHelpTemplate") - tmpl = CommandHelpTemplate - } else { - tracef("using SubcommandHelpTemplate") - tmpl = SubcommandHelpTemplate - } - } - - tracef("running HelpPrinter") - HelpPrinter(cmd.Root().Writer, tmpl, subCmd) - - tracef("returning nil after printing help") - return nil - } - - tracef("no matching command found") - - if cmd.CommandNotFound == nil { - errMsg := fmt.Sprintf("No help topic for '%v'", commandName) - - if cmd.Suggest { - if suggestion := SuggestCommand(cmd.Commands, commandName); suggestion != "" { - errMsg += ". " + suggestion - } - } - - tracef("exiting 3 with errMsg %[1]q", errMsg) - return Exit(errMsg, 3) - } - - tracef("running CommandNotFound func for %[1]q", commandName) - cmd.CommandNotFound(ctx, cmd, commandName) - - return nil -} - -// ShowSubcommandHelpAndExit - Prints help for the given subcommand and exits with exit code. -func ShowSubcommandHelpAndExit(cmd *Command, exitCode int) { - _ = ShowSubcommandHelp(cmd) - os.Exit(exitCode) -} - -// ShowSubcommandHelp prints help for the given subcommand -func ShowSubcommandHelp(cmd *Command) error { - if cmd == nil { - return nil - } - - HelpPrinter(cmd.Root().Writer, SubcommandHelpTemplate, cmd) - return nil -} - -// ShowVersion prints the version number of the App -func ShowVersion(cmd *Command) { - tracef("showing version via VersionPrinter (cmd=%[1]q)", cmd.Name) - VersionPrinter(cmd) -} - -func printVersion(cmd *Command) { - _, _ = fmt.Fprintf(cmd.Root().Writer, "%v version %v\n", cmd.Name, cmd.Version) -} - -func handleTemplateError(err error) { - if err != nil { - tracef("error encountered during template parse: %[1]v", err) - // If the writer is closed, t.Execute will fail, and there's nothing - // we can do to recover. - if os.Getenv("CLI_TEMPLATE_ERROR_DEBUG") != "" { - _, _ = fmt.Fprintf(ErrWriter, "CLI TEMPLATE ERROR: %#v\n", err) - } - return - } -} - -// printHelpCustom is the default implementation of HelpPrinterCustom. -// -// The customFuncs map will be combined with a default template.FuncMap to -// allow using arbitrary functions in template rendering. -func printHelpCustom(out io.Writer, templ string, data interface{}, customFuncs map[string]interface{}) { - const maxLineLength = 10000 - - tracef("building default funcMap") - funcMap := template.FuncMap{ - "join": strings.Join, - "subtract": subtract, - "indent": indent, - "nindent": nindent, - "trim": strings.TrimSpace, - "wrap": func(input string, offset int) string { return wrap(input, offset, maxLineLength) }, - "offset": offset, - "offsetCommands": offsetCommands, - } - - if wa, ok := customFuncs["wrapAt"]; ok { - if wrapAtFunc, ok := wa.(func() int); ok { - wrapAt := wrapAtFunc() - customFuncs["wrap"] = func(input string, offset int) string { - return wrap(input, offset, wrapAt) - } - } - } - - for key, value := range customFuncs { - funcMap[key] = value - } - - w := tabwriter.NewWriter(out, 1, 8, 2, ' ', 0) - t := template.Must(template.New("help").Funcs(funcMap).Parse(templ)) - if _, err := t.New("helpNameTemplate").Parse(helpNameTemplate); err != nil { - handleTemplateError(err) - } - - if _, err := t.New("argsTemplate").Parse(argsTemplate); err != nil { - handleTemplateError(err) - } - - if _, err := t.New("usageTemplate").Parse(usageTemplate); err != nil { - handleTemplateError(err) - } - - if _, err := t.New("descriptionTemplate").Parse(descriptionTemplate); err != nil { - handleTemplateError(err) - } - - if _, err := t.New("visibleCommandTemplate").Parse(visibleCommandTemplate); err != nil { - handleTemplateError(err) - } - - if _, err := t.New("copyrightTemplate").Parse(copyrightTemplate); err != nil { - handleTemplateError(err) - } - - if _, err := t.New("versionTemplate").Parse(versionTemplate); err != nil { - handleTemplateError(err) - } - - if _, err := t.New("visibleFlagCategoryTemplate").Parse(visibleFlagCategoryTemplate); err != nil { - handleTemplateError(err) - } - - if _, err := t.New("visibleFlagTemplate").Parse(visibleFlagTemplate); err != nil { - handleTemplateError(err) - } - - if _, err := t.New("visiblePersistentFlagTemplate").Parse(visiblePersistentFlagTemplate); err != nil { - handleTemplateError(err) - } - - if _, err := t.New("visibleGlobalFlagCategoryTemplate").Parse(strings.Replace(visibleFlagCategoryTemplate, "OPTIONS", "GLOBAL OPTIONS", -1)); err != nil { - handleTemplateError(err) - } - - if _, err := t.New("authorsTemplate").Parse(authorsTemplate); err != nil { - handleTemplateError(err) - } - - if _, err := t.New("visibleCommandCategoryTemplate").Parse(visibleCommandCategoryTemplate); err != nil { - handleTemplateError(err) - } - - tracef("executing template") - handleTemplateError(t.Execute(w, data)) - - _ = w.Flush() -} - -func printHelp(out io.Writer, templ string, data interface{}) { - HelpPrinterCustom(out, templ, data, nil) -} - -func checkVersion(cmd *Command) bool { - found := false - for _, name := range VersionFlag.Names() { - if cmd.Bool(name) { - found = true - } - } - return found -} - -func checkShellCompleteFlag(c *Command, arguments []string) (bool, []string) { - if (c.parent == nil && !c.EnableShellCompletion) || (c.parent != nil && !c.Root().shellCompletion) { - return false, arguments - } - - pos := len(arguments) - 1 - lastArg := arguments[pos] - - if lastArg != "--generate-shell-completion" { - return false, arguments - } - - for _, arg := range arguments { - // If arguments include "--", shell completion is disabled - // because after "--" only positional arguments are accepted. - // https://unix.stackexchange.com/a/11382 - if arg == "--" { - return false, arguments - } - } - - return true, arguments[:pos] -} - -func checkCompletions(ctx context.Context, cmd *Command) bool { - tracef("checking completions on command %[1]q", cmd.Name) - - if !cmd.Root().shellCompletion { - return false - } - - if argsArguments := cmd.Args(); argsArguments.Present() { - name := argsArguments.First() - if cmd := cmd.Command(name); cmd != nil { - // let the command handle the completion - return false - } - } - - if cmd.ShellComplete != nil { - tracef("running shell completion func for command %[1]q", cmd.Name) - cmd.ShellComplete(ctx, cmd) - } - - return true -} - -func subtract(a, b int) int { - return a - b -} - -func indent(spaces int, v string) string { - pad := strings.Repeat(" ", spaces) - return pad + strings.Replace(v, "\n", "\n"+pad, -1) -} - -func nindent(spaces int, v string) string { - return "\n" + indent(spaces, v) -} - -func wrap(input string, offset int, wrapAt int) string { - var ss []string - - lines := strings.Split(input, "\n") - - padding := strings.Repeat(" ", offset) - - for i, line := range lines { - if line == "" { - ss = append(ss, line) - } else { - wrapped := wrapLine(line, offset, wrapAt, padding) - if i == 0 { - ss = append(ss, wrapped) - } else { - ss = append(ss, padding+wrapped) - } - - } - } - - return strings.Join(ss, "\n") -} - -func wrapLine(input string, offset int, wrapAt int, padding string) string { - if wrapAt <= offset || len(input) <= wrapAt-offset { - return input - } - - lineWidth := wrapAt - offset - words := strings.Fields(input) - if len(words) == 0 { - return input - } - - wrapped := words[0] - spaceLeft := lineWidth - len(wrapped) - for _, word := range words[1:] { - if len(word)+1 > spaceLeft { - wrapped += "\n" + padding + word - spaceLeft = lineWidth - len(word) - } else { - wrapped += " " + word - spaceLeft -= 1 + len(word) - } - } - - return wrapped -} - -func offset(input string, fixed int) int { - return len(input) + fixed -} - -// this function tries to find the max width of the names column -// so say we have the following rows for help -// -// foo1, foo2, foo3 some string here -// bar1, b2 some other string here -// -// We want to offset the 2nd row usage by some amount so that everything -// is aligned -// -// foo1, foo2, foo3 some string here -// bar1, b2 some other string here -// -// to find that offset we find the length of all the rows and use the max -// to calculate the offset -func offsetCommands(cmds []*Command, fixed int) int { - max := 0 - for _, cmd := range cmds { - s := strings.Join(cmd.Names(), ", ") - if len(s) > max { - max = len(s) - } - } - return max + fixed -} diff --git a/vendor/github.com/urfave/cli/v3/mkdocs-reqs.txt b/vendor/github.com/urfave/cli/v3/mkdocs-reqs.txt deleted file mode 100644 index 7043b559..00000000 --- a/vendor/github.com/urfave/cli/v3/mkdocs-reqs.txt +++ /dev/null @@ -1,5 +0,0 @@ -mkdocs-git-revision-date-localized-plugin~=1.2 -mkdocs-material-extensions~=1.3 -mkdocs-material~=8.5 -mkdocs~=1.6 -pygments~=2.18 diff --git a/vendor/github.com/urfave/cli/v3/mkdocs.yml b/vendor/github.com/urfave/cli/v3/mkdocs.yml deleted file mode 100644 index 84391dbd..00000000 --- a/vendor/github.com/urfave/cli/v3/mkdocs.yml +++ /dev/null @@ -1,107 +0,0 @@ -# NOTE: the mkdocs dependencies will need to be installed out of -# band until this whole thing gets more automated: -# -# pip install -r mkdocs-reqs.txt -# - -site_name: urfave/cli -site_url: https://cli.urfave.org/ -repo_url: https://github.com/urfave/cli -edit_uri: edit/main/docs/ -nav: - - Home: - - Welcome: index.md - - Contributing: CONTRIBUTING.md - - Code of Conduct: CODE_OF_CONDUCT.md - - Releasing: RELEASING.md - - Security: SECURITY.md - - Migrate v1 to v2: migrate-v1-to-v2.md - - v2 Manual: - - Getting Started: v2/getting-started.md - - Migrating From Older Releases: v2/migrating-from-older-releases.md - - Examples: - - Greet: v2/examples/greet.md - - Arguments: v2/examples/arguments.md - - Flags: v2/examples/flags.md - - Subcommands: v2/examples/subcommands.md - - Subcommands Categories: v2/examples/subcommands-categories.md - - Exit Codes: v2/examples/exit-codes.md - - Combining Short Options: v2/examples/combining-short-options.md - - Bash Completions: v2/examples/bash-completions.md - - Generated Help Text: v2/examples/generated-help-text.md - - Version Flag: v2/examples/version-flag.md - - Timestamp Flag: v2/examples/timestamp-flag.md - - Suggestions: v2/examples/suggestions.md - - Full API Example: v2/examples/full-api-example.md - - v1 Manual: - - Getting Started: v1/getting-started.md - - Migrating to v2: v1/migrating-to-v2.md - - Examples: - - Greet: v1/examples/greet.md - - Arguments: v1/examples/arguments.md - - Flags: v1/examples/flags.md - - Subcommands: v1/examples/subcommands.md - - Subcommands (Categories): v1/examples/subcommands-categories.md - - Exit Codes: v1/examples/exit-codes.md - - Combining Short Options: v1/examples/combining-short-options.md - - Bash Completions: v1/examples/bash-completions.md - - Generated Help Text: v1/examples/generated-help-text.md - - Version Flag: v1/examples/version-flag.md - -theme: - name: material - palette: - - media: "(prefers-color-scheme: light)" - scheme: default - toggle: - icon: material/brightness-4 - name: dark mode - - media: "(prefers-color-scheme: dark)" - scheme: slate - toggle: - icon: material/brightness-7 - name: light mode - features: - - content.code.annotate - - navigation.top - - navigation.instant - - navigation.expand - - navigation.sections - - navigation.tabs - - navigation.tabs.sticky -plugins: - - git-revision-date-localized - - search - - tags -# NOTE: this is the recommended configuration from -# https://squidfunk.github.io/mkdocs-material/setup/extensions/#recommended-configuration -markdown_extensions: - - abbr - - admonition - - attr_list - - def_list - - footnotes - - meta - - md_in_html - - toc: - permalink: true - - pymdownx.arithmatex: - generic: true - - pymdownx.betterem: - smart_enable: all - - pymdownx.caret - - pymdownx.details - - pymdownx.emoji: - emoji_index: !!python/name:material.extensions.emoji.twemoji - emoji_generator: !!python/name:material.extensions.emoji.to_svg - - pymdownx.highlight - - pymdownx.inlinehilite - - pymdownx.keys - - pymdownx.mark - - pymdownx.smartsymbols - - pymdownx.superfences - - pymdownx.tabbed: - alternate_style: true - - pymdownx.tasklist: - custom_checkbox: true - - pymdownx.tilde diff --git a/vendor/github.com/urfave/cli/v3/parse.go b/vendor/github.com/urfave/cli/v3/parse.go deleted file mode 100644 index 8ec5d4c6..00000000 --- a/vendor/github.com/urfave/cli/v3/parse.go +++ /dev/null @@ -1,118 +0,0 @@ -package cli - -import ( - "flag" - "strings" -) - -type iterativeParser interface { - useShortOptionHandling() bool -} - -// To enable short-option handling (e.g., "-it" vs "-i -t") we have to -// iteratively catch parsing errors. This way we achieve LR parsing without -// transforming any arguments. Otherwise, there is no way we can discriminate -// combined short options from common arguments that should be left untouched. -// Pass `shellComplete` to continue parsing options on failure during shell -// completion when, the user-supplied options may be incomplete. -func parseIter(set *flag.FlagSet, ip iterativeParser, args []string, shellComplete bool) error { - for { - tracef("parsing args %[1]q with %[2]T (name=%[3]q)", args, set, set.Name()) - - err := set.Parse(args) - if !ip.useShortOptionHandling() || err == nil { - if shellComplete { - tracef("returning nil due to shellComplete=true") - - return nil - } - - tracef("returning err %[1]q", err) - - return err - } - - tracef("finding flag from error %[1]q", err) - - trimmed, trimErr := flagFromError(err) - if trimErr != nil { - return err - } - - tracef("regenerating the initial args with the split short opts") - - argsWereSplit := false - for i, arg := range args { - tracef("skipping args that are not part of the error message (i=%[1]v arg=%[2]q)", i, arg) - - if name := strings.TrimLeft(arg, "-"); name != trimmed { - continue - } - - tracef("trying to split short option (arg=%[1]q)", arg) - - shortOpts := splitShortOptions(set, arg) - if len(shortOpts) == 1 { - return err - } - - tracef( - "swapping current argument with the split version (shortOpts=%[1]q args=%[2]q)", - shortOpts, args, - ) - - // do not include args that parsed correctly so far as it would - // trigger Value.Set() on those args and would result in - // duplicates for slice type flags - args = append(shortOpts, args[i+1:]...) - argsWereSplit = true - break - } - - tracef("this should be an impossible to reach code path") - // but in case the arg splitting failed to happen, this - // will prevent infinite loops - if !argsWereSplit { - return err - } - } -} - -const providedButNotDefinedErrMsg = "flag provided but not defined: -" - -// flagFromError tries to parse a provided flag from an error message. If the -// parsing fails, it returns the input error and an empty string -func flagFromError(err error) (string, error) { - errStr := err.Error() - trimmed := strings.TrimPrefix(errStr, providedButNotDefinedErrMsg) - if errStr == trimmed { - return "", err - } - return trimmed, nil -} - -func splitShortOptions(set *flag.FlagSet, arg string) []string { - shortFlagsExist := func(s string) bool { - for _, c := range s[1:] { - if f := set.Lookup(string(c)); f == nil { - return false - } - } - return true - } - - if !isSplittable(arg) || !shortFlagsExist(arg) { - return []string{arg} - } - - separated := make([]string, 0, len(arg)-1) - for _, flagChar := range arg[1:] { - separated = append(separated, "-"+string(flagChar)) - } - - return separated -} - -func isSplittable(flagArg string) bool { - return strings.HasPrefix(flagArg, "-") && !strings.HasPrefix(flagArg, "--") && len(flagArg) > 2 -} diff --git a/vendor/github.com/urfave/cli/v3/sort.go b/vendor/github.com/urfave/cli/v3/sort.go deleted file mode 100644 index 23d1c2f7..00000000 --- a/vendor/github.com/urfave/cli/v3/sort.go +++ /dev/null @@ -1,29 +0,0 @@ -package cli - -import "unicode" - -// lexicographicLess compares strings alphabetically considering case. -func lexicographicLess(i, j string) bool { - iRunes := []rune(i) - jRunes := []rune(j) - - lenShared := len(iRunes) - if lenShared > len(jRunes) { - lenShared = len(jRunes) - } - - for index := 0; index < lenShared; index++ { - ir := iRunes[index] - jr := jRunes[index] - - if lir, ljr := unicode.ToLower(ir), unicode.ToLower(jr); lir != ljr { - return lir < ljr - } - - if ir != jr { - return ir < jr - } - } - - return i < j -} diff --git a/vendor/github.com/urfave/cli/v3/staticcheck.conf b/vendor/github.com/urfave/cli/v3/staticcheck.conf deleted file mode 100644 index 233d9e73..00000000 --- a/vendor/github.com/urfave/cli/v3/staticcheck.conf +++ /dev/null @@ -1 +0,0 @@ -checks=["all"] diff --git a/vendor/github.com/urfave/cli/v3/suggestions.go b/vendor/github.com/urfave/cli/v3/suggestions.go deleted file mode 100644 index 6f29f122..00000000 --- a/vendor/github.com/urfave/cli/v3/suggestions.go +++ /dev/null @@ -1,147 +0,0 @@ -package cli - -import ( - "math" -) - -const suggestDidYouMeanTemplate = "Did you mean %q?" - -var ( - SuggestFlag SuggestFlagFunc = suggestFlag - SuggestCommand SuggestCommandFunc = suggestCommand - SuggestDidYouMeanTemplate string = suggestDidYouMeanTemplate -) - -type SuggestFlagFunc func(flags []Flag, provided string, hideHelp bool) string - -type SuggestCommandFunc func(commands []*Command, provided string) string - -// jaroDistance is the measure of similarity between two strings. It returns a -// value between 0 and 1, where 1 indicates identical strings and 0 indicates -// completely different strings. -// -// Adapted from https://github.com/xrash/smetrics/blob/5f08fbb34913bc8ab95bb4f2a89a0637ca922666/jaro.go. -func jaroDistance(a, b string) float64 { - if len(a) == 0 && len(b) == 0 { - return 1 - } - if len(a) == 0 || len(b) == 0 { - return 0 - } - - lenA := float64(len(a)) - lenB := float64(len(b)) - hashA := make([]bool, len(a)) - hashB := make([]bool, len(b)) - maxDistance := int(math.Max(0, math.Floor(math.Max(lenA, lenB)/2.0)-1)) - - var matches float64 - for i := 0; i < len(a); i++ { - start := int(math.Max(0, float64(i-maxDistance))) - end := int(math.Min(lenB-1, float64(i+maxDistance))) - - for j := start; j <= end; j++ { - if hashB[j] { - continue - } - if a[i] == b[j] { - hashA[i] = true - hashB[j] = true - matches++ - break - } - } - } - if matches == 0 { - return 0 - } - - var transpositions float64 - var j int - for i := 0; i < len(a); i++ { - if !hashA[i] { - continue - } - for !hashB[j] { - j++ - } - if a[i] != b[j] { - transpositions++ - } - j++ - } - - transpositions /= 2 - return ((matches / lenA) + (matches / lenB) + ((matches - transpositions) / matches)) / 3.0 -} - -// jaroWinkler is more accurate when strings have a common prefix up to a -// defined maximum length. -// -// Adapted from https://github.com/xrash/smetrics/blob/5f08fbb34913bc8ab95bb4f2a89a0637ca922666/jaro-winkler.go. -func jaroWinkler(a, b string) float64 { - const ( - boostThreshold = 0.7 - prefixSize = 4 - ) - jaroDist := jaroDistance(a, b) - if jaroDist <= boostThreshold { - return jaroDist - } - - prefix := int(math.Min(float64(len(a)), math.Min(float64(prefixSize), float64(len(b))))) - - var prefixMatch float64 - for i := 0; i < prefix; i++ { - if a[i] == b[i] { - prefixMatch++ - } else { - break - } - } - return jaroDist + 0.1*prefixMatch*(1.0-jaroDist) -} - -func suggestFlag(flags []Flag, provided string, hideHelp bool) string { - distance := 0.0 - suggestion := "" - - for _, flag := range flags { - flagNames := flag.Names() - if !hideHelp && HelpFlag != nil { - flagNames = append(flagNames, HelpFlag.Names()...) - } - for _, name := range flagNames { - newDistance := jaroWinkler(name, provided) - if newDistance > distance { - distance = newDistance - suggestion = name - } - } - } - - if len(suggestion) == 1 { - suggestion = "-" + suggestion - } else if len(suggestion) > 1 { - suggestion = "--" + suggestion - } - - return suggestion -} - -// suggestCommand takes a list of commands and a provided string to suggest a -// command name -func suggestCommand(commands []*Command, provided string) (suggestion string) { - distance := 0.0 - for _, command := range commands { - for _, name := range append(command.Names(), helpName, helpAlias) { - newDistance := jaroWinkler(name, provided) - if newDistance > distance { - distance = newDistance - suggestion = name - } - } - } - - return suggestion -} diff --git a/vendor/github.com/urfave/cli/v3/template.go b/vendor/github.com/urfave/cli/v3/template.go deleted file mode 100644 index d809dd78..00000000 --- a/vendor/github.com/urfave/cli/v3/template.go +++ /dev/null @@ -1,125 +0,0 @@ -package cli - -var ( - helpNameTemplate = `{{$v := offset .FullName 6}}{{wrap .FullName 3}}{{if .Usage}} - {{wrap .Usage $v}}{{end}}` - argsTemplate = `{{if .Arguments}}{{range .Arguments}}{{.Usage}}{{end}}{{end}}` - usageTemplate = `{{if .UsageText}}{{wrap .UsageText 3}}{{else}}{{.FullName}}{{if .VisibleFlags}} [command [command options]]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}{{template "argsTemplate" .}}{{end}}{{end}}` - descriptionTemplate = `{{wrap .Description 3}}` - authorsTemplate = `{{with $length := len .Authors}}{{if ne 1 $length}}S{{end}}{{end}}: - {{range $index, $author := .Authors}}{{if $index}} - {{end}}{{$author}}{{end}}` -) - -var visibleCommandTemplate = `{{ $cv := offsetCommands .VisibleCommands 5}}{{range .VisibleCommands}} - {{$s := join .Names ", "}}{{$s}}{{ $sp := subtract $cv (offset $s 3) }}{{ indent $sp ""}}{{wrap .Usage $cv}}{{end}}` - -var visibleCommandCategoryTemplate = `{{range .VisibleCategories}}{{if .Name}} - - {{.Name}}:{{range .VisibleCommands}} - {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{else}}{{template "visibleCommandTemplate" .}}{{end}}{{end}}` - -var visibleFlagCategoryTemplate = `{{range .VisibleFlagCategories}} - {{if .Name}}{{.Name}} - - {{end}}{{$flglen := len .Flags}}{{range $i, $e := .Flags}}{{if eq (subtract $flglen $i) 1}}{{$e}} -{{else}}{{$e}} - {{end}}{{end}}{{end}}` - -var visibleFlagTemplate = `{{range $i, $e := .VisibleFlags}} - {{wrap $e.String 6}}{{end}}` - -var visiblePersistentFlagTemplate = `{{range $i, $e := .VisiblePersistentFlags}} - {{wrap $e.String 6}}{{end}}` - -var versionTemplate = `{{if .Version}}{{if not .HideVersion}} - -VERSION: - {{.Version}}{{end}}{{end}}` - -var copyrightTemplate = `{{wrap .Copyright 3}}` - -// RootCommandHelpTemplate is the text template for the Default help topic. -// cli.go uses text/template to render templates. You can -// render custom help text by setting this variable. -var RootCommandHelpTemplate = `NAME: - {{template "helpNameTemplate" .}} - -USAGE: - {{if .UsageText}}{{wrap .UsageText 3}}{{else}}{{.FullName}} {{if .VisibleFlags}}[global options]{{end}}{{if .VisibleCommands}} [command [command options]]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Version}}{{if not .HideVersion}} - -VERSION: - {{.Version}}{{end}}{{end}}{{if .Description}} - -DESCRIPTION: - {{template "descriptionTemplate" .}}{{end}} -{{- if len .Authors}} - -AUTHOR{{template "authorsTemplate" .}}{{end}}{{if .VisibleCommands}} - -COMMANDS:{{template "visibleCommandCategoryTemplate" .}}{{end}}{{if .VisibleFlagCategories}} - -GLOBAL OPTIONS:{{template "visibleFlagCategoryTemplate" .}}{{else if .VisibleFlags}} - -GLOBAL OPTIONS:{{template "visibleFlagTemplate" .}}{{end}}{{if .Copyright}} - -COPYRIGHT: - {{template "copyrightTemplate" .}}{{end}} -` - -// CommandHelpTemplate is the text template for the command help topic. -// cli.go uses text/template to render templates. You can -// render custom help text by setting this variable. -var CommandHelpTemplate = `NAME: - {{template "helpNameTemplate" .}} - -USAGE: - {{template "usageTemplate" .}}{{if .Category}} - -CATEGORY: - {{.Category}}{{end}}{{if .Description}} - -DESCRIPTION: - {{template "descriptionTemplate" .}}{{end}}{{if .VisibleFlagCategories}} - -OPTIONS:{{template "visibleFlagCategoryTemplate" .}}{{else if .VisibleFlags}} - -OPTIONS:{{template "visibleFlagTemplate" .}}{{end}}{{if .VisiblePersistentFlags}} - -GLOBAL OPTIONS:{{template "visiblePersistentFlagTemplate" .}}{{end}} -` - -// SubcommandHelpTemplate is the text template for the subcommand help topic. -// cli.go uses text/template to render templates. You can -// render custom help text by setting this variable. -var SubcommandHelpTemplate = `NAME: - {{template "helpNameTemplate" .}} - -USAGE: - {{if .UsageText}}{{wrap .UsageText 3}}{{else}}{{.FullName}} {{if .VisibleCommands}}[command [command options]] {{end}}{{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Category}} - -CATEGORY: - {{.Category}}{{end}}{{if .Description}} - -DESCRIPTION: - {{template "descriptionTemplate" .}}{{end}}{{if .VisibleCommands}} - -COMMANDS:{{template "visibleCommandTemplate" .}}{{end}}{{if .VisibleFlagCategories}} - -OPTIONS:{{template "visibleFlagCategoryTemplate" .}}{{else if .VisibleFlags}} - -OPTIONS:{{template "visibleFlagTemplate" .}}{{end}} -` - -var FishCompletionTemplate = `# {{ .Command.Name }} fish shell completion - -function __fish_{{ .Command.Name }}_no_subcommand --description 'Test if there has been any subcommand yet' - for i in (commandline -opc) - if contains -- $i{{ range $v := .AllCommands }} {{ $v }}{{ end }} - return 1 - end - end - return 0 -end - -{{ range $v := .Completions }}{{ $v }} -{{ end }}` diff --git a/vendor/github.com/urfave/cli/v3/value_source.go b/vendor/github.com/urfave/cli/v3/value_source.go deleted file mode 100644 index 7d3f7ee4..00000000 --- a/vendor/github.com/urfave/cli/v3/value_source.go +++ /dev/null @@ -1,145 +0,0 @@ -package cli - -import ( - "fmt" - "os" - "strings" -) - -// ValueSource is a source which can be used to look up a value, -// typically for use with a cli.Flag -type ValueSource interface { - fmt.Stringer - fmt.GoStringer - - // Lookup returns the value from the source and if it was found - // or returns an empty string and false - Lookup() (string, bool) -} - -// ValueSourceChain contains an ordered series of ValueSource that -// allows for lookup where the first ValueSource to resolve is -// returned -type ValueSourceChain struct { - Chain []ValueSource -} - -func NewValueSourceChain(src ...ValueSource) ValueSourceChain { - return ValueSourceChain{ - Chain: src, - } -} - -func (vsc *ValueSourceChain) Append(other ValueSourceChain) { - vsc.Chain = append(vsc.Chain, other.Chain...) -} - -func (vsc *ValueSourceChain) EnvKeys() []string { - vals := []string{} - - for _, src := range vsc.Chain { - if v, ok := src.(*envVarValueSource); ok { - vals = append(vals, v.Key) - } - } - - return vals -} - -func (vsc *ValueSourceChain) String() string { - s := []string{} - - for _, vs := range vsc.Chain { - s = append(s, vs.String()) - } - - return strings.Join(s, ",") -} - -func (vsc *ValueSourceChain) GoString() string { - s := []string{} - - for _, vs := range vsc.Chain { - s = append(s, vs.GoString()) - } - - return fmt.Sprintf("&ValueSourceChain{Chain:{%[1]s}}", strings.Join(s, ",")) -} - -func (vsc *ValueSourceChain) Lookup() (string, bool) { - s, _, ok := vsc.LookupWithSource() - return s, ok -} - -func (vsc *ValueSourceChain) LookupWithSource() (string, ValueSource, bool) { - for _, src := range vsc.Chain { - if value, found := src.Lookup(); found { - return value, src, true - } - } - - return "", nil, false -} - -// envVarValueSource encapsulates a ValueSource from an environment variable -type envVarValueSource struct { - Key string -} - -func (e *envVarValueSource) Lookup() (string, bool) { - return os.LookupEnv(strings.TrimSpace(string(e.Key))) -} - -func (e *envVarValueSource) String() string { return fmt.Sprintf("environment variable %[1]q", e.Key) } -func (e *envVarValueSource) GoString() string { - return fmt.Sprintf("&envVarValueSource{Key:%[1]q}", e.Key) -} - -func EnvVar(key string) ValueSource { - return &envVarValueSource{ - Key: key, - } -} - -// EnvVars is a helper function to encapsulate a number of -// envVarValueSource together as a ValueSourceChain -func EnvVars(keys ...string) ValueSourceChain { - vsc := ValueSourceChain{Chain: []ValueSource{}} - - for _, key := range keys { - vsc.Chain = append(vsc.Chain, &envVarValueSource{Key: key}) - } - - return vsc -} - -// fileValueSource encapsulates a ValueSource from a file -type fileValueSource struct { - Path string -} - -func (f *fileValueSource) Lookup() (string, bool) { - data, err := os.ReadFile(f.Path) - return string(data), err == nil -} - -func (f *fileValueSource) String() string { return fmt.Sprintf("file %[1]q", f.Path) } -func (f *fileValueSource) GoString() string { - return fmt.Sprintf("&fileValueSource{Path:%[1]q}", f.Path) -} - -func File(path string) ValueSource { - return &fileValueSource{Path: path} -} - -// Files is a helper function to encapsulate a number of -// fileValueSource together as a ValueSourceChain -func Files(paths ...string) ValueSourceChain { - vsc := ValueSourceChain{Chain: []ValueSource{}} - - for _, path := range paths { - vsc.Chain = append(vsc.Chain, &fileValueSource{Path: path}) - } - - return vsc -} diff --git a/vendor/modules.txt b/vendor/modules.txt index 5c961b94..84e3dc1d 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -528,9 +528,6 @@ github.com/theupdateframework/notary/tuf/data github.com/theupdateframework/notary/tuf/signed github.com/theupdateframework/notary/tuf/utils github.com/theupdateframework/notary/tuf/validation -# github.com/urfave/cli/v3 v3.0.0-alpha9 => github.com/urfave/cli/v3 v3.0.0-alpha9.1.0.20241019193437-5053ec708a44 -## explicit; go 1.18 -github.com/urfave/cli/v3 # github.com/xanzy/ssh-agent v0.3.3 ## explicit; go 1.16 github.com/xanzy/ssh-agent