parent
5975be6870
commit
b0cd8ccbb9
@ -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,96 +83,66 @@ Please note, "upgrade"/"rollback" do not support chaos operations.`,
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
log.Debugf("checking whether %s is already deployed", stackName)
|
||||
|
||||
cl, err := client.New(app.Server)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
deployMeta, err := stack.IsDeployed(context.Background(), cl, stackName)
|
||||
log.Debugf("checking whether %s is already deployed", app.StackName())
|
||||
|
||||
deployMeta, err := stack.IsDeployed(context.Background(), cl, app.StackName())
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// NOTE(d1): handles "[version] as git hash" use case
|
||||
var isChaosCommit bool
|
||||
|
||||
// NOTE(d1): check out specific version before dealing with secrets. This
|
||||
// is because we need to deal with GetComposeFiles under the hood and these
|
||||
// files change from version to version which therefore affects which
|
||||
// secrets might be generated
|
||||
toDeployVersion := deployMeta.Version
|
||||
if specificVersion != "" {
|
||||
toDeployVersion = specificVersion
|
||||
log.Debugf("choosing %s as version to deploy", toDeployVersion)
|
||||
|
||||
var err error
|
||||
isChaosCommit, err = app.Recipe.EnsureVersion(toDeployVersion)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if isChaosCommit {
|
||||
log.Debugf("assuming '%s' is a chaos commit", toDeployVersion)
|
||||
internal.Chaos = true
|
||||
}
|
||||
}
|
||||
|
||||
secStats, err := secret.PollSecretsStatus(cl, app)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
for _, secStat := range secStats {
|
||||
if !secStat.CreatedOnRemote {
|
||||
log.Fatalf("unable to deploy, secrets not generated (%s)?", secStat.LocalName)
|
||||
}
|
||||
}
|
||||
|
||||
if deployMeta.IsDeployed && !(internal.Force || internal.Chaos) {
|
||||
log.Fatalf("%s is already deployed", app.Name)
|
||||
}
|
||||
|
||||
if !internal.Chaos && specificVersion == "" {
|
||||
versions, err := app.Recipe.Tags()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if len(args) == 2 && args[1] != "" {
|
||||
toDeployVersion = args[1]
|
||||
}
|
||||
|
||||
if len(versions) > 0 && !internal.Chaos {
|
||||
toDeployVersion = versions[len(versions)-1]
|
||||
log.Debugf("choosing %s as version to deploy", toDeployVersion)
|
||||
if _, err := app.Recipe.EnsureVersion(toDeployVersion); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
} else {
|
||||
head, err := app.Recipe.Head()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
toDeployVersion = formatter.SmallSHA(head.String())
|
||||
if !deployMeta.IsDeployed &&
|
||||
toDeployVersion == "" &&
|
||||
app.Recipe.EnvVersion != "" && !internal.IgnoreEnvVersion {
|
||||
log.Debugf("new deployment, choosing .env version: %s", app.Recipe.EnvVersion)
|
||||
toDeployVersion = app.Recipe.EnvVersion
|
||||
}
|
||||
|
||||
if !internal.Chaos && toDeployVersion == "" {
|
||||
if err := getLatestVersionOrCommit(app, &toDeployVersion); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
toDeployChaosVersion := config.CHAOS_DEFAULT
|
||||
if internal.Chaos {
|
||||
if isChaosCommit {
|
||||
toDeployChaosVersion = specificVersion
|
||||
versionLabelLocal, err := app.Recipe.GetVersionLabelLocal()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
toDeployVersion = versionLabelLocal
|
||||
} else {
|
||||
var err error
|
||||
toDeployChaosVersion, err = app.Recipe.ChaosVersion()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if err := getChaosVersion(app, &toDeployVersion, &toDeployChaosVersion); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
isChaosCommit, err = app.Recipe.EnsureVersion(toDeployVersion)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if isChaosCommit {
|
||||
log.Debugf("assuming chaos commit: %s", toDeployVersion)
|
||||
|
||||
internal.Chaos = true
|
||||
toDeployChaosVersion = toDeployVersion
|
||||
|
||||
toDeployVersion, err = app.Recipe.GetVersionLabelLocal()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := validateSecrets(cl, app); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
abraShEnv, err := envfile.ReadAbraShEnvVars(app.Recipe.AbraShPath)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
@ -194,6 +156,7 @@ Please note, "upgrade"/"rollback" do not support chaos operations.`,
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
stackName := app.StackName()
|
||||
deployOpts := stack.Deploy{
|
||||
Composefiles: composeFiles,
|
||||
Namespace: stackName,
|
||||
@ -224,23 +187,22 @@ Please note, "upgrade"/"rollback" do not support chaos operations.`,
|
||||
|
||||
for _, envVar := range envVars {
|
||||
if !envVar.Present {
|
||||
warnMessages = append(warnMessages,
|
||||
fmt.Sprintf("env var %s missing from %s.env, present in recipe .env.sample", envVar.Name, app.Domain),
|
||||
deployWarnMessages = append(deployWarnMessages,
|
||||
fmt.Sprintf("%s missing from %s.env", envVar.Name, app.Domain),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if !internal.NoDomainChecks {
|
||||
domainName, ok := app.Env["DOMAIN"]
|
||||
if ok {
|
||||
if domainName, ok := app.Env["DOMAIN"]; ok {
|
||||
if _, err = dns.EnsureDomainsResolveSameIPv4(domainName, app.Server); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
} else {
|
||||
log.Debug("skipping domain checks as no DOMAIN=... configured for app")
|
||||
log.Debug("skipping domain checks, no DOMAIN=... configured")
|
||||
}
|
||||
} else {
|
||||
log.Debug("skipping domain checks as requested")
|
||||
log.Debug("skipping domain checks")
|
||||
}
|
||||
|
||||
deployedVersion := config.NO_VERSION_DEFAULT
|
||||
@ -248,13 +210,20 @@ Please note, "upgrade"/"rollback" do not support chaos operations.`,
|
||||
deployedVersion = deployMeta.Version
|
||||
}
|
||||
|
||||
toWriteVersion := toDeployVersion
|
||||
if internal.Chaos || isChaosCommit {
|
||||
toWriteVersion = toDeployChaosVersion
|
||||
}
|
||||
|
||||
if err := internal.DeployOverview(
|
||||
app,
|
||||
warnMessages,
|
||||
deployWarnMessages,
|
||||
deployedVersion,
|
||||
deployMeta.ChaosVersion,
|
||||
toDeployVersion,
|
||||
toDeployChaosVersion); err != nil {
|
||||
toDeployChaosVersion,
|
||||
toWriteVersion,
|
||||
); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
@ -262,7 +231,8 @@ Please note, "upgrade"/"rollback" do not support chaos operations.`,
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
log.Debugf("set waiting timeout to %d s", stack.WaitTimeout)
|
||||
|
||||
log.Debugf("set waiting timeout to %d second(s)", stack.WaitTimeout)
|
||||
|
||||
if err := stack.RunDeploy(cl, deployOpts, compose, app.Name, internal.DontWaitConverge); err != nil {
|
||||
log.Fatal(err)
|
||||
@ -276,30 +246,77 @@ Please note, "upgrade"/"rollback" do not support chaos operations.`,
|
||||
}
|
||||
}
|
||||
|
||||
app.Recipe.Version = toDeployVersion
|
||||
if toDeployChaosVersion != config.CHAOS_DEFAULT {
|
||||
app.Recipe.Version = toDeployChaosVersion
|
||||
}
|
||||
if err := app.WriteRecipeVersion(app.Recipe.Version, false); err != nil {
|
||||
log.Fatalf("writing new recipe version in env file: %s", err)
|
||||
if err := app.WriteRecipeVersion(toWriteVersion, false); err != nil {
|
||||
log.Fatalf("writing recipe version failed: %s", err)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// validateChaosXORVersion xor checks version/chaos mode
|
||||
func validateChaosXORVersion(args []string) (bool, error) {
|
||||
if getSpecifiedVersion(args) != "" && internal.Chaos {
|
||||
return false, errors.New("cannot use [version] and --chaos together")
|
||||
func getChaosVersion(app app.App, toDeployVersion, toDeployChaosVersion *string) error {
|
||||
var err error
|
||||
*toDeployChaosVersion, err = app.Recipe.ChaosVersion()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return true, nil
|
||||
|
||||
*toDeployVersion, err = app.Recipe.GetVersionLabelLocal()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// getSpecifiedVersion retrieves the specific version if available
|
||||
func getSpecifiedVersion(args []string) string {
|
||||
if len(args) >= 2 {
|
||||
return args[1]
|
||||
func getLatestVersionOrCommit(app app.App, toDeployVersion *string) error {
|
||||
versions, err := app.Recipe.Tags()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ""
|
||||
|
||||
if len(versions) > 0 && !internal.Chaos {
|
||||
*toDeployVersion = versions[len(versions)-1]
|
||||
|
||||
log.Debugf("choosing %s as version to deploy", *toDeployVersion)
|
||||
|
||||
if _, err := app.Recipe.EnsureVersion(*toDeployVersion); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
head, err := app.Recipe.Head()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*toDeployVersion = formatter.SmallSHA(head.String())
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateArgsAndFlags ensures compatible args/flags.
|
||||
func validateArgsAndFlags(args []string) error {
|
||||
if len(args) == 2 && args[1] != "" && internal.Chaos {
|
||||
return fmt.Errorf("cannot use [version] and --chaos together")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateSecrets(cl *dockerClient.Client, app app.App) error {
|
||||
secStats, err := secret.PollSecretsStatus(cl, app)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, secStat := range secStats {
|
||||
if !secStat.CreatedOnRemote {
|
||||
return fmt.Errorf("secret not generated: %s", secStat.LocalName)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
@ -332,6 +349,6 @@ func init() {
|
||||
"no-converge-checks",
|
||||
"c",
|
||||
false,
|
||||
"do not wait for converge logic checks",
|
||||
"disable converge logic checks",
|
||||
)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
if parsedSpecificVersion.IsGreaterThan(parsedDeployedVersion) && !parsedSpecificVersion.Equals(parsedDeployedVersion) {
|
||||
log.Fatalf("%s is not a downgrade for %s?", deployMeta.Version, specificVersion)
|
||||
}
|
||||
|
||||
if parsedSpecificVersion.Equals(parsedDeployedVersion) && !internal.Force {
|
||||
log.Fatalf("%s is not a downgrade for %s?", deployMeta.Version, specificVersion)
|
||||
}
|
||||
|
||||
availableDowngrades = append(availableDowngrades, specificVersion)
|
||||
availableDowngrades = append(availableDowngrades, chosenDowngrade)
|
||||
}
|
||||
|
||||
if deployMeta.Version != "unknown" && specificVersion == "" {
|
||||
if deployMeta.IsChaos {
|
||||
warnMessages = append(warnMessages, fmt.Sprintf("attempting to rollback a chaos deployment"))
|
||||
if deployMeta.Version != config.UNKNOWN_DEFAULT && chosenDowngrade == "" {
|
||||
downgradeAvailable, err := ensureDowngradesAvailable(versions, &availableDowngrades, deployMeta)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
for _, version := range versions {
|
||||
parsedDeployedVersion, err := tagcmp.Parse(deployMeta.Version)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
parsedVersion, err := tagcmp.Parse(version)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if parsedVersion.IsLessThan(parsedDeployedVersion) && !(parsedVersion.Equals(parsedDeployedVersion)) {
|
||||
availableDowngrades = append(availableDowngrades, version)
|
||||
}
|
||||
}
|
||||
|
||||
if len(availableDowngrades) == 0 && !internal.Force {
|
||||
if !downgradeAvailable {
|
||||
log.Info("no available downgrades")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var chosenDowngrade string
|
||||
if len(availableDowngrades) > 0 {
|
||||
if internal.Force || internal.NoInput || specificVersion != "" {
|
||||
if internal.Force || internal.NoInput || chosenDowngrade != "" {
|
||||
if len(availableDowngrades) > 0 {
|
||||
chosenDowngrade = availableDowngrades[len(availableDowngrades)-1]
|
||||
log.Debugf("choosing %s as version to downgrade to (--force/--no-input)", chosenDowngrade)
|
||||
} else {
|
||||
msg := fmt.Sprintf("please select a downgrade (version: %s):", deployMeta.Version)
|
||||
if deployMeta.IsChaos {
|
||||
msg = fmt.Sprintf("please select a downgrade (version: %s, chaosVersion: %s):", deployMeta.Version, deployMeta.ChaosVersion)
|
||||
}
|
||||
|
||||
prompt := &survey.Select{
|
||||
Message: msg,
|
||||
Options: internal.SortVersionsDesc(availableDowngrades),
|
||||
}
|
||||
|
||||
if err := survey.AskOne(prompt, &chosenDowngrade); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if err := chooseDowngrade(availableDowngrades, deployMeta, &chosenDowngrade); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
if internal.Force &&
|
||||
chosenDowngrade == "" &&
|
||||
deployMeta.Version != config.UNKNOWN_DEFAULT {
|
||||
chosenDowngrade = deployMeta.Version
|
||||
}
|
||||
|
||||
if chosenDowngrade == "" {
|
||||
log.Fatal("unknown deployed version, unable to downgrade")
|
||||
}
|
||||
|
||||
log.Debugf("choosing %s as version to rollback", chosenDowngrade)
|
||||
|
||||
if _, err := app.Recipe.EnsureVersion(chosenDowngrade); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
@ -194,6 +161,7 @@ beforehand.`,
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
stackName := app.StackName()
|
||||
deployOpts := stack.Deploy{
|
||||
Composefiles: composeFiles,
|
||||
Namespace: stackName,
|
||||
@ -221,12 +189,13 @@ beforehand.`,
|
||||
// NOTE(d1): no release notes implemeneted for rolling back
|
||||
if err := internal.NewVersionOverview(
|
||||
app,
|
||||
warnMessages,
|
||||
downgradeWarnMessages,
|
||||
"rollback",
|
||||
deployMeta.Version,
|
||||
chaosVersion,
|
||||
chosenDowngrade,
|
||||
""); err != nil {
|
||||
"",
|
||||
); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
@ -234,13 +203,100 @@ beforehand.`,
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
app.Recipe.Version = chosenDowngrade
|
||||
if err := app.WriteRecipeVersion(app.Recipe.Version, false); err != nil {
|
||||
log.Fatalf("writing new recipe version in env file: %s", err)
|
||||
if err := app.WriteRecipeVersion(chosenDowngrade, false); err != nil {
|
||||
log.Fatalf("writing recipe version failed: %s", err)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// chooseDowngrade prompts the user to choose an downgrade interactively.
|
||||
func chooseDowngrade(
|
||||
availableDowngrades []string,
|
||||
deployMeta stack.DeployMeta,
|
||||
chosenDowngrade *string,
|
||||
) error {
|
||||
msg := fmt.Sprintf("please select a downgrade (version: %s):", deployMeta.Version)
|
||||
|
||||
if deployMeta.IsChaos {
|
||||
chaosVersion := formatter.BoldDirtyDefault(deployMeta.ChaosVersion)
|
||||
|
||||
msg = fmt.Sprintf(
|
||||
"please select a downgrade (version: %s, chaos: %s):",
|
||||
deployMeta.Version,
|
||||
chaosVersion,
|
||||
)
|
||||
}
|
||||
|
||||
prompt := &survey.Select{
|
||||
Message: msg,
|
||||
Options: internal.SortVersionsDesc(availableDowngrades),
|
||||
}
|
||||
|
||||
if err := survey.AskOne(prompt, &chosenDowngrade); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateDownpgradeVersionArg validates the specific version.
|
||||
func validateDowngradeVersionArg(
|
||||
specificVersion string,
|
||||
app app.App,
|
||||
deployMeta stack.DeployMeta,
|
||||
) error {
|
||||
parsedDeployedVersion, err := tagcmp.Parse(deployMeta.Version)
|
||||
if err != nil {
|
||||
return fmt.Errorf("'%s' is not a known version for %s", deployMeta.Version, app.Recipe.Name)
|
||||
}
|
||||
|
||||
parsedSpecificVersion, err := tagcmp.Parse(specificVersion)
|
||||
if err != nil {
|
||||
return fmt.Errorf("'%s' is not a known version for %s", specificVersion, app.Recipe.Name)
|
||||
}
|
||||
|
||||
if parsedSpecificVersion.IsGreaterThan(parsedDeployedVersion) &&
|
||||
!parsedSpecificVersion.Equals(parsedDeployedVersion) {
|
||||
return fmt.Errorf("%s is not a downgrade for %s?", deployMeta.Version, specificVersion)
|
||||
}
|
||||
|
||||
if parsedSpecificVersion.Equals(parsedDeployedVersion) && !internal.Force {
|
||||
return fmt.Errorf("%s is not a downgrade for %s?", deployMeta.Version, specificVersion)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ensureDowngradesAvailable ensures that there are available downgrades.
|
||||
func ensureDowngradesAvailable(
|
||||
versions []string,
|
||||
availableDowngrades *[]string,
|
||||
deployMeta stack.DeployMeta,
|
||||
) (bool, error) {
|
||||
parsedDeployedVersion, err := tagcmp.Parse(deployMeta.Version)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
for _, version := range versions {
|
||||
parsedVersion, err := tagcmp.Parse(version)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if parsedVersion.IsLessThan(parsedDeployedVersion) &&
|
||||
!(parsedVersion.Equals(parsedDeployedVersion)) {
|
||||
*availableDowngrades = append(*availableDowngrades, version)
|
||||
}
|
||||
}
|
||||
|
||||
if len(*availableDowngrades) == 0 && !internal.Force {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
AppRollbackCommand.Flags().BoolVarP(
|
||||
&internal.Force,
|
||||
@ -262,6 +318,6 @@ func init() {
|
||||
&internal.DontWaitConverge, "no-converge-checks",
|
||||
"c",
|
||||
false,
|
||||
"do not wait for converge logic checks",
|
||||
"disable converge logic checks",
|
||||
)
|
||||
}
|
||||
|
@ -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 len(args) == 2 && args[1] != "" {
|
||||
chosenUpgrade = args[1]
|
||||
|
||||
if err := validateUpgradeVersionArg(chosenUpgrade, app, deployMeta); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
availableUpgrades = append(availableUpgrades, chosenUpgrade)
|
||||
}
|
||||
|
||||
if deployMeta.Version != config.UNKNOWN_DEFAULT && chosenUpgrade == "" {
|
||||
upgradeAvailable, err := ensureUpgradesAvailable(versions, &availableUpgrades, deployMeta)
|
||||
if err != nil {
|
||||
log.Fatalf("'%s' is not a known version for %s", deployMeta.Version, app.Recipe.Name)
|
||||
}
|
||||
parsedSpecificVersion, err := tagcmp.Parse(specificVersion)
|
||||
if err != nil {
|
||||
log.Fatalf("'%s' is not a known version for %s", specificVersion, app.Recipe.Name)
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if parsedSpecificVersion.IsLessThan(parsedDeployedVersion) && !parsedSpecificVersion.Equals(parsedDeployedVersion) {
|
||||
log.Fatalf("%s is not an upgrade for %s?", deployMeta.Version, specificVersion)
|
||||
}
|
||||
|
||||
if parsedSpecificVersion.Equals(parsedDeployedVersion) && !internal.Force {
|
||||
log.Fatalf("%s is not an upgrade for %s?", deployMeta.Version, specificVersion)
|
||||
}
|
||||
|
||||
availableUpgrades = append(availableUpgrades, specificVersion)
|
||||
}
|
||||
|
||||
parsedDeployedVersion, err := tagcmp.Parse(deployMeta.Version)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if deployMeta.Version != "unknown" && specificVersion == "" {
|
||||
if deployMeta.IsChaos {
|
||||
warnMessages = append(warnMessages, fmt.Sprintf("attempting to upgrade a chaos deployment"))
|
||||
}
|
||||
|
||||
for _, version := range versions {
|
||||
parsedVersion, err := tagcmp.Parse(version)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if parsedVersion.IsGreaterThan(parsedDeployedVersion) && !(parsedVersion.Equals(parsedDeployedVersion)) {
|
||||
availableUpgrades = append(availableUpgrades, version)
|
||||
}
|
||||
}
|
||||
|
||||
if len(availableUpgrades) == 0 && !internal.Force {
|
||||
if !upgradeAvailable {
|
||||
log.Info("no available upgrades")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var chosenUpgrade string
|
||||
if len(availableUpgrades) > 0 {
|
||||
if internal.Force || internal.NoInput || specificVersion != "" {
|
||||
if internal.Force || internal.NoInput || chosenUpgrade != "" {
|
||||
if len(availableUpgrades) > 0 {
|
||||
chosenUpgrade = availableUpgrades[len(availableUpgrades)-1]
|
||||
log.Debugf("choosing %s as version to upgrade to", chosenUpgrade)
|
||||
} else {
|
||||
msg := fmt.Sprintf("please select an upgrade (version: %s):", deployMeta.Version)
|
||||
if deployMeta.IsChaos {
|
||||
msg = fmt.Sprintf("please select an upgrade (version: %s, chaosVersion: %s):", deployMeta.Version, deployMeta.ChaosVersion)
|
||||
}
|
||||
|
||||
prompt := &survey.Select{
|
||||
Message: msg,
|
||||
Options: internal.SortVersionsDesc(availableUpgrades),
|
||||
}
|
||||
|
||||
if err := survey.AskOne(prompt, &chosenUpgrade); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if err := chooseUpgrade(availableUpgrades, deployMeta, &chosenUpgrade); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
if internal.Force && chosenUpgrade == "" {
|
||||
warnMessages = append(warnMessages, fmt.Sprintf("%s is already upgraded to latest", app.Name))
|
||||
if internal.Force &&
|
||||
chosenUpgrade == "" &&
|
||||
deployMeta.Version != config.UNKNOWN_DEFAULT {
|
||||
chosenUpgrade = deployMeta.Version
|
||||
}
|
||||
|
||||
// if release notes written after git tag published, read them before we
|
||||
// check out the tag and then they'll appear to be missing. this covers
|
||||
// when we obviously will forget to write release notes before publishing
|
||||
var releaseNotes string
|
||||
if chosenUpgrade != "" {
|
||||
parsedChosenUpgrade, err := tagcmp.Parse(chosenUpgrade)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
for _, version := range internal.SortVersionsDesc(versions) {
|
||||
parsedVersion, err := tagcmp.Parse(version)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if parsedVersion.IsGreaterThan(parsedDeployedVersion) && parsedVersion.IsLessThan(parsedChosenUpgrade) {
|
||||
note, err := app.Recipe.GetReleaseNotes(version)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if note != "" {
|
||||
releaseNotes += fmt.Sprintf("%s\n", note)
|
||||
}
|
||||
}
|
||||
}
|
||||
if chosenUpgrade == "" {
|
||||
log.Fatal("unknown deployed version, unable to upgrade")
|
||||
}
|
||||
|
||||
log.Debugf("choosing %s as version to upgrade", chosenUpgrade)
|
||||
|
||||
// NOTE(d1): if release notes written after git tag published, read them
|
||||
// before we check out the tag and then they'll appear to be missing. this
|
||||
// covers when we obviously will forget to write release notes before
|
||||
// publishing
|
||||
if err := getReleaseNotes(app, versions, chosenUpgrade, deployMeta, &upgradeReleaseNotes); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if _, err := app.Recipe.EnsureVersion(chosenUpgrade); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
@ -217,6 +166,7 @@ beforehand.`,
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
stackName := app.StackName()
|
||||
deployOpts := stack.Deploy{
|
||||
Composefiles: composeFiles,
|
||||
Namespace: stackName,
|
||||
@ -243,30 +193,35 @@ beforehand.`,
|
||||
|
||||
for _, envVar := range envVars {
|
||||
if !envVar.Present {
|
||||
warnMessages = append(warnMessages,
|
||||
fmt.Sprintf("env var %s missing from %s.env, present in recipe .env.sample", envVar.Name, app.Domain),
|
||||
upgradeWarnMessages = append(upgradeWarnMessages,
|
||||
fmt.Sprintf("%s missing from %s.env", envVar.Name, app.Domain),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if showReleaseNotes {
|
||||
fmt.Print(releaseNotes)
|
||||
fmt.Print(upgradeReleaseNotes)
|
||||
return
|
||||
}
|
||||
|
||||
chaosVersion := config.CHAOS_DEFAULT
|
||||
if deployMeta.IsChaos {
|
||||
chaosVersion = deployMeta.ChaosVersion
|
||||
|
||||
if deployMeta.ChaosVersion == "" {
|
||||
chaosVersion = config.UNKNOWN_DEFAULT
|
||||
}
|
||||
}
|
||||
|
||||
if err := internal.NewVersionOverview(
|
||||
app,
|
||||
warnMessages,
|
||||
upgradeWarnMessages,
|
||||
"upgrade",
|
||||
deployMeta.Version,
|
||||
chaosVersion,
|
||||
chosenUpgrade,
|
||||
releaseNotes); err != nil {
|
||||
upgradeReleaseNotes,
|
||||
); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
@ -274,7 +229,8 @@ beforehand.`,
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
log.Debugf("set waiting timeout to %d s", stack.WaitTimeout)
|
||||
|
||||
log.Debugf("set waiting timeout to %d second(s)", stack.WaitTimeout)
|
||||
|
||||
if err := stack.RunDeploy(cl, deployOpts, compose, stackName, internal.DontWaitConverge); err != nil {
|
||||
log.Fatal(err)
|
||||
@ -283,18 +239,162 @@ beforehand.`,
|
||||
postDeployCmds, ok := app.Env["POST_UPGRADE_CMDS"]
|
||||
if ok && !internal.DontWaitConverge {
|
||||
log.Debugf("run the following post-deploy commands: %s", postDeployCmds)
|
||||
|
||||
if err := internal.PostCmds(cl, app, postDeployCmds); err != nil {
|
||||
log.Fatalf("attempting to run post deploy commands, saw: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
app.Recipe.Version = chosenUpgrade
|
||||
if err := app.WriteRecipeVersion(app.Recipe.Version, false); err != nil {
|
||||
log.Fatalf("writing new recipe version in env file: %s", err)
|
||||
if err := app.WriteRecipeVersion(chosenUpgrade, false); err != nil {
|
||||
log.Fatalf("writing recipe version failed: %s", err)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// chooseUpgrade prompts the user to choose an upgrade interactively.
|
||||
func chooseUpgrade(
|
||||
availableUpgrades []string,
|
||||
deployMeta stack.DeployMeta,
|
||||
chosenUpgrade *string,
|
||||
) error {
|
||||
msg := fmt.Sprintf("please select an upgrade (version: %s):", deployMeta.Version)
|
||||
|
||||
if deployMeta.IsChaos {
|
||||
chaosVersion := formatter.BoldDirtyDefault(deployMeta.ChaosVersion)
|
||||
|
||||
msg = fmt.Sprintf(
|
||||
"please select an upgrade (version: %s, chaos: %s):",
|
||||
deployMeta.Version,
|
||||
chaosVersion,
|
||||
)
|
||||
}
|
||||
|
||||
prompt := &survey.Select{
|
||||
Message: msg,
|
||||
Options: internal.SortVersionsDesc(availableUpgrades),
|
||||
}
|
||||
|
||||
if err := survey.AskOne(prompt, &chosenUpgrade); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getReleaseNotes(
|
||||
app app.App,
|
||||
versions []string,
|
||||
chosenUpgrade string,
|
||||
deployMeta stack.DeployMeta,
|
||||
upgradeReleaseNotes *string,
|
||||
) error {
|
||||
parsedChosenUpgrade, err := tagcmp.Parse(chosenUpgrade)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
parsedDeployedVersion, err := tagcmp.Parse(deployMeta.Version)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, version := range internal.SortVersionsDesc(versions) {
|
||||
parsedVersion, err := tagcmp.Parse(version)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if parsedVersion.IsGreaterThan(parsedDeployedVersion) &&
|
||||
parsedVersion.IsLessThan(parsedChosenUpgrade) {
|
||||
note, err := app.Recipe.GetReleaseNotes(version)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if note != "" {
|
||||
*upgradeReleaseNotes += fmt.Sprintf("%s\n", note)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ensureUpgradesAvailable ensures that there are available upgrades.
|
||||
func ensureUpgradesAvailable(
|
||||
versions []string,
|
||||
availableUpgrades *[]string,
|
||||
deployMeta stack.DeployMeta,
|
||||
) (bool, error) {
|
||||
parsedDeployedVersion, err := tagcmp.Parse(deployMeta.Version)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
for _, version := range versions {
|
||||
parsedVersion, err := tagcmp.Parse(version)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if parsedVersion.IsGreaterThan(parsedDeployedVersion) &&
|
||||
!(parsedVersion.Equals(parsedDeployedVersion)) {
|
||||
*availableUpgrades = append(*availableUpgrades, version)
|
||||
}
|
||||
}
|
||||
|
||||
if len(*availableUpgrades) == 0 && !internal.Force {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// validateUpgradeVersionArg validates the specific version.
|
||||
func validateUpgradeVersionArg(
|
||||
specificVersion string,
|
||||
app app.App,
|
||||
deployMeta stack.DeployMeta,
|
||||
) error {
|
||||
parsedSpecificVersion, err := tagcmp.Parse(specificVersion)
|
||||
if err != nil {
|
||||
return fmt.Errorf("'%s' is not a known version for %s", specificVersion, app.Recipe.Name)
|
||||
}
|
||||
|
||||
parsedDeployedVersion, err := tagcmp.Parse(deployMeta.Version)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if parsedSpecificVersion.IsLessThan(parsedDeployedVersion) &&
|
||||
!parsedSpecificVersion.Equals(parsedDeployedVersion) {
|
||||
return fmt.Errorf("%s is not an upgrade for %s?", deployMeta.Version, specificVersion)
|
||||
}
|
||||
|
||||
if parsedSpecificVersion.Equals(parsedDeployedVersion) && !internal.Force {
|
||||
return fmt.Errorf("%s is not an upgrade for %s?", deployMeta.Version, specificVersion)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ensureDeployed ensures the app is deployed and if so, returns deployment
|
||||
// meta info.
|
||||
func ensureDeployed(cl *dockerClient.Client, app app.App) (stack.DeployMeta, error) {
|
||||
log.Debugf("checking whether %s is already deployed", app.StackName())
|
||||
|
||||
deployMeta, err := stack.IsDeployed(context.Background(), cl, app.StackName())
|
||||
if err != nil {
|
||||
return stack.DeployMeta{}, err
|
||||
}
|
||||
|
||||
if !deployMeta.IsDeployed {
|
||||
return stack.DeployMeta{}, fmt.Errorf("%s is not deployed?", app.Name)
|
||||
}
|
||||
|
||||
return deployMeta, nil
|
||||
}
|
||||
|
||||
var (
|
||||
showReleaseNotes bool
|
||||
)
|
||||
@ -320,7 +420,7 @@ func init() {
|
||||
&internal.DontWaitConverge, "no-converge-checks",
|
||||
"c",
|
||||
false,
|
||||
"do not wait for converge logic checks",
|
||||
"disable converge logic checks",
|
||||
)
|
||||
|
||||
AppUpgradeCommand.Flags().BoolVarP(
|
||||
|
@ -2,9 +2,10 @@ package internal
|
||||
|
||||
var (
|
||||
// NOTE(d1): global
|
||||
Debug bool
|
||||
NoInput bool
|
||||
Offline bool
|
||||
Debug bool
|
||||
NoInput bool
|
||||
Offline bool
|
||||
IgnoreEnvVersion bool
|
||||
|
||||
// NOTE(d1): sub-command specific
|
||||
Chaos bool
|
||||
|
@ -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
|
||||
|
@ -159,11 +159,11 @@ func Get(name string) Recipe {
|
||||
dir := path.Join(config.RECIPES_DIR, escapeRecipeName(name))
|
||||
|
||||
r := Recipe{
|
||||
Name: name,
|
||||
Version: version,
|
||||
Dir: dir,
|
||||
GitURL: gitURL,
|
||||
SSHURL: sshURL,
|
||||
Name: name,
|
||||
EnvVersion: version,
|
||||
Dir: dir,
|
||||
GitURL: gitURL,
|
||||
SSHURL: sshURL,
|
||||
|
||||
ComposePath: path.Join(dir, "compose.yml"),
|
||||
ReadmePath: path.Join(dir, "README.md"),
|
||||
@ -179,12 +179,12 @@ func Get(name string) Recipe {
|
||||
}
|
||||
|
||||
type Recipe struct {
|
||||
Name string
|
||||
Version string
|
||||
Dirty bool // NOTE(d1): git terminology for unstaged changes
|
||||
Dir string
|
||||
GitURL string
|
||||
SSHURL string
|
||||
Name string
|
||||
EnvVersion string
|
||||
Dirty bool // NOTE(d1): git terminology for unstaged changes
|
||||
Dir string
|
||||
GitURL string
|
||||
SSHURL string
|
||||
|
||||
ComposePath string
|
||||
ReadmePath string
|
||||
@ -195,7 +195,7 @@ type Recipe struct {
|
||||
// String outputs a human-friendly string representation.
|
||||
func (r Recipe) String() string {
|
||||
out := fmt.Sprintf("{name: %s, ", r.Name)
|
||||
out += fmt.Sprintf("version : %s, ", r.Version)
|
||||
out += fmt.Sprintf("version : %s, ", r.EnvVersion)
|
||||
out += fmt.Sprintf("dirty: %v, ", r.Dirty)
|
||||
out += fmt.Sprintf("dir: %s, ", r.Dir)
|
||||
out += fmt.Sprintf("git url: %s, ", r.GitURL)
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user