migrate to github.com/urfave/cli/v3

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
Sebastiaan van Stijn committed 2026-09-10 01:14:31 +02:00
1 parent e3119ceb8d
commit f72829f36b
179 files changed
+8906 -10554

No files matched your search

+5 -5
View File
@@ -34,7 +34,7 @@ import (
"github.com/containerd/containerd/v2/pkg/namespaces"
"github.com/containerd/containerd/v2/pkg/oci"
"github.com/containerd/log"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
var densityCommand = &cli.Command{
@@ -47,7 +47,7 @@ var densityCommand = &cli.Command{
Value: 10,
},
},
Action: func(cmd *cli.Context) error {
Action: func(ctx context.Context, cmd *cli.Command) error {
var (
pids []uint32
count = cmd.Int("count")
@@ -72,16 +72,16 @@ var densityCommand = &cli.Command{
return err
}
defer client.Close()
ctx := namespaces.WithNamespace(context.Background(), "density")
ctx = namespaces.WithNamespace(ctx, "density")
if err := cleanup(ctx, client); err != nil {
return err
}
log.L.Infof("pulling %s", cfg.Image)
log.G(ctx).Infof("pulling %s", cfg.Image)
image, err := client.Pull(ctx, cfg.Image, containerd.WithPullUnpack, containerd.WithPullSnapshotter(cfg.Snapshotter))
if err != nil {
return err
}
log.L.Info("generating spec from image")
log.G(ctx).Info("generating spec from image")
s := make(chan os.Signal, 1)
signal.Notify(s, syscall.SIGTERM, syscall.SIGINT)
+14 -14
View File
@@ -36,7 +36,7 @@ import (
"github.com/containerd/containerd/v2/version"
"github.com/containerd/log"
metrics "github.com/docker/go-metrics"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
var (
@@ -65,8 +65,8 @@ func init() {
panic(err)
}
cli.VersionPrinter = func(cmd *cli.Context) {
fmt.Println(cmd.App.Name, version.Package, cmd.App.Version)
cli.VersionPrinter = func(cmd *cli.Command) {
_, _ = fmt.Fprintln(cmd.Root().Writer, cmd.Name, version.Package, cmd.Version)
}
// Override the default flag descriptions for '--version' and '--help'
@@ -76,14 +76,14 @@ func init() {
Aliases: []string{"v"},
Usage: "Print the version",
DisableDefaultText: true,
HideDefault: true,
}
cli.HelpFlag = &cli.BoolFlag{
Name: "help",
Aliases: []string{"h"},
Usage: "Show help",
DisableDefaultText: true,
HideDefault: true,
}
}
@@ -144,7 +144,7 @@ func main() {
runtime.GOMAXPROCS(runtime.NumCPU())
ctx := context.Background()
app := &cli.App{
app := &cli.Command{
Name: "containerd-stress",
Description: "stress test a containerd daemon",
Version: version.Version,
@@ -206,23 +206,23 @@ func main() {
Value: "overlayfs",
},
},
Before: func(cmd *cli.Context) error {
Before: func(ctx context.Context, cmd *cli.Command) (context.Context, error) {
if cmd.Bool("json") {
if err := log.SetLevel(log.WarnLevel); err != nil {
return err
return ctx, err
}
}
if cmd.Bool("debug") {
if err := log.SetLevel(log.DebugLevel); err != nil {
return err
return ctx, err
}
}
return nil
return ctx, nil
},
Commands: []*cli.Command{
densityCommand,
},
Action: func(cmd *cli.Context) error {
Action: func(ctx context.Context, cmd *cli.Command) error {
cfg := config{
Address: cmd.String("address"),
Duration: cmd.Duration("duration"),
@@ -246,7 +246,7 @@ func main() {
return test(cfg)
},
}
if err := app.RunContext(ctx, os.Args); err != nil {
if err := app.Run(ctx, os.Args); err != nil {
_, _ = fmt.Fprintln(os.Stderr, "containerd-stress:", err)
os.Exit(1)
}
@@ -480,10 +480,10 @@ func cleanup(ctx context.Context, client *containerd.Client) error {
for _, c := range containers {
task, err := c.Task(ctx, nil)
if err == nil {
task.Delete(ctx, containerd.WithProcessKill)
_, _ = task.Delete(ctx, containerd.WithProcessKill)
}
if err := c.Delete(ctx, containerd.WithSnapshotCleanup); err != nil {
if derr := c.Delete(ctx); derr == nil {
if dErr := c.Delete(ctx); dErr == nil {
continue
}
return err
+4 -6
View File
@@ -33,7 +33,7 @@ import (
"github.com/containerd/plugin/registry"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pelletier/go-toml/v2"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
func outputConfig(ctx context.Context, config *srvconfig.Config) error {
@@ -85,12 +85,11 @@ func defaultConfig() *srvconfig.Config {
var configCommand = &cli.Command{
Name: "config",
Usage: "Information on the containerd config",
Subcommands: []*cli.Command{
Commands: []*cli.Command{
{
Name: "default",
Usage: "See the output of the default config",
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
return outputConfig(ctx, defaultConfig())
},
},
@@ -108,9 +107,8 @@ var configCommand = &cli.Command{
},
}
func dumpConfig(cmd *cli.Context) error {
func dumpConfig(ctx context.Context, cmd *cli.Command) error {
config := defaultConfig()
ctx := cmd.Context
g := registry.Graph(func(*plugin.Registration) bool { return false })
plugins := func() iter.Seq[plugin.Registration] {
+14 -14
View File
@@ -40,7 +40,7 @@ import (
"github.com/containerd/plugin"
"github.com/containerd/plugin/registry"
"github.com/sirupsen/logrus"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
"google.golang.org/grpc/grpclog"
)
@@ -58,8 +58,8 @@ func init() {
// Discard grpc logs so that they don't mess with our stdio
grpclog.SetLoggerV2(grpclog.NewLoggerV2(io.Discard, io.Discard, io.Discard))
cli.VersionPrinter = func(cmd *cli.Context) {
fmt.Println(cmd.App.Name, version.Package, cmd.App.Version, version.Revision)
cli.VersionPrinter = func(cmd *cli.Command) {
_, _ = fmt.Fprintln(cmd.Root().Writer, cmd.Name, version.Package, cmd.Version, version.Revision)
}
// Override the default flag descriptions for '--version' and '--help'
@@ -69,20 +69,20 @@ func init() {
Aliases: []string{"v"},
Usage: "Print the version",
DisableDefaultText: true,
HideDefault: true,
}
cli.HelpFlag = &cli.BoolFlag{
Name: "help",
Aliases: []string{"h"},
Usage: "Show help",
DisableDefaultText: true,
HideDefault: true,
}
}
// App returns a *cli.App instance.
func App() *cli.App {
return &cli.App{
// App returns a *cli.Command instance.
func App() *cli.Command {
return &cli.Command{
Name: "containerd",
Version: version.Version,
Usage: usage,
@@ -128,9 +128,9 @@ can be used and modified as necessary as a custom configuration.`,
publishCommand,
ociHook,
},
Action: func(cmd *cli.Context) error {
Action: func(ctx context.Context, cmd *cli.Command) error {
if args := cmd.Args(); args.First() != "" {
return cli.ShowCommandHelp(cmd, args.First())
return cli.ShowCommandHelp(ctx, cmd, args.First())
}
var (
@@ -140,7 +140,7 @@ can be used and modified as necessary as a custom configuration.`,
config = defaultConfig()
)
ctx, cancel := context.WithCancel(cmd.Context)
ctx, cancel := context.WithCancel(ctx)
defer cancel()
// Only try to load the config if it either exists, or the user explicitly
@@ -281,7 +281,7 @@ can be used and modified as necessary as a custom configuration.`,
}
}
func applyFlags(cmd *cli.Context, config *srvconfig.Config) error {
func applyFlags(cmd *cli.Command, config *srvconfig.Config) error {
// the order for config vs flag values is that flags will always override
// the config values if they are set
if err := setLogLevel(cmd, config); err != nil {
@@ -350,7 +350,7 @@ func applyFlags(cmd *cli.Context, config *srvconfig.Config) error {
return nil
}
func setLogLevel(cmd *cli.Context, config *srvconfig.Config) error {
func setLogLevel(cmd *cli.Command, config *srvconfig.Config) error {
l := cmd.String("log-level")
if l == "" {
l = config.Debug.Level
@@ -392,7 +392,7 @@ func dumpStacks(writeToFile bool) {
return
}
defer f.Close()
f.WriteString(string(buf))
_, _ = f.WriteString(string(buf))
log.L.Infof("goroutine stack dump written to %s", name)
}
}
+3 -2
View File
@@ -18,6 +18,7 @@ package command
import (
"bytes"
"context"
"encoding/json"
"io"
"os"
@@ -27,13 +28,13 @@ import (
"github.com/containerd/containerd/v2/pkg/oci"
"github.com/opencontainers/runtime-spec/specs-go"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
var ociHook = &cli.Command{
Name: "oci-hook",
Usage: "Provides a base for OCI runtime hooks to allow arguments to be injected.",
Action: func(cmd *cli.Context) error {
Action: func(ctx context.Context, cmd *cli.Command) error {
state, err := loadHookState(os.Stdin)
if err != nil {
return err
+3 -3
View File
@@ -27,7 +27,7 @@ import (
eventsapi "github.com/containerd/containerd/api/services/events/v1"
"github.com/containerd/errdefs"
"github.com/containerd/errdefs/pkg/errgrpc"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
"google.golang.org/grpc"
"google.golang.org/grpc/backoff"
"google.golang.org/grpc/credentials/insecure"
@@ -51,8 +51,8 @@ var publishCommand = &cli.Command{
Usage: "Topic of the event",
},
},
Action: func(cmd *cli.Context) error {
ctx := namespaces.WithNamespace(cmd.Context, cmd.String("namespace"))
Action: func(ctx context.Context, cmd *cli.Command) error {
ctx = namespaces.WithNamespace(ctx, cmd.String("namespace"))
topic := cmd.String("topic")
if topic == "" {
return fmt.Errorf("topic required to publish event: %w", errdefs.ErrInvalidArgument)
@@ -20,7 +20,7 @@ package command
import (
"github.com/containerd/containerd/v2/cmd/containerd/server"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
// serviceFlags returns an array of flags for configuring containerd to run
@@ -30,7 +30,7 @@ func serviceFlags() []cli.Flag {
}
// applyPlatformFlags applies platform-specific flags.
func applyPlatformFlags(*cli.Context) {}
func applyPlatformFlags(*cli.Command) {}
// registerUnregisterService is only relevant on Windows.
func registerUnregisterService(root string) (bool, error) {
+2 -2
View File
@@ -27,7 +27,7 @@ import (
"github.com/containerd/containerd/v2/cmd/containerd/server"
"github.com/containerd/errdefs"
"github.com/sirupsen/logrus"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
"golang.org/x/sys/windows"
"golang.org/x/sys/windows/svc"
"golang.org/x/sys/windows/svc/debug"
@@ -79,7 +79,7 @@ func serviceFlags() []cli.Flag {
}
// applyPlatformFlags applies platform-specific flags.
func applyPlatformFlags(cmd *cli.Context) {
func applyPlatformFlags(cmd *cli.Command) {
serviceNameFlag = cmd.String("service-name")
if serviceNameFlag == "" {
serviceNameFlag = defaultServiceName
+1 -1
View File
@@ -28,7 +28,7 @@ import (
func main() {
ctx := context.Background()
if err := command.App().RunContext(ctx, os.Args); err != nil {
if err := command.App().Run(ctx, os.Args); err != nil {
_, _ = fmt.Fprintln(os.Stderr, "containerd:", err)
os.Exit(1)
}
+14 -13
View File
@@ -17,11 +17,12 @@
package app
import (
"context"
"fmt"
"io"
"github.com/containerd/log"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
"google.golang.org/grpc/grpclog"
"github.com/containerd/containerd/v2/cmd/ctr/commands/containers"
@@ -55,8 +56,8 @@ func init() {
// Discard grpc logs so that they don't mess with our stdio
grpclog.SetLoggerV2(grpclog.NewLoggerV2(io.Discard, io.Discard, io.Discard))
cli.VersionPrinter = func(cmd *cli.Context) {
fmt.Println(cmd.App.Name, version.Package, cmd.App.Version)
cli.VersionPrinter = func(cmd *cli.Command) {
_, _ = fmt.Fprintln(cmd.Root().Writer, cmd.Name, version.Package, cmd.Version)
}
// Override the default flag descriptions for '--version' and '--help'
@@ -66,20 +67,20 @@ func init() {
Aliases: []string{"v"},
Usage: "Print the version",
DisableDefaultText: true,
HideDefault: true,
}
cli.HelpFlag = &cli.BoolFlag{
Name: "help",
Aliases: []string{"h"},
Usage: "Show help",
DisableDefaultText: true,
HideDefault: true,
}
}
// New returns a *cli.App instance.
func New() *cli.App {
return &cli.App{
func New() *cli.Command {
return &cli.Command{
Name: "ctr",
Version: version.Version,
Description: `
@@ -97,7 +98,7 @@ stable from release to release of the containerd project.`,
containerd CLI
`,
DisableSliceFlagSeparator: true,
EnableBashCompletion: true,
EnableShellCompletion: true,
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "debug",
@@ -108,7 +109,7 @@ containerd CLI
Aliases: []string{"a"},
Usage: "Address for containerd's GRPC server",
Value: defaults.DefaultAddress,
EnvVars: []string{"CONTAINERD_ADDRESS"},
Sources: cli.EnvVars("CONTAINERD_ADDRESS"),
},
&cli.DurationFlag{
Name: "timeout",
@@ -123,7 +124,7 @@ containerd CLI
Aliases: []string{"n"},
Usage: "Namespace to use with commands",
Value: namespaces.Default,
EnvVars: []string{namespaces.NamespaceEnvVar},
Sources: cli.EnvVars(namespaces.NamespaceEnvVar),
},
},
Commands: append([]*cli.Command{
@@ -145,11 +146,11 @@ containerd CLI
info.Command,
deprecations.Command,
}, extraCmds...),
Before: func(cmd *cli.Context) error {
Before: func(ctx context.Context, cmd *cli.Command) (context.Context, error) {
if cmd.Bool("debug") {
return log.SetLevel(log.DebugLevel)
return ctx, log.SetLevel(log.DebugLevel)
}
return nil
return ctx, nil
},
}
}
+3 -3
View File
@@ -26,7 +26,7 @@ import (
"github.com/containerd/containerd/v2/pkg/epoch"
"github.com/containerd/containerd/v2/pkg/namespaces"
"github.com/containerd/log"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
// AppContext returns the context for a command. Should only be called once per
@@ -34,7 +34,7 @@ import (
//
// This will ensure the namespace is picked up and set the timeout, if one is
// defined.
func AppContext(ctx context.Context, cmd *cli.Context) (context.Context, context.CancelFunc) {
func AppContext(ctx context.Context, cmd *cli.Command) (context.Context, context.CancelFunc) {
var (
timeout = cmd.Duration("timeout")
namespace = cmd.String("namespace")
@@ -56,7 +56,7 @@ func AppContext(ctx context.Context, cmd *cli.Context) (context.Context, context
}
// NewClient returns a new containerd client
func NewClient(ctx context.Context, cmd *cli.Context, opts ...containerd.Opt) (*containerd.Client, context.Context, context.CancelFunc, error) {
func NewClient(ctx context.Context, cmd *cli.Command, opts ...containerd.Opt) (*containerd.Client, context.Context, context.CancelFunc, error) {
timeoutOpt := containerd.WithTimeout(cmd.Duration("connect-timeout"))
opts = append(opts, timeoutOpt)
socketPath := cmd.String("address")
+3 -3
View File
@@ -26,7 +26,7 @@ import (
"github.com/containerd/containerd/v2/defaults"
"github.com/containerd/containerd/v2/pkg/atomicfile"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
var (
@@ -35,7 +35,7 @@ var (
&cli.StringFlag{
Name: "snapshotter",
Usage: "Snapshotter name. Empty value stands for the default value.",
EnvVars: []string{"CONTAINERD_SNAPSHOTTER"},
Sources: cli.EnvVars("CONTAINERD_SNAPSHOTTER"),
},
}
@@ -232,7 +232,7 @@ var (
)
// ObjectWithLabelArgs returns the first arg and a LabelArgs object
func ObjectWithLabelArgs(cmd *cli.Context) (string, map[string]string) {
func ObjectWithLabelArgs(cmd *cli.Command) (string, map[string]string) {
var (
first = cmd.Args().First()
labelStrings = cmd.Args().Tail()
+3 -3
View File
@@ -23,7 +23,7 @@ import (
"github.com/containerd/containerd/api/types/runc/options"
runtimeoptions "github.com/containerd/containerd/api/types/runtimeoptions/v1"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
func init() {
@@ -59,7 +59,7 @@ func init() {
})
}
func getRuncOptions(cmd *cli.Context) (*options.Options, error) {
func getRuncOptions(cmd *cli.Command) (*options.Options, error) {
runtimeOpts := &options.Options{}
if runcBinary := cmd.String("runc-binary"); runcBinary != "" {
runtimeOpts.BinaryName = runcBinary
@@ -78,7 +78,7 @@ func getRuncOptions(cmd *cli.Context) (*options.Options, error) {
return runtimeOpts, nil
}
func RuntimeOptions(cmd *cli.Context) (any, error) {
func RuntimeOptions(cmd *cli.Command) (any, error) {
// validate first
if (cmd.String("runc-binary") != "" || cmd.Bool("runc-systemd-cgroup")) &&
cmd.String("runtime") != "io.containerd.runc.v2" {
+2 -2
View File
@@ -17,7 +17,7 @@
package commands
import (
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
func init() {
@@ -37,6 +37,6 @@ func init() {
})
}
func RuntimeOptions(cmd *cli.Context) (any, error) {
func RuntimeOptions(cmd *cli.Command) (any, error) {
return nil, nil
}
+3 -3
View File
@@ -17,13 +17,14 @@
package containers
import (
"context"
"errors"
"fmt"
containerd "github.com/containerd/containerd/v2/client"
"github.com/containerd/containerd/v2/cmd/ctr/commands"
"github.com/containerd/errdefs"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
var checkpointCommand = &cli.Command{
@@ -44,8 +45,7 @@ var checkpointCommand = &cli.Command{
Usage: "Checkpoint container task",
},
},
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
id := cmd.Args().First()
if id == "" {
return errors.New("container id must be provided")
+7 -12
View File
@@ -31,7 +31,7 @@ import (
"github.com/containerd/errdefs"
"github.com/containerd/log"
"github.com/containerd/typeurl/v2"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
// Command is the cli command for managing containers
@@ -39,7 +39,7 @@ var Command = &cli.Command{
Name: "containers",
Usage: "Manage containers",
Aliases: []string{"c", "container"},
Subcommands: []*cli.Command{
Commands: []*cli.Command{
createCommand,
deleteCommand,
infoCommand,
@@ -55,8 +55,7 @@ var createCommand = &cli.Command{
Usage: "Create container",
ArgsUsage: "[flags] Image|RootFS CONTAINER [COMMAND] [ARG...]",
Flags: append(commands.RuntimeFlags, append(append(commands.SnapshotterFlags, []cli.Flag{commands.SnapshotterLabels}...), commands.ContainerFlags...)...),
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
var (
id string
ref string
@@ -103,8 +102,7 @@ var listCommand = &cli.Command{
Usage: "Print only the container id",
},
},
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
var (
filters = cmd.Args().Slice()
quiet = cmd.Bool("quiet")
@@ -158,8 +156,7 @@ var deleteCommand = &cli.Command{
Usage: "Do not clean up snapshot with container",
},
},
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
var exitErr error
client, ctx, cancel, err := commands.NewClient(ctx, cmd)
if err != nil {
@@ -215,8 +212,7 @@ var setLabelsCommand = &cli.Command{
ArgsUsage: "[flags] CONTAINER [<key>=<value>, ...]",
Description: "set and clear labels for a container",
Flags: []cli.Flag{},
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
containerID, labels := commands.ObjectWithLabelArgs(cmd)
if containerID == "" {
return fmt.Errorf("container id must be provided: %w", errdefs.ErrInvalidArgument)
@@ -258,8 +254,7 @@ var infoCommand = &cli.Command{
Usage: "Only display the spec",
},
},
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
id := cmd.Args().First()
if id == "" {
return fmt.Errorf("container id must be provided: %w", errdefs.ErrInvalidArgument)
+3 -3
View File
@@ -17,6 +17,7 @@
package containers
import (
"context"
"errors"
"github.com/containerd/console"
@@ -26,7 +27,7 @@ import (
"github.com/containerd/containerd/v2/pkg/cio"
"github.com/containerd/errdefs"
"github.com/containerd/log"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
var restoreCommand = &cli.Command{
@@ -43,8 +44,7 @@ var restoreCommand = &cli.Command{
Usage: "Restore the runtime and memory data from the checkpoint",
},
},
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
id := cmd.Args().First()
if id == "" {
return errors.New("container id must be provided")
+16 -25
View File
@@ -17,6 +17,7 @@
package content
import (
"context"
"errors"
"fmt"
"io"
@@ -35,7 +36,7 @@ import (
units "github.com/docker/go-units"
digest "github.com/opencontainers/go-digest"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
var (
@@ -43,7 +44,7 @@ var (
Command = &cli.Command{
Name: "content",
Usage: "Manage content",
Subcommands: []*cli.Command{
Commands: []*cli.Command{
activeIngestCommand,
deleteCommand,
editCommand,
@@ -64,8 +65,7 @@ var (
Usage: "Get the data for an object",
ArgsUsage: "[<digest>, ...]",
Description: "display the image object",
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
dgst, err := digest.Parse(cmd.Args().First())
if err != nil {
return err
@@ -104,8 +104,7 @@ var (
Usage: "Verify content against expected digest",
},
},
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
var (
ref = cmd.Args().First()
expectedSize = cmd.Int64("expected-size")
@@ -142,7 +141,7 @@ var (
Name: "timeout",
Aliases: []string{"t"},
Usage: "Total timeout for fetch",
EnvVars: []string{"CONTAINERD_FETCH_TIMEOUT"},
Sources: cli.EnvVars("CONTAINERD_FETCH_TIMEOUT"),
},
&cli.StringFlag{
Name: "root",
@@ -150,8 +149,7 @@ var (
Value: "/tmp/content", // TODO(stevvooe): for now, just use the PWD/.content
},
},
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
match := cmd.Args().First()
client, ctx, cancel, err := commands.NewClient(ctx, cmd)
if err != nil {
@@ -189,8 +187,7 @@ var (
Usage: "Print only the blob digest",
},
},
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
var (
quiet = cmd.Bool("quiet")
args = cmd.Args().Slice()
@@ -243,8 +240,7 @@ var (
Usage: "Add labels to content",
ArgsUsage: "<digest> [<label>=<value> ...]",
Description: "labels blobs in the content store",
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
object, labels := commands.ObjectWithLabelArgs(cmd)
client, ctx, cancel, err := commands.NewClient(ctx, cmd)
if err != nil {
@@ -306,11 +302,10 @@ var (
&cli.StringFlag{
Name: "editor",
Usage: "Select editor (vim, emacs, etc.)",
EnvVars: []string{"EDITOR"},
Sources: cli.EnvVars("EDITOR"),
},
},
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
var (
validate = cmd.String("validate")
object = cmd.Args().First()
@@ -370,8 +365,7 @@ var (
ArgsUsage: "[<digest>, ...]",
Description: `Delete one or more blobs permanently. Successfully deleted
blobs are printed to stdout.`,
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
var (
args = cmd.Args().Slice()
exitError error
@@ -419,8 +413,7 @@ var (
ArgsUsage: "[flags] <remote> <object> [<hint>, ...]",
Description: `Fetch objects by identifier from a remote.`,
Flags: commands.RegistryFlags,
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
ctx, cancel := commands.AppContext(ctx, cmd)
defer cancel()
@@ -465,12 +458,11 @@ var (
Usage: "Specify target mediatype for request header",
},
}...),
Action: func(cmd *cli.Context) error {
Action: func(ctx context.Context, cmd *cli.Command) error {
digests := cmd.Args().Tail()
if len(digests) == 0 {
return errors.New("must specify digests")
}
ctx := cmd.Context
ctx, cancel := commands.AppContext(ctx, cmd)
defer cancel()
@@ -519,8 +511,7 @@ var (
ArgsUsage: "[flags] <remote> <object> <type>",
Description: `Push objects by identifier to a remote.`,
Flags: commands.RegistryFlags,
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
var (
ref = cmd.Args().Get(0)
object = cmd.Args().Get(1)
@@ -600,7 +591,7 @@ func edit(editor string, rd io.Reader) (_ io.ReadCloser, retErr error) {
}
}()
_, err = io.Copy(tmp, rd)
tmp.Close()
_ = tmp.Close()
if err != nil {
return nil, err
}
+3 -4
View File
@@ -37,7 +37,7 @@ import (
"github.com/containerd/platforms"
"github.com/opencontainers/go-digest"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
var fetchCommand = &cli.Command{
@@ -76,8 +76,7 @@ pull content that will later be used with 'ctr run' or 'ctr images unpack'.`,
Usage: "Pull all metadata including manifests and configs",
},
),
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
client, ctx, cancel, err := commands.NewClient(ctx, cmd)
if err != nil {
return err
@@ -115,7 +114,7 @@ type FetchConfig struct {
}
// NewFetchConfig returns the default FetchConfig from cli flags
func NewFetchConfig(ctx context.Context, cmd *cli.Context) (*FetchConfig, error) {
func NewFetchConfig(ctx context.Context, cmd *cli.Command) (*FetchConfig, error) {
resolver, err := commands.GetResolver(ctx, cmd)
if err != nil {
return nil, err
+4 -4
View File
@@ -17,6 +17,7 @@
package content
import (
"context"
"strings"
"time"
"unicode"
@@ -25,7 +26,7 @@ import (
"github.com/containerd/containerd/v2/core/content"
"github.com/containerd/containerd/v2/core/leases"
"github.com/containerd/log"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
const (
@@ -47,7 +48,7 @@ var pruneFlags = []cli.Flag{
var pruneCommand = &cli.Command{
Name: "prune",
Usage: "Prunes content from the content store",
Subcommands: []*cli.Command{
Commands: []*cli.Command{
pruneReferencesCommand,
},
}
@@ -56,8 +57,7 @@ var pruneReferencesCommand = &cli.Command{
Name: "references",
Usage: "Prunes preference labels from the content store (layers only by default)",
Flags: pruneFlags,
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
client, ctx, cancel, err := commands.NewClient(ctx, cmd)
if err != nil {
return err
@@ -17,12 +17,13 @@
package deprecations
import (
"context"
"fmt"
"os"
"text/tabwriter"
"time"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
api "github.com/containerd/containerd/api/services/introspection/v1"
"github.com/containerd/containerd/v2/cmd/ctr/commands"
@@ -32,7 +33,7 @@ import (
// Command is the parent for all commands under "deprecations"
var Command = &cli.Command{
Name: "deprecations",
Subcommands: []*cli.Command{
Commands: []*cli.Command{
listCommand,
},
}
@@ -45,8 +46,7 @@ var listCommand = &cli.Command{
Usage: "output format to use (Examples: 'default', 'json')",
},
},
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
// Suppress automatic warnings, since we print the warnings by ourselves.
_ = os.Setenv("CONTAINERD_SUPPRESS_DEPRECATION_WARNINGS", "1")
+3 -3
View File
@@ -17,6 +17,7 @@
package events
import (
"context"
"encoding/json"
"fmt"
@@ -24,7 +25,7 @@ import (
"github.com/containerd/containerd/v2/core/events"
"github.com/containerd/log"
"github.com/containerd/typeurl/v2"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
// Register grpc event types
_ "github.com/containerd/containerd/api/events"
@@ -35,8 +36,7 @@ var Command = &cli.Command{
Name: "events",
Aliases: []string{"event"},
Usage: "Display containerd events",
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
client, ctx, cancel, err := commands.NewClient(ctx, cmd)
if err != nil {
return err
+3 -4
View File
@@ -27,7 +27,7 @@ import (
"github.com/containerd/errdefs"
"github.com/containerd/platforms"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
"github.com/containerd/containerd/v2/cmd/ctr/commands"
"github.com/containerd/containerd/v2/core/content"
@@ -69,8 +69,7 @@ dmverity_mode=on, which rejects cache hits that lack a sidecar.`,
Usage: "Generate a dm-verity hash tree and .dmverity sidecar for each blob (Linux only)",
},
},
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
ref := cmd.Args().Get(0)
cacheDir := cmd.Args().Get(1)
if ref == "" || cacheDir == "" {
@@ -111,7 +110,7 @@ dmverity_mode=on, which rejects cache hits that lack a sidecar.`,
}
return err
}
fmt.Fprintln(cmd.App.Writer, cacheDir)
_, _ = fmt.Fprintln(cmd.Writer, cacheDir)
return nil
},
}
+4 -4
View File
@@ -17,6 +17,7 @@
package images
import (
"context"
"errors"
"fmt"
"strings"
@@ -26,7 +27,7 @@ import (
"github.com/containerd/containerd/v2/core/images/converter/erofs"
"github.com/containerd/containerd/v2/core/images/converter/uncompress"
"github.com/containerd/platforms"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
var convertCommand = &cli.Command{
@@ -75,8 +76,7 @@ When '--all-platforms' is given all images in a manifest list must be available.
Usage: "Exports content from all platforms",
},
},
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
var convertOpts []converter.Opt
srcRef := cmd.Args().Get(0)
targetRef := cmd.Args().Get(1)
@@ -134,7 +134,7 @@ When '--all-platforms' is given all images in a manifest list must be available.
if err != nil {
return err
}
fmt.Fprintln(cmd.App.Writer, newImg.Target.Digest.String())
_, _ = fmt.Fprintln(cmd.Writer, newImg.Target.Digest.String())
return nil
},
}
+3 -3
View File
@@ -17,12 +17,13 @@
package images
import (
"context"
"errors"
"fmt"
"io"
"os"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
"github.com/containerd/containerd/v2/cmd/ctr/commands"
"github.com/containerd/containerd/v2/core/images/archive"
@@ -65,8 +66,7 @@ When '--all-platforms' is given all images in a manifest list must be available.
Usage: "Run export locally rather than through transfer API",
},
},
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
var (
out = cmd.Args().First()
images = cmd.Args().Tail()
+8 -12
View File
@@ -17,6 +17,7 @@
package images
import (
"context"
"errors"
"fmt"
"os"
@@ -30,7 +31,7 @@ import (
"github.com/containerd/errdefs"
"github.com/containerd/log"
"github.com/containerd/platforms"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
// Command is the cli command for managing images
@@ -38,7 +39,7 @@ var Command = &cli.Command{
Name: "images",
Aliases: []string{"image", "i"},
Usage: "Manage images",
Subcommands: []*cli.Command{
Commands: []*cli.Command{
checkCommand,
exportCommand,
importCommand,
@@ -71,8 +72,7 @@ var listCommand = &cli.Command{
Usage: "Print only the image refs",
},
},
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
var (
filters = cmd.Args().Slice()
quiet = cmd.Bool("quiet")
@@ -156,8 +156,7 @@ var setLabelsCommand = &cli.Command{
Usage: "Replace all labels",
},
},
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
var (
replaceAll = cmd.Bool("replace-all")
name, labels = commands.ObjectWithLabelArgs(cmd)
@@ -217,8 +216,7 @@ var checkCommand = &cli.Command{
Usage: "Print only the ready image refs (fully downloaded and unpacked)",
},
}, commands.SnapshotterFlags...),
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
var (
exitErr error
quiet = cmd.Bool("quiet")
@@ -332,8 +330,7 @@ var removeCommand = &cli.Command{
Usage: "Synchronously remove image and all associated resources",
},
},
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
client, ctx, cancel, err := commands.NewClient(ctx, cmd)
if err != nil {
return err
@@ -378,8 +375,7 @@ var pruneCommand = &cli.Command{
},
// adapted from `nerdctl`:
// https://github.com/containerd/nerdctl/blob/272dc9c29fc1434839d3ec63194d7efa24d7c0ef/cmd/nerdctl/image_prune.go#L86
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
client, ctx, cancel, err := commands.NewClient(ctx, cmd)
if err != nil {
return err
+3 -3
View File
@@ -17,13 +17,14 @@
package images
import (
"context"
"errors"
"fmt"
"io"
"os"
"time"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
containerd "github.com/containerd/containerd/v2/client"
"github.com/containerd/containerd/v2/cmd/ctr/commands"
@@ -105,8 +106,7 @@ If foobar.tar contains an OCI ref named "latest" and anonymous ref "sha256:deadb
},
}, append(commands.SnapshotterFlags, commands.LabelFlag)...),
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
var (
in = cmd.Args().First()
opts []containerd.ImportOpt
+3 -3
View File
@@ -17,11 +17,12 @@
package images
import (
"context"
"os"
"github.com/containerd/containerd/v2/cmd/ctr/commands"
"github.com/containerd/containerd/v2/pkg/display"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
var inspectCommand = &cli.Command{
@@ -36,8 +37,7 @@ var inspectCommand = &cli.Command{
Usage: "Show JSON content",
},
},
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
client, ctx, cancel, err := commands.NewClient(ctx, cmd)
if err != nil {
return err
+5 -5
View File
@@ -17,6 +17,7 @@
package images
import (
"context"
"crypto/rand"
"encoding/base64"
"errors"
@@ -26,7 +27,7 @@ import (
"github.com/containerd/errdefs"
"github.com/containerd/platforms"
"github.com/opencontainers/image-spec/identity"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
containerd "github.com/containerd/containerd/v2/client"
"github.com/containerd/containerd/v2/cmd/ctr/commands"
@@ -65,8 +66,7 @@ When you are done, use the unmount command.
Value: 1 * time.Hour,
},
),
Action: func(cmd *cli.Context) (retErr error) {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) (retErr error) {
var (
ref = cmd.Args().First()
target = cmd.Args().Get(1)
@@ -162,7 +162,7 @@ When you are done, use the unmount command.
if target != "" {
if err := mount.All(mounts, target); err != nil {
if err := s.Remove(ctx, key); err != nil && !errdefs.IsNotFound(err) {
fmt.Fprintln(cmd.App.ErrWriter, "Error cleaning up snapshot after mount error:", err)
_, _ = fmt.Fprintln(cmd.ErrWriter, "Error cleaning up snapshot after mount error:", err)
}
return fmt.Errorf("failed to mount %v: %w", mounts, err)
}
@@ -172,7 +172,7 @@ When you are done, use the unmount command.
return fmt.Errorf("cannot handle returned mounts: %v", mounts)
}
fmt.Fprintln(cmd.App.Writer, target)
_, _ = fmt.Fprintln(cmd.Writer, target)
return nil
},
}
+2 -3
View File
@@ -38,7 +38,7 @@ import (
"github.com/containerd/platforms"
"github.com/opencontainers/image-spec/identity"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
var pullCommand = &cli.Command{
@@ -89,8 +89,7 @@ command. As part of this process, we do the following:
Usage: "Synchronize the underlying filesystem containing files when unpack images, false by default",
},
),
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
ref := cmd.Args().First()
if ref == "" {
return errors.New("please provide an image reference to pull")
+2 -3
View File
@@ -40,7 +40,7 @@ import (
"github.com/containerd/platforms"
digest "github.com/opencontainers/go-digest"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
"golang.org/x/sync/errgroup"
)
@@ -77,8 +77,7 @@ var pushCommand = &cli.Command{
Name: "allow-non-distributable-blobs",
Usage: "Allow pushing blobs that are marked as non-distributable",
}),
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
var (
ref = cmd.Args().First()
local = cmd.Args().Get(1)
+3 -3
View File
@@ -17,10 +17,11 @@
package images
import (
"context"
"errors"
"fmt"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
"github.com/containerd/containerd/v2/cmd/ctr/commands"
"github.com/containerd/containerd/v2/core/transfer/image"
@@ -47,8 +48,7 @@ var tagCommand = &cli.Command{
Usage: "Skip the strict check for reference names",
},
},
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
ref := cmd.Args().First()
if ref == "" {
return errors.New("please provide an image reference to tag from")
+4 -4
View File
@@ -17,6 +17,7 @@
package images
import (
"context"
"errors"
"fmt"
@@ -24,7 +25,7 @@ import (
"github.com/containerd/containerd/v2/core/leases"
"github.com/containerd/containerd/v2/core/mount"
"github.com/containerd/errdefs"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
var unmountCommand = &cli.Command{
@@ -38,8 +39,7 @@ var unmountCommand = &cli.Command{
Usage: "Remove the snapshot after a successful unmount",
},
),
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
target := cmd.Args().First()
if target == "" {
return errors.New("please provide a target path to unmount from")
@@ -66,7 +66,7 @@ var unmountCommand = &cli.Command{
}
}
fmt.Fprintln(cmd.App.Writer, target)
_, _ = fmt.Fprintln(cmd.Writer, target)
return nil
},
}
+3 -3
View File
@@ -17,6 +17,7 @@
package images
import (
"context"
"errors"
"fmt"
"os"
@@ -28,7 +29,7 @@ import (
"github.com/containerd/containerd/v2/pkg/progress"
"github.com/opencontainers/image-spec/identity"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
var usageCommand = &cli.Command{
@@ -36,8 +37,7 @@ var usageCommand = &cli.Command{
Usage: "Display usage of snapshots for a given image ref",
ArgsUsage: "[flags] <ref>",
Flags: commands.SnapshotterFlags,
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
ref := cmd.Args().First()
if ref == "" {
return errors.New("please provide an image reference to mount")
+4 -3
View File
@@ -17,9 +17,11 @@
package info
import (
"context"
api "github.com/containerd/containerd/api/services/introspection/v1"
"github.com/containerd/containerd/v2/cmd/ctr/commands"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
type Info struct {
@@ -30,8 +32,7 @@ type Info struct {
var Command = &cli.Command{
Name: "info",
Usage: "Print the server info",
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
client, ctx, cancel, err := commands.NewClient(ctx, cmd)
if err != nil {
return err
+4 -3
View File
@@ -17,9 +17,11 @@
package install
import (
"context"
containerd "github.com/containerd/containerd/v2/client"
"github.com/containerd/containerd/v2/cmd/ctr/commands"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
// Command to install binary packages
@@ -44,8 +46,7 @@ var Command = &cli.Command{
Usage: "Set an optional install path other than the managed opt directory",
},
},
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
client, ctx, cancel, err := commands.NewClient(ctx, cmd)
if err != nil {
return err
+6 -8
View File
@@ -17,6 +17,7 @@
package leases
import (
"context"
"fmt"
"os"
"sort"
@@ -26,14 +27,14 @@ import (
"github.com/containerd/containerd/v2/cmd/ctr/commands"
"github.com/containerd/containerd/v2/core/leases"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
// Command is the cli command for managing content
var Command = &cli.Command{
Name: "leases",
Usage: "Manage leases",
Subcommands: []*cli.Command{
Commands: []*cli.Command{
listCommand,
createCommand,
deleteCommand,
@@ -54,8 +55,7 @@ var listCommand = &cli.Command{
Usage: "Print only the blob digest",
},
},
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
var (
filters = cmd.Args().Slice()
quiet = cmd.Bool("quiet")
@@ -118,8 +118,7 @@ var createCommand = &cli.Command{
Value: 24 * time.Hour,
},
},
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
labelstr := cmd.Args().Slice()
client, ctx, cancel, err := commands.NewClient(ctx, cmd)
if err != nil {
@@ -168,8 +167,7 @@ var deleteCommand = &cli.Command{
Usage: "Synchronously remove leases and all unreferenced resources",
},
},
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
lids := cmd.Args().Slice()
if len(lids) == 0 {
return cli.ShowSubcommandHelp(cmd)
+7 -10
View File
@@ -17,6 +17,7 @@
package namespaces
import (
"context"
"errors"
"fmt"
"os"
@@ -27,7 +28,7 @@ import (
"github.com/containerd/containerd/v2/cmd/ctr/commands"
"github.com/containerd/errdefs"
"github.com/containerd/log"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
// Command is the cli command for managing namespaces
@@ -35,7 +36,7 @@ var Command = &cli.Command{
Name: "namespaces",
Aliases: []string{"namespace", "ns"},
Usage: "Manage namespaces",
Subcommands: []*cli.Command{
Commands: []*cli.Command{
createCommand,
listCommand,
removeCommand,
@@ -49,8 +50,7 @@ var createCommand = &cli.Command{
Usage: "Create a new namespace",
ArgsUsage: "<name> [<key>=<value>]",
Description: "create a new namespace. it must be unique",
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
namespace, labels := commands.ObjectWithLabelArgs(cmd)
if namespace == "" {
return errors.New("please specify a namespace")
@@ -70,8 +70,7 @@ var setLabelsCommand = &cli.Command{
Usage: "Set and clear labels for a namespace",
ArgsUsage: "<name> [<key>=<value>, ...]",
Description: "set and clear labels for a namespace. empty value clears the label",
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
namespace, labels := commands.ObjectWithLabelArgs(cmd)
if namespace == "" {
return errors.New("please specify a namespace")
@@ -104,8 +103,7 @@ var listCommand = &cli.Command{
Usage: "Print only the namespace name",
},
},
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
client, ctx, cancel, err := commands.NewClient(ctx, cmd)
if err != nil {
return err
@@ -158,8 +156,7 @@ var removeCommand = &cli.Command{
Usage: "Delete the namespace's cgroup",
},
},
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
client, ctx, cancel, err := commands.NewClient(ctx, cmd)
if err != nil {
return err
@@ -19,10 +19,10 @@ package namespaces
import (
"github.com/containerd/containerd/v2/core/runtime/opts"
"github.com/containerd/containerd/v2/pkg/namespaces"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
func deleteOpts(cmd *cli.Context) []namespaces.DeleteOpts {
func deleteOpts(cmd *cli.Command) []namespaces.DeleteOpts {
var delOpts []namespaces.DeleteOpts
if cmd.Bool("cgroup") {
delOpts = append(delOpts, opts.WithNamespaceCgroupDeletion)
@@ -20,9 +20,9 @@ package namespaces
import (
"github.com/containerd/containerd/v2/pkg/namespaces"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
func deleteOpts(cmd *cli.Context) []namespaces.DeleteOpts {
func deleteOpts(cmd *cli.Command) []namespaces.DeleteOpts {
return nil
}
+4 -4
View File
@@ -17,9 +17,10 @@
package oci
import (
"context"
"fmt"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
"github.com/containerd/containerd/v2/cmd/ctr/commands"
"github.com/containerd/containerd/v2/core/containers"
@@ -31,7 +32,7 @@ import (
var Command = &cli.Command{
Name: "oci",
Usage: "OCI tools",
Subcommands: []*cli.Command{
Commands: []*cli.Command{
defaultSpecCommand,
},
}
@@ -45,8 +46,7 @@ var defaultSpecCommand = &cli.Command{
Usage: "Platform of the spec to print (Examples: 'linux/arm64', 'windows/amd64')",
},
},
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
ctx, cancel := commands.AppContext(ctx, cmd)
defer cancel()
+6 -7
View File
@@ -17,6 +17,7 @@
package plugins
import (
"context"
"encoding/json"
"fmt"
"os"
@@ -29,7 +30,7 @@ import (
"github.com/containerd/platforms"
pluginutils "github.com/containerd/plugin"
v1 "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
"google.golang.org/grpc/codes"
)
@@ -38,7 +39,7 @@ var Command = &cli.Command{
Name: "plugins",
Aliases: []string{"plugin"},
Usage: "Provides information about containerd plugins",
Subcommands: []*cli.Command{
Commands: []*cli.Command{
listCommand,
inspectRuntimeCommand,
},
@@ -60,8 +61,7 @@ var listCommand = &cli.Command{
Usage: "Print detailed information about each plugin",
},
},
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
var (
quiet = cmd.Bool("quiet")
detailed = cmd.Bool("detailed")
@@ -173,8 +173,7 @@ var inspectRuntimeCommand = &cli.Command{
Usage: "Display runtime info",
ArgsUsage: "[flags]",
Flags: commands.RuntimeFlags,
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
rt := cmd.String("runtime")
rtOptions, err := commands.RuntimeOptions(cmd)
if err != nil {
@@ -193,7 +192,7 @@ var inspectRuntimeCommand = &cli.Command{
if err != nil {
return err
}
_, err = fmt.Fprintln(cmd.App.Writer, string(j))
_, err = fmt.Fprintln(cmd.Writer, string(j))
return err
},
}
+17 -16
View File
@@ -17,6 +17,7 @@
package pprof
import (
"context"
"fmt"
"io"
"net/http"
@@ -24,7 +25,7 @@ import (
"time"
"github.com/containerd/containerd/v2/defaults"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
type pprofDialer struct {
@@ -44,7 +45,7 @@ var Command = &cli.Command{
Value: defaults.DefaultDebugAddress,
},
},
Subcommands: []*cli.Command{
Commands: []*cli.Command{
pprofBlockCommand,
pprofGoroutinesCommand,
pprofHeapCommand,
@@ -64,7 +65,7 @@ var pprofGoroutinesCommand = &cli.Command{
Value: 2,
},
},
Action: func(cmd *cli.Context) error {
Action: func(ctx context.Context, cmd *cli.Command) error {
return GoroutineProfile(cmd, getPProfClient)
},
}
@@ -79,7 +80,7 @@ var pprofHeapCommand = &cli.Command{
Value: 0,
},
},
Action: func(cmd *cli.Context) error {
Action: func(ctx context.Context, cmd *cli.Command) error {
return HeapProfile(cmd, getPProfClient)
},
}
@@ -100,7 +101,7 @@ var pprofProfileCommand = &cli.Command{
Value: 0,
},
},
Action: func(cmd *cli.Context) error {
Action: func(ctx context.Context, cmd *cli.Command) error {
return CPUProfile(cmd, getPProfClient)
},
}
@@ -121,7 +122,7 @@ var pprofTraceCommand = &cli.Command{
Value: 0,
},
},
Action: func(cmd *cli.Context) error {
Action: func(ctx context.Context, cmd *cli.Command) error {
return TraceProfile(cmd, getPProfClient)
},
}
@@ -136,7 +137,7 @@ var pprofBlockCommand = &cli.Command{
Value: 0,
},
},
Action: func(cmd *cli.Context) error {
Action: func(ctx context.Context, cmd *cli.Command) error {
return BlockProfile(cmd, getPProfClient)
},
}
@@ -151,16 +152,16 @@ var pprofThreadcreateCommand = &cli.Command{
Value: 0,
},
},
Action: func(cmd *cli.Context) error {
Action: func(ctx context.Context, cmd *cli.Command) error {
return ThreadcreateProfile(cmd, getPProfClient)
},
}
// Client is a func that returns a http client for a pprof server
type Client func(cmd *cli.Context) (*http.Client, error)
type Client func(cmd *cli.Command) (*http.Client, error)
// GoroutineProfile dumps goroutine stack dump
func GoroutineProfile(cmd *cli.Context, clientFunc Client) error {
func GoroutineProfile(cmd *cli.Command, clientFunc Client) error {
client, err := clientFunc(cmd)
if err != nil {
return err
@@ -176,7 +177,7 @@ func GoroutineProfile(cmd *cli.Context, clientFunc Client) error {
}
// HeapProfile dumps the heap profile
func HeapProfile(cmd *cli.Context, clientFunc Client) error {
func HeapProfile(cmd *cli.Command, clientFunc Client) error {
client, err := clientFunc(cmd)
if err != nil {
return err
@@ -192,7 +193,7 @@ func HeapProfile(cmd *cli.Context, clientFunc Client) error {
}
// CPUProfile dumps CPU profile
func CPUProfile(cmd *cli.Context, clientFunc Client) error {
func CPUProfile(cmd *cli.Command, clientFunc Client) error {
client, err := clientFunc(cmd)
if err != nil {
return err
@@ -209,7 +210,7 @@ func CPUProfile(cmd *cli.Context, clientFunc Client) error {
}
// TraceProfile collects execution trace
func TraceProfile(cmd *cli.Context, clientFunc Client) error {
func TraceProfile(cmd *cli.Command, clientFunc Client) error {
client, err := clientFunc(cmd)
if err != nil {
return err
@@ -227,7 +228,7 @@ func TraceProfile(cmd *cli.Context, clientFunc Client) error {
}
// BlockProfile collects goroutine blocking profile
func BlockProfile(cmd *cli.Context, clientFunc Client) error {
func BlockProfile(cmd *cli.Command, clientFunc Client) error {
client, err := clientFunc(cmd)
if err != nil {
return err
@@ -243,7 +244,7 @@ func BlockProfile(cmd *cli.Context, clientFunc Client) error {
}
// ThreadcreateProfile collects goroutine thread creating profile
func ThreadcreateProfile(cmd *cli.Context, clientFunc Client) error {
func ThreadcreateProfile(cmd *cli.Command, clientFunc Client) error {
client, err := clientFunc(cmd)
if err != nil {
return err
@@ -258,7 +259,7 @@ func ThreadcreateProfile(cmd *cli.Context, clientFunc Client) error {
return err
}
func getPProfClient(cmd *cli.Context) (*http.Client, error) {
func getPProfClient(cmd *cli.Command) (*http.Client, error) {
dialer := getPProfDialer(cmd.String("debug-socket"))
tr := &http.Transport{
+4 -4
View File
@@ -33,7 +33,7 @@ import (
"github.com/containerd/containerd/v2/core/remotes/docker/config"
"github.com/containerd/containerd/v2/core/transfer/registry"
"github.com/containerd/containerd/v2/pkg/httpdbg"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
// PushTracker returns a new InMemoryTracker which tracks the ref status
@@ -55,7 +55,7 @@ func passwordPrompt() (string, error) {
}
// GetResolver prepares the resolver from the environment and options
func GetResolver(ctx context.Context, cmd *cli.Context) (remotes.Resolver, error) {
func GetResolver(ctx context.Context, cmd *cli.Command) (remotes.Resolver, error) {
username := cmd.String("user")
var secret string
if i := strings.IndexByte(username, ':'); i > 0 {
@@ -111,7 +111,7 @@ func GetResolver(ctx context.Context, cmd *cli.Context) (remotes.Resolver, error
return docker.NewResolver(options), nil
}
func resolverDefaultTLS(cmd *cli.Context) (*tls.Config, error) {
func resolverDefaultTLS(cmd *cli.Command) (*tls.Config, error) {
tlsConfig := &tls.Config{}
if cmd.Bool("skip-verify") {
@@ -158,7 +158,7 @@ type staticCredentials struct {
}
// NewStaticCredentials gets credentials from passing in cli context
func NewStaticCredentials(ctx context.Context, cmd *cli.Context, ref string) (registry.CredentialHelper, error) {
func NewStaticCredentials(ctx context.Context, cmd *cli.Command, ref string) (registry.CredentialHelper, error) {
username := cmd.String("user")
var secret string
if i := strings.IndexByte(username, ':'); i > 0 {
+3 -4
View File
@@ -29,7 +29,7 @@ import (
"github.com/containerd/console"
gocni "github.com/containerd/go-cni"
specs "github.com/opencontainers/runtime-spec/specs-go"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
"github.com/containerd/errdefs"
"github.com/containerd/log"
@@ -43,7 +43,7 @@ import (
"github.com/containerd/containerd/v2/pkg/oci"
)
func withMounts(cmd *cli.Context) oci.SpecOpts {
func withMounts(cmd *cli.Command) oci.SpecOpts {
return func(ctx context.Context, client oci.Client, container *containers.Container, s *specs.Spec) error {
mounts := make([]specs.Mount, 0)
dests := make([]string, 0)
@@ -143,8 +143,7 @@ var Command = &cli.Command{
append(commands.RuntimeFlags,
append(append(commands.SnapshotterFlags, []cli.Flag{commands.SnapshotterLabels}...),
commands.ContainerFlags...)...)...)...),
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
var (
err error
id string
+2 -2
View File
@@ -43,7 +43,7 @@ import (
"github.com/intel/goresctrl/pkg/blockio"
"github.com/opencontainers/runtime-spec/specs-go"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
"tags.cncf.io/container-device-interface/pkg/cdi"
"tags.cncf.io/container-device-interface/pkg/parser"
)
@@ -90,7 +90,7 @@ var platformRunFlags = []cli.Flag{
}
// NewContainer creates a new container
func NewContainer(ctx context.Context, client *containerd.Client, cmd *cli.Context) (containerd.Container, error) {
func NewContainer(ctx context.Context, client *containerd.Client, cmd *cli.Command) (containerd.Container, error) {
var (
id string
config = cmd.IsSet("config")
+4 -4
View File
@@ -31,7 +31,7 @@ import (
"github.com/containerd/containerd/v2/pkg/oci"
"github.com/containerd/log"
specs "github.com/opencontainers/runtime-spec/specs-go"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
var platformRunFlags = []cli.Flag{
@@ -46,7 +46,7 @@ var platformRunFlags = []cli.Flag{
}
// NewContainer creates a new container
func NewContainer(ctx context.Context, client *containerd.Client, cmd *cli.Context) (containerd.Container, error) {
func NewContainer(ctx context.Context, client *containerd.Client, cmd *cli.Command) (containerd.Container, error) {
var (
id string
opts []oci.SpecOpts
@@ -126,6 +126,7 @@ func NewContainer(ctx context.Context, client *containerd.Client, cmd *cli.Conte
con := console.Current()
size, err := con.Size()
_ = con.Close()
if err != nil {
log.L.WithError(err).Error("console size")
}
@@ -184,8 +185,7 @@ func NewContainer(ctx context.Context, client *containerd.Client, cmd *cli.Conte
Debug: cmd.Bool("debug"),
}
if cmd.IsSet("scrub-logs") {
scrubLogs := cmd.Bool("scrub-logs")
opts.ScrubLogs = &scrubLogs
opts.ScrubLogs = new(cmd.Bool("scrub-logs"))
}
runtimeOpts = opts
}
+7 -10
View File
@@ -17,6 +17,7 @@
package sandboxes
import (
"context"
"encoding/json"
"fmt"
"os"
@@ -28,7 +29,7 @@ import (
"github.com/containerd/containerd/v2/pkg/oci"
"github.com/containerd/errdefs"
"github.com/containerd/log"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
// Command is a set of subcommands to manage runtimes with sandbox support
@@ -36,7 +37,7 @@ var Command = &cli.Command{
Name: "sandboxes",
Aliases: []string{"sandbox", "sb", "s"},
Usage: "Manage sandboxes",
Subcommands: []*cli.Command{
Commands: []*cli.Command{
runCommand,
listCommand,
removeCommand,
@@ -56,8 +57,7 @@ var runCommand = &cli.Command{
Value: defaults.DefaultRuntime,
},
},
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
if cmd.NArg() != 2 {
return cli.ShowSubcommandHelp(cmd)
}
@@ -110,8 +110,7 @@ var listCommand = &cli.Command{
Usage: "The list of filters to apply when querying sandboxes from the store",
},
},
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
client, ctx, cancel, err := commands.NewClient(ctx, cmd)
if err != nil {
return err
@@ -159,8 +158,7 @@ var removeCommand = &cli.Command{
Usage: "Ignore shutdown errors when removing sandbox",
},
},
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
client, ctx, cancel, err := commands.NewClient(ctx, cmd)
if err != nil {
return err
@@ -202,8 +200,7 @@ var infoCommand = &cli.Command{
Aliases: []string{"i"},
Usage: "Get info about a sandbox",
ArgsUsage: "<sandbox id>",
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
client, ctx, cancel, err := commands.NewClient(ctx, cmd)
if err != nil {
return err
+9 -9
View File
@@ -29,13 +29,13 @@ import (
"github.com/containerd/containerd/v2/cmd/ctr/commands/pprof"
"github.com/containerd/containerd/v2/pkg/namespaces"
"github.com/containerd/containerd/v2/pkg/shim"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
var pprofCommand = &cli.Command{
Name: "pprof",
Usage: "Provide golang pprof outputs for containerd-shim",
Subcommands: []*cli.Command{
Commands: []*cli.Command{
pprofBlockCommand,
pprofGoroutinesCommand,
pprofHeapCommand,
@@ -55,7 +55,7 @@ var pprofGoroutinesCommand = &cli.Command{
Value: 2,
},
},
Action: func(cmd *cli.Context) error {
Action: func(ctx context.Context, cmd *cli.Command) error {
return pprof.GoroutineProfile(cmd, getPProfClient)
},
}
@@ -70,7 +70,7 @@ var pprofHeapCommand = &cli.Command{
Value: 0,
},
},
Action: func(cmd *cli.Context) error {
Action: func(ctx context.Context, cmd *cli.Command) error {
return pprof.HeapProfile(cmd, getPProfClient)
},
}
@@ -91,7 +91,7 @@ var pprofProfileCommand = &cli.Command{
Value: 0,
},
},
Action: func(cmd *cli.Context) error {
Action: func(ctx context.Context, cmd *cli.Command) error {
return pprof.CPUProfile(cmd, getPProfClient)
},
}
@@ -112,7 +112,7 @@ var pprofTraceCommand = &cli.Command{
Value: 0,
},
},
Action: func(cmd *cli.Context) error {
Action: func(ctx context.Context, cmd *cli.Command) error {
return pprof.TraceProfile(cmd, getPProfClient)
},
}
@@ -127,7 +127,7 @@ var pprofBlockCommand = &cli.Command{
Value: 0,
},
},
Action: func(cmd *cli.Context) error {
Action: func(ctx context.Context, cmd *cli.Command) error {
return pprof.BlockProfile(cmd, getPProfClient)
},
}
@@ -142,12 +142,12 @@ var pprofThreadcreateCommand = &cli.Command{
Value: 0,
},
},
Action: func(cmd *cli.Context) error {
Action: func(ctx context.Context, cmd *cli.Command) error {
return pprof.ThreadcreateProfile(cmd, getPProfClient)
},
}
func getPProfClient(cmd *cli.Context) (*http.Client, error) {
func getPProfClient(cmd *cli.Command) (*http.Client, error) {
id := cmd.String("id")
if id == "" {
return nil, errors.New("container id must be provided")
+13 -17
View File
@@ -34,7 +34,7 @@ import (
"github.com/containerd/ttrpc"
"github.com/containerd/typeurl/v2"
"github.com/opencontainers/runtime-spec/specs-go"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
"github.com/containerd/containerd/v2/cmd/ctr/commands"
"github.com/containerd/containerd/v2/pkg/namespaces"
@@ -76,7 +76,7 @@ var Command = &cli.Command{
Usage: "shim address (default: computed from shim ID)",
},
},
Subcommands: []*cli.Command{
Commands: []*cli.Command{
deleteCommand,
execCommand,
startCommand,
@@ -89,7 +89,7 @@ var Command = &cli.Command{
var startCommand = &cli.Command{
Name: "start",
Usage: "Start a container with a task",
Action: func(cmd *cli.Context) error {
Action: func(ctx context.Context, cmd *cli.Command) error {
service, err := getTaskService(cmd)
if err != nil {
return err
@@ -104,7 +104,7 @@ var startCommand = &cli.Command{
var deleteCommand = &cli.Command{
Name: "delete",
Usage: "Delete a container with a task",
Action: func(cmd *cli.Context) error {
Action: func(ctx context.Context, cmd *cli.Command) error {
service, err := getTaskService(cmd)
if err != nil {
return err
@@ -128,7 +128,7 @@ var shutdownCommand = &cli.Command{
Name: "api-version",
Usage: "shim API version {2,3}",
Value: 3,
Action: func(cmd *cli.Context, v int) error {
Action: func(ctx context.Context, cmd *cli.Command, v int) error {
if v != 2 && v != 3 {
return fmt.Errorf("api-version must be 2 or 3")
}
@@ -136,7 +136,7 @@ var shutdownCommand = &cli.Command{
},
},
},
Action: func(cmd *cli.Context) error {
Action: func(ctx context.Context, cmd *cli.Command) error {
switch cmd.Int("api-version") {
case 2:
service, err := getTaskServiceV2(cmd)
@@ -174,7 +174,7 @@ var stateCommand = &cli.Command{
Name: "api-version",
Usage: "shim API version {2,3}",
Value: 3,
Action: func(cmd *cli.Context, v int) error {
Action: func(ctx context.Context, cmd *cli.Command, v int) error {
if v != 2 && v != 3 {
return fmt.Errorf("api-version must be 2 or 3")
}
@@ -182,7 +182,7 @@ var stateCommand = &cli.Command{
},
},
},
Action: func(cmd *cli.Context) error {
Action: func(ctx context.Context, cmd *cli.Command) error {
id := cmd.String("task-id")
if id == "" {
id = cmd.String("id")
@@ -241,16 +241,12 @@ var execCommand = &cli.Command{
Usage: "Runtime spec",
},
),
Action: func(cmd *cli.Context) error {
Action: func(ctx context.Context, cmd *cli.Command) error {
service, err := getTaskService(cmd)
if err != nil {
return err
}
var (
id = cmd.Args().First()
ctx = context.Background()
)
id := cmd.Args().First()
if id == "" {
return errors.New("exec id must be provided")
}
@@ -318,14 +314,14 @@ var execCommand = &cli.Command{
},
}
func getTaskService(cmd *cli.Context) (task.TTRPCTaskService, error) {
func getTaskService(cmd *cli.Command) (task.TTRPCTaskService, error) {
client, err := getTTRPCClient(cmd)
if err != nil {
return nil, err
}
return task.NewTTRPCTaskClient(client), nil
}
func getTaskServiceV2(cmd *cli.Context) (taskv2.TTRPCTaskService, error) {
func getTaskServiceV2(cmd *cli.Command) (taskv2.TTRPCTaskService, error) {
client, err := getTTRPCClient(cmd)
if err != nil {
return nil, err
@@ -333,7 +329,7 @@ func getTaskServiceV2(cmd *cli.Context) (taskv2.TTRPCTaskService, error) {
return taskv2.NewTTRPCTaskClient(client), nil
}
func getTTRPCClient(cmd *cli.Context) (*ttrpc.Client, error) {
func getTTRPCClient(cmd *cli.Command) (*ttrpc.Client, error) {
id := cmd.String("id")
shimAddress := cmd.String("shim-address")
if id == "" && shimAddress == "" {
+14 -26
View File
@@ -32,7 +32,7 @@ import (
"github.com/containerd/log"
digest "github.com/opencontainers/go-digest"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
containerd "github.com/containerd/containerd/v2/client"
"github.com/containerd/containerd/v2/cmd/ctr/commands"
@@ -50,7 +50,7 @@ var Command = &cli.Command{
Aliases: []string{"snapshot"},
Usage: "Manage snapshots",
Flags: commands.SnapshotterFlags,
Subcommands: []*cli.Command{
Commands: []*cli.Command{
commitCommand,
diffCommand,
infoCommand,
@@ -70,8 +70,7 @@ var listCommand = &cli.Command{
Name: "list",
Aliases: []string{"ls"},
Usage: "List snapshots",
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
client, ctx, cancel, err := commands.NewClient(ctx, cmd)
if err != nil {
return err
@@ -115,8 +114,7 @@ var diffCommand = &cli.Command{
Usage: "Keep diff content. up to creator to delete it.",
},
}, commands.LabelFlag),
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
var (
idA = cmd.Args().First()
idB = cmd.Args().Get(1)
@@ -209,8 +207,7 @@ var usageCommand = &cli.Command{
Usage: "Display size in bytes",
},
},
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
var displaySize func(int64) string
if cmd.Bool("b") {
displaySize = func(s int64) string {
@@ -261,8 +258,7 @@ var removeCommand = &cli.Command{
Aliases: []string{"del", "remove", "rm"},
ArgsUsage: "<key> [<key>, ...]",
Usage: "Remove snapshots",
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
client, ctx, cancel, err := commands.NewClient(ctx, cmd)
if err != nil {
return err
@@ -295,8 +291,7 @@ var prepareCommand = &cli.Command{
Usage: "Print out snapshot mounts as JSON",
},
},
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
if narg := cmd.NArg(); narg < 1 || narg > 2 {
return cli.ShowSubcommandHelp(cmd)
}
@@ -348,8 +343,7 @@ var viewCommand = &cli.Command{
Usage: "Print out snapshot mounts as JSON",
},
},
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
if narg := cmd.NArg(); narg < 1 || narg > 2 {
return cli.ShowSubcommandHelp(cmd)
}
@@ -393,8 +387,7 @@ var mountCommand = &cli.Command{
Usage: "Mounts the snapshot mounts as a temp mount and returns a bind mount",
},
},
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
if cmd.NArg() != 2 {
return cli.ShowSubcommandHelp(cmd)
}
@@ -436,8 +429,7 @@ var commitCommand = &cli.Command{
Name: "commit",
Usage: "Commit an active snapshot into the provided name",
ArgsUsage: "<key> <active>",
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
if cmd.NArg() != 2 {
return cli.ShowSubcommandHelp(cmd)
}
@@ -461,8 +453,7 @@ var commitCommand = &cli.Command{
var treeCommand = &cli.Command{
Name: "tree",
Usage: "Display tree view of snapshot branches",
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
client, ctx, cancel, err := commands.NewClient(ctx, cmd)
if err != nil {
return err
@@ -491,8 +482,7 @@ var infoCommand = &cli.Command{
Name: "info",
Usage: "Get info about a snapshot",
ArgsUsage: "<key>",
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
if cmd.NArg() != 1 {
return cli.ShowSubcommandHelp(cmd)
}
@@ -520,8 +510,7 @@ var setLabelCommand = &cli.Command{
Usage: "Add labels to content",
ArgsUsage: "<name> [<label>=<value> ...]",
Description: "labels snapshots in the snapshotter",
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
client, ctx, cancel, err := commands.NewClient(ctx, cmd)
if err != nil {
return err
@@ -575,8 +564,7 @@ var unpackCommand = &cli.Command{
Usage: "Synchronize the underlying filesystem containing files when unpack images, false by default",
},
}, commands.SnapshotterFlags...),
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
dgst, err := digest.Parse(cmd.Args().First())
if err != nil {
return err
+4 -3
View File
@@ -17,19 +17,20 @@
package tasks
import (
"context"
"github.com/containerd/console"
"github.com/containerd/containerd/v2/cmd/ctr/commands"
"github.com/containerd/containerd/v2/pkg/cio"
"github.com/containerd/log"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
var attachCommand = &cli.Command{
Name: "attach",
Usage: "Attach to the IO of a running container",
ArgsUsage: "CONTAINER",
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
client, ctx, cancel, err := commands.NewClient(ctx, cmd)
if err != nil {
return err
+4 -4
View File
@@ -17,13 +17,14 @@
package tasks
import (
"context"
"errors"
"fmt"
"github.com/containerd/containerd/api/types/runc/options"
containerd "github.com/containerd/containerd/v2/client"
"github.com/containerd/containerd/v2/cmd/ctr/commands"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
var checkpointCommand = &cli.Command{
@@ -44,8 +45,7 @@ var checkpointCommand = &cli.Command{
Usage: "Path to criu work files and logs",
},
},
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
id := cmd.Args().First()
if id == "" {
return errors.New("container id must be provided")
@@ -80,7 +80,7 @@ var checkpointCommand = &cli.Command{
}
// withCheckpointOpts only suitable for runc runtime now
func withCheckpointOpts(rt string, cmd *cli.Context) containerd.CheckpointTaskOpts {
func withCheckpointOpts(rt string, cmd *cli.Command) containerd.CheckpointTaskOpts {
return func(r *containerd.CheckpointTaskInfo) error {
imagePath := cmd.String("image-path")
workPath := cmd.String("work-path")
+2 -3
View File
@@ -23,7 +23,7 @@ import (
"github.com/containerd/containerd/v2/cmd/ctr/commands"
"github.com/containerd/containerd/v2/pkg/cio"
"github.com/containerd/log"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
var deleteCommand = &cli.Command{
@@ -42,8 +42,7 @@ var deleteCommand = &cli.Command{
Usage: "Process ID to kill",
},
},
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
client, ctx, cancel, err := commands.NewClient(ctx, cmd)
if err != nil {
return err
+3 -3
View File
@@ -17,6 +17,7 @@
package tasks
import (
"context"
"errors"
"io"
"net/url"
@@ -28,7 +29,7 @@ import (
"github.com/containerd/containerd/v2/pkg/cio"
"github.com/containerd/containerd/v2/pkg/oci"
"github.com/containerd/log"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
var execCommand = &cli.Command{
@@ -68,8 +69,7 @@ var execCommand = &cli.Command{
Usage: "User id or name",
},
},
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
var (
id = cmd.Args().First()
args = cmd.Args().Tail()
+2 -3
View File
@@ -27,7 +27,7 @@ import (
"github.com/containerd/log"
"github.com/containerd/typeurl/v2"
"github.com/moby/sys/signal"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
const defaultSignal = "SIGTERM"
@@ -82,8 +82,7 @@ var killCommand = &cli.Command{
Usage: "Send signal to all processes inside the container",
},
},
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
id := cmd.Args().First()
if id == "" {
return errors.New("container id must be provided")
+3 -3
View File
@@ -17,13 +17,14 @@
package tasks
import (
"context"
"fmt"
"os"
"text/tabwriter"
tasks "github.com/containerd/containerd/api/services/tasks/v1"
"github.com/containerd/containerd/v2/cmd/ctr/commands"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
var listCommand = &cli.Command{
@@ -38,8 +39,7 @@ var listCommand = &cli.Command{
Usage: "Print only the task id",
},
},
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
client, ctx, cancel, err := commands.NewClient(ctx, cmd)
if err != nil {
return err
+3 -3
View File
@@ -17,6 +17,7 @@
package tasks
import (
"context"
"encoding/json"
"errors"
"fmt"
@@ -28,7 +29,7 @@ import (
v2 "github.com/containerd/cgroups/v3/cgroup2/stats"
"github.com/containerd/containerd/v2/cmd/ctr/commands"
"github.com/containerd/typeurl/v2"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
const (
@@ -49,8 +50,7 @@ var metricsCommand = &cli.Command{
Value: formatTable,
},
},
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
client, ctx, cancel, err := commands.NewClient(ctx, cmd)
if err != nil {
return err
+4 -3
View File
@@ -17,16 +17,17 @@
package tasks
import (
"context"
"github.com/containerd/containerd/v2/cmd/ctr/commands"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
var pauseCommand = &cli.Command{
Name: "pause",
Usage: "Pause an existing container",
ArgsUsage: "CONTAINER",
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
client, ctx, cancel, err := commands.NewClient(ctx, cmd)
if err != nil {
return err
+3 -3
View File
@@ -17,6 +17,7 @@
package tasks
import (
"context"
"errors"
"fmt"
"os"
@@ -24,15 +25,14 @@ import (
"github.com/containerd/containerd/v2/cmd/ctr/commands"
"github.com/containerd/typeurl/v2"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
var psCommand = &cli.Command{
Name: "ps",
Usage: "List processes for container",
ArgsUsage: "CONTAINER",
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
id := cmd.Args().First()
if id == "" {
return errors.New("container id must be provided")
+4 -3
View File
@@ -17,16 +17,17 @@
package tasks
import (
"context"
"github.com/containerd/containerd/v2/cmd/ctr/commands"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
var resumeCommand = &cli.Command{
Name: "resume",
Usage: "Resume a paused container",
ArgsUsage: "CONTAINER",
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
client, ctx, cancel, err := commands.NewClient(ctx, cmd)
if err != nil {
return err
+3 -3
View File
@@ -17,6 +17,7 @@
package tasks
import (
"context"
"errors"
"github.com/containerd/console"
@@ -25,7 +26,7 @@ import (
"github.com/containerd/containerd/v2/pkg/cio"
"github.com/containerd/errdefs"
"github.com/containerd/log"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
var startCommand = &cli.Command{
@@ -55,8 +56,7 @@ var startCommand = &cli.Command{
Usage: "Detach from the task after it has started execution",
},
}...),
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
var (
id = cmd.Args().Get(0)
detach = cmd.Bool("detach")
+2 -2
View File
@@ -19,7 +19,7 @@ package tasks
import (
"context"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
type resizer interface {
@@ -31,7 +31,7 @@ var Command = &cli.Command{
Name: "tasks",
Usage: "Manage tasks",
Aliases: []string{"t", "task"},
Subcommands: []*cli.Command{
Commands: []*cli.Command{
attachCommand,
checkpointCommand,
deleteCommand,
+3 -3
View File
@@ -29,7 +29,7 @@ import (
containerd "github.com/containerd/containerd/v2/client"
"github.com/containerd/containerd/v2/pkg/cio"
"github.com/containerd/log"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
"golang.org/x/sys/unix"
)
@@ -115,13 +115,13 @@ func NewTask(ctx context.Context, client *containerd.Client, container container
return nil, err
}
stdinC.closer = func() {
t.CloseIO(ctx, containerd.WithStdinCloser)
_ = t.CloseIO(ctx, containerd.WithStdinCloser)
}
return t, nil
}
// GetNewTaskOpts resolves containerd.NewTaskOpts from cli.Context
func GetNewTaskOpts(cmd *cli.Context) []containerd.NewTaskOpts {
func GetNewTaskOpts(cmd *cli.Command) []containerd.NewTaskOpts {
if cmd.Bool("no-pivot") {
return []containerd.NewTaskOpts{containerd.WithNoPivotRoot}
}
+2 -2
View File
@@ -26,7 +26,7 @@ import (
containerd "github.com/containerd/containerd/v2/client"
"github.com/containerd/containerd/v2/pkg/cio"
"github.com/containerd/log"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
var platformStartFlags = []cli.Flag{}
@@ -83,6 +83,6 @@ func NewTask(ctx context.Context, client *containerd.Client, container container
}
// GetNewTaskOpts resolves containerd.NewTaskOpts from cli.Context
func GetNewTaskOpts(_ *cli.Context) []containerd.NewTaskOpts {
func GetNewTaskOpts(_ *cli.Command) []containerd.NewTaskOpts {
return nil
}
+3 -3
View File
@@ -17,20 +17,20 @@
package version
import (
"context"
"fmt"
"os"
"github.com/containerd/containerd/v2/cmd/ctr/commands"
"github.com/containerd/containerd/v2/version"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
// Command is a cli command to output the client and containerd server version
var Command = &cli.Command{
Name: "version",
Usage: "Print the client and server versions",
Action: func(cmd *cli.Context) error {
ctx := cmd.Context
Action: func(ctx context.Context, cmd *cli.Command) error {
if cmd.NArg() != 0 {
return fmt.Errorf("extra arguments: %v", cmd.Args())
}
+1 -1
View File
@@ -26,7 +26,7 @@ import (
func main() {
ctx := context.Background()
if err := app.New().RunContext(ctx, os.Args); err != nil {
if err := app.New().Run(ctx, os.Args); err != nil {
_, _ = fmt.Fprintln(os.Stderr, "ctr:", err)
os.Exit(1)
}
+4 -3
View File
@@ -25,7 +25,8 @@ import (
"github.com/containerd/containerd/v2/cmd/containerd/command"
"github.com/containerd/containerd/v2/cmd/ctr/app"
"github.com/urfave/cli/v2"
docs "github.com/urfave/cli-docs/v3"
"github.com/urfave/cli/v3"
)
func main() {
@@ -37,7 +38,7 @@ func main() {
func run() error {
flag.Parse()
apps := map[string]*cli.App{
apps := map[string]*cli.Command{
"containerd": command.App(),
"ctr": app.New(),
}
@@ -55,7 +56,7 @@ func run() error {
// clear out the usage as we use banners that do not display in man pages
appName.Usage = ""
data, err := appName.ToMan()
data, err := docs.ToMan(appName)
if err != nil {
return err
}
+2 -2
View File
@@ -62,7 +62,8 @@ require (
github.com/sirupsen/logrus v1.10.2
github.com/stretchr/testify v1.12.1
github.com/tchap/go-patricia/v2 v2.3.3
github.com/urfave/cli/v2 v2.27.7
github.com/urfave/cli-docs/v3 v3.1.0
github.com/urfave/cli/v3 v3.11.0
github.com/vishvananda/netlink v1.3.1
github.com/vishvananda/netns v0.0.5
go.etcd.io/bbolt v1.5.0
@@ -136,7 +137,6 @@ require (
github.com/stefanberger/go-pkcs11uri v0.0.0-20230803200340-78284954bff6 // indirect
github.com/tetratelabs/wazero v1.11.0 // indirect
github.com/x448/float16 v0.8.4 // indirect
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
go.opentelemetry.io/otel/metric v1.46.0 // indirect
go.opentelemetry.io/proto/otlp v1.11.0 // indirect
+4 -4
View File
@@ -278,16 +278,16 @@ github.com/tchap/go-patricia/v2 v2.3.3 h1:xfNEsODumaEcCcY3gI0hYPZ/PcpVv5ju6RMAhg
github.com/tchap/go-patricia/v2 v2.3.3/go.mod h1:VZRHKAb53DLaG+nA9EaYYiaEx6YztwDlLElMsnSHD4k=
github.com/tetratelabs/wazero v1.11.0 h1:+gKemEuKCTevU4d7ZTzlsvgd1uaToIDtlQlmNbwqYhA=
github.com/tetratelabs/wazero v1.11.0/go.mod h1:eV28rsN8Q+xwjogd7f4/Pp4xFxO7uOGbLcD/LzB1wiU=
github.com/urfave/cli/v2 v2.27.7 h1:bH59vdhbjLv3LAvIu6gd0usJHgoTTPhCFib8qqOwXYU=
github.com/urfave/cli/v2 v2.27.7/go.mod h1:CyNAG/xg+iAOg0N4MPGZqVmv2rCoP267496AOXUZjA4=
github.com/urfave/cli-docs/v3 v3.1.0 h1:Sa5xm19IpE5gpm6tZzXdfjdFxn67PnEsE4dpXF7vsKw=
github.com/urfave/cli-docs/v3 v3.1.0/go.mod h1:59d+5Hz1h6GSGJ10cvcEkbIe3j233t4XDqI72UIx7to=
github.com/urfave/cli/v3 v3.11.0 h1:P/euJp99kb9p0tlVY+iYTLYYTAQlfl0hR2gUO1Img1Q=
github.com/urfave/cli/v3 v3.11.0/go.mod h1:ysVLtOEmg2tOy6PknnYVhDoouyC/6N42TMeoMzskhso=
github.com/vishvananda/netlink v1.3.1 h1:3AEMt62VKqz90r0tmNhog0r/PpWKmrEShJU0wJW6bV0=
github.com/vishvananda/netlink v1.3.1/go.mod h1:ARtKouGSTGchR8aMwmkzC0qiNPrrWO5JS/XMVl45+b4=
github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY=
github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4=
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.etcd.io/bbolt v1.5.0 h1:S7GAl7Fxv12yohbwFfIbQCGDWbQbtDGPET4P/bD4lxU=
go.etcd.io/bbolt v1.5.0/go.mod h1:mkltfYE5aUHQxUct9N9V+Kp7aSjFqjgrhcXIS70Lrdk=
+1
View File
@@ -0,0 +1 @@
*.gotmpl text eol=lf
@@ -4,11 +4,8 @@
.*envrc
.envrc
.idea
# goimports is installed here if not available
/.local/
/site/
coverage.txt
internal/*/built-example
vendor
/cmd/urfave-cli-genflags/urfave-cli-genflags
*.exe
@@ -1,4 +1,5 @@
# https://golangci-lint.run/usage/configuration/
linters:
enable:
- makezero
- misspell
@@ -1,6 +1,6 @@
MIT License
Copyright (c) 2022 urfave/cli maintainers
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
+14
View File
@@ -0,0 +1,14 @@
.PHONY: all
all: vet test show-cover
.PHONY: vet
vet:
go vet -v ./...
.PHONY: test
test:
go test -v -cover -coverprofile=coverage.txt ./...
.PHONY: show-cover
show-cover:
go tool cover -func=coverage.txt
+84
View File
@@ -0,0 +1,84 @@
# Welcome to urfave/cli-docs/v3
[![Run Tests](https://github.com/urfave/cli-docs/actions/workflows/main.yml/badge.svg)](https://github.com/urfave/cli-docs/actions/workflows/main.yml)
[![Go Reference](https://pkg.go.dev/badge/github.com/urfave/cli-docs/v3.svg)](https://pkg.go.dev/github.com/urfave/cli-docs/v3)
[![Go Report Card](https://goreportcard.com/badge/github.com/urfave/cli-docs/v3)](https://goreportcard.com/report/github.com/urfave/cli-docs/v3)
urfave/cli-docs/v3 is an extended documentation library for use with urfave/cli/v3.
## Start using
1. Add the dependency to your project
```sh
go get github.com/urfave/cli-docs/v3@latest
```
2. Add it as import
```diff
import (
+ docs "github.com/urfave/cli-docs/v3"
)
```
3. Now use it e.g. to generate markdown document from a command
```go
package main
import (
"context"
"fmt"
"os"
docs "github.com/urfave/cli-docs/v3"
cli "github.com/urfave/cli/v3"
)
func main() {
app := &cli.Command{
Name: "greet",
Usage: "say a greeting",
Action: func(ctx context.Context, c *cli.Command) error {
fmt.Println("Greetings")
return nil
},
}
md, err := docs.ToMarkdown(app)
if err != nil {
panic(err)
}
fi, err := os.Create("cli-docs.md")
if err != nil {
panic(err)
}
defer fi.Close()
if _, err := fi.WriteString("# CLI\n\n" + md); err != nil {
panic(err)
}
}
```
This will create a file `cli-docs.md` with content:
````md
# CLI
# NAME
greet - say a greeting
# SYNOPSIS
greet
**Usage**:
```
greet [GLOBAL OPTIONS] [command [COMMAND OPTIONS]] [ARGUMENTS...]
```
````
+590
View File
@@ -0,0 +1,590 @@
package docs
import (
"bytes"
_ "embed"
"fmt"
"io"
"os"
"reflect"
"regexp"
"runtime"
"sort"
"strconv"
"strings"
"text/template"
"unicode/utf8"
"github.com/cpuguy83/go-md2man/v2/md2man"
"github.com/urfave/cli/v3"
)
var (
//go:embed markdown.md.gotmpl
MarkdownDocTemplate string
//go:embed markdown_tabular.md.gotmpl
MarkdownTabularDocTemplate string
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...,
)
}
// ToTabularMarkdown creates a tabular markdown documentation for
// the `*cli.Command`. The function errors if either parsing or
// writing of the string fails.
func ToTabularMarkdown(cmd *cli.Command, appPath string) (string, error) {
if appPath == "" {
appPath = "app"
}
const name = "cli"
t, err := template.New(name).Funcs(template.FuncMap{
"join": strings.Join,
}).Parse(MarkdownTabularDocTemplate)
if err != nil {
return "", err
}
var (
w bytes.Buffer
tt tabularTemplate
)
if err = t.ExecuteTemplate(&w, name, cliTabularAppTemplate{
AppPath: appPath,
Name: cmd.Name,
Description: tt.PrepareMultilineString(cmd.Description),
Usage: tt.PrepareMultilineString(cmd.Usage),
UsageText: strings.FieldsFunc(cmd.UsageText, func(r rune) bool { return r == '\n' }),
ArgsUsage: tt.PrepareMultilineString(cmd.ArgsUsage),
GlobalFlags: tt.PrepareFlags(cmd.VisibleFlags()),
Commands: tt.PrepareCommands(cmd.VisibleCommands(), appPath, "", 0),
}); err != nil {
return "", err
}
return tt.Prettify(w.String()), nil
}
// ToTabularToFileBetweenTags creates a tabular markdown documentation for the `*App` and updates the file between
// the tags in the file. The function errors if either parsing or writing of the string fails.
func ToTabularToFileBetweenTags(cmd *cli.Command, appPath, filePath string, startEndTags ...string) error {
var start, end = "<!--GENERATED:CLI_DOCS-->", "<!--/GENERATED:CLI_DOCS-->" // default tags
if len(startEndTags) == 2 {
start, end = startEndTags[0], startEndTags[1]
}
// read original file content
content, err := os.ReadFile(filePath)
if err != nil {
return err
}
// generate markdown
md, err := ToTabularMarkdown(cmd, appPath)
if err != nil {
return err
}
// prepare regexp to replace content between start and end tags
re, err := regexp.Compile("(?s)" + regexp.QuoteMeta(start) + "(.*?)" + regexp.QuoteMeta(end))
if err != nil {
return err
}
const comment = "<!-- Documentation inside this block generated by github.com/urfave/cli-docs/v3; DO NOT EDIT -->"
// replace content between start and end tags
updated := re.ReplaceAll(content, []byte(strings.Join([]string{start, comment, md, end}, "\n")))
// write updated content to file
if err = os.WriteFile(filePath, updated, 0664); err != nil {
return err
}
return nil
}
// ToMarkdown creates a markdown string for the `*cli.Command`
// The function errors if either parsing or writing of the string fails.
func ToMarkdown(cmd *cli.Command) (string, error) {
var w bytes.Buffer
if err := writeDocTemplate(cmd, &w, 0); err != nil {
return "", err
}
return w.String(), nil
}
// ToMan creates a man page string with section number for the
// `*cli.Command` The function errors if either parsing or writing
// of the string fails.
func ToManWithSection(cmd *cli.Command, sectionNumber int) (string, error) {
var w bytes.Buffer
if err := writeDocTemplate(cmd, &w, sectionNumber); err != nil {
return "", err
}
man := md2man.Render(w.Bytes())
return string(man), nil
}
// ToMan creates a man page string for the `*cli.Command`
// The function errors if either parsing or writing of the string fails.
func ToMan(cmd *cli.Command) (string, error) {
man, err := ToManWithSection(cmd, 8)
return man, err
}
type cliCommandTemplate struct {
Command *cli.Command
SectionNum int
Commands []string
GlobalArgs []string
SynopsisArgs []string
}
func writeDocTemplate(cmd *cli.Command, w io.Writer, sectionNum int) error {
tracef("using MarkdownDocTemplate starting %[1]q", string([]byte(MarkdownDocTemplate)[0:8]))
const name = "cli"
t, err := template.New(name).Parse(MarkdownDocTemplate)
if err != nil {
return err
}
return t.ExecuteTemplate(w, name, &cliCommandTemplate{
Command: cmd,
SectionNum: sectionNum,
Commands: prepareCommands(cmd.Commands, 0),
GlobalArgs: prepareArgsWithValues(cmd.VisibleFlags()),
SynopsisArgs: prepareArgsSynopsis(cmd.VisibleFlags()),
})
}
func prepareCommands(commands []*cli.Command, level int) []string {
var coms []string
for _, command := range commands {
if command.Hidden {
continue
}
usageText := prepareUsageText(command)
usage := prepareUsage(command, usageText)
prepared := fmt.Sprintf("%s %s\n\n%s%s",
strings.Repeat("#", level+2),
strings.Join(command.Names(), ", "),
usage,
usageText,
)
flags := prepareArgsWithValues(command.VisibleFlags())
if len(flags) > 0 {
prepared += fmt.Sprintf("\n%s", strings.Join(flags, "\n"))
}
coms = append(coms, prepared)
// recursively iterate subcommands
if len(command.Commands) > 0 {
coms = append(
coms,
prepareCommands(command.Commands, level+1)...,
)
}
}
return coms
}
func prepareArgsWithValues(flags []cli.Flag) []string {
return prepareFlags(flags, ", ", "**", "**", `""`, true)
}
func prepareArgsSynopsis(flags []cli.Flag) []string {
return prepareFlags(flags, "|", "[", "]", "[value]", false)
}
func prepareFlags(
flags []cli.Flag,
sep, opener, closer, value string,
addDetails bool,
) []string {
args := []string{}
for _, f := range flags {
flag, ok := f.(cli.DocGenerationFlag)
if !ok {
continue
}
modifiedArg := opener
for _, s := range f.Names() {
trimmed := strings.TrimSpace(s)
if len(modifiedArg) > len(opener) {
modifiedArg += sep
}
if len(trimmed) > 1 {
modifiedArg += fmt.Sprintf("--%s", trimmed)
} else {
modifiedArg += fmt.Sprintf("-%s", trimmed)
}
}
modifiedArg += closer
if flag.TakesValue() {
modifiedArg += fmt.Sprintf("=%s", value)
}
if addDetails {
modifiedArg += flagDetails(flag)
}
args = append(args, modifiedArg+"\n")
}
sort.Strings(args)
return args
}
// flagDetails returns a string containing the flags metadata
func flagDetails(flag cli.DocGenerationFlag) string {
description := flag.GetUsage()
value := getFlagDefaultValue(flag)
if value != "" {
description += " (default: " + value + ")"
}
return ": " + description
}
func prepareUsageText(command *cli.Command) string {
if command.UsageText == "" {
return ""
}
// Remove leading and trailing newlines
preparedUsageText := strings.Trim(command.UsageText, "\n")
var usageText string
if strings.Contains(preparedUsageText, "\n") {
// Format multi-line string as a code block using the 4 space schema to allow for embedded markdown such
// that it will not break the continuous code block.
for _, ln := range strings.Split(preparedUsageText, "\n") {
usageText += fmt.Sprintf(" %s\n", ln)
}
} else {
// Style a single line as a note
usageText = fmt.Sprintf(">%s\n", preparedUsageText)
}
return usageText
}
func prepareUsage(command *cli.Command, usageText string) string {
if command.Usage == "" {
return ""
}
usage := command.Usage + "\n"
// Add a newline to the Usage IFF there is a UsageText
if usageText != "" {
usage += "\n"
}
return usage
}
type (
cliTabularAppTemplate struct {
AppPath string
Name string
Usage string
ArgsUsage string
UsageText []string
Description string
GlobalFlags []cliTabularFlagTemplate
Commands []cliTabularCommandTemplate
}
cliTabularCommandTemplate struct {
AppPath string
Name string
Aliases []string
Usage string
ArgsUsage string
UsageText []string
Description string
Category string
Flags []cliTabularFlagTemplate
SubCommands []cliTabularCommandTemplate
Level uint
}
cliTabularFlagTemplate struct {
Name string
Aliases []string
Usage string
TakesValue bool
Default string
EnvVars []string
Type string
}
)
// tabularTemplate is a struct for the tabular template preparation.
type tabularTemplate struct{}
// PrepareCommands converts CLI commands into a structs for the rendering.
func (tt tabularTemplate) PrepareCommands(commands []*cli.Command, appPath, parentCommandName string, level uint) []cliTabularCommandTemplate {
var result = make([]cliTabularCommandTemplate, 0, len(commands))
for _, cmd := range commands {
var command = cliTabularCommandTemplate{
AppPath: appPath,
Name: strings.TrimSpace(strings.Join([]string{parentCommandName, cmd.Name}, " ")),
Aliases: cmd.Aliases,
Usage: tt.PrepareMultilineString(cmd.Usage),
UsageText: strings.FieldsFunc(cmd.UsageText, func(r rune) bool { return r == '\n' }),
ArgsUsage: tt.PrepareMultilineString(cmd.ArgsUsage),
Description: tt.PrepareMultilineString(cmd.Description),
Category: cmd.Category,
Flags: tt.PrepareFlags(cmd.VisibleFlags()),
SubCommands: tt.PrepareCommands( // note: recursive call
cmd.Commands,
appPath,
strings.Join([]string{parentCommandName, cmd.Name}, " "),
level+1,
),
Level: level,
}
result = append(result, command)
}
return result
}
// PrepareFlags converts CLI flags into a structs for the rendering.
func (tt tabularTemplate) PrepareFlags(flags []cli.Flag) []cliTabularFlagTemplate {
var result = make([]cliTabularFlagTemplate, 0, len(flags))
for _, appFlag := range flags {
flag, ok := appFlag.(cli.DocGenerationFlag)
if !ok {
continue
}
var f = cliTabularFlagTemplate{
Usage: tt.PrepareMultilineString(flag.GetUsage()),
EnvVars: flag.GetEnvVars(),
TakesValue: flag.TakesValue(),
Default: getFlagDefaultValue(flag),
Type: flag.TypeName(),
}
if boolFlag, isBool := appFlag.(*cli.BoolFlag); isBool {
f.Default = strconv.FormatBool(boolFlag.Value)
}
for i, name := range appFlag.Names() {
name = strings.TrimSpace(name)
if i == 0 {
f.Name = "--" + name
continue
}
if len(name) > 1 {
name = "--" + name
} else {
name = "-" + name
}
f.Aliases = append(f.Aliases, name)
}
result = append(result, f)
}
return result
}
// PrepareMultilineString prepares a string (removes line breaks).
func (tabularTemplate) PrepareMultilineString(s string) string {
return strings.TrimRight(
strings.TrimSpace(
strings.ReplaceAll(s, "\n", " "),
),
".\r\n\t",
)
}
func (tabularTemplate) Prettify(s string) string {
var max = func(x, y int) int {
if x > y {
return x
}
return y
}
var b strings.Builder
// search for tables
for _, rawTable := range regexp.MustCompile(`(?m)^(\|[^\n]+\|\r?\n)((?:\|:?-+:?)+\|)(\n(?:\|[^\n]+\|\r?\n?)*)?$`).FindAllString(s, -1) {
var lines = strings.FieldsFunc(rawTable, func(r rune) bool { return r == '\n' })
if len(lines) < 3 { // header, separator, body
continue
}
// parse table into the matrix
var matrix = make([][]string, 0, len(lines))
for _, line := range lines {
items := strings.FieldsFunc(strings.Trim(line, "| "), func(r rune) bool { return r == '|' })
for i := range items {
items[i] = strings.TrimSpace(items[i]) // trim spaces in cells
}
matrix = append(matrix, items)
}
// determine centered columns
var centered = make([]bool, 0, len(matrix[1]))
for _, cell := range matrix[1] {
centered = append(centered, strings.HasPrefix(cell, ":") && strings.HasSuffix(cell, ":"))
}
// calculate max lengths
var lengths = make([]int, len(matrix[0]))
for n, row := range matrix {
for i, cell := range row {
if n == 1 {
continue // skip separator
}
if l := utf8.RuneCountInString(cell); l > lengths[i] {
lengths[i] = l
}
}
}
// format cells
for i, row := range matrix {
for j, cell := range row {
if i == 1 { // is separator
if centered[j] {
b.Reset()
b.WriteRune(':')
b.WriteString(strings.Repeat("-", max(0, lengths[j])))
b.WriteRune(':')
row[j] = b.String()
} else {
row[j] = strings.Repeat("-", max(0, lengths[j]+2))
}
continue
}
var (
cellWidth = utf8.RuneCountInString(cell)
padLeft, padRight = 1, max(1, lengths[j]-cellWidth+1) // align to the left
)
if centered[j] { // is centered
padLeft = max(1, (lengths[j]-cellWidth)/2)
padRight = max(1, lengths[j]-cellWidth-(padLeft-1))
}
b.Reset()
b.WriteString(strings.Repeat(" ", padLeft))
if padLeft+cellWidth+padRight <= lengths[j]+1 {
b.WriteRune(' ') // add an extra space if the cell is not full
}
b.WriteString(cell)
b.WriteString(strings.Repeat(" ", padRight))
row[j] = b.String()
}
}
b.Reset()
for _, row := range matrix { // build new table
b.WriteRune('|')
b.WriteString(strings.Join(row, "|"))
b.WriteRune('|')
b.WriteRune('\n')
}
s = strings.Replace(s, rawTable, b.String(), 1)
}
s = regexp.MustCompile(`\n{2,}`).ReplaceAllString(s, "\n\n") // normalize newlines
s = strings.Trim(s, " \n") // trim spaces and newlines
return s + "\n" // add an extra newline
}
// getFlagDefaultValue returns the default value of a flag. Previously, the [cli.DocGenerationFlag] interface included
// a GetValue string method, but it was removed in https://github.com/urfave/cli/pull/1988.
// This function serves as a workaround, attempting to retrieve the value using the removed method; if that fails, it
// tries to obtain it via reflection (the [cli.FlagBase] still has a Value field).
func getFlagDefaultValue(f cli.DocGenerationFlag) string {
if !f.TakesValue() {
return ""
}
if v, ok := f.(interface{ GetValue() string }); ok {
return v.GetValue()
}
var ref = reflect.ValueOf(f)
if ref.Kind() != reflect.Ptr {
return ""
} else {
ref = ref.Elem()
}
if ref.Kind() != reflect.Struct {
return ""
}
if val := ref.FieldByName("Value"); val.IsValid() && val.Type().Kind() != reflect.Bool {
return fmt.Sprintf("%v", val.Interface())
}
return ""
}
+32
View File
@@ -0,0 +1,32 @@
{{if gt .SectionNum 0}}% {{ .Command.Name }} {{ .SectionNum }}
{{end}}# NAME
{{ .Command.Name }}{{ if .Command.Usage }} - {{ .Command.Usage }}{{ end }}
# SYNOPSIS
{{ .Command.Name }}
{{ if .SynopsisArgs }}
```
{{ range $v := .SynopsisArgs }}{{ $v }}{{ end }}```
{{ end }}{{ if .Command.Description }}
# DESCRIPTION
{{ .Command.Description }}
{{ end }}
**Usage**:
```{{ if .Command.UsageText }}
{{ .Command.UsageText }}
{{ else }}
{{ .Command.Name }} [GLOBAL OPTIONS] [command [COMMAND OPTIONS]] [ARGUMENTS...]
{{ end }}```
{{ if .GlobalArgs }}
# GLOBAL OPTIONS
{{ range $v := .GlobalArgs }}
{{ $v }}{{ end }}
{{ end }}{{ if .Commands }}
# COMMANDS
{{ range $v := .Commands }}
{{ $v }}{{ end }}{{ end -}}
+72
View File
@@ -0,0 +1,72 @@
{{ define "flags" }}
| Name | Description | Type | Default value | Environment variables |
|------|-------------|------|:-------------:|:---------------------:|
{{ range $flag := . -}}
{{- /**/ -}} | `{{ $flag.Name }}{{ if $flag.TakesValue }}="…"{{ end }}` {{ if $flag.Aliases }}(`{{ join $flag.Aliases "`, `" }}`) {{ end }}
{{- /**/ -}} | {{ $flag.Usage }}
{{- /**/ -}} | {{ $flag.Type }}
{{- /**/ -}} | {{ if $flag.Default }}`{{ $flag.Default }}`{{ end }}
{{- /**/ -}} | {{ if $flag.EnvVars }}`{{ join $flag.EnvVars "`, `" }}`{{ else }}*none*{{ end }}
{{- /**/ -}} |
{{ end }}
{{ end }}
{{ define "command" }}
### `{{ .Name }}` {{ if gt .Level 0 }}sub{{ end }}command{{ if .Aliases }} (aliases: `{{ join .Aliases "`, `" }}`){{ end }}
{{ if .Usage }}
{{ .Usage }}.
{{ end }}
{{ if .UsageText }}
{{ range $line := .UsageText -}}
> {{ $line }}
{{ end -}}
{{ end }}
{{ if .Description }}
{{ .Description }}.
{{ end }}
Usage:
```bash
$ {{ .AppPath }} [GLOBAL FLAGS] {{ .Name }}{{ if .Flags }} [COMMAND FLAGS]{{ end }} {{ if .ArgsUsage }}{{ .ArgsUsage }}{{ else }}[ARGUMENTS...]{{ end }}
```
{{ if .Flags -}}
The following flags are supported:
{{ template "flags" .Flags }}
{{ end -}}
{{ if .SubCommands -}}
{{ range $subCmd := .SubCommands -}}
{{ template "command" $subCmd }}
{{ end -}}
{{ end -}}
{{ end }}
## CLI interface{{ if .Name }} - {{ .Name }}{{ end }}
{{ if .Description }}{{ .Description }}.
{{ end }}
{{ if .Usage }}{{ .Usage }}.
{{ end }}
{{ if .UsageText }}
{{ range $line := .UsageText -}}
> {{ $line }}
{{ end -}}
{{ end }}
Usage:
```bash
$ {{ .AppPath }}{{ if .GlobalFlags }} [GLOBAL FLAGS]{{ end }} [COMMAND] [COMMAND FLAGS] {{ if .ArgsUsage }}{{ .ArgsUsage }}{{ else }}[ARGUMENTS...]{{ end }}
```
{{ if .GlobalFlags }}
Global flags:
{{ template "flags" .GlobalFlags }}
{{ end -}}
{{ if .Commands -}}
{{ range $cmd := .Commands -}}
{{ template "command" $cmd }}
{{ end }}
{{- end }}
-2
View File
@@ -1,2 +0,0 @@
[flake8]
max-line-length = 120
-19
View File
@@ -1,19 +0,0 @@
# cli
[![Run Tests](https://github.com/urfave/cli/actions/workflows/cli.yml/badge.svg?branch=v2-maint)](https://github.com/urfave/cli/actions/workflows/cli.yml)
[![Go Reference](https://pkg.go.dev/badge/github.com/urfave/cli/v2.svg)](https://pkg.go.dev/github.com/urfave/cli/v2)
[![Go Report Card](https://goreportcard.com/badge/github.com/urfave/cli/v2)](https://goreportcard.com/report/github.com/urfave/cli/v2)
[![codecov](https://codecov.io/gh/urfave/cli/branch/v2-maint/graph/badge.svg?token=t9YGWLh05g)](https://app.codecov.io/gh/urfave/cli/tree/v2-maint)
cli is a simple, fast, and fun package for building command line apps in Go. The
goal is to enable developers to write fast and distributable command line
applications in an expressive way.
## Documentation
More documentation is available in [`./docs`](./docs) or the hosted
documentation site at <https://cli.urfave.org>.
## License
See [`LICENSE`](./LICENSE)
-536
View File
@@ -1,536 +0,0 @@
package cli
import (
"context"
"flag"
"fmt"
"io"
"os"
"path/filepath"
"sort"
"strings"
"time"
)
const suggestDidYouMeanTemplate = "Did you mean %q?"
var (
changeLogURL = "https://github.com/urfave/cli/blob/main/docs/CHANGELOG.md"
appActionDeprecationURL = fmt.Sprintf("%s#deprecated-cli-app-action-signature", changeLogURL)
contactSysadmin = "This is an error in the application. Please contact the distributor of this application if this is not you."
errInvalidActionType = NewExitError("ERROR invalid Action type. "+
fmt.Sprintf("Must be `func(*Context`)` or `func(*Context) error). %s", contactSysadmin)+
fmt.Sprintf("See %s", appActionDeprecationURL), 2)
ignoreFlagPrefix = "test." // this is to ignore test flags when adding flags from other packages
SuggestFlag SuggestFlagFunc = nil // initialized in suggestions.go unless built with urfave_cli_no_suggest
SuggestCommand SuggestCommandFunc = nil // initialized in suggestions.go unless built with urfave_cli_no_suggest
SuggestDidYouMeanTemplate string = suggestDidYouMeanTemplate
)
// App is the main structure of a cli application. It is recommended that
// an app be created with the cli.NewApp() function
type App struct {
// The name of the program. Defaults to path.Base(os.Args[0])
Name string
// Full name of command for help, defaults to Name
HelpName string
// Description of the program.
Usage string
// Text to override the USAGE section of help
UsageText string
// Whether this command supports arguments
Args bool
// Description of the program argument format.
ArgsUsage string
// Version of the program
Version string
// Description of the program
Description string
// DefaultCommand is the (optional) name of a command
// to run if no command names are passed as CLI arguments.
DefaultCommand string
// List of commands to execute
Commands []*Command
// List of flags to parse
Flags []Flag
// Boolean to enable bash completion commands
EnableBashCompletion bool
// Boolean to hide built-in help command and help flag
HideHelp bool
// Boolean to hide built-in help command but keep help flag.
// Ignored if HideHelp is true.
HideHelpCommand bool
// Boolean to hide built-in version flag and the VERSION section of help
HideVersion bool
// categories contains the categorized commands and is populated on app startup
categories CommandCategories
// flagCategories contains the categorized flags and is populated on app startup
flagCategories FlagCategories
// An action to execute when the shell completion flag is set
BashComplete BashCompleteFunc
// An action to execute before any subcommands are run, but after the context is ready
// If a non-nil error is returned, no subcommands are run
Before BeforeFunc
// An action to execute after any subcommands are run, but after the subcommand has finished
// It is run even if Action() panics
After AfterFunc
// The action to execute when no subcommands are specified
Action ActionFunc
// Execute this function if the proper command cannot be found
CommandNotFound CommandNotFoundFunc
// Execute this function if a usage error occurs
OnUsageError OnUsageErrorFunc
// Execute this function when an invalid flag is accessed from the context
InvalidFlagAccessHandler InvalidFlagAccessFunc
// Compilation date
Compiled time.Time
// List of all authors who contributed
Authors []*Author
// Copyright of the binary if any
Copyright string
// Reader reader to write input to (useful for tests)
Reader io.Reader
// Writer writer to write output to
Writer io.Writer
// ErrWriter writes error output
ErrWriter io.Writer
// ExitErrHandler processes any error encountered while running an App before
// it is returned to the caller. If no function is provided, HandleExitCoder
// is used as the default behavior.
ExitErrHandler ExitErrHandlerFunc
// Other custom info
Metadata map[string]interface{}
// Carries a function which returns app specific info.
ExtraInfo func() map[string]string
// CustomAppHelpTemplate the text template for app help topic.
// cli.go uses text/template to render templates. You can
// render custom help text by setting this variable.
CustomAppHelpTemplate string
// SliceFlagSeparator is used to customize the separator for SliceFlag, the default is ","
SliceFlagSeparator string
// DisableSliceFlagSeparator is used to disable SliceFlagSeparator, the default is false
DisableSliceFlagSeparator bool
// Boolean to enable short-option handling so user can combine several
// single-character bool arguments into one
// i.e. foobar -o -v -> foobar -ov
UseShortOptionHandling bool
// Enable suggestions for commands and flags
Suggest bool
// Allows global flags set by libraries which use flag.XXXVar(...) directly
// to be parsed through this library
AllowExtFlags bool
// Treat all flags as normal arguments if true
SkipFlagParsing bool
didSetup bool
separator separatorSpec
rootCommand *Command
}
type SuggestFlagFunc func(flags []Flag, provided string, hideHelp bool) string
type SuggestCommandFunc func(commands []*Command, provided string) string
// Tries to find out when this binary was compiled.
// Returns the current time if it fails to find it.
func compileTime() time.Time {
info, err := os.Stat(os.Args[0])
if err != nil {
return time.Now()
}
return info.ModTime()
}
// NewApp creates a new cli Application with some reasonable defaults for Name,
// Usage, Version and Action.
func NewApp() *App {
return &App{
Name: filepath.Base(os.Args[0]),
Usage: "A new cli application",
UsageText: "",
BashComplete: DefaultAppComplete,
Action: helpCommand.Action,
Compiled: compileTime(),
Reader: os.Stdin,
Writer: os.Stdout,
ErrWriter: os.Stderr,
}
}
// Setup runs initialization code to ensure all data structures are ready for
// `Run` or inspection prior to `Run`. It is internally called by `Run`, but
// will return early if setup has already happened.
func (a *App) Setup() {
if a.didSetup {
return
}
a.didSetup = true
if a.Name == "" {
a.Name = filepath.Base(os.Args[0])
}
if a.HelpName == "" {
a.HelpName = a.Name
}
if a.Usage == "" {
a.Usage = "A new cli application"
}
if a.Version == "" {
a.HideVersion = true
}
if a.BashComplete == nil {
a.BashComplete = DefaultAppComplete
}
if a.Action == nil {
a.Action = helpCommand.Action
}
if a.Compiled == (time.Time{}) {
a.Compiled = compileTime()
}
if a.Reader == nil {
a.Reader = os.Stdin
}
if a.Writer == nil {
a.Writer = os.Stdout
}
if a.ErrWriter == nil {
a.ErrWriter = os.Stderr
}
if a.AllowExtFlags {
// add global flags added by other packages
flag.VisitAll(func(f *flag.Flag) {
// skip test flags
if !strings.HasPrefix(f.Name, ignoreFlagPrefix) {
a.Flags = append(a.Flags, &extFlag{f})
}
})
}
if len(a.SliceFlagSeparator) != 0 {
a.separator.customized = true
a.separator.sep = a.SliceFlagSeparator
}
if a.DisableSliceFlagSeparator {
a.separator.customized = true
a.separator.disabled = true
}
for _, c := range a.Commands {
cname := c.Name
if c.HelpName != "" {
cname = c.HelpName
}
c.separator = a.separator
c.HelpName = fmt.Sprintf("%s %s", a.HelpName, cname)
c.flagCategories = newFlagCategoriesFromFlags(c.Flags)
}
if a.Command(helpCommand.Name) == nil && !a.HideHelp {
if !a.HideHelpCommand {
a.appendCommand(helpCommand)
}
if HelpFlag != nil {
a.appendFlag(HelpFlag)
}
}
if !a.HideVersion {
a.appendFlag(VersionFlag)
}
a.categories = newCommandCategories()
for _, command := range a.Commands {
a.categories.AddCommand(command.Category, command)
}
sort.Sort(a.categories.(*commandCategories))
a.flagCategories = newFlagCategoriesFromFlags(a.Flags)
if a.Metadata == nil {
a.Metadata = make(map[string]interface{})
}
}
func (a *App) newRootCommand() *Command {
return &Command{
Name: a.Name,
Usage: a.Usage,
UsageText: a.UsageText,
Description: a.Description,
ArgsUsage: a.ArgsUsage,
BashComplete: a.BashComplete,
Before: a.Before,
After: a.After,
Action: a.Action,
OnUsageError: a.OnUsageError,
Subcommands: a.Commands,
Flags: a.Flags,
flagCategories: a.flagCategories,
HideHelp: a.HideHelp,
HideHelpCommand: a.HideHelpCommand,
UseShortOptionHandling: a.UseShortOptionHandling,
HelpName: a.HelpName,
CustomHelpTemplate: a.CustomAppHelpTemplate,
categories: a.categories,
SkipFlagParsing: a.SkipFlagParsing,
isRoot: true,
separator: a.separator,
}
}
func (a *App) newFlagSet() (*flag.FlagSet, error) {
return flagSet(a.Name, a.Flags, a.separator)
}
func (a *App) useShortOptionHandling() bool {
return a.UseShortOptionHandling
}
// Run is the entry point to the cli app. Parses the arguments slice and routes
// to the proper flag/args combination
func (a *App) Run(arguments []string) (err error) {
return a.RunContext(context.Background(), arguments)
}
// RunContext is like Run except it takes a Context that will be
// passed to its commands and sub-commands. Through this, you can
// propagate timeouts and cancellation requests
func (a *App) RunContext(ctx context.Context, arguments []string) (err error) {
a.Setup()
// handle the completion flag separately from the flagset since
// completion could be attempted after a flag, but before its value was put
// on the command line. this causes the flagset to interpret the completion
// flag name as the value of the flag before it which is undesirable
// note that we can only do this because the shell autocomplete function
// always appends the completion flag at the end of the command
shellComplete, arguments := checkShellCompleteFlag(a, arguments)
cCtx := NewContext(a, nil, &Context{Context: ctx})
cCtx.shellComplete = shellComplete
a.rootCommand = a.newRootCommand()
cCtx.Command = a.rootCommand
if err := checkDuplicatedCmds(a.rootCommand); err != nil {
return err
}
return a.rootCommand.Run(cCtx, arguments...)
}
// RunAsSubcommand is for legacy/compatibility purposes only. New code should only
// use App.RunContext. This function is slated to be removed in v3.
func (a *App) RunAsSubcommand(ctx *Context) (err error) {
a.Setup()
cCtx := NewContext(a, nil, ctx)
cCtx.shellComplete = ctx.shellComplete
a.rootCommand = a.newRootCommand()
cCtx.Command = a.rootCommand
return a.rootCommand.Run(cCtx, ctx.Args().Slice()...)
}
func (a *App) suggestFlagFromError(err error, command string) (string, error) {
flag, parseErr := flagFromError(err)
if parseErr != nil {
return "", err
}
flags := a.Flags
hideHelp := a.HideHelp
if command != "" {
cmd := a.Command(command)
if cmd == nil {
return "", err
}
flags = cmd.Flags
hideHelp = hideHelp || cmd.HideHelp
}
if SuggestFlag == nil {
return "", err
}
suggestion := SuggestFlag(flags, flag, hideHelp)
if len(suggestion) == 0 {
return "", err
}
return fmt.Sprintf(SuggestDidYouMeanTemplate+"\n\n", suggestion), nil
}
// RunAndExitOnError calls .Run() and exits non-zero if an error was returned
//
// Deprecated: instead you should return an error that fulfills cli.ExitCoder
// to cli.App.Run. This will cause the application to exit with the given error
// code in the cli.ExitCoder
func (a *App) RunAndExitOnError() {
if err := a.Run(os.Args); err != nil {
_, _ = fmt.Fprintln(a.ErrWriter, err)
OsExiter(1)
}
}
// Command returns the named command on App. Returns nil if the command does not exist
func (a *App) Command(name string) *Command {
for _, c := range a.Commands {
if c.HasName(name) {
return c
}
}
return nil
}
// VisibleCategories returns a slice of categories and commands that are
// Hidden=false
func (a *App) VisibleCategories() []CommandCategory {
ret := []CommandCategory{}
for _, category := range a.categories.Categories() {
if visible := func() CommandCategory {
if len(category.VisibleCommands()) > 0 {
return category
}
return nil
}(); visible != nil {
ret = append(ret, visible)
}
}
return ret
}
// VisibleCommands returns a slice of the Commands with Hidden=false
func (a *App) VisibleCommands() []*Command {
var ret []*Command
for _, command := range a.Commands {
if !command.Hidden {
ret = append(ret, command)
}
}
return ret
}
// VisibleFlagCategories returns a slice containing all the categories with the flags they contain
func (a *App) VisibleFlagCategories() []VisibleFlagCategory {
if a.flagCategories == nil {
return []VisibleFlagCategory{}
}
return a.flagCategories.VisibleCategories()
}
// VisibleFlags returns a slice of the Flags with Hidden=false
func (a *App) VisibleFlags() []Flag {
return visibleFlags(a.Flags)
}
func (a *App) appendFlag(fl Flag) {
if !hasFlag(a.Flags, fl) {
a.Flags = append(a.Flags, fl)
}
}
func (a *App) appendCommand(c *Command) {
if !hasCommand(a.Commands, c) {
a.Commands = append(a.Commands, c)
}
}
func (a *App) handleExitCoder(cCtx *Context, err error) {
if a.ExitErrHandler != nil {
a.ExitErrHandler(cCtx, err)
} else {
HandleExitCoder(err)
}
}
func (a *App) argsWithDefaultCommand(oldArgs Args) Args {
if a.DefaultCommand != "" {
rawArgs := append([]string{a.DefaultCommand}, oldArgs.Slice()...)
newArgs := args(rawArgs)
return &newArgs
}
return oldArgs
}
func runFlagActions(c *Context, fs []Flag) error {
for _, f := range fs {
isSet := false
for _, name := range f.Names() {
if c.IsSet(name) {
isSet = true
break
}
}
if isSet {
if af, ok := f.(ActionableFlag); ok {
if err := af.RunAction(c); err != nil {
return err
}
}
}
}
return nil
}
// Author represents someone who has contributed to a cli project.
type Author struct {
Name string // The Authors name
Email string // The Authors email
}
// String makes Author comply to the Stringer interface, to allow an easy print in the templating process
func (a *Author) String() string {
e := ""
if a.Email != "" {
e = " <" + a.Email + ">"
}
return fmt.Sprintf("%v%v", a.Name, e)
}
// HandleAction attempts to figure out which Action signature was used. If
// it's an ActionFunc or a func with the legacy signature for Action, the func
// is run!
func HandleAction(action interface{}, cCtx *Context) (err error) {
switch a := action.(type) {
case ActionFunc:
return a(cCtx)
case func(*Context) error:
return a(cCtx)
case func(*Context): // deprecated function signature
a(cCtx)
return nil
}
return errInvalidActionType
}
func checkStringSliceIncludes(want string, sSlice []string) bool {
found := false
for _, s := range sSlice {
if want == s {
found = true
break
}
}
return found
}
-54
View File
@@ -1,54 +0,0 @@
package cli
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 args []string
func (a *args) Get(n int) string {
if len(*a) > n {
return (*a)[n]
}
return ""
}
func (a *args) First() string {
return a.Get(0)
}
func (a *args) Tail() []string {
if a.Len() >= 2 {
tail := []string((*a)[1:])
ret := make([]string, len(tail))
copy(ret, tail)
return ret
}
return []string{}
}
func (a *args) Len() int {
return len(*a)
}
func (a *args) Present() bool {
return a.Len() != 0
}
func (a *args) Slice() []string {
ret := make([]string, len(*a))
copy(ret, *a)
return ret
}
-25
View File
@@ -1,25 +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.App{}).Run(os.Args)
// }
//
// Of course this application does not do much, so let's make this an actual application:
//
// func main() {
// app := &cli.App{
// Name: "greet",
// Usage: "say a greeting",
// Action: func(c *cli.Context) error {
// fmt.Println("Greetings")
// return nil
// },
// }
//
// app.Run(os.Args)
// }
package cli
//go:generate make -C cmd/urfave-cli-genflags run
-421
View File
@@ -1,421 +0,0 @@
package cli
import (
"flag"
"fmt"
"reflect"
"sort"
"strings"
)
// Command is a subcommand for a cli.App.
type Command struct {
// The name of the command
Name string
// A list of aliases for the command
Aliases []string
// A short description of the usage of this command
Usage string
// Custom text to show on USAGE section of help
UsageText string
// A longer explanation of how the command works
Description string
// Whether this command supports arguments
Args bool
// A short description of the arguments of this command
ArgsUsage string
// The category the command is part of
Category string
// The function to call when checking for bash command completions
BashComplete BashCompleteFunc
// An action to execute before any sub-subcommands are run, but after the context is ready
// If a non-nil error is returned, no sub-subcommands are run
Before BeforeFunc
// An action to execute after any subcommands are run, but after the subcommand has finished
// It is run even if Action() panics
After AfterFunc
// The function to call when this command is invoked
Action ActionFunc
// Execute this function if a usage error occurs.
OnUsageError OnUsageErrorFunc
// List of child commands
Subcommands []*Command
// List of flags to parse
Flags []Flag
flagCategories FlagCategories
// Treat all flags as normal arguments if true
SkipFlagParsing bool
// Boolean to hide built-in help command and help flag
HideHelp bool
// Boolean to hide built-in help command but keep help flag
// Ignored if HideHelp is true.
HideHelpCommand bool
// Boolean to hide this command from help or completion
Hidden bool
// Boolean to enable short-option handling so user can combine several
// single-character bool arguments into one
// i.e. foobar -o -v -> foobar -ov
UseShortOptionHandling bool
// Full name of command for help, defaults to full command name, including parent commands.
HelpName string
commandNamePath []string
// CustomHelpTemplate the text template for the command help topic.
// cli.go uses text/template to render templates. You can
// render custom help text by setting this variable.
CustomHelpTemplate string
// categories contains the categorized commands and is populated on app startup
categories CommandCategories
// if this is a root "special" command
isRoot bool
separator separatorSpec
}
type Commands []*Command
type CommandsByName []*Command
func (c CommandsByName) Len() int {
return len(c)
}
func (c CommandsByName) Less(i, j int) bool {
return lexicographicLess(c[i].Name, c[j].Name)
}
func (c CommandsByName) Swap(i, j int) {
c[i], c[j] = c[j], c[i]
}
// FullName returns the full name of the command.
// For subcommands this ensures that parent commands are part of the command path
func (c *Command) FullName() string {
if c.commandNamePath == nil {
return c.Name
}
return strings.Join(c.commandNamePath, " ")
}
func (cmd *Command) Command(name string) *Command {
for _, c := range cmd.Subcommands {
if c.HasName(name) {
return c
}
}
return nil
}
func (c *Command) setup(ctx *Context) {
if c.Command(helpCommand.Name) == nil && !c.HideHelp {
if !c.HideHelpCommand {
c.Subcommands = append(c.Subcommands, helpCommand)
}
}
if !c.HideHelp && HelpFlag != nil {
// append help to flags
c.appendFlag(HelpFlag)
}
if ctx.App.UseShortOptionHandling {
c.UseShortOptionHandling = true
}
c.categories = newCommandCategories()
for _, command := range c.Subcommands {
c.categories.AddCommand(command.Category, command)
}
sort.Sort(c.categories.(*commandCategories))
for _, scmd := range c.Subcommands {
if scmd.HelpName == "" {
scmd.HelpName = fmt.Sprintf("%s %s", c.HelpName, scmd.Name)
}
scmd.separator = c.separator
}
if c.BashComplete == nil {
c.BashComplete = DefaultCompleteWithFlags(c)
}
}
func (c *Command) Run(cCtx *Context, arguments ...string) (err error) {
if !c.isRoot {
c.setup(cCtx)
if err := checkDuplicatedCmds(c); err != nil {
return err
}
}
a := args(arguments)
set, err := c.parseFlags(&a, cCtx.shellComplete)
cCtx.flagSet = set
if checkCompletions(cCtx) {
return nil
}
if err != nil {
if c.OnUsageError != nil {
err = c.OnUsageError(cCtx, err, !c.isRoot)
cCtx.App.handleExitCoder(cCtx, err)
return err
}
_, _ = fmt.Fprintf(cCtx.App.Writer, "%s %s\n\n", "Incorrect Usage:", err.Error())
if cCtx.App.Suggest {
if suggestion, err := c.suggestFlagFromError(err, ""); err == nil {
fmt.Fprintf(cCtx.App.Writer, "%s", suggestion)
}
}
if !c.HideHelp {
if c.isRoot {
_ = ShowAppHelp(cCtx)
} else {
_ = ShowCommandHelp(cCtx.parentContext, c.Name)
}
}
return err
}
if checkHelp(cCtx) {
return helpCommand.Action(cCtx)
}
if c.isRoot && !cCtx.App.HideVersion && checkVersion(cCtx) {
ShowVersion(cCtx)
return nil
}
if c.After != nil && !cCtx.shellComplete {
defer func() {
afterErr := c.After(cCtx)
if afterErr != nil {
cCtx.App.handleExitCoder(cCtx, err)
if err != nil {
err = newMultiError(err, afterErr)
} else {
err = afterErr
}
}
}()
}
cerr := cCtx.checkRequiredFlags(c.Flags)
if cerr != nil {
_ = helpCommand.Action(cCtx)
return cerr
}
if c.Before != nil && !cCtx.shellComplete {
beforeErr := c.Before(cCtx)
if beforeErr != nil {
cCtx.App.handleExitCoder(cCtx, beforeErr)
err = beforeErr
return err
}
}
if err = runFlagActions(cCtx, c.Flags); err != nil {
return err
}
var cmd *Command
args := cCtx.Args()
if args.Present() {
name := args.First()
cmd = c.Command(name)
if cmd == nil {
hasDefault := cCtx.App.DefaultCommand != ""
isFlagName := checkStringSliceIncludes(name, cCtx.FlagNames())
var (
isDefaultSubcommand = false
defaultHasSubcommands = false
)
if hasDefault {
dc := cCtx.App.Command(cCtx.App.DefaultCommand)
defaultHasSubcommands = len(dc.Subcommands) > 0
for _, dcSub := range dc.Subcommands {
if checkStringSliceIncludes(name, dcSub.Names()) {
isDefaultSubcommand = true
break
}
}
}
if isFlagName || (hasDefault && (defaultHasSubcommands && isDefaultSubcommand)) {
argsWithDefault := cCtx.App.argsWithDefaultCommand(args)
if !reflect.DeepEqual(args, argsWithDefault) {
cmd = cCtx.App.rootCommand.Command(argsWithDefault.First())
}
}
}
} else if c.isRoot && cCtx.App.DefaultCommand != "" {
if dc := cCtx.App.Command(cCtx.App.DefaultCommand); dc != c {
cmd = dc
}
}
if cmd != nil {
newcCtx := NewContext(cCtx.App, nil, cCtx)
newcCtx.Command = cmd
return cmd.Run(newcCtx, cCtx.Args().Slice()...)
}
if c.Action == nil {
c.Action = helpCommand.Action
}
err = c.Action(cCtx)
cCtx.App.handleExitCoder(cCtx, err)
return err
}
func (c *Command) newFlagSet() (*flag.FlagSet, error) {
return flagSet(c.Name, c.Flags, c.separator)
}
func (c *Command) useShortOptionHandling() bool {
return c.UseShortOptionHandling
}
func (c *Command) suggestFlagFromError(err error, command string) (string, error) {
flag, parseErr := flagFromError(err)
if parseErr != nil {
return "", err
}
flags := c.Flags
hideHelp := c.HideHelp
if command != "" {
cmd := c.Command(command)
if cmd == nil {
return "", err
}
flags = cmd.Flags
hideHelp = hideHelp || cmd.HideHelp
}
suggestion := SuggestFlag(flags, flag, hideHelp)
if len(suggestion) == 0 {
return "", err
}
return fmt.Sprintf(SuggestDidYouMeanTemplate, suggestion) + "\n\n", nil
}
func (c *Command) parseFlags(args Args, shellComplete bool) (*flag.FlagSet, error) {
set, err := c.newFlagSet()
if err != nil {
return nil, err
}
if c.SkipFlagParsing {
return set, set.Parse(append([]string{"--"}, args.Tail()...))
}
err = parseIter(set, c, args.Tail(), shellComplete)
if err != nil {
return nil, err
}
err = normalizeFlags(c.Flags, set)
if err != nil {
return nil, err
}
return set, nil
}
// Names returns the names including short names and aliases.
func (c *Command) Names() []string {
return append([]string{c.Name}, c.Aliases...)
}
// HasName returns true if Command.Name matches given name
func (c *Command) HasName(name string) bool {
for _, n := range c.Names() {
if n == name {
return true
}
}
return false
}
// VisibleCategories returns a slice of categories and commands that are
// Hidden=false
func (c *Command) VisibleCategories() []CommandCategory {
ret := []CommandCategory{}
for _, category := range c.categories.Categories() {
if visible := func() CommandCategory {
if len(category.VisibleCommands()) > 0 {
return category
}
return nil
}(); visible != nil {
ret = append(ret, visible)
}
}
return ret
}
// VisibleCommands returns a slice of the Commands with Hidden=false
func (c *Command) VisibleCommands() []*Command {
var ret []*Command
for _, command := range c.Subcommands {
if !command.Hidden {
ret = append(ret, command)
}
}
return ret
}
// VisibleFlagCategories returns a slice containing all the visible flag categories with the flags they contain
func (c *Command) VisibleFlagCategories() []VisibleFlagCategory {
if c.flagCategories == nil {
c.flagCategories = newFlagCategoriesFromFlags(c.Flags)
}
return c.flagCategories.VisibleCategories()
}
// VisibleFlags returns a slice of the Flags with Hidden=false
func (c *Command) VisibleFlags() []Flag {
return visibleFlags(c.Flags)
}
func (c *Command) appendFlag(fl Flag) {
if !hasFlag(c.Flags, fl) {
c.Flags = append(c.Flags, fl)
}
}
func hasCommand(commands []*Command, command *Command) bool {
for _, existing := range commands {
if command == existing {
return true
}
}
return false
}
func checkDuplicatedCmds(parent *Command) error {
seen := make(map[string]struct{})
for _, c := range parent.Subcommands {
for _, name := range c.Names() {
if _, exists := seen[name]; exists {
return fmt.Errorf("parent command [%s] has duplicated subcommand name or alias: %s", parent.Name, name)
}
seen[name] = struct{}{}
}
}
return nil
}
-272
View File
@@ -1,272 +0,0 @@
package cli
import (
"context"
"flag"
"fmt"
"strings"
)
// Context is a type that is passed through to
// each Handler action in a cli application. Context
// can be used to retrieve context-specific args and
// parsed command-line options.
type Context struct {
context.Context
App *App
Command *Command
shellComplete bool
flagSet *flag.FlagSet
parentContext *Context
}
// NewContext creates a new context. For use in when invoking an App or Command action.
func NewContext(app *App, set *flag.FlagSet, parentCtx *Context) *Context {
c := &Context{App: app, flagSet: set, parentContext: parentCtx}
if parentCtx != nil {
c.Context = parentCtx.Context
c.shellComplete = parentCtx.shellComplete
if parentCtx.flagSet == nil {
parentCtx.flagSet = &flag.FlagSet{}
}
}
c.Command = &Command{}
if c.Context == nil {
c.Context = context.Background()
}
return c
}
// NumFlags returns the number of flags set
func (cCtx *Context) NumFlags() int {
return cCtx.flagSet.NFlag()
}
// Set sets a context flag to a value.
func (cCtx *Context) Set(name, value string) error {
if fs := cCtx.lookupFlagSet(name); fs != nil {
return fs.Set(name, value)
}
return fmt.Errorf("no such flag -%s", name)
}
// IsSet determines if the flag was actually set
func (cCtx *Context) IsSet(name string) bool {
if fs := cCtx.lookupFlagSet(name); fs != nil {
isSet := false
fs.Visit(func(f *flag.Flag) {
if f.Name == name {
isSet = true
}
})
if isSet {
return true
}
f := cCtx.lookupFlag(name)
if f == nil {
return false
}
if f.IsSet() {
return true
}
// now redo flagset search on aliases
aliases := f.Names()
fs.Visit(func(f *flag.Flag) {
for _, alias := range aliases {
if f.Name == alias {
isSet = true
}
}
})
if isSet {
return true
}
}
return false
}
// LocalFlagNames returns a slice of flag names used in this context.
func (cCtx *Context) LocalFlagNames() []string {
var names []string
cCtx.flagSet.Visit(makeFlagNameVisitor(&names))
// Check the flags which have been set via env or file
if cCtx.Command != nil && cCtx.Command.Flags != nil {
for _, f := range cCtx.Command.Flags {
if f.IsSet() {
names = append(names, f.Names()...)
}
}
}
// Sort out the duplicates since flag could be set via multiple
// paths
m := map[string]struct{}{}
var unames []string
for _, name := range names {
if _, ok := m[name]; !ok {
m[name] = struct{}{}
unames = append(unames, name)
}
}
return unames
}
// FlagNames returns a slice of flag names used by the this context and all of
// its parent contexts.
func (cCtx *Context) FlagNames() []string {
var names []string
for _, pCtx := range cCtx.Lineage() {
names = append(names, pCtx.LocalFlagNames()...)
}
return names
}
// Lineage returns *this* context and all of its ancestor contexts in order from
// child to parent
func (cCtx *Context) Lineage() []*Context {
var lineage []*Context
for cur := cCtx; cur != nil; cur = cur.parentContext {
lineage = append(lineage, cur)
}
return lineage
}
// Count returns the num of occurrences of this flag
func (cCtx *Context) Count(name string) int {
if fs := cCtx.lookupFlagSet(name); fs != nil {
if cf, ok := fs.Lookup(name).Value.(Countable); ok {
return cf.Count()
}
}
return 0
}
// Value returns the value of the flag corresponding to `name`
func (cCtx *Context) Value(name string) interface{} {
if fs := cCtx.lookupFlagSet(name); fs != nil {
return fs.Lookup(name).Value.(flag.Getter).Get()
}
return nil
}
// Args returns the command line arguments associated with the context.
func (cCtx *Context) Args() Args {
ret := args(cCtx.flagSet.Args())
return &ret
}
// NArg returns the number of the command line arguments.
func (cCtx *Context) NArg() int {
return cCtx.Args().Len()
}
func (cCtx *Context) lookupFlag(name string) Flag {
for _, c := range cCtx.Lineage() {
if c.Command == nil {
continue
}
for _, f := range c.Command.Flags {
for _, n := range f.Names() {
if n == name {
return f
}
}
}
}
if cCtx.App != nil {
for _, f := range cCtx.App.Flags {
for _, n := range f.Names() {
if n == name {
return f
}
}
}
}
return nil
}
func (cCtx *Context) lookupFlagSet(name string) *flag.FlagSet {
for _, c := range cCtx.Lineage() {
if c.flagSet == nil {
continue
}
if f := c.flagSet.Lookup(name); f != nil {
return c.flagSet
}
}
cCtx.onInvalidFlag(name)
return nil
}
func (cCtx *Context) checkRequiredFlags(flags []Flag) requiredFlagsErr {
var missingFlags []string
for _, f := range flags {
if rf, ok := f.(RequiredFlag); ok && rf.IsRequired() {
var flagPresent bool
var flagName string
flagNames := f.Names()
flagName = flagNames[0]
for _, key := range flagNames {
if cCtx.IsSet(strings.TrimSpace(key)) {
flagPresent = true
}
}
if !flagPresent && flagName != "" {
missingFlags = append(missingFlags, flagName)
}
}
}
if len(missingFlags) != 0 {
return &errRequiredFlags{missingFlags: missingFlags}
}
return nil
}
func (cCtx *Context) onInvalidFlag(name string) {
for cCtx != nil {
if cCtx.App != nil && cCtx.App.InvalidFlagAccessHandler != nil {
cCtx.App.InvalidFlagAccessHandler(cCtx, name)
break
}
cCtx = cCtx.parentContext
}
}
func makeFlagNameVisitor(names *[]string) func(*flag.Flag) {
return func(f *flag.Flag) {
nameParts := strings.Split(f.Name, ",")
name := strings.TrimSpace(nameParts[0])
for _, part := range nameParts {
part = strings.TrimSpace(part)
if len(part) > len(name) {
name = part
}
}
if name != "" {
*names = append(*names, name)
}
}
}
-203
View File
@@ -1,203 +0,0 @@
//go:build !urfave_cli_no_docs
// +build !urfave_cli_no_docs
package cli
import (
"bytes"
"fmt"
"io"
"sort"
"strings"
"text/template"
"github.com/cpuguy83/go-md2man/v2/md2man"
)
// ToMarkdown creates a markdown string for the `*App`
// The function errors if either parsing or writing of the string fails.
func (a *App) ToMarkdown() (string, error) {
var w bytes.Buffer
if err := a.writeDocTemplate(&w, 0); err != nil {
return "", err
}
return w.String(), nil
}
// ToMan creates a man page string with section number for the `*App`
// The function errors if either parsing or writing of the string fails.
func (a *App) ToManWithSection(sectionNumber int) (string, error) {
var w bytes.Buffer
if err := a.writeDocTemplate(&w, sectionNumber); err != nil {
return "", err
}
man := md2man.Render(w.Bytes())
return string(man), nil
}
// ToMan creates a man page string for the `*App`
// The function errors if either parsing or writing of the string fails.
func (a *App) ToMan() (string, error) {
man, err := a.ToManWithSection(8)
return man, err
}
type cliTemplate struct {
App *App
SectionNum int
Commands []string
GlobalArgs []string
SynopsisArgs []string
}
func (a *App) writeDocTemplate(w io.Writer, sectionNum int) error {
const name = "cli"
t, err := template.New(name).Parse(MarkdownDocTemplate)
if err != nil {
return err
}
return t.ExecuteTemplate(w, name, &cliTemplate{
App: a,
SectionNum: sectionNum,
Commands: prepareCommands(a.Commands, 0),
GlobalArgs: prepareArgsWithValues(a.VisibleFlags()),
SynopsisArgs: prepareArgsSynopsis(a.VisibleFlags()),
})
}
func prepareCommands(commands []*Command, level int) []string {
var coms []string
for _, command := range commands {
if command.Hidden {
continue
}
usageText := prepareUsageText(command)
usage := prepareUsage(command, usageText)
prepared := fmt.Sprintf("%s %s\n\n%s%s",
strings.Repeat("#", level+2),
strings.Join(command.Names(), ", "),
usage,
usageText,
)
flags := prepareArgsWithValues(command.VisibleFlags())
if len(flags) > 0 {
prepared += fmt.Sprintf("\n%s", strings.Join(flags, "\n"))
}
coms = append(coms, prepared)
// recursively iterate subcommands
if len(command.Subcommands) > 0 {
coms = append(
coms,
prepareCommands(command.Subcommands, level+1)...,
)
}
}
return coms
}
func prepareArgsWithValues(flags []Flag) []string {
return prepareFlags(flags, ", ", "**", "**", `""`, true)
}
func prepareArgsSynopsis(flags []Flag) []string {
return prepareFlags(flags, "|", "[", "]", "[value]", false)
}
func prepareFlags(
flags []Flag,
sep, opener, closer, value string,
addDetails bool,
) []string {
args := []string{}
for _, f := range flags {
flag, ok := f.(DocGenerationFlag)
if !ok {
continue
}
modifiedArg := opener
for _, s := range flag.Names() {
trimmed := strings.TrimSpace(s)
if len(modifiedArg) > len(opener) {
modifiedArg += sep
}
if len(trimmed) > 1 {
modifiedArg += fmt.Sprintf("--%s", trimmed)
} else {
modifiedArg += fmt.Sprintf("-%s", trimmed)
}
}
modifiedArg += closer
if flag.TakesValue() {
modifiedArg += fmt.Sprintf("=%s", value)
}
if addDetails {
modifiedArg += flagDetails(flag)
}
args = append(args, modifiedArg+"\n")
}
sort.Strings(args)
return args
}
// flagDetails returns a string containing the flags metadata
func flagDetails(flag DocGenerationFlag) string {
description := flag.GetUsage()
if flag.TakesValue() {
defaultText := flag.GetDefaultText()
if defaultText == "" {
defaultText = flag.GetValue()
}
if defaultText != "" {
description += " (default: " + defaultText + ")"
}
}
return ": " + description
}
func prepareUsageText(command *Command) string {
if command.UsageText == "" {
return ""
}
// Remove leading and trailing newlines
preparedUsageText := strings.Trim(command.UsageText, "\n")
var usageText string
if strings.Contains(preparedUsageText, "\n") {
// Format multi-line string as a code block using the 4 space schema to allow for embedded markdown such
// that it will not break the continuous code block.
for _, ln := range strings.Split(preparedUsageText, "\n") {
usageText += fmt.Sprintf(" %s\n", ln)
}
} else {
// Style a single line as a note
usageText = fmt.Sprintf(">%s\n", preparedUsageText)
}
return usageText
}
func prepareUsage(command *Command, usageText string) string {
if command.Usage == "" {
return ""
}
usage := command.Usage + "\n"
// Add a newline to the Usage IFF there is a UsageText
if usageText != "" {
usage += "\n"
}
return usage
}
-196
View File
@@ -1,196 +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 (a *App) ToFishCompletion() (string, error) {
var w bytes.Buffer
if err := a.writeFishCompletionTemplate(&w); err != nil {
return "", err
}
return w.String(), nil
}
type fishCompletionTemplate struct {
App *App
Completions []string
AllCommands []string
}
func (a *App) 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 := a.prepareFishFlags(a.VisibleFlags(), allCommands)
// Add help flag
if !a.HideHelp {
completions = append(
completions,
a.prepareFishFlags([]Flag{HelpFlag}, allCommands)...,
)
}
// Add version flag
if !a.HideVersion {
completions = append(
completions,
a.prepareFishFlags([]Flag{VersionFlag}, allCommands)...,
)
}
// Add commands and their flags
completions = append(
completions,
a.prepareFishCommands(a.VisibleCommands(), &allCommands, []string{})...,
)
return t.ExecuteTemplate(w, name, &fishCompletionTemplate{
App: a,
Completions: completions,
AllCommands: allCommands,
})
}
func (a *App) 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'",
a.Name,
a.fishSubcommandHelper(previousCommands),
strings.Join(command.Names(), " "),
))
if command.Usage != "" {
completion.WriteString(fmt.Sprintf(" -d '%s'",
escapeSingleQuotes(command.Usage)))
}
if !command.HideHelp {
completions = append(
completions,
a.prepareFishFlags([]Flag{HelpFlag}, command.Names())...,
)
}
*allCommands = append(*allCommands, command.Names()...)
completions = append(completions, completion.String())
completions = append(
completions,
a.prepareFishFlags(command.VisibleFlags(), command.Names())...,
)
// recursively iterate subcommands
if len(command.Subcommands) > 0 {
completions = append(
completions,
a.prepareFishCommands(
command.Subcommands, allCommands, command.Names(),
)...,
)
}
}
return completions
}
func (a *App) prepareFishFlags(flags []Flag, previousCommands []string) []string {
completions := []string{}
for _, f := range flags {
flag, ok := f.(DocGenerationFlag)
if !ok {
continue
}
completion := &strings.Builder{}
completion.WriteString(fmt.Sprintf(
"complete -c %s -n '%s'",
a.Name,
a.fishSubcommandHelper(previousCommands),
))
fishAddFileFlag(f, completion)
for idx, opt := range flag.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.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 *GenericFlag:
if f.TakesFile {
return
}
case *StringFlag:
if f.TakesFile {
return
}
case *StringSliceFlag:
if f.TakesFile {
return
}
case *PathFlag:
if f.TakesFile {
return
}
}
completion.WriteString(" -f")
}
func (a *App) fishSubcommandHelper(allCommands []string) string {
fishHelper := fmt.Sprintf("__fish_%s_no_subcommand", a.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)
}
-131
View File
@@ -1,131 +0,0 @@
# NOTE: this file is used by the tool defined in
# ./cmd/urfave-cli-genflags/main.go which uses the
# `Spec` type that maps to this file structure.
flag_types:
bool:
struct_fields:
- name: Count
type: int
pointer: true
- name: DisableDefaultText
type: bool
- name: Action
type: "func(*Context, bool) error"
float64:
struct_fields:
- name: Action
type: "func(*Context, float64) error"
Float64Slice:
value_pointer: true
skip_interfaces:
- fmt.Stringer
struct_fields:
- name: separator
type: separatorSpec
- name: Action
type: "func(*Context, []float64) error"
int:
struct_fields:
- name: Base
type: int
- name: Action
type: "func(*Context, int) error"
IntSlice:
value_pointer: true
skip_interfaces:
- fmt.Stringer
struct_fields:
- name: separator
type: separatorSpec
- name: Action
type: "func(*Context, []int) error"
int64:
struct_fields:
- name: Base
type: int
- name: Action
type: "func(*Context, int64) error"
Int64Slice:
value_pointer: true
skip_interfaces:
- fmt.Stringer
struct_fields:
- name: separator
type: separatorSpec
- name: Action
type: "func(*Context, []int64) error"
uint:
struct_fields:
- name: Base
type: int
- name: Action
type: "func(*Context, uint) error"
UintSlice:
value_pointer: true
skip_interfaces:
- fmt.Stringer
struct_fields:
- name: separator
type: separatorSpec
- name: Action
type: "func(*Context, []uint) error"
uint64:
struct_fields:
- name: Base
type: int
- name: Action
type: "func(*Context, uint64) error"
Uint64Slice:
value_pointer: true
skip_interfaces:
- fmt.Stringer
struct_fields:
- name: separator
type: separatorSpec
- name: Action
type: "func(*Context, []uint64) error"
string:
struct_fields:
- name: TakesFile
type: bool
- name: Action
type: "func(*Context, string) error"
StringSlice:
value_pointer: true
skip_interfaces:
- fmt.Stringer
struct_fields:
- name: separator
type: separatorSpec
- name: TakesFile
type: bool
- name: Action
type: "func(*Context, []string) error"
- name: KeepSpace
type: bool
time.Duration:
struct_fields:
- name: Action
type: "func(*Context, time.Duration) error"
Timestamp:
value_pointer: true
struct_fields:
- name: Layout
type: string
- name: Timezone
type: "*time.Location"
- name: Action
type: "func(*Context, *time.Time) error"
Generic:
no_destination_pointer: true
struct_fields:
- name: TakesFile
type: bool
- name: Action
type: "func(*Context, interface{}) error"
Path:
struct_fields:
- name: TakesFile
type: bool
- name: Action
type: "func(*Context, Path) error"
-419
View File
@@ -1,419 +0,0 @@
package cli
import (
"errors"
"flag"
"fmt"
"io"
"os"
"regexp"
"runtime"
"strings"
"syscall"
"time"
)
const defaultPlaceholder = "value"
const (
defaultSliceFlagSeparator = ","
disableSliceFlagSeparator = false
)
var (
slPfx = fmt.Sprintf("sl:::%d:::", time.Now().UTC().UnixNano())
commaWhitespace = regexp.MustCompile("[, ]+.*")
)
// BashCompletionFlag enables bash-completion for all commands and subcommands
var BashCompletionFlag Flag = &BoolFlag{
Name: "generate-bash-completion",
Hidden: true,
}
// VersionFlag prints the version for the application
var VersionFlag Flag = &BoolFlag{
Name: "version",
Aliases: []string{"v"},
Usage: "print the version",
DisableDefaultText: 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",
DisableDefaultText: 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 {
Flag
RunAction(*Context) 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
Names() []string
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 {
Flag
IsRequired() bool
}
// DocGenerationFlag is an interface that allows documentation generation for the flag
type DocGenerationFlag interface {
Flag
// 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
}
// DocGenerationSliceFlag extends DocGenerationFlag for slice-based flags.
type DocGenerationSliceFlag interface {
DocGenerationFlag
// IsSliceFlag returns true for flags that can be given multiple times.
IsSliceFlag() bool
}
// VisibleFlag is an interface that allows to check if a flag is visible
type VisibleFlag interface {
Flag
// 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 {
VisibleFlag
GetCategory() string
}
// Countable is an interface to enable detection of flag values which support
// repetitive flags
type Countable interface {
Count() int
}
func flagSet(name string, flags []Flag, spec separatorSpec) (*flag.FlagSet, error) {
set := flag.NewFlagSet(name, flag.ContinueOnError)
for _, f := range flags {
if c, ok := f.(customizedSeparator); ok {
c.WithSeparatorSpec(spec)
}
if err := f.Apply(set); err != nil {
return nil, err
}
}
set.SetOutput(io.Discard)
return set, nil
}
func copyFlag(name string, ff *flag.Flag, set *flag.FlagSet) {
switch ff.Value.(type) {
case Serializer:
_ = set.Set(name, ff.Value.(Serializer).Serialize())
default:
_ = set.Set(name, ff.Value.String())
}
}
func normalizeFlags(flags []Flag, set *flag.FlagSet) error {
visited := make(map[string]bool)
set.Visit(func(f *flag.Flag) {
visited[f.Name] = true
})
for _, f := range flags {
parts := f.Names()
if len(parts) == 1 {
continue
}
var ff *flag.Flag
for _, name := range parts {
name = strings.Trim(name, " ")
if visited[name] {
if ff != nil {
return errors.New("Cannot use two forms of the same flag: " + name + " " + ff.Name)
}
ff = set.Lookup(name)
}
}
if ff == nil {
continue
}
for _, name := range parts {
name = strings.Trim(name, " ")
if !visited[name] {
copyFlag(name, ff, set)
}
}
}
return 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 := ""
// set default text for all flags except bool flags
// for bool flags display default text if DisableDefaultText is not
// set
if bf, ok := f.(*BoolFlag); !ok || !bf.DisableDefaultText {
if s := df.GetDefaultText(); s != "" {
defaultValueString = fmt.Sprintf(formatDefault("%s"), s)
}
}
usageWithDefault := strings.TrimSpace(usage + defaultValueString)
pn := prefixedNames(df.Names(), placeholder)
sliceFlag, ok := f.(DocGenerationSliceFlag)
if ok && sliceFlag.IsSliceFlag() {
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
}
// Return the first value from a list of environment variables and files
// (which may or may not exist), a description of where the value was found,
// and a boolean which is true if a value was found.
func flagFromEnvOrFile(envVars []string, filePath string) (value string, fromWhere string, found bool) {
for _, envVar := range envVars {
envVar = strings.TrimSpace(envVar)
if value, found := syscall.Getenv(envVar); found {
return value, fmt.Sprintf("environment variable %q", envVar), true
}
}
for _, fileVar := range strings.Split(filePath, ",") {
if fileVar != "" {
if data, err := os.ReadFile(fileVar); err == nil {
return string(data), fmt.Sprintf("file %q", filePath), true
}
}
}
return "", "", false
}
type customizedSeparator interface {
WithSeparatorSpec(separatorSpec)
}
type separatorSpec struct {
sep string
disabled bool
customized bool
}
func (s separatorSpec) flagSplitMultiValues(val string) []string {
var (
disabled bool = s.disabled
sep string = s.sep
)
if !s.customized {
disabled = disableSliceFlagSeparator
sep = defaultSliceFlagSeparator
}
if disabled {
return []string{val}
}
return strings.Split(val, sep)
}
-178
View File
@@ -1,178 +0,0 @@
package cli
import (
"errors"
"flag"
"fmt"
"strconv"
)
// 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 newBoolValue(val bool, p *bool, count *int) *boolValue {
*p = val
return &boolValue{
destination: p,
count: count,
}
}
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 && *b.count > 0 {
return *b.count
}
return 0
}
// TakesValue returns true of the flag takes a value, otherwise false
func (f *BoolFlag) TakesValue() bool {
return false
}
// GetUsage returns the usage string for the flag
func (f *BoolFlag) GetUsage() string {
return f.Usage
}
// GetCategory returns the category for the flag
func (f *BoolFlag) GetCategory() string {
return f.Category
}
// GetValue returns the flags value as string representation and an empty
// string if the flag takes no value at all.
func (f *BoolFlag) GetValue() string {
return ""
}
// GetDefaultText returns the default text for this flag
func (f *BoolFlag) GetDefaultText() string {
if f.DefaultText != "" {
return f.DefaultText
}
if f.defaultValueSet {
return fmt.Sprintf("%v", f.defaultValue)
}
return fmt.Sprintf("%v", f.Value)
}
// GetEnvVars returns the env vars for this flag
func (f *BoolFlag) GetEnvVars() []string {
return f.EnvVars
}
// RunAction executes flag action if set
func (f *BoolFlag) RunAction(c *Context) error {
if f.Action != nil {
return f.Action(c, c.Bool(f.Name))
}
return nil
}
// Apply populates the flag given the flag set and environment
func (f *BoolFlag) Apply(set *flag.FlagSet) error {
// set default value so that environment wont be able to overwrite it
f.defaultValue = f.Value
f.defaultValueSet = true
if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
if val != "" {
valBool, err := strconv.ParseBool(val)
if err != nil {
return fmt.Errorf("could not parse %q as bool value from %s for flag %s: %s", val, source, f.Name, err)
}
f.Value = valBool
} else {
// empty value implies that the env is defined but set to empty string, we have to assume that this is
// what the user wants. If user doesnt want this then the env needs to be deleted or the flag removed from
// file
f.Value = false
}
f.HasBeenSet = true
}
count := f.Count
dest := f.Destination
if count == nil {
count = new(int)
}
// since count will be incremented for each alias as well
// subtract number of aliases from overall count
*count -= len(f.Aliases)
if dest == nil {
dest = new(bool)
}
for _, name := range f.Names() {
value := newBoolValue(f.Value, dest, count)
set.Var(value, name, f.Usage)
}
return nil
}
// Get returns the flag’s value in the given Context.
func (f *BoolFlag) Get(ctx *Context) bool {
return ctx.Bool(f.Name)
}
// Bool looks up the value of a local BoolFlag, returns
// false if not found
func (cCtx *Context) Bool(name string) bool {
if fs := cCtx.lookupFlagSet(name); fs != nil {
return lookupBool(name, fs)
}
return false
}
func lookupBool(name string, set *flag.FlagSet) bool {
f := set.Lookup(name)
if f != nil {
parsed, err := strconv.ParseBool(f.Value.String())
if err != nil {
return false
}
return parsed
}
return false
}
-108
View File
@@ -1,108 +0,0 @@
package cli
import (
"flag"
"fmt"
"time"
)
// TakesValue returns true of the flag takes a value, otherwise false
func (f *DurationFlag) TakesValue() bool {
return true
}
// GetUsage returns the usage string for the flag
func (f *DurationFlag) GetUsage() string {
return f.Usage
}
// GetCategory returns the category for the flag
func (f *DurationFlag) GetCategory() string {
return f.Category
}
// GetValue returns the flags value as string representation and an empty
// string if the flag takes no value at all.
func (f *DurationFlag) GetValue() string {
return f.Value.String()
}
// GetDefaultText returns the default text for this flag
func (f *DurationFlag) GetDefaultText() string {
if f.DefaultText != "" {
return f.DefaultText
}
if f.defaultValueSet {
return f.defaultValue.String()
}
return f.Value.String()
}
// GetEnvVars returns the env vars for this flag
func (f *DurationFlag) GetEnvVars() []string {
return f.EnvVars
}
// Apply populates the flag given the flag set and environment
func (f *DurationFlag) Apply(set *flag.FlagSet) error {
// set default value so that environment wont be able to overwrite it
f.defaultValue = f.Value
f.defaultValueSet = true
if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
if val != "" {
valDuration, err := time.ParseDuration(val)
if err != nil {
return fmt.Errorf("could not parse %q as duration value from %s for flag %s: %s", val, source, f.Name, err)
}
f.Value = valDuration
f.HasBeenSet = true
}
}
for _, name := range f.Names() {
if f.Destination != nil {
set.DurationVar(f.Destination, name, f.Value, f.Usage)
continue
}
set.Duration(name, f.Value, f.Usage)
}
return nil
}
// Get returns the flag’s value in the given Context.
func (f *DurationFlag) Get(ctx *Context) time.Duration {
return ctx.Duration(f.Name)
}
// RunAction executes flag action if set
func (f *DurationFlag) RunAction(c *Context) error {
if f.Action != nil {
return f.Action(c, c.Duration(f.Name))
}
return nil
}
// Duration looks up the value of a local DurationFlag, returns
// 0 if not found
func (cCtx *Context) Duration(name string) time.Duration {
if fs := cCtx.lookupFlagSet(name); fs != nil {
return lookupDuration(name, fs)
}
return 0
}
func lookupDuration(name string, set *flag.FlagSet) time.Duration {
f := set.Lookup(name)
if f != nil {
parsed, err := time.ParseDuration(f.Value.String())
if err != nil {
return 0
}
return parsed
}
return 0
}
-48
View File
@@ -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
}
-107
View File
@@ -1,107 +0,0 @@
package cli
import (
"flag"
"fmt"
"strconv"
)
// TakesValue returns true of the flag takes a value, otherwise false
func (f *Float64Flag) TakesValue() bool {
return true
}
// GetUsage returns the usage string for the flag
func (f *Float64Flag) GetUsage() string {
return f.Usage
}
// GetCategory returns the category for the flag
func (f *Float64Flag) GetCategory() string {
return f.Category
}
// GetValue returns the flags value as string representation and an empty
// string if the flag takes no value at all.
func (f *Float64Flag) GetValue() string {
return fmt.Sprintf("%v", f.Value)
}
// GetDefaultText returns the default text for this flag
func (f *Float64Flag) GetDefaultText() string {
if f.DefaultText != "" {
return f.DefaultText
}
if f.defaultValueSet {
return fmt.Sprintf("%v", f.defaultValue)
}
return fmt.Sprintf("%v", f.Value)
}
// GetEnvVars returns the env vars for this flag
func (f *Float64Flag) GetEnvVars() []string {
return f.EnvVars
}
// Apply populates the flag given the flag set and environment
func (f *Float64Flag) Apply(set *flag.FlagSet) error {
f.defaultValue = f.Value
f.defaultValueSet = true
if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
if val != "" {
valFloat, err := strconv.ParseFloat(val, 64)
if err != nil {
return fmt.Errorf("could not parse %q as float64 value from %s for flag %s: %s", val, source, f.Name, err)
}
f.Value = valFloat
f.HasBeenSet = true
}
}
for _, name := range f.Names() {
if f.Destination != nil {
set.Float64Var(f.Destination, name, f.Value, f.Usage)
continue
}
set.Float64(name, f.Value, f.Usage)
}
return nil
}
// Get returns the flag’s value in the given Context.
func (f *Float64Flag) Get(ctx *Context) float64 {
return ctx.Float64(f.Name)
}
// RunAction executes flag action if set
func (f *Float64Flag) RunAction(c *Context) error {
if f.Action != nil {
return f.Action(c, c.Float64(f.Name))
}
return nil
}
// Float64 looks up the value of a local Float64Flag, returns
// 0 if not found
func (cCtx *Context) Float64(name string) float64 {
if fs := cCtx.lookupFlagSet(name); fs != nil {
return lookupFloat64(name, fs)
}
return 0
}
func lookupFloat64(name string, set *flag.FlagSet) float64 {
f := set.Lookup(name)
if f != nil {
parsed, err := strconv.ParseFloat(f.Value.String(), 64)
if err != nil {
return 0
}
return parsed
}
return 0
}
-216
View File
@@ -1,216 +0,0 @@
package cli
import (
"encoding/json"
"flag"
"fmt"
"strconv"
"strings"
)
// Float64Slice wraps []float64 to satisfy flag.Value
type Float64Slice struct {
slice []float64
separator separatorSpec
hasBeenSet bool
}
// NewFloat64Slice makes a *Float64Slice with default values
func NewFloat64Slice(defaults ...float64) *Float64Slice {
return &Float64Slice{slice: append([]float64{}, defaults...)}
}
// clone allocate a copy of self object
func (f *Float64Slice) clone() *Float64Slice {
n := &Float64Slice{
slice: make([]float64, len(f.slice)),
hasBeenSet: f.hasBeenSet,
}
copy(n.slice, f.slice)
return n
}
func (f *Float64Slice) WithSeparatorSpec(spec separatorSpec) {
f.separator = spec
}
// Set parses the value into a float64 and appends it to the list of values
func (f *Float64Slice) Set(value string) error {
if !f.hasBeenSet {
f.slice = []float64{}
f.hasBeenSet = true
}
if strings.HasPrefix(value, slPfx) {
// Deserializing assumes overwrite
_ = json.Unmarshal([]byte(strings.Replace(value, slPfx, "", 1)), &f.slice)
f.hasBeenSet = true
return nil
}
for _, s := range f.separator.flagSplitMultiValues(value) {
tmp, err := strconv.ParseFloat(strings.TrimSpace(s), 64)
if err != nil {
return err
}
f.slice = append(f.slice, tmp)
}
return nil
}
// String returns a readable representation of this value (for usage defaults)
func (f *Float64Slice) String() string {
v := f.slice
if v == nil {
// treat nil the same as zero length non-nil
v = make([]float64, 0)
}
return fmt.Sprintf("%#v", v)
}
// Serialize allows Float64Slice to fulfill Serializer
func (f *Float64Slice) Serialize() string {
jsonBytes, _ := json.Marshal(f.slice)
return fmt.Sprintf("%s%s", slPfx, string(jsonBytes))
}
// Value returns the slice of float64s set by this flag
func (f *Float64Slice) Value() []float64 {
return f.slice
}
// Get returns the slice of float64s set by this flag
func (f *Float64Slice) Get() interface{} {
return *f
}
// String returns a readable representation of this value
// (for usage defaults)
func (f *Float64SliceFlag) String() string {
return FlagStringer(f)
}
// TakesValue returns true if the flag takes a value, otherwise false
func (f *Float64SliceFlag) TakesValue() bool {
return true
}
// GetUsage returns the usage string for the flag
func (f *Float64SliceFlag) GetUsage() string {
return f.Usage
}
// GetCategory returns the category for the flag
func (f *Float64SliceFlag) GetCategory() string {
return f.Category
}
// GetValue returns the flags value as string representation and an empty
// string if the flag takes no value at all.
func (f *Float64SliceFlag) GetValue() string {
var defaultVals []string
if f.Value != nil && len(f.Value.Value()) > 0 {
for _, i := range f.Value.Value() {
defaultVals = append(defaultVals, strings.TrimRight(strings.TrimRight(fmt.Sprintf("%f", i), "0"), "."))
}
}
return strings.Join(defaultVals, ", ")
}
// GetDefaultText returns the default text for this flag
func (f *Float64SliceFlag) GetDefaultText() string {
if f.DefaultText != "" {
return f.DefaultText
}
return f.GetValue()
}
// GetEnvVars returns the env vars for this flag
func (f *Float64SliceFlag) GetEnvVars() []string {
return f.EnvVars
}
// IsSliceFlag implements DocGenerationSliceFlag.
func (f *Float64SliceFlag) IsSliceFlag() bool {
return true
}
// Apply populates the flag given the flag set and environment
func (f *Float64SliceFlag) Apply(set *flag.FlagSet) error {
// apply any default
if f.Destination != nil && f.Value != nil {
f.Destination.slice = make([]float64, len(f.Value.slice))
copy(f.Destination.slice, f.Value.slice)
}
// resolve setValue (what we will assign to the set)
var setValue *Float64Slice
switch {
case f.Destination != nil:
setValue = f.Destination
case f.Value != nil:
setValue = f.Value.clone()
default:
setValue = new(Float64Slice)
setValue.WithSeparatorSpec(f.separator)
}
if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
if val != "" {
for _, s := range f.separator.flagSplitMultiValues(val) {
if err := setValue.Set(strings.TrimSpace(s)); err != nil {
return fmt.Errorf("could not parse %q as float64 slice value from %s for flag %s: %s", val, source, f.Name, err)
}
}
// Set this to false so that we reset the slice if we then set values from
// flags that have already been set by the environment.
setValue.hasBeenSet = false
f.HasBeenSet = true
}
}
for _, name := range f.Names() {
set.Var(setValue, name, f.Usage)
}
return nil
}
func (f *Float64SliceFlag) WithSeparatorSpec(spec separatorSpec) {
f.separator = spec
}
// Get returns the flag’s value in the given Context.
func (f *Float64SliceFlag) Get(ctx *Context) []float64 {
return ctx.Float64Slice(f.Name)
}
// RunAction executes flag action if set
func (f *Float64SliceFlag) RunAction(c *Context) error {
if f.Action != nil {
return f.Action(c, c.Float64Slice(f.Name))
}
return nil
}
// Float64Slice looks up the value of a local Float64SliceFlag, returns
// nil if not found
func (cCtx *Context) Float64Slice(name string) []float64 {
if fs := cCtx.lookupFlagSet(name); fs != nil {
return lookupFloat64Slice(name, fs)
}
return nil
}
func lookupFloat64Slice(name string, set *flag.FlagSet) []float64 {
f := set.Lookup(name)
if f != nil {
if slice, ok := unwrapFlagValue(f.Value).(*Float64Slice); ok {
return slice.Value()
}
}
return nil
}
-131
View File
@@ -1,131 +0,0 @@
package cli
import (
"flag"
"fmt"
)
// Generic is a generic parseable type identified by a specific flag
type Generic interface {
Set(value string) error
String() string
}
type stringGeneric struct {
value string
}
func (s *stringGeneric) Set(value string) error {
s.value = value
return nil
}
func (s *stringGeneric) String() string {
return s.value
}
// TakesValue returns true of the flag takes a value, otherwise false
func (f *GenericFlag) TakesValue() bool {
return true
}
// GetUsage returns the usage string for the flag
func (f *GenericFlag) GetUsage() string {
return f.Usage
}
// GetCategory returns the category for the flag
func (f *GenericFlag) GetCategory() string {
return f.Category
}
// GetValue returns the flags value as string representation and an empty
// string if the flag takes no value at all.
func (f *GenericFlag) GetValue() string {
if f.Value != nil {
return f.Value.String()
}
return ""
}
// GetDefaultText returns the default text for this flag
func (f *GenericFlag) GetDefaultText() string {
if f.DefaultText != "" {
return f.DefaultText
}
val := f.Value
if f.defaultValueSet {
val = f.defaultValue
}
if val != nil {
return val.String()
}
return ""
}
// GetEnvVars returns the env vars for this flag
func (f *GenericFlag) GetEnvVars() []string {
return f.EnvVars
}
// Apply takes the flagset and calls Set on the generic flag with the value
// provided by the user for parsing by the flag
func (f *GenericFlag) Apply(set *flag.FlagSet) error {
// set default value so that environment wont be able to overwrite it
if f.Value != nil {
f.defaultValue = &stringGeneric{value: f.Value.String()}
f.defaultValueSet = true
}
if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
if val != "" {
if err := f.Value.Set(val); err != nil {
return fmt.Errorf("could not parse %q from %s as value for flag %s: %s", val, source, f.Name, err)
}
f.HasBeenSet = true
}
}
for _, name := range f.Names() {
if f.Destination != nil {
set.Var(f.Destination, name, f.Usage)
continue
}
set.Var(f.Value, name, f.Usage)
}
return nil
}
// Get returns the flag’s value in the given Context.
func (f *GenericFlag) Get(ctx *Context) interface{} {
return ctx.Generic(f.Name)
}
// RunAction executes flag action if set
func (f *GenericFlag) RunAction(c *Context) error {
if f.Action != nil {
return f.Action(c, c.Generic(f.Name))
}
return nil
}
// Generic looks up the value of a local GenericFlag, returns
// nil if not found
func (cCtx *Context) Generic(name string) interface{} {
if fs := cCtx.lookupFlagSet(name); fs != nil {
return lookupGeneric(name, fs)
}
return nil
}
func lookupGeneric(name string, set *flag.FlagSet) interface{} {
if f := set.Lookup(name); f != nil {
return f.Value
}
return nil
}
-109
View File
@@ -1,109 +0,0 @@
package cli
import (
"flag"
"fmt"
"strconv"
)
// TakesValue returns true of the flag takes a value, otherwise false
func (f *IntFlag) TakesValue() bool {
return true
}
// GetUsage returns the usage string for the flag
func (f *IntFlag) GetUsage() string {
return f.Usage
}
// GetCategory returns the category for the flag
func (f *IntFlag) GetCategory() string {
return f.Category
}
// GetValue returns the flags value as string representation and an empty
// string if the flag takes no value at all.
func (f *IntFlag) GetValue() string {
return fmt.Sprintf("%d", f.Value)
}
// GetDefaultText returns the default text for this flag
func (f *IntFlag) GetDefaultText() string {
if f.DefaultText != "" {
return f.DefaultText
}
if f.defaultValueSet {
return fmt.Sprintf("%d", f.defaultValue)
}
return fmt.Sprintf("%d", f.Value)
}
// GetEnvVars returns the env vars for this flag
func (f *IntFlag) GetEnvVars() []string {
return f.EnvVars
}
// Apply populates the flag given the flag set and environment
func (f *IntFlag) Apply(set *flag.FlagSet) error {
// set default value so that environment wont be able to overwrite it
f.defaultValue = f.Value
f.defaultValueSet = true
if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
if val != "" {
valInt, err := strconv.ParseInt(val, f.Base, 64)
if err != nil {
return fmt.Errorf("could not parse %q as int value from %s for flag %s: %s", val, source, f.Name, err)
}
f.Value = int(valInt)
f.HasBeenSet = true
}
}
for _, name := range f.Names() {
if f.Destination != nil {
set.IntVar(f.Destination, name, f.Value, f.Usage)
continue
}
set.Int(name, f.Value, f.Usage)
}
return nil
}
// Get returns the flag’s value in the given Context.
func (f *IntFlag) Get(ctx *Context) int {
return ctx.Int(f.Name)
}
// RunAction executes flag action if set
func (f *IntFlag) RunAction(c *Context) error {
if f.Action != nil {
return f.Action(c, c.Int(f.Name))
}
return nil
}
// Int looks up the value of a local IntFlag, returns
// 0 if not found
func (cCtx *Context) Int(name string) int {
if fs := cCtx.lookupFlagSet(name); fs != nil {
return lookupInt(name, fs)
}
return 0
}
func lookupInt(name string, set *flag.FlagSet) int {
f := set.Lookup(name)
if f != nil {
parsed, err := strconv.ParseInt(f.Value.String(), 0, 64)
if err != nil {
return 0
}
return int(parsed)
}
return 0
}
-108
View File
@@ -1,108 +0,0 @@
package cli
import (
"flag"
"fmt"
"strconv"
)
// TakesValue returns true of the flag takes a value, otherwise false
func (f *Int64Flag) TakesValue() bool {
return true
}
// GetUsage returns the usage string for the flag
func (f *Int64Flag) GetUsage() string {
return f.Usage
}
// GetCategory returns the category for the flag
func (f *Int64Flag) GetCategory() string {
return f.Category
}
// GetValue returns the flags value as string representation and an empty
// string if the flag takes no value at all.
func (f *Int64Flag) GetValue() string {
return fmt.Sprintf("%d", f.Value)
}
// GetDefaultText returns the default text for this flag
func (f *Int64Flag) GetDefaultText() string {
if f.DefaultText != "" {
return f.DefaultText
}
if f.defaultValueSet {
return fmt.Sprintf("%d", f.defaultValue)
}
return fmt.Sprintf("%d", f.Value)
}
// GetEnvVars returns the env vars for this flag
func (f *Int64Flag) GetEnvVars() []string {
return f.EnvVars
}
// Apply populates the flag given the flag set and environment
func (f *Int64Flag) Apply(set *flag.FlagSet) error {
// set default value so that environment wont be able to overwrite it
f.defaultValue = f.Value
f.defaultValueSet = true
if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
if val != "" {
valInt, err := strconv.ParseInt(val, f.Base, 64)
if err != nil {
return fmt.Errorf("could not parse %q as int value from %s for flag %s: %s", val, source, f.Name, err)
}
f.Value = valInt
f.HasBeenSet = true
}
}
for _, name := range f.Names() {
if f.Destination != nil {
set.Int64Var(f.Destination, name, f.Value, f.Usage)
continue
}
set.Int64(name, f.Value, f.Usage)
}
return nil
}
// Get returns the flag’s value in the given Context.
func (f *Int64Flag) Get(ctx *Context) int64 {
return ctx.Int64(f.Name)
}
// RunAction executes flag action if set
func (f *Int64Flag) RunAction(c *Context) error {
if f.Action != nil {
return f.Action(c, c.Int64(f.Name))
}
return nil
}
// Int64 looks up the value of a local Int64Flag, returns
// 0 if not found
func (cCtx *Context) Int64(name string) int64 {
if fs := cCtx.lookupFlagSet(name); fs != nil {
return lookupInt64(name, fs)
}
return 0
}
func lookupInt64(name string, set *flag.FlagSet) int64 {
f := set.Lookup(name)
if f != nil {
parsed, err := strconv.ParseInt(f.Value.String(), 0, 64)
if err != nil {
return 0
}
return parsed
}
return 0
}
-215
View File
@@ -1,215 +0,0 @@
package cli
import (
"encoding/json"
"flag"
"fmt"
"strconv"
"strings"
)
// Int64Slice wraps []int64 to satisfy flag.Value
type Int64Slice struct {
slice []int64
separator separatorSpec
hasBeenSet bool
}
// NewInt64Slice makes an *Int64Slice with default values
func NewInt64Slice(defaults ...int64) *Int64Slice {
return &Int64Slice{slice: append([]int64{}, defaults...)}
}
// clone allocate a copy of self object
func (i *Int64Slice) clone() *Int64Slice {
n := &Int64Slice{
slice: make([]int64, len(i.slice)),
hasBeenSet: i.hasBeenSet,
}
copy(n.slice, i.slice)
return n
}
func (i *Int64Slice) WithSeparatorSpec(spec separatorSpec) {
i.separator = spec
}
// Set parses the value into an integer and appends it to the list of values
func (i *Int64Slice) Set(value string) error {
if !i.hasBeenSet {
i.slice = []int64{}
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 i.separator.flagSplitMultiValues(value) {
tmp, err := strconv.ParseInt(strings.TrimSpace(s), 0, 64)
if err != nil {
return err
}
i.slice = append(i.slice, tmp)
}
return nil
}
// String returns a readable representation of this value (for usage defaults)
func (i *Int64Slice) String() string {
v := i.slice
if v == nil {
// treat nil the same as zero length non-nil
v = make([]int64, 0)
}
return fmt.Sprintf("%#v", v)
}
// Serialize allows Int64Slice to fulfill Serializer
func (i *Int64Slice) Serialize() string {
jsonBytes, _ := json.Marshal(i.slice)
return fmt.Sprintf("%s%s", slPfx, string(jsonBytes))
}
// Value returns the slice of ints set by this flag
func (i *Int64Slice) Value() []int64 {
return i.slice
}
// Get returns the slice of ints set by this flag
func (i *Int64Slice) Get() interface{} {
return *i
}
// String returns a readable representation of this value
// (for usage defaults)
func (f *Int64SliceFlag) String() string {
return FlagStringer(f)
}
// TakesValue returns true of the flag takes a value, otherwise false
func (f *Int64SliceFlag) TakesValue() bool {
return true
}
// GetUsage returns the usage string for the flag
func (f *Int64SliceFlag) GetUsage() string {
return f.Usage
}
// GetCategory returns the category for the flag
func (f *Int64SliceFlag) GetCategory() string {
return f.Category
}
// GetValue returns the flags value as string representation and an empty
// string if the flag takes no value at all.
func (f *Int64SliceFlag) GetValue() string {
var defaultVals []string
if f.Value != nil && len(f.Value.Value()) > 0 {
for _, i := range f.Value.Value() {
defaultVals = append(defaultVals, strconv.FormatInt(i, 10))
}
}
return strings.Join(defaultVals, ", ")
}
// GetDefaultText returns the default text for this flag
func (f *Int64SliceFlag) GetDefaultText() string {
if f.DefaultText != "" {
return f.DefaultText
}
return f.GetValue()
}
// GetEnvVars returns the env vars for this flag
func (f *Int64SliceFlag) GetEnvVars() []string {
return f.EnvVars
}
// IsSliceFlag implements DocGenerationSliceFlag.
func (f *Int64SliceFlag) IsSliceFlag() bool {
return true
}
// Apply populates the flag given the flag set and environment
func (f *Int64SliceFlag) Apply(set *flag.FlagSet) error {
// apply any default
if f.Destination != nil && f.Value != nil {
f.Destination.slice = make([]int64, len(f.Value.slice))
copy(f.Destination.slice, f.Value.slice)
}
// resolve setValue (what we will assign to the set)
var setValue *Int64Slice
switch {
case f.Destination != nil:
setValue = f.Destination
case f.Value != nil:
setValue = f.Value.clone()
default:
setValue = new(Int64Slice)
setValue.WithSeparatorSpec(f.separator)
}
if val, source, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok && val != "" {
for _, s := range f.separator.flagSplitMultiValues(val) {
if err := setValue.Set(strings.TrimSpace(s)); err != nil {
return fmt.Errorf("could not parse %q as int64 slice value from %s for flag %s: %s", val, source, f.Name, err)
}
}
// Set this to false so that we reset the slice if we then set values from
// flags that have already been set by the environment.
setValue.hasBeenSet = false
f.HasBeenSet = true
}
for _, name := range f.Names() {
set.Var(setValue, name, f.Usage)
}
return nil
}
func (f *Int64SliceFlag) WithSeparatorSpec(spec separatorSpec) {
f.separator = spec
}
// Get returns the flag’s value in the given Context.
func (f *Int64SliceFlag) Get(ctx *Context) []int64 {
return ctx.Int64Slice(f.Name)
}
// RunAction executes flag action if set
func (f *Int64SliceFlag) RunAction(c *Context) error {
if f.Action != nil {
return f.Action(c, c.Int64Slice(f.Name))
}
return nil
}
// Int64Slice looks up the value of a local Int64SliceFlag, returns
// nil if not found
func (cCtx *Context) Int64Slice(name string) []int64 {
if fs := cCtx.lookupFlagSet(name); fs != nil {
return lookupInt64Slice(name, fs)
}
return nil
}
func lookupInt64Slice(name string, set *flag.FlagSet) []int64 {
f := set.Lookup(name)
if f != nil {
if slice, ok := unwrapFlagValue(f.Value).(*Int64Slice); ok {
return slice.Value()
}
}
return nil
}
Loaded 100 of 179 files, more files were not shown because too many files have changed in this diff. Show more