forked from toolshed/abra
		
	@ -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)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -36,7 +36,7 @@ ${FOO:<default>} 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)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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,94 +83,64 @@ 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)
 | 
			
		||||
		if !deployMeta.IsDeployed &&
 | 
			
		||||
			toDeployVersion == "" &&
 | 
			
		||||
			app.Recipe.EnvVersion != "" && !internal.IgnoreEnvVersion {
 | 
			
		||||
			log.Debugf("new deployment, choosing .env version: %s", app.Recipe.EnvVersion)
 | 
			
		||||
			toDeployVersion = app.Recipe.EnvVersion
 | 
			
		||||
		}
 | 
			
		||||
			} else {
 | 
			
		||||
				head, err := app.Recipe.Head()
 | 
			
		||||
				if err != nil {
 | 
			
		||||
 | 
			
		||||
		if !internal.Chaos && toDeployVersion == "" {
 | 
			
		||||
			if err := getLatestVersionOrCommit(app, &toDeployVersion); err != nil {
 | 
			
		||||
				log.Fatal(err)
 | 
			
		||||
			}
 | 
			
		||||
				toDeployVersion = formatter.SmallSHA(head.String())
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		toDeployChaosVersion := config.CHAOS_DEFAULT
 | 
			
		||||
		if internal.Chaos {
 | 
			
		||||
			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 {
 | 
			
		||||
				toDeployChaosVersion = specificVersion
 | 
			
		||||
				versionLabelLocal, err := app.Recipe.GetVersionLabelLocal()
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					log.Fatal(err)
 | 
			
		||||
				}
 | 
			
		||||
				toDeployVersion = versionLabelLocal
 | 
			
		||||
			} else {
 | 
			
		||||
				var err error
 | 
			
		||||
				toDeployChaosVersion, err = app.Recipe.ChaosVersion()
 | 
			
		||||
			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)
 | 
			
		||||
@ -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")
 | 
			
		||||
	}
 | 
			
		||||
	return true, nil
 | 
			
		||||
func getChaosVersion(app app.App, toDeployVersion, toDeployChaosVersion *string) error {
 | 
			
		||||
	var err error
 | 
			
		||||
	*toDeployChaosVersion, err = app.Recipe.ChaosVersion()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
// getSpecifiedVersion retrieves the specific version if available
 | 
			
		||||
func getSpecifiedVersion(args []string) string {
 | 
			
		||||
	if len(args) >= 2 {
 | 
			
		||||
		return args[1]
 | 
			
		||||
	*toDeployVersion, err = app.Recipe.GetVersionLabelLocal()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return ""
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getLatestVersionOrCommit(app app.App, toDeployVersion *string) error {
 | 
			
		||||
	versions, err := app.Recipe.Tags()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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",
 | 
			
		||||
	)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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,
 | 
			
		||||
			)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -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)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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)
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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 "[<version>]".
 | 
			
		||||
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)
 | 
			
		||||
			availableDowngrades = append(availableDowngrades, chosenDowngrade)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
			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)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if deployMeta.Version != "unknown" && specificVersion == "" {
 | 
			
		||||
			if deployMeta.IsChaos {
 | 
			
		||||
				warnMessages = append(warnMessages, fmt.Sprintf("attempting to rollback a chaos deployment"))
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			for _, version := range versions {
 | 
			
		||||
				parsedDeployedVersion, err := tagcmp.Parse(deployMeta.Version)
 | 
			
		||||
		if deployMeta.Version != config.UNKNOWN_DEFAULT && chosenDowngrade == "" {
 | 
			
		||||
			downgradeAvailable, err := ensureDowngradesAvailable(versions, &availableDowngrades, deployMeta)
 | 
			
		||||
			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 internal.Force || internal.NoInput || chosenDowngrade != "" {
 | 
			
		||||
			if len(availableDowngrades) > 0 {
 | 
			
		||||
			if internal.Force || internal.NoInput || specificVersion != "" {
 | 
			
		||||
				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)
 | 
			
		||||
			if err := chooseDowngrade(availableDowngrades, deployMeta, &chosenDowngrade); err != nil {
 | 
			
		||||
				log.Fatal(err)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
				prompt := &survey.Select{
 | 
			
		||||
					Message: msg,
 | 
			
		||||
					Options: internal.SortVersionsDesc(availableDowngrades),
 | 
			
		||||
		if internal.Force &&
 | 
			
		||||
			chosenDowngrade == "" &&
 | 
			
		||||
			deployMeta.Version != config.UNKNOWN_DEFAULT {
 | 
			
		||||
			chosenDowngrade = deployMeta.Version
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
				if err := survey.AskOne(prompt, &chosenDowngrade); err != nil {
 | 
			
		||||
					return
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		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",
 | 
			
		||||
	)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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)
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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 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)
 | 
			
		||||
		if len(args) == 2 && args[1] != "" {
 | 
			
		||||
			chosenUpgrade = args[1]
 | 
			
		||||
 | 
			
		||||
			if err := validateUpgradeVersionArg(chosenUpgrade, app, deployMeta); err != nil {
 | 
			
		||||
				log.Fatal(err)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if parsedSpecificVersion.IsLessThan(parsedDeployedVersion) && !parsedSpecificVersion.Equals(parsedDeployedVersion) {
 | 
			
		||||
				log.Fatalf("%s is not an upgrade for %s?", deployMeta.Version, specificVersion)
 | 
			
		||||
			availableUpgrades = append(availableUpgrades, chosenUpgrade)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
			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 deployMeta.Version != config.UNKNOWN_DEFAULT && chosenUpgrade == "" {
 | 
			
		||||
			upgradeAvailable, err := ensureUpgradesAvailable(versions, &availableUpgrades, deployMeta)
 | 
			
		||||
			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 internal.Force || internal.NoInput || chosenUpgrade != "" {
 | 
			
		||||
			if len(availableUpgrades) > 0 {
 | 
			
		||||
			if internal.Force || internal.NoInput || specificVersion != "" {
 | 
			
		||||
				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
 | 
			
		||||
				}
 | 
			
		||||
			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(
 | 
			
		||||
 | 
			
		||||
@ -5,6 +5,7 @@ var (
 | 
			
		||||
	Debug            bool
 | 
			
		||||
	NoInput          bool
 | 
			
		||||
	Offline          bool
 | 
			
		||||
	IgnoreEnvVersion bool
 | 
			
		||||
 | 
			
		||||
	// NOTE(d1): sub-command specific
 | 
			
		||||
	Chaos            bool
 | 
			
		||||
 | 
			
		||||
@ -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)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										11
									
								
								cli/internal/ensure.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								cli/internal/ensure.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,11 @@
 | 
			
		||||
package internal
 | 
			
		||||
 | 
			
		||||
import "coopcloud.tech/abra/pkg/recipe"
 | 
			
		||||
 | 
			
		||||
func GetEnsureContext() recipe.EnsureContext {
 | 
			
		||||
	return recipe.EnsureContext{
 | 
			
		||||
		Chaos,
 | 
			
		||||
		Offline,
 | 
			
		||||
		IgnoreEnvVersion,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -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)
 | 
			
		||||
}
 | 
			
		||||
@ -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)
 | 
			
		||||
 | 
			
		||||
@ -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)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										23
									
								
								cli/run.go
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								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,
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										1
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								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
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										61
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										61
									
								
								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=
 | 
			
		||||
 | 
			
		||||
@ -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)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -118,4 +118,6 @@ var (
 | 
			
		||||
 | 
			
		||||
	NO_DOMAIN_DEFAULT  = "N/A"
 | 
			
		||||
	NO_VERSION_DEFAULT = "N/A"
 | 
			
		||||
 | 
			
		||||
	UNKNOWN_DEFAULT = "unknown"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@ -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.
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
@ -160,7 +160,7 @@ func Get(name string) Recipe {
 | 
			
		||||
 | 
			
		||||
	r := Recipe{
 | 
			
		||||
		Name:       name,
 | 
			
		||||
		Version: version,
 | 
			
		||||
		EnvVersion: version,
 | 
			
		||||
		Dir:        dir,
 | 
			
		||||
		GitURL:     gitURL,
 | 
			
		||||
		SSHURL:     sshURL,
 | 
			
		||||
@ -180,7 +180,7 @@ func Get(name string) Recipe {
 | 
			
		||||
 | 
			
		||||
type Recipe struct {
 | 
			
		||||
	Name       string
 | 
			
		||||
	Version string
 | 
			
		||||
	EnvVersion string
 | 
			
		||||
	Dirty      bool // NOTE(d1): git terminology for unstaged changes
 | 
			
		||||
	Dir        string
 | 
			
		||||
	GitURL     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)
 | 
			
		||||
 | 
			
		||||
@ -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)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
@ -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"
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
@ -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"
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										11
									
								
								vendor/github.com/urfave/cli/v3/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										11
									
								
								vendor/github.com/urfave/cli/v3/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
								
							@ -1,11 +0,0 @@
 | 
			
		||||
*.coverprofile
 | 
			
		||||
*.exe
 | 
			
		||||
*.orig
 | 
			
		||||
.*envrc
 | 
			
		||||
.envrc
 | 
			
		||||
.idea
 | 
			
		||||
/.local/
 | 
			
		||||
/site/
 | 
			
		||||
coverage.txt
 | 
			
		||||
internal/*/built-example
 | 
			
		||||
vendor
 | 
			
		||||
							
								
								
									
										5
									
								
								vendor/github.com/urfave/cli/v3/.golangci.yaml
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								vendor/github.com/urfave/cli/v3/.golangci.yaml
									
									
									
										generated
									
									
										vendored
									
									
								
							@ -1,5 +0,0 @@
 | 
			
		||||
# https://golangci-lint.run/usage/configuration/
 | 
			
		||||
linters:
 | 
			
		||||
  enable:
 | 
			
		||||
    - makezero
 | 
			
		||||
    - misspell
 | 
			
		||||
							
								
								
									
										75
									
								
								vendor/github.com/urfave/cli/v3/CODE_OF_CONDUCT.md
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										75
									
								
								vendor/github.com/urfave/cli/v3/CODE_OF_CONDUCT.md
									
									
									
										generated
									
									
										vendored
									
									
								
							@ -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
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										21
									
								
								vendor/github.com/urfave/cli/v3/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										21
									
								
								vendor/github.com/urfave/cli/v3/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
								
							@ -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.
 | 
			
		||||
							
								
								
									
										26
									
								
								vendor/github.com/urfave/cli/v3/Makefile
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										26
									
								
								vendor/github.com/urfave/cli/v3/Makefile
									
									
									
										generated
									
									
										vendored
									
									
								
							@ -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
 | 
			
		||||
							
								
								
									
										56
									
								
								vendor/github.com/urfave/cli/v3/README.md
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										56
									
								
								vendor/github.com/urfave/cli/v3/README.md
									
									
									
										generated
									
									
										vendored
									
									
								
							@ -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 <https://cli.urfave.org>. 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
 | 
			
		||||
							
								
								
									
										153
									
								
								vendor/github.com/urfave/cli/v3/args.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										153
									
								
								vendor/github.com/urfave/cli/v3/args.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@ -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]
 | 
			
		||||
)
 | 
			
		||||
							
								
								
									
										35
									
								
								vendor/github.com/urfave/cli/v3/autocomplete/bash_autocomplete
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										35
									
								
								vendor/github.com/urfave/cli/v3/autocomplete/bash_autocomplete
									
									
									
										generated
									
									
										vendored
									
									
								
							@ -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
 | 
			
		||||
							
								
								
									
										9
									
								
								vendor/github.com/urfave/cli/v3/autocomplete/powershell_autocomplete.ps1
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										9
									
								
								vendor/github.com/urfave/cli/v3/autocomplete/powershell_autocomplete.ps1
									
									
									
										generated
									
									
										vendored
									
									
								
							@ -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', $_)
 | 
			
		||||
         }
 | 
			
		||||
 }
 | 
			
		||||
							
								
								
									
										30
									
								
								vendor/github.com/urfave/cli/v3/autocomplete/zsh_autocomplete
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										30
									
								
								vendor/github.com/urfave/cli/v3/autocomplete/zsh_autocomplete
									
									
									
										generated
									
									
										vendored
									
									
								
							@ -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
 | 
			
		||||
							
								
								
									
										195
									
								
								vendor/github.com/urfave/cli/v3/category.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										195
									
								
								vendor/github.com/urfave/cli/v3/category.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@ -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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										60
									
								
								vendor/github.com/urfave/cli/v3/cli.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										60
									
								
								vendor/github.com/urfave/cli/v3/cli.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@ -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...,
 | 
			
		||||
	)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										1296
									
								
								vendor/github.com/urfave/cli/v3/command.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										1296
									
								
								vendor/github.com/urfave/cli/v3/command.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										68
									
								
								vendor/github.com/urfave/cli/v3/completion.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										68
									
								
								vendor/github.com/urfave/cli/v3/completion.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@ -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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										209
									
								
								vendor/github.com/urfave/cli/v3/errors.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										209
									
								
								vendor/github.com/urfave/cli/v3/errors.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@ -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)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										183
									
								
								vendor/github.com/urfave/cli/v3/fish.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										183
									
								
								vendor/github.com/urfave/cli/v3/fish.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@ -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)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										355
									
								
								vendor/github.com/urfave/cli/v3/flag.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										355
									
								
								vendor/github.com/urfave/cli/v3/flag.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@ -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)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										87
									
								
								vendor/github.com/urfave/cli/v3/flag_bool.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										87
									
								
								vendor/github.com/urfave/cli/v3/flag_bool.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@ -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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										194
									
								
								vendor/github.com/urfave/cli/v3/flag_bool_with_inverse.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										194
									
								
								vendor/github.com/urfave/cli/v3/flag_bool_with_inverse.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@ -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:])
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										47
									
								
								vendor/github.com/urfave/cli/v3/flag_duration.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										47
									
								
								vendor/github.com/urfave/cli/v3/flag_duration.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@ -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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										48
									
								
								vendor/github.com/urfave/cli/v3/flag_ext.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										48
									
								
								vendor/github.com/urfave/cli/v3/flag_ext.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@ -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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										48
									
								
								vendor/github.com/urfave/cli/v3/flag_float.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										48
									
								
								vendor/github.com/urfave/cli/v3/flag_float.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@ -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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										35
									
								
								vendor/github.com/urfave/cli/v3/flag_float_slice.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										35
									
								
								vendor/github.com/urfave/cli/v3/flag_float_slice.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@ -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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										286
									
								
								vendor/github.com/urfave/cli/v3/flag_impl.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										286
									
								
								vendor/github.com/urfave/cli/v3/flag_impl.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@ -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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										59
									
								
								vendor/github.com/urfave/cli/v3/flag_int.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										59
									
								
								vendor/github.com/urfave/cli/v3/flag_int.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@ -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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										20
									
								
								vendor/github.com/urfave/cli/v3/flag_int_slice.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										20
									
								
								vendor/github.com/urfave/cli/v3/flag_int_slice.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@ -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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										116
									
								
								vendor/github.com/urfave/cli/v3/flag_map_impl.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										116
									
								
								vendor/github.com/urfave/cli/v3/flag_map_impl.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@ -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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										56
									
								
								vendor/github.com/urfave/cli/v3/flag_mutex.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										56
									
								
								vendor/github.com/urfave/cli/v3/flag_mutex.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@ -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)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										110
									
								
								vendor/github.com/urfave/cli/v3/flag_slice_base.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										110
									
								
								vendor/github.com/urfave/cli/v3/flag_slice_base.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@ -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, ", ")
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										66
									
								
								vendor/github.com/urfave/cli/v3/flag_string.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										66
									
								
								vendor/github.com/urfave/cli/v3/flag_string.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@ -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 ""
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										20
									
								
								vendor/github.com/urfave/cli/v3/flag_string_map.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										20
									
								
								vendor/github.com/urfave/cli/v3/flag_string_map.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@ -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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										20
									
								
								vendor/github.com/urfave/cli/v3/flag_string_slice.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										20
									
								
								vendor/github.com/urfave/cli/v3/flag_string_slice.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@ -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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										152
									
								
								vendor/github.com/urfave/cli/v3/flag_timestamp.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										152
									
								
								vendor/github.com/urfave/cli/v3/flag_timestamp.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@ -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{}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										54
									
								
								vendor/github.com/urfave/cli/v3/flag_uint.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										54
									
								
								vendor/github.com/urfave/cli/v3/flag_uint.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@ -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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										20
									
								
								vendor/github.com/urfave/cli/v3/flag_uint_slice.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										20
									
								
								vendor/github.com/urfave/cli/v3/flag_uint_slice.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@ -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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										50
									
								
								vendor/github.com/urfave/cli/v3/funcs.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										50
									
								
								vendor/github.com/urfave/cli/v3/funcs.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@ -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
 | 
			
		||||
							
								
								
									
										1038
									
								
								vendor/github.com/urfave/cli/v3/godoc-current.txt
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										1038
									
								
								vendor/github.com/urfave/cli/v3/godoc-current.txt
									
									
									
										generated
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										588
									
								
								vendor/github.com/urfave/cli/v3/help.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										588
									
								
								vendor/github.com/urfave/cli/v3/help.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@ -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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										5
									
								
								vendor/github.com/urfave/cli/v3/mkdocs-reqs.txt
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								vendor/github.com/urfave/cli/v3/mkdocs-reqs.txt
									
									
									
										generated
									
									
										vendored
									
									
								
							@ -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
 | 
			
		||||
							
								
								
									
										107
									
								
								vendor/github.com/urfave/cli/v3/mkdocs.yml
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										107
									
								
								vendor/github.com/urfave/cli/v3/mkdocs.yml
									
									
									
										generated
									
									
										vendored
									
									
								
							@ -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
 | 
			
		||||
							
								
								
									
										118
									
								
								vendor/github.com/urfave/cli/v3/parse.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										118
									
								
								vendor/github.com/urfave/cli/v3/parse.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@ -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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										29
									
								
								vendor/github.com/urfave/cli/v3/sort.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										29
									
								
								vendor/github.com/urfave/cli/v3/sort.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@ -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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										1
									
								
								vendor/github.com/urfave/cli/v3/staticcheck.conf
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								vendor/github.com/urfave/cli/v3/staticcheck.conf
									
									
									
										generated
									
									
										vendored
									
									
								
							@ -1 +0,0 @@
 | 
			
		||||
checks=["all"]
 | 
			
		||||
							
								
								
									
										147
									
								
								vendor/github.com/urfave/cli/v3/suggestions.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										147
									
								
								vendor/github.com/urfave/cli/v3/suggestions.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@ -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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										125
									
								
								vendor/github.com/urfave/cli/v3/template.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										125
									
								
								vendor/github.com/urfave/cli/v3/template.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@ -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 }}`
 | 
			
		||||
							
								
								
									
										145
									
								
								vendor/github.com/urfave/cli/v3/value_source.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										145
									
								
								vendor/github.com/urfave/cli/v3/value_source.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@ -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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										3
									
								
								vendor/modules.txt
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								vendor/modules.txt
									
									
									
									
										vendored
									
									
								
							@ -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
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user