mirror of
https://github.com/containers/skopeo.git
synced 2025-09-01 22:58:24 +00:00
Merge pull request #523 from mtrmac/cli-parsing
RFC: Reliable CLI parsing
This commit is contained in:
@@ -3,7 +3,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"io"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/containers/image/copy"
|
"github.com/containers/image/copy"
|
||||||
@@ -11,100 +11,33 @@ import (
|
|||||||
"github.com/containers/image/manifest"
|
"github.com/containers/image/manifest"
|
||||||
"github.com/containers/image/transports"
|
"github.com/containers/image/transports"
|
||||||
"github.com/containers/image/transports/alltransports"
|
"github.com/containers/image/transports/alltransports"
|
||||||
"github.com/containers/image/types"
|
|
||||||
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
|
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
// contextsFromGlobalOptions returns source and destionation types.SystemContext depending on c.
|
type copyOptions struct {
|
||||||
func contextsFromGlobalOptions(c *cli.Context) (*types.SystemContext, *types.SystemContext, error) {
|
global *globalOptions
|
||||||
sourceCtx, err := contextFromGlobalOptions(c, "src-")
|
srcImage *imageOptions
|
||||||
if err != nil {
|
destImage *imageDestOptions
|
||||||
return nil, nil, err
|
additionalTags cli.StringSlice // For docker-archive: destinations, in addition to the name:tag specified as destination, also add these
|
||||||
}
|
removeSignatures bool // Do not copy signatures from the source image
|
||||||
|
signByFingerprint string // Sign the image using a GPG key with the specified fingerprint
|
||||||
destinationCtx, err := contextFromGlobalOptions(c, "dest-")
|
format optionalString // Force conversion of the image to a specified format
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return sourceCtx, destinationCtx, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func copyHandler(c *cli.Context) error {
|
func copyCmd(global *globalOptions) cli.Command {
|
||||||
if len(c.Args()) != 2 {
|
sharedFlags, sharedOpts := sharedImageFlags()
|
||||||
cli.ShowCommandHelp(c, "copy")
|
srcFlags, srcOpts := imageFlags(global, sharedOpts, "src-", "screds")
|
||||||
return errors.New("Exactly two arguments expected")
|
destFlags, destOpts := imageDestFlags(global, sharedOpts, "dest-", "dcreds")
|
||||||
|
opts := copyOptions{global: global,
|
||||||
|
srcImage: srcOpts,
|
||||||
|
destImage: destOpts,
|
||||||
}
|
}
|
||||||
|
|
||||||
policyContext, err := getPolicyContext(c)
|
return cli.Command{
|
||||||
if err != nil {
|
Name: "copy",
|
||||||
return fmt.Errorf("Error loading trust policy: %v", err)
|
Usage: "Copy an IMAGE-NAME from one location to another",
|
||||||
}
|
Description: fmt.Sprintf(`
|
||||||
defer policyContext.Destroy()
|
|
||||||
|
|
||||||
srcRef, err := alltransports.ParseImageName(c.Args()[0])
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Invalid source name %s: %v", c.Args()[0], err)
|
|
||||||
}
|
|
||||||
destRef, err := alltransports.ParseImageName(c.Args()[1])
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Invalid destination name %s: %v", c.Args()[1], err)
|
|
||||||
}
|
|
||||||
signBy := c.String("sign-by")
|
|
||||||
removeSignatures := c.Bool("remove-signatures")
|
|
||||||
|
|
||||||
sourceCtx, destinationCtx, err := contextsFromGlobalOptions(c)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var manifestType string
|
|
||||||
if c.IsSet("format") {
|
|
||||||
switch c.String("format") {
|
|
||||||
case "oci":
|
|
||||||
manifestType = imgspecv1.MediaTypeImageManifest
|
|
||||||
case "v2s1":
|
|
||||||
manifestType = manifest.DockerV2Schema1SignedMediaType
|
|
||||||
case "v2s2":
|
|
||||||
manifestType = manifest.DockerV2Schema2MediaType
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("unknown format %q. Choose on of the supported formats: 'oci', 'v2s1', or 'v2s2'", c.String("format"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.IsSet("additional-tag") {
|
|
||||||
for _, image := range c.StringSlice("additional-tag") {
|
|
||||||
ref, err := reference.ParseNormalizedNamed(image)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("error parsing additional-tag '%s': %v", image, err)
|
|
||||||
}
|
|
||||||
namedTagged, isNamedTagged := ref.(reference.NamedTagged)
|
|
||||||
if !isNamedTagged {
|
|
||||||
return fmt.Errorf("additional-tag '%s' must be a tagged reference", image)
|
|
||||||
}
|
|
||||||
destinationCtx.DockerArchiveAdditionalTags = append(destinationCtx.DockerArchiveAdditionalTags, namedTagged)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx, cancel := commandTimeoutContextFromGlobalOptions(c)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
_, err = copy.Image(ctx, policyContext, destRef, srcRef, ©.Options{
|
|
||||||
RemoveSignatures: removeSignatures,
|
|
||||||
SignBy: signBy,
|
|
||||||
ReportWriter: os.Stdout,
|
|
||||||
SourceCtx: sourceCtx,
|
|
||||||
DestinationCtx: destinationCtx,
|
|
||||||
ForceManifestMIMEType: manifestType,
|
|
||||||
})
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var copyCmd = cli.Command{
|
|
||||||
Name: "copy",
|
|
||||||
Usage: "Copy an IMAGE-NAME from one location to another",
|
|
||||||
Description: fmt.Sprintf(`
|
|
||||||
|
|
||||||
Container "IMAGE-NAME" uses a "transport":"details" format.
|
Container "IMAGE-NAME" uses a "transport":"details" format.
|
||||||
|
|
||||||
@@ -113,86 +46,99 @@ var copyCmd = cli.Command{
|
|||||||
|
|
||||||
See skopeo(1) section "IMAGE NAMES" for the expected format
|
See skopeo(1) section "IMAGE NAMES" for the expected format
|
||||||
`, strings.Join(transports.ListNames(), ", ")),
|
`, strings.Join(transports.ListNames(), ", ")),
|
||||||
ArgsUsage: "SOURCE-IMAGE DESTINATION-IMAGE",
|
ArgsUsage: "SOURCE-IMAGE DESTINATION-IMAGE",
|
||||||
Action: copyHandler,
|
Action: commandAction(opts.run),
|
||||||
// FIXME: Do we need to namespace the GPG aspect?
|
// FIXME: Do we need to namespace the GPG aspect?
|
||||||
Flags: []cli.Flag{
|
Flags: append(append(append([]cli.Flag{
|
||||||
cli.StringSliceFlag{
|
cli.StringSliceFlag{
|
||||||
Name: "additional-tag",
|
Name: "additional-tag",
|
||||||
Usage: "additional tags (supports docker-archive)",
|
Usage: "additional tags (supports docker-archive)",
|
||||||
},
|
Value: &opts.additionalTags, // Surprisingly StringSliceFlag does not support Destination:, but modifies Value: in place.
|
||||||
cli.StringFlag{
|
},
|
||||||
Name: "authfile",
|
cli.BoolFlag{
|
||||||
Usage: "path of the authentication file. Default is ${XDG_RUNTIME_DIR}/containers/auth.json",
|
Name: "remove-signatures",
|
||||||
},
|
Usage: "Do not copy signatures from SOURCE-IMAGE",
|
||||||
cli.BoolFlag{
|
Destination: &opts.removeSignatures,
|
||||||
Name: "remove-signatures",
|
},
|
||||||
Usage: "Do not copy signatures from SOURCE-IMAGE",
|
cli.StringFlag{
|
||||||
},
|
Name: "sign-by",
|
||||||
cli.StringFlag{
|
Usage: "Sign the image using a GPG key with the specified `FINGERPRINT`",
|
||||||
Name: "sign-by",
|
Destination: &opts.signByFingerprint,
|
||||||
Usage: "Sign the image using a GPG key with the specified `FINGERPRINT`",
|
},
|
||||||
},
|
cli.GenericFlag{
|
||||||
cli.StringFlag{
|
Name: "format, f",
|
||||||
Name: "src-creds, screds",
|
Usage: "`MANIFEST TYPE` (oci, v2s1, or v2s2) to use when saving image to directory using the 'dir:' transport (default is manifest type of source)",
|
||||||
Value: "",
|
Value: newOptionalStringValue(&opts.format),
|
||||||
Usage: "Use `USERNAME[:PASSWORD]` for accessing the source registry",
|
},
|
||||||
},
|
}, sharedFlags...), srcFlags...), destFlags...),
|
||||||
cli.StringFlag{
|
}
|
||||||
Name: "dest-creds, dcreds",
|
}
|
||||||
Value: "",
|
|
||||||
Usage: "Use `USERNAME[:PASSWORD]` for accessing the destination registry",
|
func (opts *copyOptions) run(args []string, stdout io.Writer) error {
|
||||||
},
|
if len(args) != 2 {
|
||||||
cli.StringFlag{
|
return errorShouldDisplayUsage{errors.New("Exactly two arguments expected")}
|
||||||
Name: "src-cert-dir",
|
}
|
||||||
Value: "",
|
|
||||||
Usage: "use certificates at `PATH` (*.crt, *.cert, *.key) to connect to the source registry or daemon",
|
policyContext, err := opts.global.getPolicyContext()
|
||||||
},
|
if err != nil {
|
||||||
cli.BoolTFlag{
|
return fmt.Errorf("Error loading trust policy: %v", err)
|
||||||
Name: "src-tls-verify",
|
}
|
||||||
Usage: "require HTTPS and verify certificates when talking to the container source registry or daemon (defaults to true)",
|
defer policyContext.Destroy()
|
||||||
},
|
|
||||||
cli.StringFlag{
|
srcRef, err := alltransports.ParseImageName(args[0])
|
||||||
Name: "dest-cert-dir",
|
if err != nil {
|
||||||
Value: "",
|
return fmt.Errorf("Invalid source name %s: %v", args[0], err)
|
||||||
Usage: "use certificates at `PATH` (*.crt, *.cert, *.key) to connect to the destination registry or daemon",
|
}
|
||||||
},
|
destRef, err := alltransports.ParseImageName(args[1])
|
||||||
cli.BoolTFlag{
|
if err != nil {
|
||||||
Name: "dest-tls-verify",
|
return fmt.Errorf("Invalid destination name %s: %v", args[1], err)
|
||||||
Usage: "require HTTPS and verify certificates when talking to the container destination registry or daemon (defaults to true)",
|
}
|
||||||
},
|
|
||||||
cli.StringFlag{
|
sourceCtx, err := opts.srcImage.newSystemContext()
|
||||||
Name: "dest-ostree-tmp-dir",
|
if err != nil {
|
||||||
Value: "",
|
return err
|
||||||
Usage: "`DIRECTORY` to use for OSTree temporary files",
|
}
|
||||||
},
|
destinationCtx, err := opts.destImage.newSystemContext()
|
||||||
cli.StringFlag{
|
if err != nil {
|
||||||
Name: "src-shared-blob-dir",
|
return err
|
||||||
Value: "",
|
}
|
||||||
Usage: "`DIRECTORY` to use to fetch retrieved blobs (OCI layout sources only)",
|
|
||||||
},
|
var manifestType string
|
||||||
cli.StringFlag{
|
if opts.format.present {
|
||||||
Name: "dest-shared-blob-dir",
|
switch opts.format.value {
|
||||||
Value: "",
|
case "oci":
|
||||||
Usage: "`DIRECTORY` to use to store retrieved blobs (OCI layout destinations only)",
|
manifestType = imgspecv1.MediaTypeImageManifest
|
||||||
},
|
case "v2s1":
|
||||||
cli.StringFlag{
|
manifestType = manifest.DockerV2Schema1SignedMediaType
|
||||||
Name: "format, f",
|
case "v2s2":
|
||||||
Usage: "`MANIFEST TYPE` (oci, v2s1, or v2s2) to use when saving image to directory using the 'dir:' transport (default is manifest type of source)",
|
manifestType = manifest.DockerV2Schema2MediaType
|
||||||
},
|
default:
|
||||||
cli.BoolFlag{
|
return fmt.Errorf("unknown format %q. Choose one of the supported formats: 'oci', 'v2s1', or 'v2s2'", opts.format.value)
|
||||||
Name: "dest-compress",
|
}
|
||||||
Usage: "Compress tarball image layers when saving to directory using the 'dir' transport. (default is same compression type as source)",
|
}
|
||||||
},
|
|
||||||
cli.StringFlag{
|
for _, image := range opts.additionalTags {
|
||||||
Name: "src-daemon-host",
|
ref, err := reference.ParseNormalizedNamed(image)
|
||||||
Value: "",
|
if err != nil {
|
||||||
Usage: "use docker daemon host at `HOST` (docker-daemon sources only)",
|
return fmt.Errorf("error parsing additional-tag '%s': %v", image, err)
|
||||||
},
|
}
|
||||||
cli.StringFlag{
|
namedTagged, isNamedTagged := ref.(reference.NamedTagged)
|
||||||
Name: "dest-daemon-host",
|
if !isNamedTagged {
|
||||||
Value: "",
|
return fmt.Errorf("additional-tag '%s' must be a tagged reference", image)
|
||||||
Usage: "use docker daemon host at `HOST` (docker-daemon destinations only)",
|
}
|
||||||
},
|
destinationCtx.DockerArchiveAdditionalTags = append(destinationCtx.DockerArchiveAdditionalTags, namedTagged)
|
||||||
},
|
}
|
||||||
|
|
||||||
|
ctx, cancel := opts.global.commandTimeoutContext()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
_, err = copy.Image(ctx, policyContext, destRef, srcRef, ©.Options{
|
||||||
|
RemoveSignatures: opts.removeSignatures,
|
||||||
|
SignBy: opts.signByFingerprint,
|
||||||
|
ReportWriter: stdout,
|
||||||
|
SourceCtx: sourceCtx,
|
||||||
|
DestinationCtx: destinationCtx,
|
||||||
|
ForceManifestMIMEType: manifestType,
|
||||||
|
})
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
@@ -3,6 +3,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/containers/image/transports"
|
"github.com/containers/image/transports"
|
||||||
@@ -10,30 +11,22 @@ import (
|
|||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
func deleteHandler(c *cli.Context) error {
|
type deleteOptions struct {
|
||||||
if len(c.Args()) != 1 {
|
global *globalOptions
|
||||||
return errors.New("Usage: delete imageReference")
|
image *imageOptions
|
||||||
}
|
|
||||||
|
|
||||||
ref, err := alltransports.ParseImageName(c.Args()[0])
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Invalid source name %s: %v", c.Args()[0], err)
|
|
||||||
}
|
|
||||||
|
|
||||||
sys, err := contextFromGlobalOptions(c, "")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx, cancel := commandTimeoutContextFromGlobalOptions(c)
|
|
||||||
defer cancel()
|
|
||||||
return ref.DeleteImage(ctx, sys)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var deleteCmd = cli.Command{
|
func deleteCmd(global *globalOptions) cli.Command {
|
||||||
Name: "delete",
|
sharedFlags, sharedOpts := sharedImageFlags()
|
||||||
Usage: "Delete image IMAGE-NAME",
|
imageFlags, imageOpts := imageFlags(global, sharedOpts, "", "")
|
||||||
Description: fmt.Sprintf(`
|
opts := deleteOptions{
|
||||||
|
global: global,
|
||||||
|
image: imageOpts,
|
||||||
|
}
|
||||||
|
return cli.Command{
|
||||||
|
Name: "delete",
|
||||||
|
Usage: "Delete image IMAGE-NAME",
|
||||||
|
Description: fmt.Sprintf(`
|
||||||
Delete an "IMAGE_NAME" from a transport
|
Delete an "IMAGE_NAME" from a transport
|
||||||
|
|
||||||
Supported transports:
|
Supported transports:
|
||||||
@@ -41,26 +34,28 @@ var deleteCmd = cli.Command{
|
|||||||
|
|
||||||
See skopeo(1) section "IMAGE NAMES" for the expected format
|
See skopeo(1) section "IMAGE NAMES" for the expected format
|
||||||
`, strings.Join(transports.ListNames(), ", ")),
|
`, strings.Join(transports.ListNames(), ", ")),
|
||||||
ArgsUsage: "IMAGE-NAME",
|
ArgsUsage: "IMAGE-NAME",
|
||||||
Action: deleteHandler,
|
Action: commandAction(opts.run),
|
||||||
Flags: []cli.Flag{
|
Flags: append(sharedFlags, imageFlags...),
|
||||||
cli.StringFlag{
|
}
|
||||||
Name: "authfile",
|
}
|
||||||
Usage: "path of the authentication file. Default is ${XDG_RUNTIME_DIR}/containers/auth.json",
|
|
||||||
},
|
func (opts *deleteOptions) run(args []string, stdout io.Writer) error {
|
||||||
cli.StringFlag{
|
if len(args) != 1 {
|
||||||
Name: "creds",
|
return errors.New("Usage: delete imageReference")
|
||||||
Value: "",
|
}
|
||||||
Usage: "Use `USERNAME[:PASSWORD]` for accessing the registry",
|
|
||||||
},
|
ref, err := alltransports.ParseImageName(args[0])
|
||||||
cli.StringFlag{
|
if err != nil {
|
||||||
Name: "cert-dir",
|
return fmt.Errorf("Invalid source name %s: %v", args[0], err)
|
||||||
Value: "",
|
}
|
||||||
Usage: "use certificates at `PATH` (*.crt, *.cert, *.key) to connect to the registry",
|
|
||||||
},
|
sys, err := opts.image.newSystemContext()
|
||||||
cli.BoolTFlag{
|
if err != nil {
|
||||||
Name: "tls-verify",
|
return err
|
||||||
Usage: "require HTTPS and verify certificates when talking to container registries (defaults to true)",
|
}
|
||||||
},
|
|
||||||
},
|
ctx, cancel := opts.global.commandTimeoutContext()
|
||||||
|
defer cancel()
|
||||||
|
return ref.DeleteImage(ctx, sys)
|
||||||
}
|
}
|
||||||
|
75
cmd/skopeo/flag.go
Normal file
75
cmd/skopeo/flag.go
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/urfave/cli"
|
||||||
|
)
|
||||||
|
|
||||||
|
// optionalBool is a boolean with a separate presence flag.
|
||||||
|
type optionalBool struct {
|
||||||
|
present bool
|
||||||
|
value bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// optionalBool is a cli.Generic == flag.Value implementation equivalent to
|
||||||
|
// the one underlying flag.Bool, except that it records whether the flag has been set.
|
||||||
|
// This is distinct from optionalBool to (pretend to) force callers to use
|
||||||
|
// newOptionalBool
|
||||||
|
type optionalBoolValue optionalBool
|
||||||
|
|
||||||
|
func newOptionalBoolValue(p *optionalBool) cli.Generic {
|
||||||
|
p.present = false
|
||||||
|
return (*optionalBoolValue)(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ob *optionalBoolValue) Set(s string) error {
|
||||||
|
v, err := strconv.ParseBool(s)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ob.value = v
|
||||||
|
ob.present = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ob *optionalBoolValue) String() string {
|
||||||
|
if !ob.present {
|
||||||
|
return "" // This is, sadly, not round-trip safe: --flag is interpreted as --flag=true
|
||||||
|
}
|
||||||
|
return strconv.FormatBool(ob.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ob *optionalBoolValue) IsBoolFlag() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// optionalString is a string with a separate presence flag.
|
||||||
|
type optionalString struct {
|
||||||
|
present bool
|
||||||
|
value string
|
||||||
|
}
|
||||||
|
|
||||||
|
// optionalString is a cli.Generic == flag.Value implementation equivalent to
|
||||||
|
// the one underlying flag.String, except that it records whether the flag has been set.
|
||||||
|
// This is distinct from optionalString to (pretend to) force callers to use
|
||||||
|
// newoptionalString
|
||||||
|
type optionalStringValue optionalString
|
||||||
|
|
||||||
|
func newOptionalStringValue(p *optionalString) cli.Generic {
|
||||||
|
p.present = false
|
||||||
|
return (*optionalStringValue)(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ob *optionalStringValue) Set(s string) error {
|
||||||
|
ob.value = s
|
||||||
|
ob.present = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ob *optionalStringValue) String() string {
|
||||||
|
if !ob.present {
|
||||||
|
return "" // This is, sadly, not round-trip safe: --flag= is interpreted as {present:true, value:""}
|
||||||
|
}
|
||||||
|
return ob.value
|
||||||
|
}
|
239
cmd/skopeo/flag_test.go
Normal file
239
cmd/skopeo/flag_test.go
Normal file
@@ -0,0 +1,239 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/urfave/cli"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestOptionalBoolSet(t *testing.T) {
|
||||||
|
for _, c := range []struct {
|
||||||
|
input string
|
||||||
|
accepted bool
|
||||||
|
value bool
|
||||||
|
}{
|
||||||
|
// Valid inputs documented for strconv.ParseBool == flag.BoolVar
|
||||||
|
{"1", true, true},
|
||||||
|
{"t", true, true},
|
||||||
|
{"T", true, true},
|
||||||
|
{"TRUE", true, true},
|
||||||
|
{"true", true, true},
|
||||||
|
{"True", true, true},
|
||||||
|
{"0", true, false},
|
||||||
|
{"f", true, false},
|
||||||
|
{"F", true, false},
|
||||||
|
{"FALSE", true, false},
|
||||||
|
{"false", true, false},
|
||||||
|
{"False", true, false},
|
||||||
|
// A few invalid inputs
|
||||||
|
{"", false, false},
|
||||||
|
{"yes", false, false},
|
||||||
|
{"no", false, false},
|
||||||
|
{"2", false, false},
|
||||||
|
} {
|
||||||
|
var ob optionalBool
|
||||||
|
v := newOptionalBoolValue(&ob)
|
||||||
|
require.False(t, ob.present)
|
||||||
|
err := v.Set(c.input)
|
||||||
|
if c.accepted {
|
||||||
|
assert.NoError(t, err, c.input)
|
||||||
|
assert.Equal(t, c.value, ob.value)
|
||||||
|
} else {
|
||||||
|
assert.Error(t, err, c.input)
|
||||||
|
assert.False(t, ob.present) // Just to be extra paranoid.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nothing actually explicitly says that .Set() is never called when the flag is not present on the command line;
|
||||||
|
// so, check that it is not being called, at least in the straightforward case (it's not possible to test that it
|
||||||
|
// is not called in any possible situation).
|
||||||
|
var globalOB, commandOB optionalBool
|
||||||
|
actionRun := false
|
||||||
|
app := cli.NewApp()
|
||||||
|
app.EnableBashCompletion = true
|
||||||
|
app.Flags = []cli.Flag{
|
||||||
|
cli.GenericFlag{
|
||||||
|
Name: "global-OB",
|
||||||
|
Value: newOptionalBoolValue(&globalOB),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
app.Commands = []cli.Command{{
|
||||||
|
Name: "cmd",
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
cli.GenericFlag{
|
||||||
|
Name: "command-OB",
|
||||||
|
Value: newOptionalBoolValue(&commandOB),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Action: func(*cli.Context) error {
|
||||||
|
assert.False(t, globalOB.present)
|
||||||
|
assert.False(t, commandOB.present)
|
||||||
|
actionRun = true
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
err := app.Run([]string{"app", "cmd"})
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.True(t, actionRun)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOptionalBoolString(t *testing.T) {
|
||||||
|
for _, c := range []struct {
|
||||||
|
input optionalBool
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{optionalBool{present: true, value: true}, "true"},
|
||||||
|
{optionalBool{present: true, value: false}, "false"},
|
||||||
|
{optionalBool{present: false, value: true}, ""},
|
||||||
|
{optionalBool{present: false, value: false}, ""},
|
||||||
|
} {
|
||||||
|
var ob optionalBool
|
||||||
|
v := newOptionalBoolValue(&ob)
|
||||||
|
ob = c.input
|
||||||
|
res := v.String()
|
||||||
|
assert.Equal(t, c.expected, res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOptionalBoolIsBoolFlag(t *testing.T) {
|
||||||
|
// IsBoolFlag means that the argument value must either be part of the same argument, with =;
|
||||||
|
// if there is no =, the value is set to true.
|
||||||
|
// This differs form other flags, where the argument is required and may be either separated with = or supplied in the next argument.
|
||||||
|
for _, c := range []struct {
|
||||||
|
input []string
|
||||||
|
expectedOB optionalBool
|
||||||
|
expectedArgs []string
|
||||||
|
}{
|
||||||
|
{[]string{"1", "2"}, optionalBool{present: false}, []string{"1", "2"}}, // Flag not present
|
||||||
|
{[]string{"--OB=true", "1", "2"}, optionalBool{present: true, value: true}, []string{"1", "2"}}, // --OB=true
|
||||||
|
{[]string{"--OB=false", "1", "2"}, optionalBool{present: true, value: false}, []string{"1", "2"}}, // --OB=false
|
||||||
|
{[]string{"--OB", "true", "1", "2"}, optionalBool{present: true, value: true}, []string{"true", "1", "2"}}, // --OB true
|
||||||
|
{[]string{"--OB", "false", "1", "2"}, optionalBool{present: true, value: true}, []string{"false", "1", "2"}}, // --OB false
|
||||||
|
} {
|
||||||
|
var ob optionalBool
|
||||||
|
actionRun := false
|
||||||
|
app := cli.NewApp()
|
||||||
|
app.Commands = []cli.Command{{
|
||||||
|
Name: "cmd",
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
cli.GenericFlag{
|
||||||
|
Name: "OB",
|
||||||
|
Value: newOptionalBoolValue(&ob),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Action: func(ctx *cli.Context) error {
|
||||||
|
assert.Equal(t, c.expectedOB, ob)
|
||||||
|
assert.Equal(t, c.expectedArgs, ([]string)(ctx.Args()))
|
||||||
|
actionRun = true
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
err := app.Run(append([]string{"app", "cmd"}, c.input...))
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.True(t, actionRun)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOptionalStringSet(t *testing.T) {
|
||||||
|
// Really just a smoke test, but differentiating between not present and empty.
|
||||||
|
for _, c := range []string{"", "hello"} {
|
||||||
|
var os optionalString
|
||||||
|
v := newOptionalStringValue(&os)
|
||||||
|
require.False(t, os.present)
|
||||||
|
err := v.Set(c)
|
||||||
|
assert.NoError(t, err, c)
|
||||||
|
assert.Equal(t, c, os.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nothing actually explicitly says that .Set() is never called when the flag is not present on the command line;
|
||||||
|
// so, check that it is not being called, at least in the straightforward case (it's not possible to test that it
|
||||||
|
// is not called in any possible situation).
|
||||||
|
var globalOS, commandOS optionalString
|
||||||
|
actionRun := false
|
||||||
|
app := cli.NewApp()
|
||||||
|
app.EnableBashCompletion = true
|
||||||
|
app.Flags = []cli.Flag{
|
||||||
|
cli.GenericFlag{
|
||||||
|
Name: "global-OS",
|
||||||
|
Value: newOptionalStringValue(&globalOS),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
app.Commands = []cli.Command{{
|
||||||
|
Name: "cmd",
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
cli.GenericFlag{
|
||||||
|
Name: "command-OS",
|
||||||
|
Value: newOptionalStringValue(&commandOS),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Action: func(*cli.Context) error {
|
||||||
|
assert.False(t, globalOS.present)
|
||||||
|
assert.False(t, commandOS.present)
|
||||||
|
actionRun = true
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
err := app.Run([]string{"app", "cmd"})
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.True(t, actionRun)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOptionalStringString(t *testing.T) {
|
||||||
|
for _, c := range []struct {
|
||||||
|
input optionalString
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{optionalString{present: true, value: "hello"}, "hello"},
|
||||||
|
{optionalString{present: true, value: ""}, ""},
|
||||||
|
{optionalString{present: false, value: "hello"}, ""},
|
||||||
|
{optionalString{present: false, value: ""}, ""},
|
||||||
|
} {
|
||||||
|
var os optionalString
|
||||||
|
v := newOptionalStringValue(&os)
|
||||||
|
os = c.input
|
||||||
|
res := v.String()
|
||||||
|
assert.Equal(t, c.expected, res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOptionalStringIsBoolFlag(t *testing.T) {
|
||||||
|
// NOTE: optionalStringValue does not implement IsBoolFlag!
|
||||||
|
// IsBoolFlag means that the argument value must either be part of the same argument, with =;
|
||||||
|
// if there is no =, the value is set to true.
|
||||||
|
// This differs form other flags, where the argument is required and may be either separated with = or supplied in the next argument.
|
||||||
|
for _, c := range []struct {
|
||||||
|
input []string
|
||||||
|
expectedOS optionalString
|
||||||
|
expectedArgs []string
|
||||||
|
}{
|
||||||
|
{[]string{"1", "2"}, optionalString{present: false}, []string{"1", "2"}}, // Flag not present
|
||||||
|
{[]string{"--OS=hello", "1", "2"}, optionalString{present: true, value: "hello"}, []string{"1", "2"}}, // --OS=true
|
||||||
|
{[]string{"--OS=", "1", "2"}, optionalString{present: true, value: ""}, []string{"1", "2"}}, // --OS=false
|
||||||
|
{[]string{"--OS", "hello", "1", "2"}, optionalString{present: true, value: "hello"}, []string{"1", "2"}}, // --OS true
|
||||||
|
{[]string{"--OS", "", "1", "2"}, optionalString{present: true, value: ""}, []string{"1", "2"}}, // --OS false
|
||||||
|
} {
|
||||||
|
var os optionalString
|
||||||
|
actionRun := false
|
||||||
|
app := cli.NewApp()
|
||||||
|
app.Commands = []cli.Command{{
|
||||||
|
Name: "cmd",
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
cli.GenericFlag{
|
||||||
|
Name: "OS",
|
||||||
|
Value: newOptionalStringValue(&os),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Action: func(ctx *cli.Context) error {
|
||||||
|
assert.Equal(t, c.expectedOS, os)
|
||||||
|
assert.Equal(t, c.expectedArgs, ([]string)(ctx.Args()))
|
||||||
|
actionRun = true
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
err := app.Run(append([]string{"app", "cmd"}, c.input...))
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.True(t, actionRun)
|
||||||
|
}
|
||||||
|
}
|
@@ -3,6 +3,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -29,10 +30,23 @@ type inspectOutput struct {
|
|||||||
Layers []string
|
Layers []string
|
||||||
}
|
}
|
||||||
|
|
||||||
var inspectCmd = cli.Command{
|
type inspectOptions struct {
|
||||||
Name: "inspect",
|
global *globalOptions
|
||||||
Usage: "Inspect image IMAGE-NAME",
|
image *imageOptions
|
||||||
Description: fmt.Sprintf(`
|
raw bool // Output the raw manifest instead of parsing information about the image
|
||||||
|
}
|
||||||
|
|
||||||
|
func inspectCmd(global *globalOptions) cli.Command {
|
||||||
|
sharedFlags, sharedOpts := sharedImageFlags()
|
||||||
|
imageFlags, imageOpts := imageFlags(global, sharedOpts, "", "")
|
||||||
|
opts := inspectOptions{
|
||||||
|
global: global,
|
||||||
|
image: imageOpts,
|
||||||
|
}
|
||||||
|
return cli.Command{
|
||||||
|
Name: "inspect",
|
||||||
|
Usage: "Inspect image IMAGE-NAME",
|
||||||
|
Description: fmt.Sprintf(`
|
||||||
Return low-level information about "IMAGE-NAME" in a registry/transport
|
Return low-level information about "IMAGE-NAME" in a registry/transport
|
||||||
|
|
||||||
Supported transports:
|
Supported transports:
|
||||||
@@ -40,101 +54,90 @@ var inspectCmd = cli.Command{
|
|||||||
|
|
||||||
See skopeo(1) section "IMAGE NAMES" for the expected format
|
See skopeo(1) section "IMAGE NAMES" for the expected format
|
||||||
`, strings.Join(transports.ListNames(), ", ")),
|
`, strings.Join(transports.ListNames(), ", ")),
|
||||||
ArgsUsage: "IMAGE-NAME",
|
ArgsUsage: "IMAGE-NAME",
|
||||||
Flags: []cli.Flag{
|
Flags: append(append([]cli.Flag{
|
||||||
cli.StringFlag{
|
cli.BoolFlag{
|
||||||
Name: "authfile",
|
Name: "raw",
|
||||||
Usage: "path of the authentication file. Default is ${XDG_RUNTIME_DIR}/containers/auth.json",
|
Usage: "output raw manifest",
|
||||||
},
|
Destination: &opts.raw,
|
||||||
cli.StringFlag{
|
},
|
||||||
Name: "cert-dir",
|
}, sharedFlags...), imageFlags...),
|
||||||
Value: "",
|
Action: commandAction(opts.run),
|
||||||
Usage: "use certificates at `PATH` (*.crt, *.cert, *.key) to connect to the registry",
|
}
|
||||||
},
|
}
|
||||||
cli.BoolTFlag{
|
|
||||||
Name: "tls-verify",
|
func (opts *inspectOptions) run(args []string, stdout io.Writer) (retErr error) {
|
||||||
Usage: "require HTTPS and verify certificates when talking to container registries (defaults to true)",
|
ctx, cancel := opts.global.commandTimeoutContext()
|
||||||
},
|
defer cancel()
|
||||||
cli.BoolFlag{
|
|
||||||
Name: "raw",
|
if len(args) != 1 {
|
||||||
Usage: "output raw manifest",
|
return errors.New("Exactly one argument expected")
|
||||||
},
|
}
|
||||||
cli.StringFlag{
|
img, err := parseImage(ctx, opts.image, args[0])
|
||||||
Name: "creds",
|
if err != nil {
|
||||||
Value: "",
|
return err
|
||||||
Usage: "Use `USERNAME[:PASSWORD]` for accessing the registry",
|
}
|
||||||
},
|
|
||||||
},
|
defer func() {
|
||||||
Action: func(c *cli.Context) (retErr error) {
|
if err := img.Close(); err != nil {
|
||||||
ctx, cancel := commandTimeoutContextFromGlobalOptions(c)
|
retErr = errors.Wrapf(retErr, fmt.Sprintf("(could not close image: %v) ", err))
|
||||||
defer cancel()
|
}
|
||||||
|
}()
|
||||||
img, err := parseImage(ctx, c)
|
|
||||||
if err != nil {
|
rawManifest, _, err := img.Manifest(ctx)
|
||||||
return err
|
if err != nil {
|
||||||
}
|
return err
|
||||||
|
}
|
||||||
defer func() {
|
if opts.raw {
|
||||||
if err := img.Close(); err != nil {
|
_, err := stdout.Write(rawManifest)
|
||||||
retErr = errors.Wrapf(retErr, fmt.Sprintf("(could not close image: %v) ", err))
|
if err != nil {
|
||||||
}
|
return fmt.Errorf("Error writing manifest to standard output: %v", err)
|
||||||
}()
|
}
|
||||||
|
return nil
|
||||||
rawManifest, _, err := img.Manifest(ctx)
|
}
|
||||||
if err != nil {
|
imgInspect, err := img.Inspect(ctx)
|
||||||
return err
|
if err != nil {
|
||||||
}
|
return err
|
||||||
if c.Bool("raw") {
|
}
|
||||||
_, err := c.App.Writer.Write(rawManifest)
|
outputData := inspectOutput{
|
||||||
if err != nil {
|
Name: "", // Set below if DockerReference() is known
|
||||||
return fmt.Errorf("Error writing manifest to standard output: %v", err)
|
Tag: imgInspect.Tag,
|
||||||
}
|
// Digest is set below.
|
||||||
return nil
|
RepoTags: []string{}, // Possibly overriden for docker.Transport.
|
||||||
}
|
Created: imgInspect.Created,
|
||||||
imgInspect, err := img.Inspect(ctx)
|
DockerVersion: imgInspect.DockerVersion,
|
||||||
if err != nil {
|
Labels: imgInspect.Labels,
|
||||||
return err
|
Architecture: imgInspect.Architecture,
|
||||||
}
|
Os: imgInspect.Os,
|
||||||
outputData := inspectOutput{
|
Layers: imgInspect.Layers,
|
||||||
Name: "", // Set below if DockerReference() is known
|
}
|
||||||
Tag: imgInspect.Tag,
|
outputData.Digest, err = manifest.Digest(rawManifest)
|
||||||
// Digest is set below.
|
if err != nil {
|
||||||
RepoTags: []string{}, // Possibly overriden for docker.Transport.
|
return fmt.Errorf("Error computing manifest digest: %v", err)
|
||||||
Created: imgInspect.Created,
|
}
|
||||||
DockerVersion: imgInspect.DockerVersion,
|
if dockerRef := img.Reference().DockerReference(); dockerRef != nil {
|
||||||
Labels: imgInspect.Labels,
|
outputData.Name = dockerRef.Name()
|
||||||
Architecture: imgInspect.Architecture,
|
}
|
||||||
Os: imgInspect.Os,
|
if img.Reference().Transport() == docker.Transport {
|
||||||
Layers: imgInspect.Layers,
|
sys, err := opts.image.newSystemContext()
|
||||||
}
|
if err != nil {
|
||||||
outputData.Digest, err = manifest.Digest(rawManifest)
|
return err
|
||||||
if err != nil {
|
}
|
||||||
return fmt.Errorf("Error computing manifest digest: %v", err)
|
outputData.RepoTags, err = docker.GetRepositoryTags(ctx, sys, img.Reference())
|
||||||
}
|
if err != nil {
|
||||||
if dockerRef := img.Reference().DockerReference(); dockerRef != nil {
|
// some registries may decide to block the "list all tags" endpoint
|
||||||
outputData.Name = dockerRef.Name()
|
// gracefully allow the inspect to continue in this case. Currently
|
||||||
}
|
// the IBM Bluemix container registry has this restriction.
|
||||||
if img.Reference().Transport() == docker.Transport {
|
if !strings.Contains(err.Error(), "401") {
|
||||||
sys, err := contextFromGlobalOptions(c, "")
|
return fmt.Errorf("Error determining repository tags: %v", err)
|
||||||
if err != nil {
|
}
|
||||||
return err
|
logrus.Warnf("Registry disallows tag list retrieval; skipping")
|
||||||
}
|
}
|
||||||
outputData.RepoTags, err = docker.GetRepositoryTags(ctx, sys, img.Reference())
|
}
|
||||||
if err != nil {
|
out, err := json.MarshalIndent(outputData, "", " ")
|
||||||
// some registries may decide to block the "list all tags" endpoint
|
if err != nil {
|
||||||
// gracefully allow the inspect to continue in this case. Currently
|
return err
|
||||||
// the IBM Bluemix container registry has this restriction.
|
}
|
||||||
if !strings.Contains(err.Error(), "401") {
|
fmt.Fprintf(stdout, "%s\n", string(out))
|
||||||
return fmt.Errorf("Error determining repository tags: %v", err)
|
return nil
|
||||||
}
|
|
||||||
logrus.Warnf("Registry disallows tag list retrieval; skipping")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
out, err := json.MarshalIndent(outputData, "", " ")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fmt.Fprintln(c.App.Writer, string(out))
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
@@ -2,6 +2,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -15,114 +16,130 @@ import (
|
|||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
var layersCmd = cli.Command{
|
type layersOptions struct {
|
||||||
Name: "layers",
|
global *globalOptions
|
||||||
Usage: "Get layers of IMAGE-NAME",
|
image *imageOptions
|
||||||
ArgsUsage: "IMAGE-NAME [LAYER...]",
|
}
|
||||||
Hidden: true,
|
|
||||||
Action: func(c *cli.Context) (retErr error) {
|
func layersCmd(global *globalOptions) cli.Command {
|
||||||
fmt.Fprintln(os.Stderr, `DEPRECATED: skopeo layers is deprecated in favor of skopeo copy`)
|
sharedFlags, sharedOpts := sharedImageFlags()
|
||||||
if c.NArg() == 0 {
|
imageFlags, imageOpts := imageFlags(global, sharedOpts, "", "")
|
||||||
return errors.New("Usage: layers imageReference [layer...]")
|
opts := layersOptions{
|
||||||
|
global: global,
|
||||||
|
image: imageOpts,
|
||||||
|
}
|
||||||
|
return cli.Command{
|
||||||
|
Name: "layers",
|
||||||
|
Usage: "Get layers of IMAGE-NAME",
|
||||||
|
ArgsUsage: "IMAGE-NAME [LAYER...]",
|
||||||
|
Hidden: true,
|
||||||
|
Action: commandAction(opts.run),
|
||||||
|
Flags: append(sharedFlags, imageFlags...),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (opts *layersOptions) run(args []string, stdout io.Writer) (retErr error) {
|
||||||
|
fmt.Fprintln(os.Stderr, `DEPRECATED: skopeo layers is deprecated in favor of skopeo copy`)
|
||||||
|
if len(args) == 0 {
|
||||||
|
return errors.New("Usage: layers imageReference [layer...]")
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := opts.global.commandTimeoutContext()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
sys, err := opts.image.newSystemContext()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
cache := blobinfocache.DefaultCache(sys)
|
||||||
|
rawSource, err := parseImageSource(ctx, opts.image, args[0])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
src, err := image.FromSource(ctx, sys, rawSource)
|
||||||
|
if err != nil {
|
||||||
|
if closeErr := rawSource.Close(); closeErr != nil {
|
||||||
|
return errors.Wrapf(err, " (close error: %v)", closeErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := commandTimeoutContextFromGlobalOptions(c)
|
return err
|
||||||
defer cancel()
|
}
|
||||||
|
defer func() {
|
||||||
|
if err := src.Close(); err != nil {
|
||||||
|
retErr = errors.Wrapf(retErr, " (close error: %v)", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
sys, err := contextFromGlobalOptions(c, "")
|
type blobDigest struct {
|
||||||
|
digest digest.Digest
|
||||||
|
isConfig bool
|
||||||
|
}
|
||||||
|
var blobDigests []blobDigest
|
||||||
|
for _, dString := range args[1:] {
|
||||||
|
if !strings.HasPrefix(dString, "sha256:") {
|
||||||
|
dString = "sha256:" + dString
|
||||||
|
}
|
||||||
|
d, err := digest.Parse(dString)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
cache := blobinfocache.DefaultCache(sys)
|
blobDigests = append(blobDigests, blobDigest{digest: d, isConfig: false})
|
||||||
rawSource, err := parseImageSource(ctx, c, c.Args()[0])
|
}
|
||||||
|
|
||||||
|
if len(blobDigests) == 0 {
|
||||||
|
layers := src.LayerInfos()
|
||||||
|
seenLayers := map[digest.Digest]struct{}{}
|
||||||
|
for _, info := range layers {
|
||||||
|
if _, ok := seenLayers[info.Digest]; !ok {
|
||||||
|
blobDigests = append(blobDigests, blobDigest{digest: info.Digest, isConfig: false})
|
||||||
|
seenLayers[info.Digest] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
configInfo := src.ConfigInfo()
|
||||||
|
if configInfo.Digest != "" {
|
||||||
|
blobDigests = append(blobDigests, blobDigest{digest: configInfo.Digest, isConfig: true})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tmpDir, err := ioutil.TempDir(".", "layers-")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
tmpDirRef, err := directory.NewReference(tmpDir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
dest, err := tmpDirRef.NewImageDestination(ctx, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if err := dest.Close(); err != nil {
|
||||||
|
retErr = errors.Wrapf(retErr, " (close error: %v)", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
for _, bd := range blobDigests {
|
||||||
|
r, blobSize, err := rawSource.GetBlob(ctx, types.BlobInfo{Digest: bd.digest, Size: -1}, cache)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
src, err := image.FromSource(ctx, sys, rawSource)
|
if _, err := dest.PutBlob(ctx, r, types.BlobInfo{Digest: bd.digest, Size: blobSize}, cache, bd.isConfig); err != nil {
|
||||||
if err != nil {
|
if closeErr := r.Close(); closeErr != nil {
|
||||||
if closeErr := rawSource.Close(); closeErr != nil {
|
|
||||||
return errors.Wrapf(err, " (close error: %v)", closeErr)
|
return errors.Wrapf(err, " (close error: %v)", closeErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer func() {
|
}
|
||||||
if err := src.Close(); err != nil {
|
|
||||||
retErr = errors.Wrapf(retErr, " (close error: %v)", err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
type blobDigest struct {
|
manifest, _, err := src.Manifest(ctx)
|
||||||
digest digest.Digest
|
if err != nil {
|
||||||
isConfig bool
|
return err
|
||||||
}
|
}
|
||||||
var blobDigests []blobDigest
|
if err := dest.PutManifest(ctx, manifest); err != nil {
|
||||||
for _, dString := range c.Args().Tail() {
|
return err
|
||||||
if !strings.HasPrefix(dString, "sha256:") {
|
}
|
||||||
dString = "sha256:" + dString
|
|
||||||
}
|
|
||||||
d, err := digest.Parse(dString)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
blobDigests = append(blobDigests, blobDigest{digest: d, isConfig: false})
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(blobDigests) == 0 {
|
return dest.Commit(ctx)
|
||||||
layers := src.LayerInfos()
|
|
||||||
seenLayers := map[digest.Digest]struct{}{}
|
|
||||||
for _, info := range layers {
|
|
||||||
if _, ok := seenLayers[info.Digest]; !ok {
|
|
||||||
blobDigests = append(blobDigests, blobDigest{digest: info.Digest, isConfig: false})
|
|
||||||
seenLayers[info.Digest] = struct{}{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
configInfo := src.ConfigInfo()
|
|
||||||
if configInfo.Digest != "" {
|
|
||||||
blobDigests = append(blobDigests, blobDigest{digest: configInfo.Digest, isConfig: true})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tmpDir, err := ioutil.TempDir(".", "layers-")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
tmpDirRef, err := directory.NewReference(tmpDir)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
dest, err := tmpDirRef.NewImageDestination(ctx, nil)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
if err := dest.Close(); err != nil {
|
|
||||||
retErr = errors.Wrapf(retErr, " (close error: %v)", err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
for _, bd := range blobDigests {
|
|
||||||
r, blobSize, err := rawSource.GetBlob(ctx, types.BlobInfo{Digest: bd.digest, Size: -1}, cache)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if _, err := dest.PutBlob(ctx, r, types.BlobInfo{Digest: bd.digest, Size: blobSize}, cache, bd.isConfig); err != nil {
|
|
||||||
if closeErr := r.Close(); closeErr != nil {
|
|
||||||
return errors.Wrapf(err, " (close error: %v)", closeErr)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
manifest, _, err := src.Manifest(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := dest.PutManifest(ctx, manifest); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return dest.Commit(ctx)
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
@@ -1,8 +1,10 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/containers/image/signature"
|
"github.com/containers/image/signature"
|
||||||
"github.com/containers/skopeo/version"
|
"github.com/containers/skopeo/version"
|
||||||
@@ -15,8 +17,21 @@ import (
|
|||||||
// and will be populated by the Makefile
|
// and will be populated by the Makefile
|
||||||
var gitCommit = ""
|
var gitCommit = ""
|
||||||
|
|
||||||
// createApp returns a cli.App to be run or tested.
|
type globalOptions struct {
|
||||||
func createApp() *cli.App {
|
debug bool // Enable debug output
|
||||||
|
tlsVerify optionalBool // Require HTTPS and verify certificates (for docker: and docker-daemon:)
|
||||||
|
policyPath string // Path to a signature verification policy file
|
||||||
|
insecurePolicy bool // Use an "allow everything" signature verification policy
|
||||||
|
registriesDirPath string // Path to a "registries.d" registry configuratio directory
|
||||||
|
overrideArch string // Architecture to use for choosing images, instead of the runtime one
|
||||||
|
overrideOS string // OS to use for choosing images, instead of the runtime one
|
||||||
|
commandTimeout time.Duration // Timeout for the command execution
|
||||||
|
}
|
||||||
|
|
||||||
|
// createApp returns a cli.App, and the underlying globalOptions object, to be run or tested.
|
||||||
|
func createApp() (*cli.App, *globalOptions) {
|
||||||
|
opts := globalOptions{}
|
||||||
|
|
||||||
app := cli.NewApp()
|
app := cli.NewApp()
|
||||||
app.EnableBashCompletion = true
|
app.EnableBashCompletion = true
|
||||||
app.Name = "skopeo"
|
app.Name = "skopeo"
|
||||||
@@ -28,89 +43,106 @@ func createApp() *cli.App {
|
|||||||
app.Usage = "Various operations with container images and container image registries"
|
app.Usage = "Various operations with container images and container image registries"
|
||||||
app.Flags = []cli.Flag{
|
app.Flags = []cli.Flag{
|
||||||
cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "debug",
|
Name: "debug",
|
||||||
Usage: "enable debug output",
|
Usage: "enable debug output",
|
||||||
|
Destination: &opts.debug,
|
||||||
},
|
},
|
||||||
cli.BoolTFlag{
|
cli.GenericFlag{
|
||||||
Name: "tls-verify",
|
Name: "tls-verify",
|
||||||
Usage: "require HTTPS and verify certificates when talking to container registries (defaults to true)",
|
Usage: "require HTTPS and verify certificates when talking to container registries (defaults to true)",
|
||||||
Hidden: true,
|
Hidden: true,
|
||||||
|
Value: newOptionalBoolValue(&opts.tlsVerify),
|
||||||
},
|
},
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "policy",
|
Name: "policy",
|
||||||
Value: "",
|
Usage: "Path to a trust policy file",
|
||||||
Usage: "Path to a trust policy file",
|
Destination: &opts.policyPath,
|
||||||
},
|
},
|
||||||
cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "insecure-policy",
|
Name: "insecure-policy",
|
||||||
Usage: "run the tool without any policy check",
|
Usage: "run the tool without any policy check",
|
||||||
|
Destination: &opts.insecurePolicy,
|
||||||
},
|
},
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "registries.d",
|
Name: "registries.d",
|
||||||
Value: "",
|
Usage: "use registry configuration files in `DIR` (e.g. for container signature storage)",
|
||||||
Usage: "use registry configuration files in `DIR` (e.g. for container signature storage)",
|
Destination: &opts.registriesDirPath,
|
||||||
},
|
},
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "override-arch",
|
Name: "override-arch",
|
||||||
Value: "",
|
Usage: "use `ARCH` instead of the architecture of the machine for choosing images",
|
||||||
Usage: "use `ARCH` instead of the architecture of the machine for choosing images",
|
Destination: &opts.overrideArch,
|
||||||
},
|
},
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "override-os",
|
Name: "override-os",
|
||||||
Value: "",
|
Usage: "use `OS` instead of the running OS for choosing images",
|
||||||
Usage: "use `OS` instead of the running OS for choosing images",
|
Destination: &opts.overrideOS,
|
||||||
},
|
},
|
||||||
cli.DurationFlag{
|
cli.DurationFlag{
|
||||||
Name: "command-timeout",
|
Name: "command-timeout",
|
||||||
Usage: "timeout for the command execution",
|
Usage: "timeout for the command execution",
|
||||||
|
Destination: &opts.commandTimeout,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
app.Before = func(c *cli.Context) error {
|
app.Before = opts.before
|
||||||
if c.GlobalBool("debug") {
|
|
||||||
logrus.SetLevel(logrus.DebugLevel)
|
|
||||||
}
|
|
||||||
if c.GlobalIsSet("tls-verify") {
|
|
||||||
logrus.Warn("'--tls-verify' is deprecated, please set this on the specific subcommand")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
app.Commands = []cli.Command{
|
app.Commands = []cli.Command{
|
||||||
copyCmd,
|
copyCmd(&opts),
|
||||||
inspectCmd,
|
inspectCmd(&opts),
|
||||||
layersCmd,
|
layersCmd(&opts),
|
||||||
deleteCmd,
|
deleteCmd(&opts),
|
||||||
manifestDigestCmd,
|
manifestDigestCmd(),
|
||||||
standaloneSignCmd,
|
standaloneSignCmd(),
|
||||||
standaloneVerifyCmd,
|
standaloneVerifyCmd(),
|
||||||
untrustedSignatureDumpCmd,
|
untrustedSignatureDumpCmd(),
|
||||||
}
|
}
|
||||||
return app
|
return app, &opts
|
||||||
|
}
|
||||||
|
|
||||||
|
// before is run by the cli package for any command, before running the command-specific handler.
|
||||||
|
func (opts *globalOptions) before(_ *cli.Context) error {
|
||||||
|
if opts.debug {
|
||||||
|
logrus.SetLevel(logrus.DebugLevel)
|
||||||
|
}
|
||||||
|
if opts.tlsVerify.present {
|
||||||
|
logrus.Warn("'--tls-verify' is deprecated, please set this on the specific subcommand")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
if reexec.Init() {
|
if reexec.Init() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
app := createApp()
|
app, _ := createApp()
|
||||||
if err := app.Run(os.Args); err != nil {
|
if err := app.Run(os.Args); err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// getPolicyContext handles the global "policy" flag.
|
// getPolicyContext returns a *signature.PolicyContext based on opts.
|
||||||
func getPolicyContext(c *cli.Context) (*signature.PolicyContext, error) {
|
func (opts *globalOptions) getPolicyContext() (*signature.PolicyContext, error) {
|
||||||
policyPath := c.GlobalString("policy")
|
var policy *signature.Policy // This could be cached across calls in opts.
|
||||||
var policy *signature.Policy // This could be cached across calls, if we had an application context.
|
|
||||||
var err error
|
var err error
|
||||||
if c.GlobalBool("insecure-policy") {
|
if opts.insecurePolicy {
|
||||||
policy = &signature.Policy{Default: []signature.PolicyRequirement{signature.NewPRInsecureAcceptAnything()}}
|
policy = &signature.Policy{Default: []signature.PolicyRequirement{signature.NewPRInsecureAcceptAnything()}}
|
||||||
} else if policyPath == "" {
|
} else if opts.policyPath == "" {
|
||||||
policy, err = signature.DefaultPolicy(nil)
|
policy, err = signature.DefaultPolicy(nil)
|
||||||
} else {
|
} else {
|
||||||
policy, err = signature.NewPolicyFromFile(policyPath)
|
policy, err = signature.NewPolicyFromFile(opts.policyPath)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return signature.NewPolicyContext(policy)
|
return signature.NewPolicyContext(policy)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// commandTimeoutContext returns a context.Context and a cancellation callback based on opts.
|
||||||
|
// The caller should usually "defer cancel()" immediately after calling this.
|
||||||
|
func (opts *globalOptions) commandTimeoutContext() (context.Context, context.CancelFunc) {
|
||||||
|
ctx := context.Background()
|
||||||
|
var cancel context.CancelFunc = func() {}
|
||||||
|
if opts.commandTimeout > 0 {
|
||||||
|
ctx, cancel = context.WithTimeout(ctx, opts.commandTimeout)
|
||||||
|
}
|
||||||
|
return ctx, cancel
|
||||||
|
}
|
||||||
|
@@ -5,7 +5,7 @@ import "bytes"
|
|||||||
// runSkopeo creates an app object and runs it with args, with an implied first "skopeo".
|
// runSkopeo creates an app object and runs it with args, with an implied first "skopeo".
|
||||||
// Returns output intended for stdout and the returned error, if any.
|
// Returns output intended for stdout and the returned error, if any.
|
||||||
func runSkopeo(args ...string) (string, error) {
|
func runSkopeo(args ...string) (string, error) {
|
||||||
app := createApp()
|
app, _ := createApp()
|
||||||
stdout := bytes.Buffer{}
|
stdout := bytes.Buffer{}
|
||||||
app.Writer = &stdout
|
app.Writer = &stdout
|
||||||
args = append([]string{"skopeo"}, args...)
|
args = append([]string{"skopeo"}, args...)
|
||||||
|
@@ -3,17 +3,31 @@ package main
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
|
||||||
"github.com/containers/image/manifest"
|
"github.com/containers/image/manifest"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
func manifestDigest(context *cli.Context) error {
|
type manifestDigestOptions struct {
|
||||||
if len(context.Args()) != 1 {
|
}
|
||||||
|
|
||||||
|
func manifestDigestCmd() cli.Command {
|
||||||
|
opts := manifestDigestOptions{}
|
||||||
|
return cli.Command{
|
||||||
|
Name: "manifest-digest",
|
||||||
|
Usage: "Compute a manifest digest of a file",
|
||||||
|
ArgsUsage: "MANIFEST",
|
||||||
|
Action: commandAction(opts.run),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (opts *manifestDigestOptions) run(args []string, stdout io.Writer) error {
|
||||||
|
if len(args) != 1 {
|
||||||
return errors.New("Usage: skopeo manifest-digest manifest")
|
return errors.New("Usage: skopeo manifest-digest manifest")
|
||||||
}
|
}
|
||||||
manifestPath := context.Args()[0]
|
manifestPath := args[0]
|
||||||
|
|
||||||
man, err := ioutil.ReadFile(manifestPath)
|
man, err := ioutil.ReadFile(manifestPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -23,13 +37,6 @@ func manifestDigest(context *cli.Context) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Error computing digest: %v", err)
|
return fmt.Errorf("Error computing digest: %v", err)
|
||||||
}
|
}
|
||||||
fmt.Fprintf(context.App.Writer, "%s\n", digest)
|
fmt.Fprintf(stdout, "%s\n", digest)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var manifestDigestCmd = cli.Command{
|
|
||||||
Name: "manifest-digest",
|
|
||||||
Usage: "Compute a manifest digest of a file",
|
|
||||||
ArgsUsage: "MANIFEST",
|
|
||||||
Action: manifestDigest,
|
|
||||||
}
|
|
||||||
|
@@ -4,20 +4,41 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
|
||||||
"github.com/containers/image/signature"
|
"github.com/containers/image/signature"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
func standaloneSign(c *cli.Context) error {
|
type standaloneSignOptions struct {
|
||||||
outputFile := c.String("output")
|
output string // Output file path
|
||||||
if len(c.Args()) != 3 || outputFile == "" {
|
}
|
||||||
|
|
||||||
|
func standaloneSignCmd() cli.Command {
|
||||||
|
opts := standaloneSignOptions{}
|
||||||
|
return cli.Command{
|
||||||
|
Name: "standalone-sign",
|
||||||
|
Usage: "Create a signature using local files",
|
||||||
|
ArgsUsage: "MANIFEST DOCKER-REFERENCE KEY-FINGERPRINT",
|
||||||
|
Action: commandAction(opts.run),
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "output, o",
|
||||||
|
Usage: "output the signature to `SIGNATURE`",
|
||||||
|
Destination: &opts.output,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (opts *standaloneSignOptions) run(args []string, stdout io.Writer) error {
|
||||||
|
if len(args) != 3 || opts.output == "" {
|
||||||
return errors.New("Usage: skopeo standalone-sign manifest docker-reference key-fingerprint -o signature")
|
return errors.New("Usage: skopeo standalone-sign manifest docker-reference key-fingerprint -o signature")
|
||||||
}
|
}
|
||||||
manifestPath := c.Args()[0]
|
manifestPath := args[0]
|
||||||
dockerReference := c.Args()[1]
|
dockerReference := args[1]
|
||||||
fingerprint := c.Args()[2]
|
fingerprint := args[2]
|
||||||
|
|
||||||
manifest, err := ioutil.ReadFile(manifestPath)
|
manifest, err := ioutil.ReadFile(manifestPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -34,33 +55,33 @@ func standaloneSign(c *cli.Context) error {
|
|||||||
return fmt.Errorf("Error creating signature: %v", err)
|
return fmt.Errorf("Error creating signature: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := ioutil.WriteFile(outputFile, signature, 0644); err != nil {
|
if err := ioutil.WriteFile(opts.output, signature, 0644); err != nil {
|
||||||
return fmt.Errorf("Error writing signature to %s: %v", outputFile, err)
|
return fmt.Errorf("Error writing signature to %s: %v", opts.output, err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var standaloneSignCmd = cli.Command{
|
type standaloneVerifyOptions struct {
|
||||||
Name: "standalone-sign",
|
|
||||||
Usage: "Create a signature using local files",
|
|
||||||
ArgsUsage: "MANIFEST DOCKER-REFERENCE KEY-FINGERPRINT",
|
|
||||||
Action: standaloneSign,
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "output, o",
|
|
||||||
Usage: "output the signature to `SIGNATURE`",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func standaloneVerify(c *cli.Context) error {
|
func standaloneVerifyCmd() cli.Command {
|
||||||
if len(c.Args()) != 4 {
|
opts := standaloneVerifyOptions{}
|
||||||
|
return cli.Command{
|
||||||
|
Name: "standalone-verify",
|
||||||
|
Usage: "Verify a signature using local files",
|
||||||
|
ArgsUsage: "MANIFEST DOCKER-REFERENCE KEY-FINGERPRINT SIGNATURE",
|
||||||
|
Action: commandAction(opts.run),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (opts *standaloneVerifyOptions) run(args []string, stdout io.Writer) error {
|
||||||
|
if len(args) != 4 {
|
||||||
return errors.New("Usage: skopeo standalone-verify manifest docker-reference key-fingerprint signature")
|
return errors.New("Usage: skopeo standalone-verify manifest docker-reference key-fingerprint signature")
|
||||||
}
|
}
|
||||||
manifestPath := c.Args()[0]
|
manifestPath := args[0]
|
||||||
expectedDockerReference := c.Args()[1]
|
expectedDockerReference := args[1]
|
||||||
expectedFingerprint := c.Args()[2]
|
expectedFingerprint := args[2]
|
||||||
signaturePath := c.Args()[3]
|
signaturePath := args[3]
|
||||||
|
|
||||||
unverifiedManifest, err := ioutil.ReadFile(manifestPath)
|
unverifiedManifest, err := ioutil.ReadFile(manifestPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -81,22 +102,35 @@ func standaloneVerify(c *cli.Context) error {
|
|||||||
return fmt.Errorf("Error verifying signature: %v", err)
|
return fmt.Errorf("Error verifying signature: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Fprintf(c.App.Writer, "Signature verified, digest %s\n", sig.DockerManifestDigest)
|
fmt.Fprintf(stdout, "Signature verified, digest %s\n", sig.DockerManifestDigest)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var standaloneVerifyCmd = cli.Command{
|
// WARNING: Do not use the contents of this for ANY security decisions,
|
||||||
Name: "standalone-verify",
|
// and be VERY CAREFUL about showing this information to humans in any way which suggest that these values “are probably” reliable.
|
||||||
Usage: "Verify a signature using local files",
|
// There is NO REASON to expect the values to be correct, or not intentionally misleading
|
||||||
ArgsUsage: "MANIFEST DOCKER-REFERENCE KEY-FINGERPRINT SIGNATURE",
|
// (including things like “✅ Verified by $authority”)
|
||||||
Action: standaloneVerify,
|
//
|
||||||
|
// The subcommand is undocumented, and it may be renamed or entirely disappear in the future.
|
||||||
|
type untrustedSignatureDumpOptions struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func untrustedSignatureDump(c *cli.Context) error {
|
func untrustedSignatureDumpCmd() cli.Command {
|
||||||
if len(c.Args()) != 1 {
|
opts := untrustedSignatureDumpOptions{}
|
||||||
|
return cli.Command{
|
||||||
|
Name: "untrusted-signature-dump-without-verification",
|
||||||
|
Usage: "Dump contents of a signature WITHOUT VERIFYING IT",
|
||||||
|
ArgsUsage: "SIGNATURE",
|
||||||
|
Hidden: true,
|
||||||
|
Action: commandAction(opts.run),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (opts *untrustedSignatureDumpOptions) run(args []string, stdout io.Writer) error {
|
||||||
|
if len(args) != 1 {
|
||||||
return errors.New("Usage: skopeo untrusted-signature-dump-without-verification signature")
|
return errors.New("Usage: skopeo untrusted-signature-dump-without-verification signature")
|
||||||
}
|
}
|
||||||
untrustedSignaturePath := c.Args()[0]
|
untrustedSignaturePath := args[0]
|
||||||
|
|
||||||
untrustedSignature, err := ioutil.ReadFile(untrustedSignaturePath)
|
untrustedSignature, err := ioutil.ReadFile(untrustedSignaturePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -111,20 +145,6 @@ func untrustedSignatureDump(c *cli.Context) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
fmt.Fprintln(c.App.Writer, string(untrustedOut))
|
fmt.Fprintln(stdout, string(untrustedOut))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// WARNING: Do not use the contents of this for ANY security decisions,
|
|
||||||
// and be VERY CAREFUL about showing this information to humans in any way which suggest that these values “are probably” reliable.
|
|
||||||
// There is NO REASON to expect the values to be correct, or not intentionally misleading
|
|
||||||
// (including things like “✅ Verified by $authority”)
|
|
||||||
//
|
|
||||||
// The subcommand is undocumented, and it may be renamed or entirely disappear in the future.
|
|
||||||
var untrustedSignatureDumpCmd = cli.Command{
|
|
||||||
Name: "untrusted-signature-dump-without-verification",
|
|
||||||
Usage: "Dump contents of a signature WITHOUT VERIFYING IT",
|
|
||||||
ArgsUsage: "SIGNATURE",
|
|
||||||
Hidden: true,
|
|
||||||
Action: untrustedSignatureDump,
|
|
||||||
}
|
|
||||||
|
@@ -3,6 +3,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
"io"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/containers/image/transports/alltransports"
|
"github.com/containers/image/transports/alltransports"
|
||||||
@@ -10,31 +11,125 @@ import (
|
|||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
func contextFromGlobalOptions(c *cli.Context, flagPrefix string) (*types.SystemContext, error) {
|
// errorShouldDisplayUsage is a subtype of error used by command handlers to indicate that cli.ShowSubcommandHelp should be called.
|
||||||
|
type errorShouldDisplayUsage struct {
|
||||||
|
error
|
||||||
|
}
|
||||||
|
|
||||||
|
// commandAction intermediates between the cli.ActionFunc interface and the real handler,
|
||||||
|
// primarily to ensure that cli.Context is not available to the handler, which in turn
|
||||||
|
// makes sure that the cli.String() etc. flag access functions are not used,
|
||||||
|
// and everything is done using the *Options structures and the Destination: members of cli.Flag.
|
||||||
|
// handler may return errorShouldDisplayUsage to cause cli.ShowSubcommandHelp to be called.
|
||||||
|
func commandAction(handler func(args []string, stdout io.Writer) error) cli.ActionFunc {
|
||||||
|
return func(c *cli.Context) error {
|
||||||
|
err := handler(([]string)(c.Args()), c.App.Writer)
|
||||||
|
if _, ok := err.(errorShouldDisplayUsage); ok {
|
||||||
|
cli.ShowSubcommandHelp(c)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// sharedImageOptions collects CLI flags which are image-related, but do not change across images.
|
||||||
|
// This really should be a part of globalOptions, but that would break existing users of (skopeo copy --authfile=).
|
||||||
|
type sharedImageOptions struct {
|
||||||
|
authFilePath string // Path to a */containers/auth.json
|
||||||
|
}
|
||||||
|
|
||||||
|
// imageFlags prepares a collection of CLI flags writing into sharedImageOptions, and the managed sharedImageOptions structure.
|
||||||
|
func sharedImageFlags() ([]cli.Flag, *sharedImageOptions) {
|
||||||
|
opts := sharedImageOptions{}
|
||||||
|
return []cli.Flag{
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "authfile",
|
||||||
|
Usage: "path of the authentication file. Default is ${XDG_RUNTIME_DIR}/containers/auth.json",
|
||||||
|
Destination: &opts.authFilePath,
|
||||||
|
},
|
||||||
|
}, &opts
|
||||||
|
}
|
||||||
|
|
||||||
|
// imageOptions collects CLI flags which are the same across subcommands, but may be different for each image
|
||||||
|
// (e.g. may differ between the source and destination of a copy)
|
||||||
|
type imageOptions struct {
|
||||||
|
global *globalOptions // May be shared across several imageOptions instances.
|
||||||
|
shared *sharedImageOptions // May be shared across several imageOptions instances.
|
||||||
|
credsOption optionalString // username[:password] for accessing a registry
|
||||||
|
dockerCertPath string // A directory using Docker-like *.{crt,cert,key} files for connecting to a registry or a daemon
|
||||||
|
tlsVerify optionalBool // Require HTTPS and verify certificates (for docker: and docker-daemon:)
|
||||||
|
sharedBlobDir string // A directory to use for OCI blobs, shared across repositories
|
||||||
|
dockerDaemonHost string // docker-daemon: host to connect to
|
||||||
|
}
|
||||||
|
|
||||||
|
// imageFlags prepares a collection of CLI flags writing into imageOptions, and the managed imageOptions structure.
|
||||||
|
func imageFlags(global *globalOptions, shared *sharedImageOptions, flagPrefix, credsOptionAlias string) ([]cli.Flag, *imageOptions) {
|
||||||
|
opts := imageOptions{
|
||||||
|
global: global,
|
||||||
|
shared: shared,
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is horribly ugly, but we need to support the old option forms of (skopeo copy) for compatibility.
|
||||||
|
// Don't add any more cases like this.
|
||||||
|
credsOptionExtra := ""
|
||||||
|
if credsOptionAlias != "" {
|
||||||
|
credsOptionExtra += "," + credsOptionAlias
|
||||||
|
}
|
||||||
|
|
||||||
|
return []cli.Flag{
|
||||||
|
cli.GenericFlag{
|
||||||
|
Name: flagPrefix + "creds" + credsOptionExtra,
|
||||||
|
Usage: "Use `USERNAME[:PASSWORD]` for accessing the registry",
|
||||||
|
Value: newOptionalStringValue(&opts.credsOption),
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: flagPrefix + "cert-dir",
|
||||||
|
Usage: "use certificates at `PATH` (*.crt, *.cert, *.key) to connect to the registry or daemon",
|
||||||
|
Destination: &opts.dockerCertPath,
|
||||||
|
},
|
||||||
|
cli.GenericFlag{
|
||||||
|
Name: flagPrefix + "tls-verify",
|
||||||
|
Usage: "require HTTPS and verify certificates when talking to the container registry or daemon (defaults to true)",
|
||||||
|
Value: newOptionalBoolValue(&opts.tlsVerify),
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: flagPrefix + "shared-blob-dir",
|
||||||
|
Usage: "`DIRECTORY` to use to share blobs across OCI repositories",
|
||||||
|
Destination: &opts.sharedBlobDir,
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: flagPrefix + "daemon-host",
|
||||||
|
Usage: "use docker daemon host at `HOST` (docker-daemon: only)",
|
||||||
|
Destination: &opts.dockerDaemonHost,
|
||||||
|
},
|
||||||
|
}, &opts
|
||||||
|
}
|
||||||
|
|
||||||
|
// newSystemContext returns a *types.SystemContext corresponding to opts.
|
||||||
|
// It is guaranteed to return a fresh instance, so it is safe to make additional updates to it.
|
||||||
|
func (opts *imageOptions) newSystemContext() (*types.SystemContext, error) {
|
||||||
ctx := &types.SystemContext{
|
ctx := &types.SystemContext{
|
||||||
RegistriesDirPath: c.GlobalString("registries.d"),
|
RegistriesDirPath: opts.global.registriesDirPath,
|
||||||
ArchitectureChoice: c.GlobalString("override-arch"),
|
ArchitectureChoice: opts.global.overrideArch,
|
||||||
OSChoice: c.GlobalString("override-os"),
|
OSChoice: opts.global.overrideOS,
|
||||||
DockerCertPath: c.String(flagPrefix + "cert-dir"),
|
DockerCertPath: opts.dockerCertPath,
|
||||||
OSTreeTmpDirPath: c.String(flagPrefix + "ostree-tmp-dir"),
|
OCISharedBlobDirPath: opts.sharedBlobDir,
|
||||||
OCISharedBlobDirPath: c.String(flagPrefix + "shared-blob-dir"),
|
AuthFilePath: opts.shared.authFilePath,
|
||||||
DirForceCompress: c.Bool(flagPrefix + "compress"),
|
DockerDaemonHost: opts.dockerDaemonHost,
|
||||||
AuthFilePath: c.String("authfile"),
|
DockerDaemonCertPath: opts.dockerCertPath,
|
||||||
DockerDaemonHost: c.String(flagPrefix + "daemon-host"),
|
|
||||||
DockerDaemonCertPath: c.String(flagPrefix + "cert-dir"),
|
|
||||||
DockerDaemonInsecureSkipTLSVerify: !c.BoolT(flagPrefix + "tls-verify"),
|
|
||||||
}
|
}
|
||||||
// DEPRECATED: we support --tls-verify for backward compatibility, but override
|
if opts.tlsVerify.present {
|
||||||
// it if per-subcommand flags are provided (see below).
|
ctx.DockerDaemonInsecureSkipTLSVerify = !opts.tlsVerify.value
|
||||||
if c.GlobalIsSet("tls-verify") {
|
|
||||||
ctx.DockerInsecureSkipTLSVerify = types.NewOptionalBool(!c.GlobalBoolT("tls-verify"))
|
|
||||||
}
|
}
|
||||||
if c.IsSet(flagPrefix + "tls-verify") {
|
// DEPRECATED: We support this for backward compatibility, but override it if a per-image flag is provided.
|
||||||
ctx.DockerInsecureSkipTLSVerify = types.NewOptionalBool(!c.BoolT(flagPrefix + "tls-verify"))
|
if opts.global.tlsVerify.present {
|
||||||
|
ctx.DockerInsecureSkipTLSVerify = types.NewOptionalBool(!opts.global.tlsVerify.value)
|
||||||
}
|
}
|
||||||
if c.IsSet(flagPrefix + "creds") {
|
if opts.tlsVerify.present {
|
||||||
|
ctx.DockerInsecureSkipTLSVerify = types.NewOptionalBool(!opts.tlsVerify.value)
|
||||||
|
}
|
||||||
|
if opts.credsOption.present {
|
||||||
var err error
|
var err error
|
||||||
ctx.DockerAuthConfig, err = getDockerAuth(c.String(flagPrefix + "creds"))
|
ctx.DockerAuthConfig, err = getDockerAuth(opts.credsOption.value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -42,13 +137,43 @@ func contextFromGlobalOptions(c *cli.Context, flagPrefix string) (*types.SystemC
|
|||||||
return ctx, nil
|
return ctx, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func commandTimeoutContextFromGlobalOptions(c *cli.Context) (context.Context, context.CancelFunc) {
|
// imageDestOptions is a superset of imageOptions specialized for iamge destinations.
|
||||||
ctx := context.Background()
|
type imageDestOptions struct {
|
||||||
var cancel context.CancelFunc = func() {}
|
*imageOptions
|
||||||
if c.GlobalDuration("command-timeout") > 0 {
|
osTreeTmpDir string // A directory to use for OSTree temporary files
|
||||||
ctx, cancel = context.WithTimeout(ctx, c.GlobalDuration("command-timeout"))
|
dirForceCompression bool // Compress layers when saving to the dir: transport
|
||||||
|
}
|
||||||
|
|
||||||
|
// imageDestFlags prepares a collection of CLI flags writing into imageDestOptions, and the managed imageDestOptions structure.
|
||||||
|
func imageDestFlags(global *globalOptions, shared *sharedImageOptions, flagPrefix, credsOptionAlias string) ([]cli.Flag, *imageDestOptions) {
|
||||||
|
genericFlags, genericOptions := imageFlags(global, shared, flagPrefix, credsOptionAlias)
|
||||||
|
opts := imageDestOptions{imageOptions: genericOptions}
|
||||||
|
|
||||||
|
return append(genericFlags, []cli.Flag{
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: flagPrefix + "ostree-tmp-dir",
|
||||||
|
Usage: "`DIRECTORY` to use for OSTree temporary files",
|
||||||
|
Destination: &opts.osTreeTmpDir,
|
||||||
|
},
|
||||||
|
cli.BoolFlag{
|
||||||
|
Name: flagPrefix + "compress",
|
||||||
|
Usage: "Compress tarball image layers when saving to directory using the 'dir' transport. (default is same compression type as source)",
|
||||||
|
Destination: &opts.dirForceCompression,
|
||||||
|
},
|
||||||
|
}...), &opts
|
||||||
|
}
|
||||||
|
|
||||||
|
// newSystemContext returns a *types.SystemContext corresponding to opts.
|
||||||
|
// It is guaranteed to return a fresh instance, so it is safe to make additional updates to it.
|
||||||
|
func (opts *imageDestOptions) newSystemContext() (*types.SystemContext, error) {
|
||||||
|
ctx, err := opts.imageOptions.newSystemContext()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
return ctx, cancel
|
|
||||||
|
ctx.OSTreeTmpDirPath = opts.osTreeTmpDir
|
||||||
|
ctx.DirForceCompress = opts.dirForceCompression
|
||||||
|
return ctx, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseCreds(creds string) (string, string, error) {
|
func parseCreds(creds string) (string, string, error) {
|
||||||
@@ -78,13 +203,12 @@ func getDockerAuth(creds string) (*types.DockerAuthConfig, error) {
|
|||||||
|
|
||||||
// parseImage converts image URL-like string to an initialized handler for that image.
|
// parseImage converts image URL-like string to an initialized handler for that image.
|
||||||
// The caller must call .Close() on the returned ImageCloser.
|
// The caller must call .Close() on the returned ImageCloser.
|
||||||
func parseImage(ctx context.Context, c *cli.Context) (types.ImageCloser, error) {
|
func parseImage(ctx context.Context, opts *imageOptions, name string) (types.ImageCloser, error) {
|
||||||
imgName := c.Args().First()
|
ref, err := alltransports.ParseImageName(name)
|
||||||
ref, err := alltransports.ParseImageName(imgName)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
sys, err := contextFromGlobalOptions(c, "")
|
sys, err := opts.newSystemContext()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -93,12 +217,12 @@ func parseImage(ctx context.Context, c *cli.Context) (types.ImageCloser, error)
|
|||||||
|
|
||||||
// parseImageSource converts image URL-like string to an ImageSource.
|
// parseImageSource converts image URL-like string to an ImageSource.
|
||||||
// The caller must call .Close() on the returned ImageSource.
|
// The caller must call .Close() on the returned ImageSource.
|
||||||
func parseImageSource(ctx context.Context, c *cli.Context, name string) (types.ImageSource, error) {
|
func parseImageSource(ctx context.Context, opts *imageOptions, name string) (types.ImageSource, error) {
|
||||||
ref, err := alltransports.ParseImageName(name)
|
ref, err := alltransports.ParseImageName(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
sys, err := contextFromGlobalOptions(c, "")
|
sys, err := opts.newSystemContext()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@@ -7,67 +7,60 @@ import (
|
|||||||
"github.com/containers/image/types"
|
"github.com/containers/image/types"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"github.com/urfave/cli"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// fakeContext creates inputs for contextFromGlobalOptions.
|
// fakeGlobalOptions creates globalOptions and sets it according to flags.
|
||||||
// NOTE: This is QUITE FAKE; none of the urfave/cli normalization and the like happens.
|
// NOTE: This is QUITE FAKE; none of the urfave/cli normalization and the like happens.
|
||||||
func fakeContext(t *testing.T, cmdName string, globalFlags []string, cmdFlags []string) *cli.Context {
|
func fakeGlobalOptions(t *testing.T, flags []string) *globalOptions {
|
||||||
app := createApp()
|
app, opts := createApp()
|
||||||
|
|
||||||
globalSet := flag.NewFlagSet(app.Name, flag.ContinueOnError)
|
flagSet := flag.NewFlagSet(app.Name, flag.ContinueOnError)
|
||||||
for _, f := range app.Flags {
|
for _, f := range app.Flags {
|
||||||
f.Apply(globalSet)
|
f.Apply(flagSet)
|
||||||
}
|
}
|
||||||
err := globalSet.Parse(globalFlags)
|
err := flagSet.Parse(flags)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
globalCtx := cli.NewContext(app, globalSet, nil)
|
|
||||||
|
|
||||||
cmd := app.Command(cmdName)
|
return opts
|
||||||
require.NotNil(t, cmd)
|
|
||||||
cmdSet := flag.NewFlagSet(cmd.Name, flag.ContinueOnError)
|
|
||||||
for _, f := range cmd.Flags {
|
|
||||||
f.Apply(cmdSet)
|
|
||||||
}
|
|
||||||
err = cmdSet.Parse(cmdFlags)
|
|
||||||
require.NoError(t, err)
|
|
||||||
return cli.NewContext(app, cmdSet, globalCtx)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestContextFromGlobalOptions(t *testing.T) {
|
// fakeImageOptions creates imageOptions and sets it according to globalFlags/cmdFlags.
|
||||||
// FIXME: All of this only tests (skopeo copy --dest)
|
// NOTE: This is QUITE FAKE; none of the urfave/cli normalization and the like happens.
|
||||||
// FIXME FIXME: Apparently BoolT values are set to false if the flag is not declared for the specific subcommand!!
|
func fakeImageOptions(t *testing.T, flagPrefix string, globalFlags []string, cmdFlags []string) *imageOptions {
|
||||||
|
globalOpts := fakeGlobalOptions(t, globalFlags)
|
||||||
|
|
||||||
// Default state
|
sharedFlags, sharedOpts := sharedImageFlags()
|
||||||
c := fakeContext(t, "copy", []string{}, []string{})
|
imageFlags, imageOpts := imageFlags(globalOpts, sharedOpts, flagPrefix, "")
|
||||||
res, err := contextFromGlobalOptions(c, "dest-")
|
flagSet := flag.NewFlagSet("fakeImageOptions", flag.ContinueOnError)
|
||||||
|
for _, f := range append(sharedFlags, imageFlags...) {
|
||||||
|
f.Apply(flagSet)
|
||||||
|
}
|
||||||
|
err := flagSet.Parse(cmdFlags)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, &types.SystemContext{}, res)
|
return imageOpts
|
||||||
|
}
|
||||||
|
|
||||||
// Explicitly set everything to default, except for when the default is “not present”
|
func TestImageOptionsNewSystemContext(t *testing.T) {
|
||||||
c = fakeContext(t, "copy", []string{}, []string{
|
// Default state
|
||||||
"--dest-compress=false",
|
opts := fakeImageOptions(t, "dest-", []string{}, []string{})
|
||||||
})
|
res, err := opts.newSystemContext()
|
||||||
res, err = contextFromGlobalOptions(c, "dest-")
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, &types.SystemContext{}, res)
|
assert.Equal(t, &types.SystemContext{}, res)
|
||||||
|
|
||||||
// Set everything to non-default values.
|
// Set everything to non-default values.
|
||||||
c = fakeContext(t, "copy", []string{
|
opts = fakeImageOptions(t, "dest-", []string{
|
||||||
"--registries.d", "/srv/registries.d",
|
"--registries.d", "/srv/registries.d",
|
||||||
"--override-arch", "overridden-arch",
|
"--override-arch", "overridden-arch",
|
||||||
"--override-os", "overridden-os",
|
"--override-os", "overridden-os",
|
||||||
}, []string{
|
}, []string{
|
||||||
"--authfile", "/srv/authfile",
|
"--authfile", "/srv/authfile",
|
||||||
"--dest-cert-dir", "/srv/cert-dir",
|
"--dest-cert-dir", "/srv/cert-dir",
|
||||||
"--dest-ostree-tmp-dir", "/srv/ostree-tmp-dir",
|
|
||||||
"--dest-shared-blob-dir", "/srv/shared-blob-dir",
|
"--dest-shared-blob-dir", "/srv/shared-blob-dir",
|
||||||
"--dest-compress=true",
|
|
||||||
"--dest-daemon-host", "daemon-host.example.com",
|
"--dest-daemon-host", "daemon-host.example.com",
|
||||||
"--dest-tls-verify=false",
|
"--dest-tls-verify=false",
|
||||||
"--dest-creds", "creds-user:creds-password",
|
"--dest-creds", "creds-user:creds-password",
|
||||||
})
|
})
|
||||||
res, err = contextFromGlobalOptions(c, "dest-")
|
res, err = opts.newSystemContext()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, &types.SystemContext{
|
assert.Equal(t, &types.SystemContext{
|
||||||
RegistriesDirPath: "/srv/registries.d",
|
RegistriesDirPath: "/srv/registries.d",
|
||||||
@@ -78,11 +71,9 @@ func TestContextFromGlobalOptions(t *testing.T) {
|
|||||||
DockerCertPath: "/srv/cert-dir",
|
DockerCertPath: "/srv/cert-dir",
|
||||||
DockerInsecureSkipTLSVerify: types.OptionalBoolTrue,
|
DockerInsecureSkipTLSVerify: types.OptionalBoolTrue,
|
||||||
DockerAuthConfig: &types.DockerAuthConfig{Username: "creds-user", Password: "creds-password"},
|
DockerAuthConfig: &types.DockerAuthConfig{Username: "creds-user", Password: "creds-password"},
|
||||||
OSTreeTmpDirPath: "/srv/ostree-tmp-dir",
|
|
||||||
DockerDaemonCertPath: "/srv/cert-dir",
|
DockerDaemonCertPath: "/srv/cert-dir",
|
||||||
DockerDaemonHost: "daemon-host.example.com",
|
DockerDaemonHost: "daemon-host.example.com",
|
||||||
DockerDaemonInsecureSkipTLSVerify: true,
|
DockerDaemonInsecureSkipTLSVerify: true,
|
||||||
DirForceCompress: true,
|
|
||||||
}, res)
|
}, res)
|
||||||
|
|
||||||
// Global/per-command tlsVerify behavior
|
// Global/per-command tlsVerify behavior
|
||||||
@@ -109,15 +100,85 @@ func TestContextFromGlobalOptions(t *testing.T) {
|
|||||||
if c.cmd != "" {
|
if c.cmd != "" {
|
||||||
cmdFlags = append(cmdFlags, "--dest-tls-verify="+c.cmd)
|
cmdFlags = append(cmdFlags, "--dest-tls-verify="+c.cmd)
|
||||||
}
|
}
|
||||||
ctx := fakeContext(t, "copy", globalFlags, cmdFlags)
|
opts := fakeImageOptions(t, "dest-", globalFlags, cmdFlags)
|
||||||
res, err = contextFromGlobalOptions(ctx, "dest-")
|
res, err = opts.newSystemContext()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, c.expectedDocker, res.DockerInsecureSkipTLSVerify, "%#v", c)
|
assert.Equal(t, c.expectedDocker, res.DockerInsecureSkipTLSVerify, "%#v", c)
|
||||||
assert.Equal(t, c.expectedDockerDaemon, res.DockerDaemonInsecureSkipTLSVerify, "%#v", c)
|
assert.Equal(t, c.expectedDockerDaemon, res.DockerDaemonInsecureSkipTLSVerify, "%#v", c)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Invalid option values
|
// Invalid option values
|
||||||
c = fakeContext(t, "copy", []string{}, []string{"--dest-creds", ""})
|
opts = fakeImageOptions(t, "dest-", []string{}, []string{"--dest-creds", ""})
|
||||||
_, err = contextFromGlobalOptions(c, "dest-")
|
_, err = opts.newSystemContext()
|
||||||
|
assert.Error(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// fakeImageDestOptions creates imageDestOptions and sets it according to globalFlags/cmdFlags.
|
||||||
|
// NOTE: This is QUITE FAKE; none of the urfave/cli normalization and the like happens.
|
||||||
|
func fakeImageDestOptions(t *testing.T, flagPrefix string, globalFlags []string, cmdFlags []string) *imageDestOptions {
|
||||||
|
globalOpts := fakeGlobalOptions(t, globalFlags)
|
||||||
|
|
||||||
|
sharedFlags, sharedOpts := sharedImageFlags()
|
||||||
|
imageFlags, imageOpts := imageDestFlags(globalOpts, sharedOpts, flagPrefix, "")
|
||||||
|
flagSet := flag.NewFlagSet("fakeImageDestOptions", flag.ContinueOnError)
|
||||||
|
for _, f := range append(sharedFlags, imageFlags...) {
|
||||||
|
f.Apply(flagSet)
|
||||||
|
}
|
||||||
|
err := flagSet.Parse(cmdFlags)
|
||||||
|
require.NoError(t, err)
|
||||||
|
return imageOpts
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestImageDestOptionsNewSystemContext(t *testing.T) {
|
||||||
|
// Default state
|
||||||
|
opts := fakeImageDestOptions(t, "dest-", []string{}, []string{})
|
||||||
|
res, err := opts.newSystemContext()
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, &types.SystemContext{}, res)
|
||||||
|
|
||||||
|
// Explicitly set everything to default, except for when the default is “not present”
|
||||||
|
opts = fakeImageDestOptions(t, "dest-", []string{}, []string{
|
||||||
|
"--dest-compress=false",
|
||||||
|
})
|
||||||
|
res, err = opts.newSystemContext()
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, &types.SystemContext{}, res)
|
||||||
|
|
||||||
|
// Set everything to non-default values.
|
||||||
|
opts = fakeImageDestOptions(t, "dest-", []string{
|
||||||
|
"--registries.d", "/srv/registries.d",
|
||||||
|
"--override-arch", "overridden-arch",
|
||||||
|
"--override-os", "overridden-os",
|
||||||
|
}, []string{
|
||||||
|
"--authfile", "/srv/authfile",
|
||||||
|
"--dest-cert-dir", "/srv/cert-dir",
|
||||||
|
"--dest-ostree-tmp-dir", "/srv/ostree-tmp-dir",
|
||||||
|
"--dest-shared-blob-dir", "/srv/shared-blob-dir",
|
||||||
|
"--dest-compress=true",
|
||||||
|
"--dest-daemon-host", "daemon-host.example.com",
|
||||||
|
"--dest-tls-verify=false",
|
||||||
|
"--dest-creds", "creds-user:creds-password",
|
||||||
|
})
|
||||||
|
res, err = opts.newSystemContext()
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, &types.SystemContext{
|
||||||
|
RegistriesDirPath: "/srv/registries.d",
|
||||||
|
AuthFilePath: "/srv/authfile",
|
||||||
|
ArchitectureChoice: "overridden-arch",
|
||||||
|
OSChoice: "overridden-os",
|
||||||
|
OCISharedBlobDirPath: "/srv/shared-blob-dir",
|
||||||
|
DockerCertPath: "/srv/cert-dir",
|
||||||
|
DockerInsecureSkipTLSVerify: types.OptionalBoolTrue,
|
||||||
|
DockerAuthConfig: &types.DockerAuthConfig{Username: "creds-user", Password: "creds-password"},
|
||||||
|
OSTreeTmpDirPath: "/srv/ostree-tmp-dir",
|
||||||
|
DockerDaemonCertPath: "/srv/cert-dir",
|
||||||
|
DockerDaemonHost: "daemon-host.example.com",
|
||||||
|
DockerDaemonInsecureSkipTLSVerify: true,
|
||||||
|
DirForceCompress: true,
|
||||||
|
}, res)
|
||||||
|
|
||||||
|
// Invalid option values in imageOptions
|
||||||
|
opts = fakeImageDestOptions(t, "dest-", []string{}, []string{"--dest-creds", ""})
|
||||||
|
_, err = opts.newSystemContext()
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
github.com/urfave/cli v1.17.0
|
github.com/urfave/cli v1.20.0
|
||||||
github.com/kr/pretty v0.1.0
|
github.com/kr/pretty v0.1.0
|
||||||
github.com/kr/text v0.1.0
|
github.com/kr/text v0.1.0
|
||||||
github.com/containers/image master
|
github.com/containers/image master
|
||||||
|
28
vendor/github.com/urfave/cli/LICENSE
generated
vendored
28
vendor/github.com/urfave/cli/LICENSE
generated
vendored
@@ -1,21 +1,21 @@
|
|||||||
Copyright (C) 2013 Jeremy Saenz
|
MIT License
|
||||||
All Rights Reserved.
|
|
||||||
|
|
||||||
MIT LICENSE
|
Copyright (c) 2016 Jeremy Saenz & Contributors
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
this software and associated documentation files (the "Software"), to deal in
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
the Software without restriction, including without limitation the rights to
|
in the Software without restriction, including without limitation the rights
|
||||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
subject to the following conditions:
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
The above copyright notice and this permission notice shall be included in all
|
||||||
copies or substantial portions of the Software.
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
1202
vendor/github.com/urfave/cli/README.md
generated
vendored
1202
vendor/github.com/urfave/cli/README.md
generated
vendored
File diff suppressed because it is too large
Load Diff
141
vendor/github.com/urfave/cli/app.go
generated
vendored
141
vendor/github.com/urfave/cli/app.go
generated
vendored
@@ -6,23 +6,19 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"reflect"
|
|
||||||
"sort"
|
"sort"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
changeLogURL = "https://github.com/codegangsta/cli/blob/master/CHANGELOG.md"
|
changeLogURL = "https://github.com/urfave/cli/blob/master/CHANGELOG.md"
|
||||||
appActionDeprecationURL = fmt.Sprintf("%s#deprecated-cli-app-action-signature", changeLogURL)
|
appActionDeprecationURL = fmt.Sprintf("%s#deprecated-cli-app-action-signature", changeLogURL)
|
||||||
runAndExitOnErrorDeprecationURL = fmt.Sprintf("%s#deprecated-cli-app-runandexitonerror", changeLogURL)
|
runAndExitOnErrorDeprecationURL = fmt.Sprintf("%s#deprecated-cli-app-runandexitonerror", changeLogURL)
|
||||||
|
|
||||||
contactSysadmin = "This is an error in the application. Please contact the distributor of this application if this is not you."
|
contactSysadmin = "This is an error in the application. Please contact the distributor of this application if this is not you."
|
||||||
|
|
||||||
errNonFuncAction = NewExitError("ERROR invalid Action type. "+
|
errInvalidActionType = NewExitError("ERROR invalid Action type. "+
|
||||||
fmt.Sprintf("Must be a func of type `cli.ActionFunc`. %s", contactSysadmin)+
|
fmt.Sprintf("Must be `func(*Context`)` or `func(*Context) error). %s", contactSysadmin)+
|
||||||
fmt.Sprintf("See %s", appActionDeprecationURL), 2)
|
|
||||||
errInvalidActionSignature = NewExitError("ERROR invalid Action signature. "+
|
|
||||||
fmt.Sprintf("Must be `cli.ActionFunc`. %s", contactSysadmin)+
|
|
||||||
fmt.Sprintf("See %s", appActionDeprecationURL), 2)
|
fmt.Sprintf("See %s", appActionDeprecationURL), 2)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -41,6 +37,8 @@ type App struct {
|
|||||||
ArgsUsage string
|
ArgsUsage string
|
||||||
// Version of the program
|
// Version of the program
|
||||||
Version string
|
Version string
|
||||||
|
// Description of the program
|
||||||
|
Description string
|
||||||
// List of commands to execute
|
// List of commands to execute
|
||||||
Commands []Command
|
Commands []Command
|
||||||
// List of flags to parse
|
// List of flags to parse
|
||||||
@@ -61,10 +59,11 @@ type App struct {
|
|||||||
// An action to execute after any subcommands are run, but after the subcommand has finished
|
// An action to execute after any subcommands are run, but after the subcommand has finished
|
||||||
// It is run even if Action() panics
|
// It is run even if Action() panics
|
||||||
After AfterFunc
|
After AfterFunc
|
||||||
|
|
||||||
// The action to execute when no subcommands are specified
|
// The action to execute when no subcommands are specified
|
||||||
|
// Expects a `cli.ActionFunc` but will accept the *deprecated* signature of `func(*cli.Context) {}`
|
||||||
|
// *Note*: support for the deprecated `Action` signature will be removed in a future version
|
||||||
Action interface{}
|
Action interface{}
|
||||||
// TODO: replace `Action: interface{}` with `Action: ActionFunc` once some kind
|
|
||||||
// of deprecation period has passed, maybe?
|
|
||||||
|
|
||||||
// Execute this function if the proper command cannot be found
|
// Execute this function if the proper command cannot be found
|
||||||
CommandNotFound CommandNotFoundFunc
|
CommandNotFound CommandNotFoundFunc
|
||||||
@@ -86,6 +85,12 @@ type App struct {
|
|||||||
ErrWriter io.Writer
|
ErrWriter io.Writer
|
||||||
// Other custom info
|
// Other custom info
|
||||||
Metadata map[string]interface{}
|
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
|
||||||
|
|
||||||
didSetup bool
|
didSetup bool
|
||||||
}
|
}
|
||||||
@@ -139,13 +144,6 @@ func (a *App) Setup() {
|
|||||||
}
|
}
|
||||||
a.Commands = newCmds
|
a.Commands = newCmds
|
||||||
|
|
||||||
a.categories = CommandCategories{}
|
|
||||||
for _, command := range a.Commands {
|
|
||||||
a.categories = a.categories.AddCommand(command.Category, command)
|
|
||||||
}
|
|
||||||
sort.Sort(a.categories)
|
|
||||||
|
|
||||||
// append help to commands
|
|
||||||
if a.Command(helpCommand.Name) == nil && !a.HideHelp {
|
if a.Command(helpCommand.Name) == nil && !a.HideHelp {
|
||||||
a.Commands = append(a.Commands, helpCommand)
|
a.Commands = append(a.Commands, helpCommand)
|
||||||
if (HelpFlag != BoolFlag{}) {
|
if (HelpFlag != BoolFlag{}) {
|
||||||
@@ -153,14 +151,23 @@ func (a *App) Setup() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//append version/help flags
|
|
||||||
if a.EnableBashCompletion {
|
|
||||||
a.appendFlag(BashCompletionFlag)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !a.HideVersion {
|
if !a.HideVersion {
|
||||||
a.appendFlag(VersionFlag)
|
a.appendFlag(VersionFlag)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a.categories = CommandCategories{}
|
||||||
|
for _, command := range a.Commands {
|
||||||
|
a.categories = a.categories.AddCommand(command.Category, command)
|
||||||
|
}
|
||||||
|
sort.Sort(a.categories)
|
||||||
|
|
||||||
|
if a.Metadata == nil {
|
||||||
|
a.Metadata = make(map[string]interface{})
|
||||||
|
}
|
||||||
|
|
||||||
|
if a.Writer == nil {
|
||||||
|
a.Writer = os.Stdout
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run is the entry point to the cli app. Parses the arguments slice and routes
|
// Run is the entry point to the cli app. Parses the arguments slice and routes
|
||||||
@@ -168,8 +175,20 @@ func (a *App) Setup() {
|
|||||||
func (a *App) Run(arguments []string) (err error) {
|
func (a *App) Run(arguments []string) (err error) {
|
||||||
a.Setup()
|
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)
|
||||||
|
|
||||||
// parse flags
|
// parse flags
|
||||||
set := flagSet(a.Name, a.Flags)
|
set, err := flagSet(a.Name, a.Flags)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
set.SetOutput(ioutil.Discard)
|
set.SetOutput(ioutil.Discard)
|
||||||
err = set.Parse(arguments[1:])
|
err = set.Parse(arguments[1:])
|
||||||
nerr := normalizeFlags(a.Flags, set)
|
nerr := normalizeFlags(a.Flags, set)
|
||||||
@@ -179,6 +198,7 @@ func (a *App) Run(arguments []string) (err error) {
|
|||||||
ShowAppHelp(context)
|
ShowAppHelp(context)
|
||||||
return nerr
|
return nerr
|
||||||
}
|
}
|
||||||
|
context.shellComplete = shellComplete
|
||||||
|
|
||||||
if checkCompletions(context) {
|
if checkCompletions(context) {
|
||||||
return nil
|
return nil
|
||||||
@@ -190,7 +210,7 @@ func (a *App) Run(arguments []string) (err error) {
|
|||||||
HandleExitCoder(err)
|
HandleExitCoder(err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
fmt.Fprintf(a.Writer, "%s\n\n", "Incorrect Usage.")
|
fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error())
|
||||||
ShowAppHelp(context)
|
ShowAppHelp(context)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -220,7 +240,6 @@ func (a *App) Run(arguments []string) (err error) {
|
|||||||
if a.Before != nil {
|
if a.Before != nil {
|
||||||
beforeErr := a.Before(context)
|
beforeErr := a.Before(context)
|
||||||
if beforeErr != nil {
|
if beforeErr != nil {
|
||||||
fmt.Fprintf(a.Writer, "%v\n\n", beforeErr)
|
|
||||||
ShowAppHelp(context)
|
ShowAppHelp(context)
|
||||||
HandleExitCoder(beforeErr)
|
HandleExitCoder(beforeErr)
|
||||||
err = beforeErr
|
err = beforeErr
|
||||||
@@ -237,6 +256,10 @@ func (a *App) Run(arguments []string) (err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if a.Action == nil {
|
||||||
|
a.Action = helpCommand.Action
|
||||||
|
}
|
||||||
|
|
||||||
// Run default Action
|
// Run default Action
|
||||||
err = HandleAction(a.Action, context)
|
err = HandleAction(a.Action, context)
|
||||||
|
|
||||||
@@ -244,11 +267,12 @@ func (a *App) Run(arguments []string) (err error) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// DEPRECATED: Another entry point to the cli app, takes care of passing arguments and error handling
|
// 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 eror
|
||||||
|
// code in the cli.ExitCoder
|
||||||
func (a *App) RunAndExitOnError() {
|
func (a *App) RunAndExitOnError() {
|
||||||
fmt.Fprintf(a.errWriter(),
|
|
||||||
"DEPRECATED cli.App.RunAndExitOnError. %s See %s\n",
|
|
||||||
contactSysadmin, runAndExitOnErrorDeprecationURL)
|
|
||||||
if err := a.Run(os.Args); err != nil {
|
if err := a.Run(os.Args); err != nil {
|
||||||
fmt.Fprintln(a.errWriter(), err)
|
fmt.Fprintln(a.errWriter(), err)
|
||||||
OsExiter(1)
|
OsExiter(1)
|
||||||
@@ -277,13 +301,12 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) {
|
|||||||
}
|
}
|
||||||
a.Commands = newCmds
|
a.Commands = newCmds
|
||||||
|
|
||||||
// append flags
|
// parse flags
|
||||||
if a.EnableBashCompletion {
|
set, err := flagSet(a.Name, a.Flags)
|
||||||
a.appendFlag(BashCompletionFlag)
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse flags
|
|
||||||
set := flagSet(a.Name, a.Flags)
|
|
||||||
set.SetOutput(ioutil.Discard)
|
set.SetOutput(ioutil.Discard)
|
||||||
err = set.Parse(ctx.Args().Tail())
|
err = set.Parse(ctx.Args().Tail())
|
||||||
nerr := normalizeFlags(a.Flags, set)
|
nerr := normalizeFlags(a.Flags, set)
|
||||||
@@ -310,7 +333,7 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) {
|
|||||||
HandleExitCoder(err)
|
HandleExitCoder(err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
fmt.Fprintf(a.Writer, "%s\n\n", "Incorrect Usage.")
|
fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error())
|
||||||
ShowSubcommandHelp(context)
|
ShowSubcommandHelp(context)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -451,48 +474,24 @@ type Author struct {
|
|||||||
func (a Author) String() string {
|
func (a Author) String() string {
|
||||||
e := ""
|
e := ""
|
||||||
if a.Email != "" {
|
if a.Email != "" {
|
||||||
e = "<" + a.Email + "> "
|
e = " <" + a.Email + ">"
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Sprintf("%v %v", a.Name, e)
|
return fmt.Sprintf("%v%v", a.Name, e)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HandleAction uses ✧✧✧reflection✧✧✧ to figure out if the given Action is an
|
// HandleAction attempts to figure out which Action signature was used. If
|
||||||
// ActionFunc, a func with the legacy signature for Action, or some other
|
// it's an ActionFunc or a func with the legacy signature for Action, the func
|
||||||
// invalid thing. If it's an ActionFunc or a func with the legacy signature for
|
// is run!
|
||||||
// Action, the func is run!
|
|
||||||
func HandleAction(action interface{}, context *Context) (err error) {
|
func HandleAction(action interface{}, context *Context) (err error) {
|
||||||
defer func() {
|
if a, ok := action.(ActionFunc); ok {
|
||||||
if r := recover(); r != nil {
|
return a(context)
|
||||||
switch r.(type) {
|
} else if a, ok := action.(func(*Context) error); ok {
|
||||||
case error:
|
return a(context)
|
||||||
err = r.(error)
|
} else if a, ok := action.(func(*Context)); ok { // deprecated function signature
|
||||||
default:
|
a(context)
|
||||||
err = NewExitError(fmt.Sprintf("ERROR unknown Action error: %v. See %s", r, appActionDeprecationURL), 2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
if reflect.TypeOf(action).Kind() != reflect.Func {
|
|
||||||
return errNonFuncAction
|
|
||||||
}
|
|
||||||
|
|
||||||
vals := reflect.ValueOf(action).Call([]reflect.Value{reflect.ValueOf(context)})
|
|
||||||
|
|
||||||
if len(vals) == 0 {
|
|
||||||
fmt.Fprintf(ErrWriter,
|
|
||||||
"DEPRECATED Action signature. Must be `cli.ActionFunc`. %s See %s\n",
|
|
||||||
contactSysadmin, appActionDeprecationURL)
|
|
||||||
return nil
|
return nil
|
||||||
|
} else {
|
||||||
|
return errInvalidActionType
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(vals) > 1 {
|
|
||||||
return errInvalidActionSignature
|
|
||||||
}
|
|
||||||
|
|
||||||
if retErr, ok := vals[0].Interface().(error); vals[0].IsValid() && ok {
|
|
||||||
return retErr
|
|
||||||
}
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
3
vendor/github.com/urfave/cli/cli.go
generated
vendored
3
vendor/github.com/urfave/cli/cli.go
generated
vendored
@@ -12,8 +12,11 @@
|
|||||||
// app.Usage = "say a greeting"
|
// app.Usage = "say a greeting"
|
||||||
// app.Action = func(c *cli.Context) error {
|
// app.Action = func(c *cli.Context) error {
|
||||||
// println("Greetings")
|
// println("Greetings")
|
||||||
|
// return nil
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// app.Run(os.Args)
|
// app.Run(os.Args)
|
||||||
// }
|
// }
|
||||||
package cli
|
package cli
|
||||||
|
|
||||||
|
//go:generate python ./generate-flag-types cli -i flag-types.json -o flag_generated.go
|
||||||
|
85
vendor/github.com/urfave/cli/command.go
generated
vendored
85
vendor/github.com/urfave/cli/command.go
generated
vendored
@@ -46,6 +46,11 @@ type Command struct {
|
|||||||
Flags []Flag
|
Flags []Flag
|
||||||
// Treat all flags as normal arguments if true
|
// Treat all flags as normal arguments if true
|
||||||
SkipFlagParsing bool
|
SkipFlagParsing bool
|
||||||
|
// Skip argument reordering which attempts to move flags before arguments,
|
||||||
|
// but only works if all flags appear after all arguments. This behavior was
|
||||||
|
// removed n version 2 since it only works under specific conditions so we
|
||||||
|
// backport here by exposing it as an option for compatibility.
|
||||||
|
SkipArgReorder bool
|
||||||
// Boolean to hide built-in help command
|
// Boolean to hide built-in help command
|
||||||
HideHelp bool
|
HideHelp bool
|
||||||
// Boolean to hide this command from help or completion
|
// Boolean to hide this command from help or completion
|
||||||
@@ -54,6 +59,25 @@ type Command struct {
|
|||||||
// Full name of command for help, defaults to full command name, including parent commands.
|
// Full name of command for help, defaults to full command name, including parent commands.
|
||||||
HelpName string
|
HelpName string
|
||||||
commandNamePath []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
|
||||||
|
}
|
||||||
|
|
||||||
|
type CommandsByName []Command
|
||||||
|
|
||||||
|
func (c CommandsByName) Len() int {
|
||||||
|
return len(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c CommandsByName) Less(i, j int) bool {
|
||||||
|
return 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.
|
// FullName returns the full name of the command.
|
||||||
@@ -82,14 +106,15 @@ func (c Command) Run(ctx *Context) (err error) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if ctx.App.EnableBashCompletion {
|
set, err := flagSet(c.Name, c.Flags)
|
||||||
c.Flags = append(c.Flags, BashCompletionFlag)
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
set := flagSet(c.Name, c.Flags)
|
|
||||||
set.SetOutput(ioutil.Discard)
|
set.SetOutput(ioutil.Discard)
|
||||||
|
|
||||||
if !c.SkipFlagParsing {
|
if c.SkipFlagParsing {
|
||||||
|
err = set.Parse(append([]string{"--"}, ctx.Args().Tail()...))
|
||||||
|
} else if !c.SkipArgReorder {
|
||||||
firstFlagIndex := -1
|
firstFlagIndex := -1
|
||||||
terminatorIndex := -1
|
terminatorIndex := -1
|
||||||
for index, arg := range ctx.Args() {
|
for index, arg := range ctx.Args() {
|
||||||
@@ -122,21 +147,7 @@ func (c Command) Run(ctx *Context) (err error) {
|
|||||||
err = set.Parse(ctx.Args().Tail())
|
err = set.Parse(ctx.Args().Tail())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if c.SkipFlagParsing {
|
err = set.Parse(ctx.Args().Tail())
|
||||||
err = set.Parse(append([]string{"--"}, ctx.Args().Tail()...))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
if c.OnUsageError != nil {
|
|
||||||
err := c.OnUsageError(ctx, err, false)
|
|
||||||
HandleExitCoder(err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fmt.Fprintln(ctx.App.Writer, "Incorrect Usage.")
|
|
||||||
fmt.Fprintln(ctx.App.Writer)
|
|
||||||
ShowCommandHelp(ctx, c.Name)
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nerr := normalizeFlags(c.Flags, set)
|
nerr := normalizeFlags(c.Flags, set)
|
||||||
@@ -148,11 +159,23 @@ func (c Command) Run(ctx *Context) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
context := NewContext(ctx.App, set, ctx)
|
context := NewContext(ctx.App, set, ctx)
|
||||||
|
context.Command = c
|
||||||
if checkCommandCompletions(context, c.Name) {
|
if checkCommandCompletions(context, c.Name) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
if c.OnUsageError != nil {
|
||||||
|
err := c.OnUsageError(context, err, false)
|
||||||
|
HandleExitCoder(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Fprintln(context.App.Writer, "Incorrect Usage:", err.Error())
|
||||||
|
fmt.Fprintln(context.App.Writer)
|
||||||
|
ShowCommandHelp(context, c.Name)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if checkCommandHelp(context, c.Name) {
|
if checkCommandHelp(context, c.Name) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -174,15 +197,16 @@ func (c Command) Run(ctx *Context) (err error) {
|
|||||||
if c.Before != nil {
|
if c.Before != nil {
|
||||||
err = c.Before(context)
|
err = c.Before(context)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintln(ctx.App.Writer, err)
|
ShowCommandHelp(context, c.Name)
|
||||||
fmt.Fprintln(ctx.App.Writer)
|
|
||||||
ShowCommandHelp(ctx, c.Name)
|
|
||||||
HandleExitCoder(err)
|
HandleExitCoder(err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
context.Command = c
|
if c.Action == nil {
|
||||||
|
c.Action = helpSubcommand.Action
|
||||||
|
}
|
||||||
|
|
||||||
err = HandleAction(c.Action, context)
|
err = HandleAction(c.Action, context)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -223,14 +247,13 @@ func (c Command) startApp(ctx *Context) error {
|
|||||||
app.HelpName = app.Name
|
app.HelpName = app.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.Description != "" {
|
app.Usage = c.Usage
|
||||||
app.Usage = c.Description
|
app.Description = c.Description
|
||||||
} else {
|
app.ArgsUsage = c.ArgsUsage
|
||||||
app.Usage = c.Usage
|
|
||||||
}
|
|
||||||
|
|
||||||
// set CommandNotFound
|
// set CommandNotFound
|
||||||
app.CommandNotFound = ctx.App.CommandNotFound
|
app.CommandNotFound = ctx.App.CommandNotFound
|
||||||
|
app.CustomAppHelpTemplate = c.CustomHelpTemplate
|
||||||
|
|
||||||
// set the flags and commands
|
// set the flags and commands
|
||||||
app.Commands = c.Subcommands
|
app.Commands = c.Subcommands
|
||||||
@@ -243,6 +266,7 @@ func (c Command) startApp(ctx *Context) error {
|
|||||||
app.Author = ctx.App.Author
|
app.Author = ctx.App.Author
|
||||||
app.Email = ctx.App.Email
|
app.Email = ctx.App.Email
|
||||||
app.Writer = ctx.App.Writer
|
app.Writer = ctx.App.Writer
|
||||||
|
app.ErrWriter = ctx.App.ErrWriter
|
||||||
|
|
||||||
app.categories = CommandCategories{}
|
app.categories = CommandCategories{}
|
||||||
for _, command := range c.Subcommands {
|
for _, command := range c.Subcommands {
|
||||||
@@ -265,6 +289,7 @@ func (c Command) startApp(ctx *Context) error {
|
|||||||
} else {
|
} else {
|
||||||
app.Action = helpSubcommand.Action
|
app.Action = helpSubcommand.Action
|
||||||
}
|
}
|
||||||
|
app.OnUsageError = c.OnUsageError
|
||||||
|
|
||||||
for index, cc := range app.Commands {
|
for index, cc := range app.Commands {
|
||||||
app.Commands[index].commandNamePath = []string{c.Name, cc.Name}
|
app.Commands[index].commandNamePath = []string{c.Name, cc.Name}
|
||||||
|
330
vendor/github.com/urfave/cli/context.go
generated
vendored
330
vendor/github.com/urfave/cli/context.go
generated
vendored
@@ -3,9 +3,9 @@ package cli
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"flag"
|
"flag"
|
||||||
"strconv"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"syscall"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Context is a type that is passed through to
|
// Context is a type that is passed through to
|
||||||
@@ -13,147 +13,23 @@ import (
|
|||||||
// can be used to retrieve context-specific Args and
|
// can be used to retrieve context-specific Args and
|
||||||
// parsed command-line options.
|
// parsed command-line options.
|
||||||
type Context struct {
|
type Context struct {
|
||||||
App *App
|
App *App
|
||||||
Command Command
|
Command Command
|
||||||
flagSet *flag.FlagSet
|
shellComplete bool
|
||||||
setFlags map[string]bool
|
flagSet *flag.FlagSet
|
||||||
globalSetFlags map[string]bool
|
setFlags map[string]bool
|
||||||
parentContext *Context
|
parentContext *Context
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewContext creates a new context. For use in when invoking an App or Command action.
|
// 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 {
|
func NewContext(app *App, set *flag.FlagSet, parentCtx *Context) *Context {
|
||||||
return &Context{App: app, flagSet: set, parentContext: parentCtx}
|
c := &Context{App: app, flagSet: set, parentContext: parentCtx}
|
||||||
}
|
|
||||||
|
|
||||||
// Int looks up the value of a local int flag, returns 0 if no int flag exists
|
if parentCtx != nil {
|
||||||
func (c *Context) Int(name string) int {
|
c.shellComplete = parentCtx.shellComplete
|
||||||
return lookupInt(name, c.flagSet)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Duration looks up the value of a local time.Duration flag, returns 0 if no
|
|
||||||
// time.Duration flag exists
|
|
||||||
func (c *Context) Duration(name string) time.Duration {
|
|
||||||
return lookupDuration(name, c.flagSet)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Float64 looks up the value of a local float64 flag, returns 0 if no float64
|
|
||||||
// flag exists
|
|
||||||
func (c *Context) Float64(name string) float64 {
|
|
||||||
return lookupFloat64(name, c.flagSet)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bool looks up the value of a local bool flag, returns false if no bool flag exists
|
|
||||||
func (c *Context) Bool(name string) bool {
|
|
||||||
return lookupBool(name, c.flagSet)
|
|
||||||
}
|
|
||||||
|
|
||||||
// BoolT looks up the value of a local boolT flag, returns false if no bool flag exists
|
|
||||||
func (c *Context) BoolT(name string) bool {
|
|
||||||
return lookupBoolT(name, c.flagSet)
|
|
||||||
}
|
|
||||||
|
|
||||||
// String looks up the value of a local string flag, returns "" if no string flag exists
|
|
||||||
func (c *Context) String(name string) string {
|
|
||||||
return lookupString(name, c.flagSet)
|
|
||||||
}
|
|
||||||
|
|
||||||
// StringSlice looks up the value of a local string slice flag, returns nil if no
|
|
||||||
// string slice flag exists
|
|
||||||
func (c *Context) StringSlice(name string) []string {
|
|
||||||
return lookupStringSlice(name, c.flagSet)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IntSlice looks up the value of a local int slice flag, returns nil if no int
|
|
||||||
// slice flag exists
|
|
||||||
func (c *Context) IntSlice(name string) []int {
|
|
||||||
return lookupIntSlice(name, c.flagSet)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generic looks up the value of a local generic flag, returns nil if no generic
|
|
||||||
// flag exists
|
|
||||||
func (c *Context) Generic(name string) interface{} {
|
|
||||||
return lookupGeneric(name, c.flagSet)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GlobalInt looks up the value of a global int flag, returns 0 if no int flag exists
|
|
||||||
func (c *Context) GlobalInt(name string) int {
|
|
||||||
if fs := lookupGlobalFlagSet(name, c); fs != nil {
|
|
||||||
return lookupInt(name, fs)
|
|
||||||
}
|
}
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// GlobalFloat64 looks up the value of a global float64 flag, returns float64(0)
|
return c
|
||||||
// if no float64 flag exists
|
|
||||||
func (c *Context) GlobalFloat64(name string) float64 {
|
|
||||||
if fs := lookupGlobalFlagSet(name, c); fs != nil {
|
|
||||||
return lookupFloat64(name, fs)
|
|
||||||
}
|
|
||||||
return float64(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GlobalDuration looks up the value of a global time.Duration flag, returns 0
|
|
||||||
// if no time.Duration flag exists
|
|
||||||
func (c *Context) GlobalDuration(name string) time.Duration {
|
|
||||||
if fs := lookupGlobalFlagSet(name, c); fs != nil {
|
|
||||||
return lookupDuration(name, fs)
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// GlobalBool looks up the value of a global bool flag, returns false if no bool
|
|
||||||
// flag exists
|
|
||||||
func (c *Context) GlobalBool(name string) bool {
|
|
||||||
if fs := lookupGlobalFlagSet(name, c); fs != nil {
|
|
||||||
return lookupBool(name, fs)
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// GlobalBoolT looks up the value of a global bool flag, returns true if no bool
|
|
||||||
// flag exists
|
|
||||||
func (c *Context) GlobalBoolT(name string) bool {
|
|
||||||
if fs := lookupGlobalFlagSet(name, c); fs != nil {
|
|
||||||
return lookupBoolT(name, fs)
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// GlobalString looks up the value of a global string flag, returns "" if no
|
|
||||||
// string flag exists
|
|
||||||
func (c *Context) GlobalString(name string) string {
|
|
||||||
if fs := lookupGlobalFlagSet(name, c); fs != nil {
|
|
||||||
return lookupString(name, fs)
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// GlobalStringSlice looks up the value of a global string slice flag, returns
|
|
||||||
// nil if no string slice flag exists
|
|
||||||
func (c *Context) GlobalStringSlice(name string) []string {
|
|
||||||
if fs := lookupGlobalFlagSet(name, c); fs != nil {
|
|
||||||
return lookupStringSlice(name, fs)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GlobalIntSlice looks up the value of a global int slice flag, returns nil if
|
|
||||||
// no int slice flag exists
|
|
||||||
func (c *Context) GlobalIntSlice(name string) []int {
|
|
||||||
if fs := lookupGlobalFlagSet(name, c); fs != nil {
|
|
||||||
return lookupIntSlice(name, fs)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GlobalGeneric looks up the value of a global generic flag, returns nil if no
|
|
||||||
// generic flag exists
|
|
||||||
func (c *Context) GlobalGeneric(name string) interface{} {
|
|
||||||
if fs := lookupGlobalFlagSet(name, c); fs != nil {
|
|
||||||
return lookupGeneric(name, fs)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NumFlags returns the number of flags set
|
// NumFlags returns the number of flags set
|
||||||
@@ -163,11 +39,13 @@ func (c *Context) NumFlags() int {
|
|||||||
|
|
||||||
// Set sets a context flag to a value.
|
// Set sets a context flag to a value.
|
||||||
func (c *Context) Set(name, value string) error {
|
func (c *Context) Set(name, value string) error {
|
||||||
|
c.setFlags = nil
|
||||||
return c.flagSet.Set(name, value)
|
return c.flagSet.Set(name, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GlobalSet sets a context flag to a value on the global flagset
|
// GlobalSet sets a context flag to a value on the global flagset
|
||||||
func (c *Context) GlobalSet(name, value string) error {
|
func (c *Context) GlobalSet(name, value string) error {
|
||||||
|
globalContext(c).setFlags = nil
|
||||||
return globalContext(c).flagSet.Set(name, value)
|
return globalContext(c).flagSet.Set(name, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,28 +53,78 @@ func (c *Context) GlobalSet(name, value string) error {
|
|||||||
func (c *Context) IsSet(name string) bool {
|
func (c *Context) IsSet(name string) bool {
|
||||||
if c.setFlags == nil {
|
if c.setFlags == nil {
|
||||||
c.setFlags = make(map[string]bool)
|
c.setFlags = make(map[string]bool)
|
||||||
|
|
||||||
c.flagSet.Visit(func(f *flag.Flag) {
|
c.flagSet.Visit(func(f *flag.Flag) {
|
||||||
c.setFlags[f.Name] = true
|
c.setFlags[f.Name] = true
|
||||||
})
|
})
|
||||||
|
|
||||||
|
c.flagSet.VisitAll(func(f *flag.Flag) {
|
||||||
|
if _, ok := c.setFlags[f.Name]; ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.setFlags[f.Name] = false
|
||||||
|
})
|
||||||
|
|
||||||
|
// XXX hack to support IsSet for flags with EnvVar
|
||||||
|
//
|
||||||
|
// There isn't an easy way to do this with the current implementation since
|
||||||
|
// whether a flag was set via an environment variable is very difficult to
|
||||||
|
// determine here. Instead, we intend to introduce a backwards incompatible
|
||||||
|
// change in version 2 to add `IsSet` to the Flag interface to push the
|
||||||
|
// responsibility closer to where the information required to determine
|
||||||
|
// whether a flag is set by non-standard means such as environment
|
||||||
|
// variables is avaliable.
|
||||||
|
//
|
||||||
|
// See https://github.com/urfave/cli/issues/294 for additional discussion
|
||||||
|
flags := c.Command.Flags
|
||||||
|
if c.Command.Name == "" { // cannot == Command{} since it contains slice types
|
||||||
|
if c.App != nil {
|
||||||
|
flags = c.App.Flags
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, f := range flags {
|
||||||
|
eachName(f.GetName(), func(name string) {
|
||||||
|
if isSet, ok := c.setFlags[name]; isSet || !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val := reflect.ValueOf(f)
|
||||||
|
if val.Kind() == reflect.Ptr {
|
||||||
|
val = val.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
envVarValue := val.FieldByName("EnvVar")
|
||||||
|
if !envVarValue.IsValid() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
eachName(envVarValue.String(), func(envVar string) {
|
||||||
|
envVar = strings.TrimSpace(envVar)
|
||||||
|
if _, ok := syscall.Getenv(envVar); ok {
|
||||||
|
c.setFlags[name] = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return c.setFlags[name] == true
|
|
||||||
|
return c.setFlags[name]
|
||||||
}
|
}
|
||||||
|
|
||||||
// GlobalIsSet determines if the global flag was actually set
|
// GlobalIsSet determines if the global flag was actually set
|
||||||
func (c *Context) GlobalIsSet(name string) bool {
|
func (c *Context) GlobalIsSet(name string) bool {
|
||||||
if c.globalSetFlags == nil {
|
ctx := c
|
||||||
c.globalSetFlags = make(map[string]bool)
|
if ctx.parentContext != nil {
|
||||||
ctx := c
|
ctx = ctx.parentContext
|
||||||
if ctx.parentContext != nil {
|
}
|
||||||
ctx = ctx.parentContext
|
|
||||||
}
|
for ; ctx != nil; ctx = ctx.parentContext {
|
||||||
for ; ctx != nil && c.globalSetFlags[name] == false; ctx = ctx.parentContext {
|
if ctx.IsSet(name) {
|
||||||
ctx.flagSet.Visit(func(f *flag.Flag) {
|
return true
|
||||||
c.globalSetFlags[f.Name] = true
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return c.globalSetFlags[name]
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// FlagNames returns a slice of flag names used in this context.
|
// FlagNames returns a slice of flag names used in this context.
|
||||||
@@ -228,6 +156,11 @@ func (c *Context) Parent() *Context {
|
|||||||
return c.parentContext
|
return c.parentContext
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// value returns the value of the flag coressponding to `name`
|
||||||
|
func (c *Context) value(name string) interface{} {
|
||||||
|
return c.flagSet.Lookup(name).Value.(flag.Getter).Get()
|
||||||
|
}
|
||||||
|
|
||||||
// Args contains apps console arguments
|
// Args contains apps console arguments
|
||||||
type Args []string
|
type Args []string
|
||||||
|
|
||||||
@@ -303,107 +236,6 @@ func lookupGlobalFlagSet(name string, ctx *Context) *flag.FlagSet {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func lookupInt(name string, set *flag.FlagSet) int {
|
|
||||||
f := set.Lookup(name)
|
|
||||||
if f != nil {
|
|
||||||
val, err := strconv.Atoi(f.Value.String())
|
|
||||||
if err != nil {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return val
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func lookupDuration(name string, set *flag.FlagSet) time.Duration {
|
|
||||||
f := set.Lookup(name)
|
|
||||||
if f != nil {
|
|
||||||
val, err := time.ParseDuration(f.Value.String())
|
|
||||||
if err == nil {
|
|
||||||
return val
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func lookupFloat64(name string, set *flag.FlagSet) float64 {
|
|
||||||
f := set.Lookup(name)
|
|
||||||
if f != nil {
|
|
||||||
val, err := strconv.ParseFloat(f.Value.String(), 64)
|
|
||||||
if err != nil {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return val
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func lookupString(name string, set *flag.FlagSet) string {
|
|
||||||
f := set.Lookup(name)
|
|
||||||
if f != nil {
|
|
||||||
return f.Value.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func lookupStringSlice(name string, set *flag.FlagSet) []string {
|
|
||||||
f := set.Lookup(name)
|
|
||||||
if f != nil {
|
|
||||||
return (f.Value.(*StringSlice)).Value()
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func lookupIntSlice(name string, set *flag.FlagSet) []int {
|
|
||||||
f := set.Lookup(name)
|
|
||||||
if f != nil {
|
|
||||||
return (f.Value.(*IntSlice)).Value()
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func lookupGeneric(name string, set *flag.FlagSet) interface{} {
|
|
||||||
f := set.Lookup(name)
|
|
||||||
if f != nil {
|
|
||||||
return f.Value
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func lookupBool(name string, set *flag.FlagSet) bool {
|
|
||||||
f := set.Lookup(name)
|
|
||||||
if f != nil {
|
|
||||||
val, err := strconv.ParseBool(f.Value.String())
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return val
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func lookupBoolT(name string, set *flag.FlagSet) bool {
|
|
||||||
f := set.Lookup(name)
|
|
||||||
if f != nil {
|
|
||||||
val, err := strconv.ParseBool(f.Value.String())
|
|
||||||
if err != nil {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return val
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func copyFlag(name string, ff *flag.Flag, set *flag.FlagSet) {
|
func copyFlag(name string, ff *flag.Flag, set *flag.FlagSet) {
|
||||||
switch ff.Value.(type) {
|
switch ff.Value.(type) {
|
||||||
case *StringSlice:
|
case *StringSlice:
|
||||||
|
41
vendor/github.com/urfave/cli/errors.go
generated
vendored
41
vendor/github.com/urfave/cli/errors.go
generated
vendored
@@ -24,7 +24,7 @@ func NewMultiError(err ...error) MultiError {
|
|||||||
return MultiError{Errors: err}
|
return MultiError{Errors: err}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Error implents the error interface.
|
// Error implements the error interface.
|
||||||
func (m MultiError) Error() string {
|
func (m MultiError) Error() string {
|
||||||
errs := make([]string, len(m.Errors))
|
errs := make([]string, len(m.Errors))
|
||||||
for i, err := range m.Errors {
|
for i, err := range m.Errors {
|
||||||
@@ -34,6 +34,10 @@ func (m MultiError) Error() string {
|
|||||||
return strings.Join(errs, "\n")
|
return strings.Join(errs, "\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ErrorFormatter interface {
|
||||||
|
Format(s fmt.State, verb rune)
|
||||||
|
}
|
||||||
|
|
||||||
// ExitCoder is the interface checked by `App` and `Command` for a custom exit
|
// ExitCoder is the interface checked by `App` and `Command` for a custom exit
|
||||||
// code
|
// code
|
||||||
type ExitCoder interface {
|
type ExitCoder interface {
|
||||||
@@ -44,11 +48,11 @@ type ExitCoder interface {
|
|||||||
// ExitError fulfills both the builtin `error` interface and `ExitCoder`
|
// ExitError fulfills both the builtin `error` interface and `ExitCoder`
|
||||||
type ExitError struct {
|
type ExitError struct {
|
||||||
exitCode int
|
exitCode int
|
||||||
message string
|
message interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewExitError makes a new *ExitError
|
// NewExitError makes a new *ExitError
|
||||||
func NewExitError(message string, exitCode int) *ExitError {
|
func NewExitError(message interface{}, exitCode int) *ExitError {
|
||||||
return &ExitError{
|
return &ExitError{
|
||||||
exitCode: exitCode,
|
exitCode: exitCode,
|
||||||
message: message,
|
message: message,
|
||||||
@@ -58,7 +62,7 @@ func NewExitError(message string, exitCode int) *ExitError {
|
|||||||
// Error returns the string message, fulfilling the interface required by
|
// Error returns the string message, fulfilling the interface required by
|
||||||
// `error`
|
// `error`
|
||||||
func (ee *ExitError) Error() string {
|
func (ee *ExitError) Error() string {
|
||||||
return ee.message
|
return fmt.Sprintf("%v", ee.message)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExitCode returns the exit code, fulfilling the interface required by
|
// ExitCode returns the exit code, fulfilling the interface required by
|
||||||
@@ -70,7 +74,7 @@ func (ee *ExitError) ExitCode() int {
|
|||||||
// HandleExitCoder checks if the error fulfills the ExitCoder interface, and if
|
// HandleExitCoder checks if the error fulfills the ExitCoder interface, and if
|
||||||
// so prints the error to stderr (if it is non-empty) and calls OsExiter with the
|
// so prints the error to stderr (if it is non-empty) and calls OsExiter with the
|
||||||
// given exit code. If the given error is a MultiError, then this func is
|
// given exit code. If the given error is a MultiError, then this func is
|
||||||
// called on all members of the Errors slice.
|
// called on all members of the Errors slice and calls OsExiter with the last exit code.
|
||||||
func HandleExitCoder(err error) {
|
func HandleExitCoder(err error) {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return
|
return
|
||||||
@@ -78,15 +82,34 @@ func HandleExitCoder(err error) {
|
|||||||
|
|
||||||
if exitErr, ok := err.(ExitCoder); ok {
|
if exitErr, ok := err.(ExitCoder); ok {
|
||||||
if err.Error() != "" {
|
if err.Error() != "" {
|
||||||
fmt.Fprintln(ErrWriter, err)
|
if _, ok := exitErr.(ErrorFormatter); ok {
|
||||||
|
fmt.Fprintf(ErrWriter, "%+v\n", err)
|
||||||
|
} else {
|
||||||
|
fmt.Fprintln(ErrWriter, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
OsExiter(exitErr.ExitCode())
|
OsExiter(exitErr.ExitCode())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if multiErr, ok := err.(MultiError); ok {
|
if multiErr, ok := err.(MultiError); ok {
|
||||||
for _, merr := range multiErr.Errors {
|
code := handleMultiError(multiErr)
|
||||||
HandleExitCoder(merr)
|
OsExiter(code)
|
||||||
}
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func handleMultiError(multiErr MultiError) int {
|
||||||
|
code := 1
|
||||||
|
for _, merr := range multiErr.Errors {
|
||||||
|
if multiErr2, ok := merr.(MultiError); ok {
|
||||||
|
code = handleMultiError(multiErr2)
|
||||||
|
} else {
|
||||||
|
fmt.Fprintln(ErrWriter, merr)
|
||||||
|
if exitErr, ok := merr.(ExitCoder); ok {
|
||||||
|
code = exitErr.ExitCode()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
562
vendor/github.com/urfave/cli/flag.go
generated
vendored
562
vendor/github.com/urfave/cli/flag.go
generated
vendored
@@ -3,24 +3,24 @@ package cli
|
|||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
const defaultPlaceholder = "value"
|
const defaultPlaceholder = "value"
|
||||||
|
|
||||||
// BashCompletionFlag enables bash-completion for all commands and subcommands
|
// BashCompletionFlag enables bash-completion for all commands and subcommands
|
||||||
var BashCompletionFlag = BoolFlag{
|
var BashCompletionFlag Flag = BoolFlag{
|
||||||
Name: "generate-bash-completion",
|
Name: "generate-bash-completion",
|
||||||
Hidden: true,
|
Hidden: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
// VersionFlag prints the version for the application
|
// VersionFlag prints the version for the application
|
||||||
var VersionFlag = BoolFlag{
|
var VersionFlag Flag = BoolFlag{
|
||||||
Name: "version, v",
|
Name: "version, v",
|
||||||
Usage: "print the version",
|
Usage: "print the version",
|
||||||
}
|
}
|
||||||
@@ -28,7 +28,7 @@ var VersionFlag = BoolFlag{
|
|||||||
// HelpFlag prints the help for all commands and subcommands
|
// HelpFlag prints the help for all commands and subcommands
|
||||||
// Set to the zero value (BoolFlag{}) to disable flag -- keeps subcommand
|
// Set to the zero value (BoolFlag{}) to disable flag -- keeps subcommand
|
||||||
// unless HideHelp is set to true)
|
// unless HideHelp is set to true)
|
||||||
var HelpFlag = BoolFlag{
|
var HelpFlag Flag = BoolFlag{
|
||||||
Name: "help, h",
|
Name: "help, h",
|
||||||
Usage: "show help",
|
Usage: "show help",
|
||||||
}
|
}
|
||||||
@@ -37,6 +37,21 @@ var HelpFlag = BoolFlag{
|
|||||||
// to display a flag.
|
// to display a flag.
|
||||||
var FlagStringer FlagStringFunc = stringifyFlag
|
var FlagStringer FlagStringFunc = stringifyFlag
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
return f[i].GetName() < f[j].GetName()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f FlagsByName) Swap(i, j int) {
|
||||||
|
f[i], f[j] = f[j], f[i]
|
||||||
|
}
|
||||||
|
|
||||||
// Flag is a common interface related to parsing flags in cli.
|
// Flag is a common interface related to parsing flags in cli.
|
||||||
// For more advanced flag parsing techniques, it is recommended that
|
// For more advanced flag parsing techniques, it is recommended that
|
||||||
// this interface be implemented.
|
// this interface be implemented.
|
||||||
@@ -47,13 +62,29 @@ type Flag interface {
|
|||||||
GetName() string
|
GetName() string
|
||||||
}
|
}
|
||||||
|
|
||||||
func flagSet(name string, flags []Flag) *flag.FlagSet {
|
// errorableFlag is an interface that allows us to return errors during apply
|
||||||
|
// it allows flags defined in this library to return errors in a fashion backwards compatible
|
||||||
|
// TODO remove in v2 and modify the existing Flag interface to return errors
|
||||||
|
type errorableFlag interface {
|
||||||
|
Flag
|
||||||
|
|
||||||
|
ApplyWithError(*flag.FlagSet) error
|
||||||
|
}
|
||||||
|
|
||||||
|
func flagSet(name string, flags []Flag) (*flag.FlagSet, error) {
|
||||||
set := flag.NewFlagSet(name, flag.ContinueOnError)
|
set := flag.NewFlagSet(name, flag.ContinueOnError)
|
||||||
|
|
||||||
for _, f := range flags {
|
for _, f := range flags {
|
||||||
f.Apply(set)
|
//TODO remove in v2 when errorableFlag is removed
|
||||||
|
if ef, ok := f.(errorableFlag); ok {
|
||||||
|
if err := ef.ApplyWithError(set); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
f.Apply(set)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return set
|
return set, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func eachName(longName string, fn func(string)) {
|
func eachName(longName string, fn func(string)) {
|
||||||
@@ -70,31 +101,24 @@ type Generic interface {
|
|||||||
String() string
|
String() string
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenericFlag is the flag type for types implementing Generic
|
|
||||||
type GenericFlag struct {
|
|
||||||
Name string
|
|
||||||
Value Generic
|
|
||||||
Usage string
|
|
||||||
EnvVar string
|
|
||||||
Hidden bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns the string representation of the generic flag to display the
|
|
||||||
// help text to the user (uses the String() method of the generic flag to show
|
|
||||||
// the value)
|
|
||||||
func (f GenericFlag) String() string {
|
|
||||||
return FlagStringer(f)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply takes the flagset and calls Set on the generic flag with the value
|
// Apply takes the flagset and calls Set on the generic flag with the value
|
||||||
// provided by the user for parsing by the flag
|
// provided by the user for parsing by the flag
|
||||||
|
// Ignores parsing errors
|
||||||
func (f GenericFlag) Apply(set *flag.FlagSet) {
|
func (f GenericFlag) Apply(set *flag.FlagSet) {
|
||||||
|
f.ApplyWithError(set)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApplyWithError 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) ApplyWithError(set *flag.FlagSet) error {
|
||||||
val := f.Value
|
val := f.Value
|
||||||
if f.EnvVar != "" {
|
if f.EnvVar != "" {
|
||||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||||
envVar = strings.TrimSpace(envVar)
|
envVar = strings.TrimSpace(envVar)
|
||||||
if envVal := os.Getenv(envVar); envVal != "" {
|
if envVal, ok := syscall.Getenv(envVar); ok {
|
||||||
val.Set(envVal)
|
if err := val.Set(envVal); err != nil {
|
||||||
|
return fmt.Errorf("could not parse %s as value for flag %s: %s", envVal, f.Name, err)
|
||||||
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -103,14 +127,11 @@ func (f GenericFlag) Apply(set *flag.FlagSet) {
|
|||||||
eachName(f.Name, func(name string) {
|
eachName(f.Name, func(name string) {
|
||||||
set.Var(f.Value, name, f.Usage)
|
set.Var(f.Value, name, f.Usage)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetName returns the name of a flag.
|
// StringSlice is an opaque type for []string to satisfy flag.Value and flag.Getter
|
||||||
func (f GenericFlag) GetName() string {
|
|
||||||
return f.Name
|
|
||||||
}
|
|
||||||
|
|
||||||
// StringSlice is an opaque type for []string to satisfy flag.Value
|
|
||||||
type StringSlice []string
|
type StringSlice []string
|
||||||
|
|
||||||
// Set appends the string value to the list of values
|
// Set appends the string value to the list of values
|
||||||
@@ -129,31 +150,29 @@ func (f *StringSlice) Value() []string {
|
|||||||
return *f
|
return *f
|
||||||
}
|
}
|
||||||
|
|
||||||
// StringSliceFlag is a string flag that can be specified multiple times on the
|
// Get returns the slice of strings set by this flag
|
||||||
// command-line
|
func (f *StringSlice) Get() interface{} {
|
||||||
type StringSliceFlag struct {
|
return *f
|
||||||
Name string
|
|
||||||
Value *StringSlice
|
|
||||||
Usage string
|
|
||||||
EnvVar string
|
|
||||||
Hidden bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns the usage
|
|
||||||
func (f StringSliceFlag) String() string {
|
|
||||||
return FlagStringer(f)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply populates the flag given the flag set and environment
|
// Apply populates the flag given the flag set and environment
|
||||||
|
// Ignores errors
|
||||||
func (f StringSliceFlag) Apply(set *flag.FlagSet) {
|
func (f StringSliceFlag) Apply(set *flag.FlagSet) {
|
||||||
|
f.ApplyWithError(set)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApplyWithError populates the flag given the flag set and environment
|
||||||
|
func (f StringSliceFlag) ApplyWithError(set *flag.FlagSet) error {
|
||||||
if f.EnvVar != "" {
|
if f.EnvVar != "" {
|
||||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||||
envVar = strings.TrimSpace(envVar)
|
envVar = strings.TrimSpace(envVar)
|
||||||
if envVal := os.Getenv(envVar); envVal != "" {
|
if envVal, ok := syscall.Getenv(envVar); ok {
|
||||||
newVal := &StringSlice{}
|
newVal := &StringSlice{}
|
||||||
for _, s := range strings.Split(envVal, ",") {
|
for _, s := range strings.Split(envVal, ",") {
|
||||||
s = strings.TrimSpace(s)
|
s = strings.TrimSpace(s)
|
||||||
newVal.Set(s)
|
if err := newVal.Set(s); err != nil {
|
||||||
|
return fmt.Errorf("could not parse %s as string value for flag %s: %s", envVal, f.Name, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
f.Value = newVal
|
f.Value = newVal
|
||||||
break
|
break
|
||||||
@@ -167,14 +186,11 @@ func (f StringSliceFlag) Apply(set *flag.FlagSet) {
|
|||||||
}
|
}
|
||||||
set.Var(f.Value, name, f.Usage)
|
set.Var(f.Value, name, f.Usage)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetName returns the name of a flag.
|
// IntSlice is an opaque type for []int to satisfy flag.Value and flag.Getter
|
||||||
func (f StringSliceFlag) GetName() string {
|
|
||||||
return f.Name
|
|
||||||
}
|
|
||||||
|
|
||||||
// IntSlice is an opaque type for []int to satisfy flag.Value
|
|
||||||
type IntSlice []int
|
type IntSlice []int
|
||||||
|
|
||||||
// Set parses the value into an integer and appends it to the list of values
|
// Set parses the value into an integer and appends it to the list of values
|
||||||
@@ -189,7 +205,7 @@ func (f *IntSlice) Set(value string) error {
|
|||||||
|
|
||||||
// String returns a readable representation of this value (for usage defaults)
|
// String returns a readable representation of this value (for usage defaults)
|
||||||
func (f *IntSlice) String() string {
|
func (f *IntSlice) String() string {
|
||||||
return fmt.Sprintf("%d", *f)
|
return fmt.Sprintf("%#v", *f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Value returns the slice of ints set by this flag
|
// Value returns the slice of ints set by this flag
|
||||||
@@ -197,33 +213,28 @@ func (f *IntSlice) Value() []int {
|
|||||||
return *f
|
return *f
|
||||||
}
|
}
|
||||||
|
|
||||||
// IntSliceFlag is an int flag that can be specified multiple times on the
|
// Get returns the slice of ints set by this flag
|
||||||
// command-line
|
func (f *IntSlice) Get() interface{} {
|
||||||
type IntSliceFlag struct {
|
return *f
|
||||||
Name string
|
|
||||||
Value *IntSlice
|
|
||||||
Usage string
|
|
||||||
EnvVar string
|
|
||||||
Hidden bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns the usage
|
|
||||||
func (f IntSliceFlag) String() string {
|
|
||||||
return FlagStringer(f)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply populates the flag given the flag set and environment
|
// Apply populates the flag given the flag set and environment
|
||||||
|
// Ignores errors
|
||||||
func (f IntSliceFlag) Apply(set *flag.FlagSet) {
|
func (f IntSliceFlag) Apply(set *flag.FlagSet) {
|
||||||
|
f.ApplyWithError(set)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApplyWithError populates the flag given the flag set and environment
|
||||||
|
func (f IntSliceFlag) ApplyWithError(set *flag.FlagSet) error {
|
||||||
if f.EnvVar != "" {
|
if f.EnvVar != "" {
|
||||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||||
envVar = strings.TrimSpace(envVar)
|
envVar = strings.TrimSpace(envVar)
|
||||||
if envVal := os.Getenv(envVar); envVal != "" {
|
if envVal, ok := syscall.Getenv(envVar); ok {
|
||||||
newVal := &IntSlice{}
|
newVal := &IntSlice{}
|
||||||
for _, s := range strings.Split(envVal, ",") {
|
for _, s := range strings.Split(envVal, ",") {
|
||||||
s = strings.TrimSpace(s)
|
s = strings.TrimSpace(s)
|
||||||
err := newVal.Set(s)
|
if err := newVal.Set(s); err != nil {
|
||||||
if err != nil {
|
return fmt.Errorf("could not parse %s as int slice value for flag %s: %s", envVal, f.Name, err)
|
||||||
fmt.Fprintf(ErrWriter, err.Error())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
f.Value = newVal
|
f.Value = newVal
|
||||||
@@ -238,38 +249,96 @@ func (f IntSliceFlag) Apply(set *flag.FlagSet) {
|
|||||||
}
|
}
|
||||||
set.Var(f.Value, name, f.Usage)
|
set.Var(f.Value, name, f.Usage)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetName returns the name of the flag.
|
// Int64Slice is an opaque type for []int to satisfy flag.Value and flag.Getter
|
||||||
func (f IntSliceFlag) GetName() string {
|
type Int64Slice []int64
|
||||||
return f.Name
|
|
||||||
}
|
|
||||||
|
|
||||||
// BoolFlag is a switch that defaults to false
|
// Set parses the value into an integer and appends it to the list of values
|
||||||
type BoolFlag struct {
|
func (f *Int64Slice) Set(value string) error {
|
||||||
Name string
|
tmp, err := strconv.ParseInt(value, 10, 64)
|
||||||
Usage string
|
if err != nil {
|
||||||
EnvVar string
|
return err
|
||||||
Destination *bool
|
}
|
||||||
Hidden bool
|
*f = append(*f, tmp)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// String returns a readable representation of this value (for usage defaults)
|
// String returns a readable representation of this value (for usage defaults)
|
||||||
func (f BoolFlag) String() string {
|
func (f *Int64Slice) String() string {
|
||||||
return FlagStringer(f)
|
return fmt.Sprintf("%#v", *f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value returns the slice of ints set by this flag
|
||||||
|
func (f *Int64Slice) Value() []int64 {
|
||||||
|
return *f
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns the slice of ints set by this flag
|
||||||
|
func (f *Int64Slice) Get() interface{} {
|
||||||
|
return *f
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply populates the flag given the flag set and environment
|
// Apply populates the flag given the flag set and environment
|
||||||
|
// Ignores errors
|
||||||
|
func (f Int64SliceFlag) Apply(set *flag.FlagSet) {
|
||||||
|
f.ApplyWithError(set)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApplyWithError populates the flag given the flag set and environment
|
||||||
|
func (f Int64SliceFlag) ApplyWithError(set *flag.FlagSet) error {
|
||||||
|
if f.EnvVar != "" {
|
||||||
|
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||||
|
envVar = strings.TrimSpace(envVar)
|
||||||
|
if envVal, ok := syscall.Getenv(envVar); ok {
|
||||||
|
newVal := &Int64Slice{}
|
||||||
|
for _, s := range strings.Split(envVal, ",") {
|
||||||
|
s = strings.TrimSpace(s)
|
||||||
|
if err := newVal.Set(s); err != nil {
|
||||||
|
return fmt.Errorf("could not parse %s as int64 slice value for flag %s: %s", envVal, f.Name, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f.Value = newVal
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
eachName(f.Name, func(name string) {
|
||||||
|
if f.Value == nil {
|
||||||
|
f.Value = &Int64Slice{}
|
||||||
|
}
|
||||||
|
set.Var(f.Value, name, f.Usage)
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply populates the flag given the flag set and environment
|
||||||
|
// Ignores errors
|
||||||
func (f BoolFlag) Apply(set *flag.FlagSet) {
|
func (f BoolFlag) Apply(set *flag.FlagSet) {
|
||||||
|
f.ApplyWithError(set)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApplyWithError populates the flag given the flag set and environment
|
||||||
|
func (f BoolFlag) ApplyWithError(set *flag.FlagSet) error {
|
||||||
val := false
|
val := false
|
||||||
if f.EnvVar != "" {
|
if f.EnvVar != "" {
|
||||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||||
envVar = strings.TrimSpace(envVar)
|
envVar = strings.TrimSpace(envVar)
|
||||||
if envVal := os.Getenv(envVar); envVal != "" {
|
if envVal, ok := syscall.Getenv(envVar); ok {
|
||||||
envValBool, err := strconv.ParseBool(envVal)
|
if envVal == "" {
|
||||||
if err == nil {
|
val = false
|
||||||
val = envValBool
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
envValBool, err := strconv.ParseBool(envVal)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not parse %s as bool value for flag %s: %s", envVal, f.Name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
val = envValBool
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -282,40 +351,35 @@ func (f BoolFlag) Apply(set *flag.FlagSet) {
|
|||||||
}
|
}
|
||||||
set.Bool(name, val, f.Usage)
|
set.Bool(name, val, f.Usage)
|
||||||
})
|
})
|
||||||
}
|
|
||||||
|
|
||||||
// GetName returns the name of the flag.
|
return nil
|
||||||
func (f BoolFlag) GetName() string {
|
|
||||||
return f.Name
|
|
||||||
}
|
|
||||||
|
|
||||||
// BoolTFlag this represents a boolean flag that is true by default, but can
|
|
||||||
// still be set to false by --some-flag=false
|
|
||||||
type BoolTFlag struct {
|
|
||||||
Name string
|
|
||||||
Usage string
|
|
||||||
EnvVar string
|
|
||||||
Destination *bool
|
|
||||||
Hidden bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns a readable representation of this value (for usage defaults)
|
|
||||||
func (f BoolTFlag) String() string {
|
|
||||||
return FlagStringer(f)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply populates the flag given the flag set and environment
|
// Apply populates the flag given the flag set and environment
|
||||||
|
// Ignores errors
|
||||||
func (f BoolTFlag) Apply(set *flag.FlagSet) {
|
func (f BoolTFlag) Apply(set *flag.FlagSet) {
|
||||||
|
f.ApplyWithError(set)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApplyWithError populates the flag given the flag set and environment
|
||||||
|
func (f BoolTFlag) ApplyWithError(set *flag.FlagSet) error {
|
||||||
val := true
|
val := true
|
||||||
if f.EnvVar != "" {
|
if f.EnvVar != "" {
|
||||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||||
envVar = strings.TrimSpace(envVar)
|
envVar = strings.TrimSpace(envVar)
|
||||||
if envVal := os.Getenv(envVar); envVal != "" {
|
if envVal, ok := syscall.Getenv(envVar); ok {
|
||||||
envValBool, err := strconv.ParseBool(envVal)
|
if envVal == "" {
|
||||||
if err == nil {
|
val = false
|
||||||
val = envValBool
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
envValBool, err := strconv.ParseBool(envVal)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not parse %s as bool value for flag %s: %s", envVal, f.Name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
val = envValBool
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -327,34 +391,22 @@ func (f BoolTFlag) Apply(set *flag.FlagSet) {
|
|||||||
}
|
}
|
||||||
set.Bool(name, val, f.Usage)
|
set.Bool(name, val, f.Usage)
|
||||||
})
|
})
|
||||||
}
|
|
||||||
|
|
||||||
// GetName returns the name of the flag.
|
return nil
|
||||||
func (f BoolTFlag) GetName() string {
|
|
||||||
return f.Name
|
|
||||||
}
|
|
||||||
|
|
||||||
// StringFlag represents a flag that takes as string value
|
|
||||||
type StringFlag struct {
|
|
||||||
Name string
|
|
||||||
Value string
|
|
||||||
Usage string
|
|
||||||
EnvVar string
|
|
||||||
Destination *string
|
|
||||||
Hidden bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns the usage
|
|
||||||
func (f StringFlag) String() string {
|
|
||||||
return FlagStringer(f)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply populates the flag given the flag set and environment
|
// Apply populates the flag given the flag set and environment
|
||||||
|
// Ignores errors
|
||||||
func (f StringFlag) Apply(set *flag.FlagSet) {
|
func (f StringFlag) Apply(set *flag.FlagSet) {
|
||||||
|
f.ApplyWithError(set)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApplyWithError populates the flag given the flag set and environment
|
||||||
|
func (f StringFlag) ApplyWithError(set *flag.FlagSet) error {
|
||||||
if f.EnvVar != "" {
|
if f.EnvVar != "" {
|
||||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||||
envVar = strings.TrimSpace(envVar)
|
envVar = strings.TrimSpace(envVar)
|
||||||
if envVal := os.Getenv(envVar); envVal != "" {
|
if envVal, ok := syscall.Getenv(envVar); ok {
|
||||||
f.Value = envVal
|
f.Value = envVal
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -368,40 +420,28 @@ func (f StringFlag) Apply(set *flag.FlagSet) {
|
|||||||
}
|
}
|
||||||
set.String(name, f.Value, f.Usage)
|
set.String(name, f.Value, f.Usage)
|
||||||
})
|
})
|
||||||
}
|
|
||||||
|
|
||||||
// GetName returns the name of the flag.
|
return nil
|
||||||
func (f StringFlag) GetName() string {
|
|
||||||
return f.Name
|
|
||||||
}
|
|
||||||
|
|
||||||
// IntFlag is a flag that takes an integer
|
|
||||||
// Errors if the value provided cannot be parsed
|
|
||||||
type IntFlag struct {
|
|
||||||
Name string
|
|
||||||
Value int
|
|
||||||
Usage string
|
|
||||||
EnvVar string
|
|
||||||
Destination *int
|
|
||||||
Hidden bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns the usage
|
|
||||||
func (f IntFlag) String() string {
|
|
||||||
return FlagStringer(f)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply populates the flag given the flag set and environment
|
// Apply populates the flag given the flag set and environment
|
||||||
|
// Ignores errors
|
||||||
func (f IntFlag) Apply(set *flag.FlagSet) {
|
func (f IntFlag) Apply(set *flag.FlagSet) {
|
||||||
|
f.ApplyWithError(set)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApplyWithError populates the flag given the flag set and environment
|
||||||
|
func (f IntFlag) ApplyWithError(set *flag.FlagSet) error {
|
||||||
if f.EnvVar != "" {
|
if f.EnvVar != "" {
|
||||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||||
envVar = strings.TrimSpace(envVar)
|
envVar = strings.TrimSpace(envVar)
|
||||||
if envVal := os.Getenv(envVar); envVal != "" {
|
if envVal, ok := syscall.Getenv(envVar); ok {
|
||||||
envValInt, err := strconv.ParseInt(envVal, 0, 64)
|
envValInt, err := strconv.ParseInt(envVal, 0, 64)
|
||||||
if err == nil {
|
if err != nil {
|
||||||
f.Value = int(envValInt)
|
return fmt.Errorf("could not parse %s as int value for flag %s: %s", envVal, f.Name, err)
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
f.Value = int(envValInt)
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -413,40 +453,131 @@ func (f IntFlag) Apply(set *flag.FlagSet) {
|
|||||||
}
|
}
|
||||||
set.Int(name, f.Value, f.Usage)
|
set.Int(name, f.Value, f.Usage)
|
||||||
})
|
})
|
||||||
}
|
|
||||||
|
|
||||||
// GetName returns the name of the flag.
|
return nil
|
||||||
func (f IntFlag) GetName() string {
|
|
||||||
return f.Name
|
|
||||||
}
|
|
||||||
|
|
||||||
// DurationFlag is a flag that takes a duration specified in Go's duration
|
|
||||||
// format: https://golang.org/pkg/time/#ParseDuration
|
|
||||||
type DurationFlag struct {
|
|
||||||
Name string
|
|
||||||
Value time.Duration
|
|
||||||
Usage string
|
|
||||||
EnvVar string
|
|
||||||
Destination *time.Duration
|
|
||||||
Hidden bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns a readable representation of this value (for usage defaults)
|
|
||||||
func (f DurationFlag) String() string {
|
|
||||||
return FlagStringer(f)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply populates the flag given the flag set and environment
|
// Apply populates the flag given the flag set and environment
|
||||||
func (f DurationFlag) Apply(set *flag.FlagSet) {
|
// Ignores errors
|
||||||
|
func (f Int64Flag) Apply(set *flag.FlagSet) {
|
||||||
|
f.ApplyWithError(set)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApplyWithError populates the flag given the flag set and environment
|
||||||
|
func (f Int64Flag) ApplyWithError(set *flag.FlagSet) error {
|
||||||
if f.EnvVar != "" {
|
if f.EnvVar != "" {
|
||||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||||
envVar = strings.TrimSpace(envVar)
|
envVar = strings.TrimSpace(envVar)
|
||||||
if envVal := os.Getenv(envVar); envVal != "" {
|
if envVal, ok := syscall.Getenv(envVar); ok {
|
||||||
envValDuration, err := time.ParseDuration(envVal)
|
envValInt, err := strconv.ParseInt(envVal, 0, 64)
|
||||||
if err == nil {
|
if err != nil {
|
||||||
f.Value = envValDuration
|
return fmt.Errorf("could not parse %s as int value for flag %s: %s", envVal, f.Name, err)
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
f.Value = envValInt
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
eachName(f.Name, func(name string) {
|
||||||
|
if f.Destination != nil {
|
||||||
|
set.Int64Var(f.Destination, name, f.Value, f.Usage)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
set.Int64(name, f.Value, f.Usage)
|
||||||
|
})
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply populates the flag given the flag set and environment
|
||||||
|
// Ignores errors
|
||||||
|
func (f UintFlag) Apply(set *flag.FlagSet) {
|
||||||
|
f.ApplyWithError(set)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApplyWithError populates the flag given the flag set and environment
|
||||||
|
func (f UintFlag) ApplyWithError(set *flag.FlagSet) error {
|
||||||
|
if f.EnvVar != "" {
|
||||||
|
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||||
|
envVar = strings.TrimSpace(envVar)
|
||||||
|
if envVal, ok := syscall.Getenv(envVar); ok {
|
||||||
|
envValInt, err := strconv.ParseUint(envVal, 0, 64)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not parse %s as uint value for flag %s: %s", envVal, f.Name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
f.Value = uint(envValInt)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
eachName(f.Name, func(name string) {
|
||||||
|
if f.Destination != nil {
|
||||||
|
set.UintVar(f.Destination, name, f.Value, f.Usage)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
set.Uint(name, f.Value, f.Usage)
|
||||||
|
})
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply populates the flag given the flag set and environment
|
||||||
|
// Ignores errors
|
||||||
|
func (f Uint64Flag) Apply(set *flag.FlagSet) {
|
||||||
|
f.ApplyWithError(set)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApplyWithError populates the flag given the flag set and environment
|
||||||
|
func (f Uint64Flag) ApplyWithError(set *flag.FlagSet) error {
|
||||||
|
if f.EnvVar != "" {
|
||||||
|
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||||
|
envVar = strings.TrimSpace(envVar)
|
||||||
|
if envVal, ok := syscall.Getenv(envVar); ok {
|
||||||
|
envValInt, err := strconv.ParseUint(envVal, 0, 64)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not parse %s as uint64 value for flag %s: %s", envVal, f.Name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
f.Value = uint64(envValInt)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
eachName(f.Name, func(name string) {
|
||||||
|
if f.Destination != nil {
|
||||||
|
set.Uint64Var(f.Destination, name, f.Value, f.Usage)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
set.Uint64(name, f.Value, f.Usage)
|
||||||
|
})
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply populates the flag given the flag set and environment
|
||||||
|
// Ignores errors
|
||||||
|
func (f DurationFlag) Apply(set *flag.FlagSet) {
|
||||||
|
f.ApplyWithError(set)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApplyWithError populates the flag given the flag set and environment
|
||||||
|
func (f DurationFlag) ApplyWithError(set *flag.FlagSet) error {
|
||||||
|
if f.EnvVar != "" {
|
||||||
|
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||||
|
envVar = strings.TrimSpace(envVar)
|
||||||
|
if envVal, ok := syscall.Getenv(envVar); ok {
|
||||||
|
envValDuration, err := time.ParseDuration(envVal)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not parse %s as duration for flag %s: %s", envVal, f.Name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
f.Value = envValDuration
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -458,39 +589,29 @@ func (f DurationFlag) Apply(set *flag.FlagSet) {
|
|||||||
}
|
}
|
||||||
set.Duration(name, f.Value, f.Usage)
|
set.Duration(name, f.Value, f.Usage)
|
||||||
})
|
})
|
||||||
}
|
|
||||||
|
|
||||||
// GetName returns the name of the flag.
|
return nil
|
||||||
func (f DurationFlag) GetName() string {
|
|
||||||
return f.Name
|
|
||||||
}
|
|
||||||
|
|
||||||
// Float64Flag is a flag that takes an float value
|
|
||||||
// Errors if the value provided cannot be parsed
|
|
||||||
type Float64Flag struct {
|
|
||||||
Name string
|
|
||||||
Value float64
|
|
||||||
Usage string
|
|
||||||
EnvVar string
|
|
||||||
Destination *float64
|
|
||||||
Hidden bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns the usage
|
|
||||||
func (f Float64Flag) String() string {
|
|
||||||
return FlagStringer(f)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply populates the flag given the flag set and environment
|
// Apply populates the flag given the flag set and environment
|
||||||
|
// Ignores errors
|
||||||
func (f Float64Flag) Apply(set *flag.FlagSet) {
|
func (f Float64Flag) Apply(set *flag.FlagSet) {
|
||||||
|
f.ApplyWithError(set)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApplyWithError populates the flag given the flag set and environment
|
||||||
|
func (f Float64Flag) ApplyWithError(set *flag.FlagSet) error {
|
||||||
if f.EnvVar != "" {
|
if f.EnvVar != "" {
|
||||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||||
envVar = strings.TrimSpace(envVar)
|
envVar = strings.TrimSpace(envVar)
|
||||||
if envVal := os.Getenv(envVar); envVal != "" {
|
if envVal, ok := syscall.Getenv(envVar); ok {
|
||||||
envValFloat, err := strconv.ParseFloat(envVal, 10)
|
envValFloat, err := strconv.ParseFloat(envVal, 10)
|
||||||
if err == nil {
|
if err != nil {
|
||||||
f.Value = float64(envValFloat)
|
return fmt.Errorf("could not parse %s as float64 value for flag %s: %s", envVal, f.Name, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
f.Value = float64(envValFloat)
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -502,17 +623,15 @@ func (f Float64Flag) Apply(set *flag.FlagSet) {
|
|||||||
}
|
}
|
||||||
set.Float64(name, f.Value, f.Usage)
|
set.Float64(name, f.Value, f.Usage)
|
||||||
})
|
})
|
||||||
}
|
|
||||||
|
|
||||||
// GetName returns the name of the flag.
|
return nil
|
||||||
func (f Float64Flag) GetName() string {
|
|
||||||
return f.Name
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func visibleFlags(fl []Flag) []Flag {
|
func visibleFlags(fl []Flag) []Flag {
|
||||||
visible := []Flag{}
|
visible := []Flag{}
|
||||||
for _, flag := range fl {
|
for _, flag := range fl {
|
||||||
if !reflect.ValueOf(flag).FieldByName("Hidden").Bool() {
|
field := flagValue(flag).FieldByName("Hidden")
|
||||||
|
if !field.IsValid() || !field.Bool() {
|
||||||
visible = append(visible, flag)
|
visible = append(visible, flag)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -578,13 +697,24 @@ func withEnvHint(envVar, str string) string {
|
|||||||
return str + envText
|
return str + envText
|
||||||
}
|
}
|
||||||
|
|
||||||
func stringifyFlag(f Flag) string {
|
func flagValue(f Flag) reflect.Value {
|
||||||
fv := reflect.ValueOf(f)
|
fv := reflect.ValueOf(f)
|
||||||
|
for fv.Kind() == reflect.Ptr {
|
||||||
|
fv = reflect.Indirect(fv)
|
||||||
|
}
|
||||||
|
return fv
|
||||||
|
}
|
||||||
|
|
||||||
|
func stringifyFlag(f Flag) string {
|
||||||
|
fv := flagValue(f)
|
||||||
|
|
||||||
switch f.(type) {
|
switch f.(type) {
|
||||||
case IntSliceFlag:
|
case IntSliceFlag:
|
||||||
return withEnvHint(fv.FieldByName("EnvVar").String(),
|
return withEnvHint(fv.FieldByName("EnvVar").String(),
|
||||||
stringifyIntSliceFlag(f.(IntSliceFlag)))
|
stringifyIntSliceFlag(f.(IntSliceFlag)))
|
||||||
|
case Int64SliceFlag:
|
||||||
|
return withEnvHint(fv.FieldByName("EnvVar").String(),
|
||||||
|
stringifyInt64SliceFlag(f.(Int64SliceFlag)))
|
||||||
case StringSliceFlag:
|
case StringSliceFlag:
|
||||||
return withEnvHint(fv.FieldByName("EnvVar").String(),
|
return withEnvHint(fv.FieldByName("EnvVar").String(),
|
||||||
stringifyStringSliceFlag(f.(StringSliceFlag)))
|
stringifyStringSliceFlag(f.(StringSliceFlag)))
|
||||||
@@ -594,9 +724,8 @@ func stringifyFlag(f Flag) string {
|
|||||||
|
|
||||||
needsPlaceholder := false
|
needsPlaceholder := false
|
||||||
defaultValueString := ""
|
defaultValueString := ""
|
||||||
val := fv.FieldByName("Value")
|
|
||||||
|
|
||||||
if val.IsValid() {
|
if val := fv.FieldByName("Value"); val.IsValid() {
|
||||||
needsPlaceholder = true
|
needsPlaceholder = true
|
||||||
defaultValueString = fmt.Sprintf(" (default: %v)", val.Interface())
|
defaultValueString = fmt.Sprintf(" (default: %v)", val.Interface())
|
||||||
|
|
||||||
@@ -630,6 +759,17 @@ func stringifyIntSliceFlag(f IntSliceFlag) string {
|
|||||||
return stringifySliceFlag(f.Usage, f.Name, defaultVals)
|
return stringifySliceFlag(f.Usage, f.Name, defaultVals)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func stringifyInt64SliceFlag(f Int64SliceFlag) string {
|
||||||
|
defaultVals := []string{}
|
||||||
|
if f.Value != nil && len(f.Value.Value()) > 0 {
|
||||||
|
for _, i := range f.Value.Value() {
|
||||||
|
defaultVals = append(defaultVals, fmt.Sprintf("%d", i))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return stringifySliceFlag(f.Usage, f.Name, defaultVals)
|
||||||
|
}
|
||||||
|
|
||||||
func stringifyStringSliceFlag(f StringSliceFlag) string {
|
func stringifyStringSliceFlag(f StringSliceFlag) string {
|
||||||
defaultVals := []string{}
|
defaultVals := []string{}
|
||||||
if f.Value != nil && len(f.Value.Value()) > 0 {
|
if f.Value != nil && len(f.Value.Value()) > 0 {
|
||||||
|
627
vendor/github.com/urfave/cli/flag_generated.go
generated
vendored
Normal file
627
vendor/github.com/urfave/cli/flag_generated.go
generated
vendored
Normal file
@@ -0,0 +1,627 @@
|
|||||||
|
package cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// WARNING: This file is generated!
|
||||||
|
|
||||||
|
// BoolFlag is a flag with type bool
|
||||||
|
type BoolFlag struct {
|
||||||
|
Name string
|
||||||
|
Usage string
|
||||||
|
EnvVar string
|
||||||
|
Hidden bool
|
||||||
|
Destination *bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a readable representation of this value
|
||||||
|
// (for usage defaults)
|
||||||
|
func (f BoolFlag) String() string {
|
||||||
|
return FlagStringer(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetName returns the name of the flag
|
||||||
|
func (f BoolFlag) GetName() string {
|
||||||
|
return f.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bool looks up the value of a local BoolFlag, returns
|
||||||
|
// false if not found
|
||||||
|
func (c *Context) Bool(name string) bool {
|
||||||
|
return lookupBool(name, c.flagSet)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GlobalBool looks up the value of a global BoolFlag, returns
|
||||||
|
// false if not found
|
||||||
|
func (c *Context) GlobalBool(name string) bool {
|
||||||
|
if fs := lookupGlobalFlagSet(name, c); 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// BoolTFlag is a flag with type bool that is true by default
|
||||||
|
type BoolTFlag struct {
|
||||||
|
Name string
|
||||||
|
Usage string
|
||||||
|
EnvVar string
|
||||||
|
Hidden bool
|
||||||
|
Destination *bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a readable representation of this value
|
||||||
|
// (for usage defaults)
|
||||||
|
func (f BoolTFlag) String() string {
|
||||||
|
return FlagStringer(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetName returns the name of the flag
|
||||||
|
func (f BoolTFlag) GetName() string {
|
||||||
|
return f.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
// BoolT looks up the value of a local BoolTFlag, returns
|
||||||
|
// false if not found
|
||||||
|
func (c *Context) BoolT(name string) bool {
|
||||||
|
return lookupBoolT(name, c.flagSet)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GlobalBoolT looks up the value of a global BoolTFlag, returns
|
||||||
|
// false if not found
|
||||||
|
func (c *Context) GlobalBoolT(name string) bool {
|
||||||
|
if fs := lookupGlobalFlagSet(name, c); fs != nil {
|
||||||
|
return lookupBoolT(name, fs)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func lookupBoolT(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
|
||||||
|
}
|
||||||
|
|
||||||
|
// DurationFlag is a flag with type time.Duration (see https://golang.org/pkg/time/#ParseDuration)
|
||||||
|
type DurationFlag struct {
|
||||||
|
Name string
|
||||||
|
Usage string
|
||||||
|
EnvVar string
|
||||||
|
Hidden bool
|
||||||
|
Value time.Duration
|
||||||
|
Destination *time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a readable representation of this value
|
||||||
|
// (for usage defaults)
|
||||||
|
func (f DurationFlag) String() string {
|
||||||
|
return FlagStringer(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetName returns the name of the flag
|
||||||
|
func (f DurationFlag) GetName() string {
|
||||||
|
return f.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
// Duration looks up the value of a local DurationFlag, returns
|
||||||
|
// 0 if not found
|
||||||
|
func (c *Context) Duration(name string) time.Duration {
|
||||||
|
return lookupDuration(name, c.flagSet)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GlobalDuration looks up the value of a global DurationFlag, returns
|
||||||
|
// 0 if not found
|
||||||
|
func (c *Context) GlobalDuration(name string) time.Duration {
|
||||||
|
if fs := lookupGlobalFlagSet(name, c); 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float64Flag is a flag with type float64
|
||||||
|
type Float64Flag struct {
|
||||||
|
Name string
|
||||||
|
Usage string
|
||||||
|
EnvVar string
|
||||||
|
Hidden bool
|
||||||
|
Value float64
|
||||||
|
Destination *float64
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a readable representation of this value
|
||||||
|
// (for usage defaults)
|
||||||
|
func (f Float64Flag) String() string {
|
||||||
|
return FlagStringer(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetName returns the name of the flag
|
||||||
|
func (f Float64Flag) GetName() string {
|
||||||
|
return f.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float64 looks up the value of a local Float64Flag, returns
|
||||||
|
// 0 if not found
|
||||||
|
func (c *Context) Float64(name string) float64 {
|
||||||
|
return lookupFloat64(name, c.flagSet)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GlobalFloat64 looks up the value of a global Float64Flag, returns
|
||||||
|
// 0 if not found
|
||||||
|
func (c *Context) GlobalFloat64(name string) float64 {
|
||||||
|
if fs := lookupGlobalFlagSet(name, c); 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenericFlag is a flag with type Generic
|
||||||
|
type GenericFlag struct {
|
||||||
|
Name string
|
||||||
|
Usage string
|
||||||
|
EnvVar string
|
||||||
|
Hidden bool
|
||||||
|
Value Generic
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a readable representation of this value
|
||||||
|
// (for usage defaults)
|
||||||
|
func (f GenericFlag) String() string {
|
||||||
|
return FlagStringer(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetName returns the name of the flag
|
||||||
|
func (f GenericFlag) GetName() string {
|
||||||
|
return f.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generic looks up the value of a local GenericFlag, returns
|
||||||
|
// nil if not found
|
||||||
|
func (c *Context) Generic(name string) interface{} {
|
||||||
|
return lookupGeneric(name, c.flagSet)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GlobalGeneric looks up the value of a global GenericFlag, returns
|
||||||
|
// nil if not found
|
||||||
|
func (c *Context) GlobalGeneric(name string) interface{} {
|
||||||
|
if fs := lookupGlobalFlagSet(name, c); fs != nil {
|
||||||
|
return lookupGeneric(name, fs)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func lookupGeneric(name string, set *flag.FlagSet) interface{} {
|
||||||
|
f := set.Lookup(name)
|
||||||
|
if f != nil {
|
||||||
|
parsed, err := f.Value, error(nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return parsed
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int64Flag is a flag with type int64
|
||||||
|
type Int64Flag struct {
|
||||||
|
Name string
|
||||||
|
Usage string
|
||||||
|
EnvVar string
|
||||||
|
Hidden bool
|
||||||
|
Value int64
|
||||||
|
Destination *int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a readable representation of this value
|
||||||
|
// (for usage defaults)
|
||||||
|
func (f Int64Flag) String() string {
|
||||||
|
return FlagStringer(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetName returns the name of the flag
|
||||||
|
func (f Int64Flag) GetName() string {
|
||||||
|
return f.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int64 looks up the value of a local Int64Flag, returns
|
||||||
|
// 0 if not found
|
||||||
|
func (c *Context) Int64(name string) int64 {
|
||||||
|
return lookupInt64(name, c.flagSet)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GlobalInt64 looks up the value of a global Int64Flag, returns
|
||||||
|
// 0 if not found
|
||||||
|
func (c *Context) GlobalInt64(name string) int64 {
|
||||||
|
if fs := lookupGlobalFlagSet(name, c); 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// IntFlag is a flag with type int
|
||||||
|
type IntFlag struct {
|
||||||
|
Name string
|
||||||
|
Usage string
|
||||||
|
EnvVar string
|
||||||
|
Hidden bool
|
||||||
|
Value int
|
||||||
|
Destination *int
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a readable representation of this value
|
||||||
|
// (for usage defaults)
|
||||||
|
func (f IntFlag) String() string {
|
||||||
|
return FlagStringer(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetName returns the name of the flag
|
||||||
|
func (f IntFlag) GetName() string {
|
||||||
|
return f.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int looks up the value of a local IntFlag, returns
|
||||||
|
// 0 if not found
|
||||||
|
func (c *Context) Int(name string) int {
|
||||||
|
return lookupInt(name, c.flagSet)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GlobalInt looks up the value of a global IntFlag, returns
|
||||||
|
// 0 if not found
|
||||||
|
func (c *Context) GlobalInt(name string) int {
|
||||||
|
if fs := lookupGlobalFlagSet(name, c); 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// IntSliceFlag is a flag with type *IntSlice
|
||||||
|
type IntSliceFlag struct {
|
||||||
|
Name string
|
||||||
|
Usage string
|
||||||
|
EnvVar string
|
||||||
|
Hidden bool
|
||||||
|
Value *IntSlice
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a readable representation of this value
|
||||||
|
// (for usage defaults)
|
||||||
|
func (f IntSliceFlag) String() string {
|
||||||
|
return FlagStringer(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetName returns the name of the flag
|
||||||
|
func (f IntSliceFlag) GetName() string {
|
||||||
|
return f.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
// IntSlice looks up the value of a local IntSliceFlag, returns
|
||||||
|
// nil if not found
|
||||||
|
func (c *Context) IntSlice(name string) []int {
|
||||||
|
return lookupIntSlice(name, c.flagSet)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GlobalIntSlice looks up the value of a global IntSliceFlag, returns
|
||||||
|
// nil if not found
|
||||||
|
func (c *Context) GlobalIntSlice(name string) []int {
|
||||||
|
if fs := lookupGlobalFlagSet(name, c); fs != nil {
|
||||||
|
return lookupIntSlice(name, fs)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func lookupIntSlice(name string, set *flag.FlagSet) []int {
|
||||||
|
f := set.Lookup(name)
|
||||||
|
if f != nil {
|
||||||
|
parsed, err := (f.Value.(*IntSlice)).Value(), error(nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return parsed
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int64SliceFlag is a flag with type *Int64Slice
|
||||||
|
type Int64SliceFlag struct {
|
||||||
|
Name string
|
||||||
|
Usage string
|
||||||
|
EnvVar string
|
||||||
|
Hidden bool
|
||||||
|
Value *Int64Slice
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a readable representation of this value
|
||||||
|
// (for usage defaults)
|
||||||
|
func (f Int64SliceFlag) String() string {
|
||||||
|
return FlagStringer(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetName returns the name of the flag
|
||||||
|
func (f Int64SliceFlag) GetName() string {
|
||||||
|
return f.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int64Slice looks up the value of a local Int64SliceFlag, returns
|
||||||
|
// nil if not found
|
||||||
|
func (c *Context) Int64Slice(name string) []int64 {
|
||||||
|
return lookupInt64Slice(name, c.flagSet)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GlobalInt64Slice looks up the value of a global Int64SliceFlag, returns
|
||||||
|
// nil if not found
|
||||||
|
func (c *Context) GlobalInt64Slice(name string) []int64 {
|
||||||
|
if fs := lookupGlobalFlagSet(name, c); fs != nil {
|
||||||
|
return lookupInt64Slice(name, fs)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func lookupInt64Slice(name string, set *flag.FlagSet) []int64 {
|
||||||
|
f := set.Lookup(name)
|
||||||
|
if f != nil {
|
||||||
|
parsed, err := (f.Value.(*Int64Slice)).Value(), error(nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return parsed
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringFlag is a flag with type string
|
||||||
|
type StringFlag struct {
|
||||||
|
Name string
|
||||||
|
Usage string
|
||||||
|
EnvVar string
|
||||||
|
Hidden bool
|
||||||
|
Value string
|
||||||
|
Destination *string
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a readable representation of this value
|
||||||
|
// (for usage defaults)
|
||||||
|
func (f StringFlag) String() string {
|
||||||
|
return FlagStringer(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetName returns the name of the flag
|
||||||
|
func (f StringFlag) GetName() string {
|
||||||
|
return f.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
// String looks up the value of a local StringFlag, returns
|
||||||
|
// "" if not found
|
||||||
|
func (c *Context) String(name string) string {
|
||||||
|
return lookupString(name, c.flagSet)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GlobalString looks up the value of a global StringFlag, returns
|
||||||
|
// "" if not found
|
||||||
|
func (c *Context) GlobalString(name string) string {
|
||||||
|
if fs := lookupGlobalFlagSet(name, c); fs != nil {
|
||||||
|
return lookupString(name, fs)
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func lookupString(name string, set *flag.FlagSet) string {
|
||||||
|
f := set.Lookup(name)
|
||||||
|
if f != nil {
|
||||||
|
parsed, err := f.Value.String(), error(nil)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return parsed
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringSliceFlag is a flag with type *StringSlice
|
||||||
|
type StringSliceFlag struct {
|
||||||
|
Name string
|
||||||
|
Usage string
|
||||||
|
EnvVar string
|
||||||
|
Hidden bool
|
||||||
|
Value *StringSlice
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a readable representation of this value
|
||||||
|
// (for usage defaults)
|
||||||
|
func (f StringSliceFlag) String() string {
|
||||||
|
return FlagStringer(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetName returns the name of the flag
|
||||||
|
func (f StringSliceFlag) GetName() string {
|
||||||
|
return f.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringSlice looks up the value of a local StringSliceFlag, returns
|
||||||
|
// nil if not found
|
||||||
|
func (c *Context) StringSlice(name string) []string {
|
||||||
|
return lookupStringSlice(name, c.flagSet)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GlobalStringSlice looks up the value of a global StringSliceFlag, returns
|
||||||
|
// nil if not found
|
||||||
|
func (c *Context) GlobalStringSlice(name string) []string {
|
||||||
|
if fs := lookupGlobalFlagSet(name, c); fs != nil {
|
||||||
|
return lookupStringSlice(name, fs)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func lookupStringSlice(name string, set *flag.FlagSet) []string {
|
||||||
|
f := set.Lookup(name)
|
||||||
|
if f != nil {
|
||||||
|
parsed, err := (f.Value.(*StringSlice)).Value(), error(nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return parsed
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint64Flag is a flag with type uint64
|
||||||
|
type Uint64Flag struct {
|
||||||
|
Name string
|
||||||
|
Usage string
|
||||||
|
EnvVar string
|
||||||
|
Hidden bool
|
||||||
|
Value uint64
|
||||||
|
Destination *uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a readable representation of this value
|
||||||
|
// (for usage defaults)
|
||||||
|
func (f Uint64Flag) String() string {
|
||||||
|
return FlagStringer(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetName returns the name of the flag
|
||||||
|
func (f Uint64Flag) GetName() string {
|
||||||
|
return f.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint64 looks up the value of a local Uint64Flag, returns
|
||||||
|
// 0 if not found
|
||||||
|
func (c *Context) Uint64(name string) uint64 {
|
||||||
|
return lookupUint64(name, c.flagSet)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GlobalUint64 looks up the value of a global Uint64Flag, returns
|
||||||
|
// 0 if not found
|
||||||
|
func (c *Context) GlobalUint64(name string) uint64 {
|
||||||
|
if fs := lookupGlobalFlagSet(name, c); fs != nil {
|
||||||
|
return lookupUint64(name, fs)
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func lookupUint64(name string, set *flag.FlagSet) uint64 {
|
||||||
|
f := set.Lookup(name)
|
||||||
|
if f != nil {
|
||||||
|
parsed, err := strconv.ParseUint(f.Value.String(), 0, 64)
|
||||||
|
if err != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return parsed
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// UintFlag is a flag with type uint
|
||||||
|
type UintFlag struct {
|
||||||
|
Name string
|
||||||
|
Usage string
|
||||||
|
EnvVar string
|
||||||
|
Hidden bool
|
||||||
|
Value uint
|
||||||
|
Destination *uint
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a readable representation of this value
|
||||||
|
// (for usage defaults)
|
||||||
|
func (f UintFlag) String() string {
|
||||||
|
return FlagStringer(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetName returns the name of the flag
|
||||||
|
func (f UintFlag) GetName() string {
|
||||||
|
return f.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint looks up the value of a local UintFlag, returns
|
||||||
|
// 0 if not found
|
||||||
|
func (c *Context) Uint(name string) uint {
|
||||||
|
return lookupUint(name, c.flagSet)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GlobalUint looks up the value of a global UintFlag, returns
|
||||||
|
// 0 if not found
|
||||||
|
func (c *Context) GlobalUint(name string) uint {
|
||||||
|
if fs := lookupGlobalFlagSet(name, c); fs != nil {
|
||||||
|
return lookupUint(name, fs)
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func lookupUint(name string, set *flag.FlagSet) uint {
|
||||||
|
f := set.Lookup(name)
|
||||||
|
if f != nil {
|
||||||
|
parsed, err := strconv.ParseUint(f.Value.String(), 0, 64)
|
||||||
|
if err != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return uint(parsed)
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
146
vendor/github.com/urfave/cli/help.go
generated
vendored
146
vendor/github.com/urfave/cli/help.go
generated
vendored
@@ -13,27 +13,31 @@ import (
|
|||||||
// cli.go uses text/template to render templates. You can
|
// cli.go uses text/template to render templates. You can
|
||||||
// render custom help text by setting this variable.
|
// render custom help text by setting this variable.
|
||||||
var AppHelpTemplate = `NAME:
|
var AppHelpTemplate = `NAME:
|
||||||
{{.Name}} - {{.Usage}}
|
{{.Name}}{{if .Usage}} - {{.Usage}}{{end}}
|
||||||
|
|
||||||
USAGE:
|
USAGE:
|
||||||
{{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}
|
{{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Version}}{{if not .HideVersion}}
|
||||||
{{if .Version}}{{if not .HideVersion}}
|
|
||||||
VERSION:
|
VERSION:
|
||||||
{{.Version}}
|
{{.Version}}{{end}}{{end}}{{if .Description}}
|
||||||
{{end}}{{end}}{{if len .Authors}}
|
|
||||||
AUTHOR(S):
|
DESCRIPTION:
|
||||||
{{range .Authors}}{{.}}{{end}}
|
{{.Description}}{{end}}{{if len .Authors}}
|
||||||
{{end}}{{if .VisibleCommands}}
|
|
||||||
|
AUTHOR{{with $length := len .Authors}}{{if ne 1 $length}}S{{end}}{{end}}:
|
||||||
|
{{range $index, $author := .Authors}}{{if $index}}
|
||||||
|
{{end}}{{$author}}{{end}}{{end}}{{if .VisibleCommands}}
|
||||||
|
|
||||||
COMMANDS:{{range .VisibleCategories}}{{if .Name}}
|
COMMANDS:{{range .VisibleCategories}}{{if .Name}}
|
||||||
{{.Name}}:{{end}}{{range .VisibleCommands}}
|
{{.Name}}:{{end}}{{range .VisibleCommands}}
|
||||||
{{.Name}}{{with .ShortName}}, {{.}}{{end}}{{"\t"}}{{.Usage}}{{end}}
|
{{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{end}}{{end}}{{if .VisibleFlags}}
|
||||||
{{end}}{{end}}{{if .VisibleFlags}}
|
|
||||||
GLOBAL OPTIONS:
|
GLOBAL OPTIONS:
|
||||||
{{range .VisibleFlags}}{{.}}
|
{{range $index, $option := .VisibleFlags}}{{if $index}}
|
||||||
{{end}}{{end}}{{if .Copyright}}
|
{{end}}{{$option}}{{end}}{{end}}{{if .Copyright}}
|
||||||
|
|
||||||
COPYRIGHT:
|
COPYRIGHT:
|
||||||
{{.Copyright}}
|
{{.Copyright}}{{end}}
|
||||||
{{end}}
|
|
||||||
`
|
`
|
||||||
|
|
||||||
// CommandHelpTemplate is the text template for the command help topic.
|
// CommandHelpTemplate is the text template for the command help topic.
|
||||||
@@ -43,7 +47,7 @@ var CommandHelpTemplate = `NAME:
|
|||||||
{{.HelpName}} - {{.Usage}}
|
{{.HelpName}} - {{.Usage}}
|
||||||
|
|
||||||
USAGE:
|
USAGE:
|
||||||
{{.HelpName}}{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{if .Category}}
|
{{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}}{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Category}}
|
||||||
|
|
||||||
CATEGORY:
|
CATEGORY:
|
||||||
{{.Category}}{{end}}{{if .Description}}
|
{{.Category}}{{end}}{{if .Description}}
|
||||||
@@ -60,14 +64,14 @@ OPTIONS:
|
|||||||
// cli.go uses text/template to render templates. You can
|
// cli.go uses text/template to render templates. You can
|
||||||
// render custom help text by setting this variable.
|
// render custom help text by setting this variable.
|
||||||
var SubcommandHelpTemplate = `NAME:
|
var SubcommandHelpTemplate = `NAME:
|
||||||
{{.HelpName}} - {{.Usage}}
|
{{.HelpName}} - {{if .Description}}{{.Description}}{{else}}{{.Usage}}{{end}}
|
||||||
|
|
||||||
USAGE:
|
USAGE:
|
||||||
{{.HelpName}} command{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}
|
{{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}} command{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}
|
||||||
|
|
||||||
COMMANDS:{{range .VisibleCategories}}{{if .Name}}
|
COMMANDS:{{range .VisibleCategories}}{{if .Name}}
|
||||||
{{.Name}}:{{end}}{{range .VisibleCommands}}
|
{{.Name}}:{{end}}{{range .VisibleCommands}}
|
||||||
{{.Name}}{{with .ShortName}}, {{.}}{{end}}{{"\t"}}{{.Usage}}{{end}}
|
{{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}
|
||||||
{{end}}{{if .VisibleFlags}}
|
{{end}}{{if .VisibleFlags}}
|
||||||
OPTIONS:
|
OPTIONS:
|
||||||
{{range .VisibleFlags}}{{.}}
|
{{range .VisibleFlags}}{{.}}
|
||||||
@@ -108,17 +112,43 @@ var helpSubcommand = Command{
|
|||||||
// Prints help for the App or Command
|
// Prints help for the App or Command
|
||||||
type helpPrinter func(w io.Writer, templ string, data interface{})
|
type helpPrinter func(w io.Writer, templ string, data interface{})
|
||||||
|
|
||||||
|
// Prints help for the App or Command with custom template function.
|
||||||
|
type helpPrinterCustom func(w io.Writer, templ string, data interface{}, customFunc map[string]interface{})
|
||||||
|
|
||||||
// HelpPrinter is a function that writes the help output. If not set a default
|
// HelpPrinter is a function that writes the help output. If not set a default
|
||||||
// is used. The function signature is:
|
// is used. The function signature is:
|
||||||
// func(w io.Writer, templ string, data interface{})
|
// func(w io.Writer, templ string, data interface{})
|
||||||
var HelpPrinter helpPrinter = printHelp
|
var HelpPrinter helpPrinter = printHelp
|
||||||
|
|
||||||
|
// HelpPrinterCustom is same as HelpPrinter but
|
||||||
|
// takes a custom function for template function map.
|
||||||
|
var HelpPrinterCustom helpPrinterCustom = printHelpCustom
|
||||||
|
|
||||||
// VersionPrinter prints the version for the App
|
// VersionPrinter prints the version for the App
|
||||||
var VersionPrinter = printVersion
|
var VersionPrinter = printVersion
|
||||||
|
|
||||||
|
// ShowAppHelpAndExit - Prints the list of subcommands for the app and exits with exit code.
|
||||||
|
func ShowAppHelpAndExit(c *Context, exitCode int) {
|
||||||
|
ShowAppHelp(c)
|
||||||
|
os.Exit(exitCode)
|
||||||
|
}
|
||||||
|
|
||||||
// ShowAppHelp is an action that displays the help.
|
// ShowAppHelp is an action that displays the help.
|
||||||
func ShowAppHelp(c *Context) {
|
func ShowAppHelp(c *Context) (err error) {
|
||||||
HelpPrinter(c.App.Writer, AppHelpTemplate, c.App)
|
if c.App.CustomAppHelpTemplate == "" {
|
||||||
|
HelpPrinter(c.App.Writer, AppHelpTemplate, c.App)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
customAppData := func() map[string]interface{} {
|
||||||
|
if c.App.ExtraInfo == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return map[string]interface{}{
|
||||||
|
"ExtraInfo": c.App.ExtraInfo,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
HelpPrinterCustom(c.App.Writer, c.App.CustomAppHelpTemplate, c.App, customAppData())
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultAppComplete prints the list of subcommands as the default app completion method
|
// DefaultAppComplete prints the list of subcommands as the default app completion method
|
||||||
@@ -133,6 +163,12 @@ func DefaultAppComplete(c *Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ShowCommandHelpAndExit - exits with code after showing help
|
||||||
|
func ShowCommandHelpAndExit(c *Context, command string, code int) {
|
||||||
|
ShowCommandHelp(c, command)
|
||||||
|
os.Exit(code)
|
||||||
|
}
|
||||||
|
|
||||||
// ShowCommandHelp prints help for the given command
|
// ShowCommandHelp prints help for the given command
|
||||||
func ShowCommandHelp(ctx *Context, command string) error {
|
func ShowCommandHelp(ctx *Context, command string) error {
|
||||||
// show the subcommand help for a command with subcommands
|
// show the subcommand help for a command with subcommands
|
||||||
@@ -143,7 +179,11 @@ func ShowCommandHelp(ctx *Context, command string) error {
|
|||||||
|
|
||||||
for _, c := range ctx.App.Commands {
|
for _, c := range ctx.App.Commands {
|
||||||
if c.HasName(command) {
|
if c.HasName(command) {
|
||||||
HelpPrinter(ctx.App.Writer, CommandHelpTemplate, c)
|
if c.CustomHelpTemplate != "" {
|
||||||
|
HelpPrinterCustom(ctx.App.Writer, c.CustomHelpTemplate, c, nil)
|
||||||
|
} else {
|
||||||
|
HelpPrinter(ctx.App.Writer, CommandHelpTemplate, c)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -186,12 +226,17 @@ func ShowCommandCompletions(ctx *Context, command string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func printHelp(out io.Writer, templ string, data interface{}) {
|
func printHelpCustom(out io.Writer, templ string, data interface{}, customFunc map[string]interface{}) {
|
||||||
funcMap := template.FuncMap{
|
funcMap := template.FuncMap{
|
||||||
"join": strings.Join,
|
"join": strings.Join,
|
||||||
}
|
}
|
||||||
|
if customFunc != nil {
|
||||||
|
for key, value := range customFunc {
|
||||||
|
funcMap[key] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
w := tabwriter.NewWriter(out, 0, 8, 1, '\t', 0)
|
w := tabwriter.NewWriter(out, 1, 8, 2, ' ', 0)
|
||||||
t := template.Must(template.New("help").Funcs(funcMap).Parse(templ))
|
t := template.Must(template.New("help").Funcs(funcMap).Parse(templ))
|
||||||
err := t.Execute(w, data)
|
err := t.Execute(w, data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -205,10 +250,14 @@ func printHelp(out io.Writer, templ string, data interface{}) {
|
|||||||
w.Flush()
|
w.Flush()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func printHelp(out io.Writer, templ string, data interface{}) {
|
||||||
|
printHelpCustom(out, templ, data, nil)
|
||||||
|
}
|
||||||
|
|
||||||
func checkVersion(c *Context) bool {
|
func checkVersion(c *Context) bool {
|
||||||
found := false
|
found := false
|
||||||
if VersionFlag.Name != "" {
|
if VersionFlag.GetName() != "" {
|
||||||
eachName(VersionFlag.Name, func(name string) {
|
eachName(VersionFlag.GetName(), func(name string) {
|
||||||
if c.GlobalBool(name) || c.Bool(name) {
|
if c.GlobalBool(name) || c.Bool(name) {
|
||||||
found = true
|
found = true
|
||||||
}
|
}
|
||||||
@@ -219,8 +268,8 @@ func checkVersion(c *Context) bool {
|
|||||||
|
|
||||||
func checkHelp(c *Context) bool {
|
func checkHelp(c *Context) bool {
|
||||||
found := false
|
found := false
|
||||||
if HelpFlag.Name != "" {
|
if HelpFlag.GetName() != "" {
|
||||||
eachName(HelpFlag.Name, func(name string) {
|
eachName(HelpFlag.GetName(), func(name string) {
|
||||||
if c.GlobalBool(name) || c.Bool(name) {
|
if c.GlobalBool(name) || c.Bool(name) {
|
||||||
found = true
|
found = true
|
||||||
}
|
}
|
||||||
@@ -239,7 +288,7 @@ func checkCommandHelp(c *Context, name string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func checkSubcommandHelp(c *Context) bool {
|
func checkSubcommandHelp(c *Context) bool {
|
||||||
if c.GlobalBool("h") || c.GlobalBool("help") {
|
if c.Bool("h") || c.Bool("help") {
|
||||||
ShowSubcommandHelp(c)
|
ShowSubcommandHelp(c)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -247,20 +296,43 @@ func checkSubcommandHelp(c *Context) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkCompletions(c *Context) bool {
|
func checkShellCompleteFlag(a *App, arguments []string) (bool, []string) {
|
||||||
if (c.GlobalBool(BashCompletionFlag.Name) || c.Bool(BashCompletionFlag.Name)) && c.App.EnableBashCompletion {
|
if !a.EnableBashCompletion {
|
||||||
ShowCompletions(c)
|
return false, arguments
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
pos := len(arguments) - 1
|
||||||
|
lastArg := arguments[pos]
|
||||||
|
|
||||||
|
if lastArg != "--"+BashCompletionFlag.GetName() {
|
||||||
|
return false, arguments
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, arguments[:pos]
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkCompletions(c *Context) bool {
|
||||||
|
if !c.shellComplete {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if args := c.Args(); args.Present() {
|
||||||
|
name := args.First()
|
||||||
|
if cmd := c.App.Command(name); cmd != nil {
|
||||||
|
// let the command handle the completion
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ShowCompletions(c)
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkCommandCompletions(c *Context, name string) bool {
|
func checkCommandCompletions(c *Context, name string) bool {
|
||||||
if c.Bool(BashCompletionFlag.Name) && c.App.EnableBashCompletion {
|
if !c.shellComplete {
|
||||||
ShowCommandCompletions(c, name)
|
return false
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
ShowCommandCompletions(c, name)
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user