Merge component 'cli' from git@github.com:docker/cli master
This commit is contained in:
10
components/cli/.github/CODEOWNERS
vendored
10
components/cli/.github/CODEOWNERS
vendored
@ -1,8 +1,8 @@
|
||||
# Github code owners
|
||||
# See https://github.com/blog/2392-introducing-code-owners
|
||||
|
||||
cli/compose/* @dnephin @vdemeester
|
||||
contrib/completion/bash/* @albers
|
||||
contrib/completion/zsh/* @sdurrheimer
|
||||
docs/* @mstanleyjones @vdemeester @thaJeztah
|
||||
scripts/* @dnephin
|
||||
cli/compose/** @dnephin @vdemeester
|
||||
contrib/completion/bash/** @albers
|
||||
contrib/completion/zsh/** @sdurrheimer
|
||||
docs/** @mstanleyjones @vdemeester @thaJeztah
|
||||
scripts/** @dnephin
|
||||
|
||||
@ -54,6 +54,11 @@ manpages:
|
||||
yamldocs:
|
||||
scripts/docs/generate-yaml.sh
|
||||
|
||||
## Shellcheck validation
|
||||
.PHONY: shellcheck
|
||||
shellcheck:
|
||||
scripts/validate/shellcheck
|
||||
|
||||
cli/compose/schema/bindata.go: cli/compose/schema/data/*.json
|
||||
go generate github.com/docker/cli/cli/compose/schema
|
||||
|
||||
|
||||
@ -4,7 +4,7 @@ jobs:
|
||||
|
||||
lint:
|
||||
working_directory: /work
|
||||
docker: [{image: 'docker:17.05-git'}]
|
||||
docker: [{image: 'docker:17.06-git'}]
|
||||
steps:
|
||||
- checkout
|
||||
- setup_remote_docker:
|
||||
@ -22,7 +22,7 @@ jobs:
|
||||
|
||||
cross:
|
||||
working_directory: /work
|
||||
docker: [{image: 'docker:17.05-git'}]
|
||||
docker: [{image: 'docker:17.06-git'}]
|
||||
parallelism: 3
|
||||
steps:
|
||||
- checkout
|
||||
@ -48,7 +48,7 @@ jobs:
|
||||
|
||||
test:
|
||||
working_directory: /work
|
||||
docker: [{image: 'docker:17.05-git'}]
|
||||
docker: [{image: 'docker:17.06-git'}]
|
||||
steps:
|
||||
- checkout
|
||||
- setup_remote_docker:
|
||||
@ -75,9 +75,8 @@ jobs:
|
||||
|
||||
validate:
|
||||
working_directory: /work
|
||||
docker: [{image: 'docker:17.05-git'}]
|
||||
docker: [{image: 'docker:17.06-git'}]
|
||||
steps:
|
||||
- run: apk add -U git openssh
|
||||
- checkout
|
||||
- setup_remote_docker:
|
||||
reusable: true
|
||||
@ -91,7 +90,20 @@ jobs:
|
||||
docker build -f $dockerfile --tag cli-builder-with-git:$CIRCLE_BUILD_NUM .
|
||||
docker run --rm cli-builder-with-git:$CIRCLE_BUILD_NUM \
|
||||
make -B vendor compose-jsonschema manpages yamldocs
|
||||
|
||||
shellcheck:
|
||||
working_directory: /work
|
||||
docker: [{image: 'docker:17.06-git'}]
|
||||
steps:
|
||||
- checkout
|
||||
- setup_remote_docker
|
||||
- run:
|
||||
name: "Run shellcheck"
|
||||
command: |
|
||||
dockerfile=dockerfiles/Dockerfile.shellcheck
|
||||
echo "COPY . ." >> $dockerfile
|
||||
docker build -f $dockerfile --tag cli-validator:$CIRCLE_BUILD_NUM .
|
||||
docker run --rm cli-validator:$CIRCLE_BUILD_NUM \
|
||||
make -B shellcheck
|
||||
workflows:
|
||||
version: 2
|
||||
ci:
|
||||
@ -100,3 +112,4 @@ workflows:
|
||||
- cross
|
||||
- test
|
||||
- validate
|
||||
- shellcheck
|
||||
|
||||
@ -1,15 +1,27 @@
|
||||
package node
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
"github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/command/formatter"
|
||||
"github.com/docker/cli/opts"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/net/context"
|
||||
"vbom.ml/util/sortorder"
|
||||
)
|
||||
|
||||
type byHostname []swarm.Node
|
||||
|
||||
func (n byHostname) Len() int { return len(n) }
|
||||
func (n byHostname) Swap(i, j int) { n[i], n[j] = n[j], n[i] }
|
||||
func (n byHostname) Less(i, j int) bool {
|
||||
return sortorder.NaturalLess(n[i].Description.Hostname, n[j].Description.Hostname)
|
||||
}
|
||||
|
||||
type listOptions struct {
|
||||
quiet bool
|
||||
format string
|
||||
@ -68,5 +80,6 @@ func runList(dockerCli command.Cli, options listOptions) error {
|
||||
Output: dockerCli.Out(),
|
||||
Format: formatter.NewNodeFormat(format, options.quiet),
|
||||
}
|
||||
sort.Sort(byHostname(nodes))
|
||||
return formatter.NodeWrite(nodesCtx, nodes, info)
|
||||
}
|
||||
|
||||
@ -9,6 +9,8 @@ import (
|
||||
"github.com/docker/cli/cli/internal/test"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"github.com/docker/docker/pkg/testutil"
|
||||
"github.com/docker/docker/pkg/testutil/golden"
|
||||
"github.com/pkg/errors"
|
||||
// Import builders to get the builder function as package function
|
||||
. "github.com/docker/cli/cli/internal/test/builders"
|
||||
@ -156,3 +158,22 @@ func TestNodeListFormat(t *testing.T) {
|
||||
assert.Contains(t, buf.String(), `nodeHostname1: Leader`)
|
||||
assert.Contains(t, buf.String(), `nodeHostname2: Reachable`)
|
||||
}
|
||||
|
||||
func TestNodeListOrder(t *testing.T) {
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
nodeListFunc: func() ([]swarm.Node, error) {
|
||||
return []swarm.Node{
|
||||
*Node(Hostname("node-2-foo"), Manager(Leader())),
|
||||
*Node(Hostname("node-10-foo"), Manager()),
|
||||
*Node(Hostname("node-1-foo")),
|
||||
}, nil
|
||||
|
||||
},
|
||||
})
|
||||
cmd := newListCommand(cli)
|
||||
cmd.Flags().Set("format", "{{.Hostname}}: {{.ManagerStatus}}")
|
||||
assert.NoError(t, cmd.Execute())
|
||||
actual := cli.OutBuffer().String()
|
||||
expected := golden.Get(t, []byte(actual), "node-list-sort.golden")
|
||||
testutil.EqualNormalizedString(t, testutil.RemoveSpace, actual, string(expected))
|
||||
}
|
||||
|
||||
3
components/cli/cli/command/node/testdata/node-list-sort.golden
vendored
Normal file
3
components/cli/cli/command/node/testdata/node-list-sort.golden
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
node-1-foo:
|
||||
node-2-foo: Leader
|
||||
node-10-foo: Reachable
|
||||
@ -1,30 +0,0 @@
|
||||
package prune
|
||||
|
||||
import (
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/command/container"
|
||||
"github.com/docker/cli/cli/command/image"
|
||||
"github.com/docker/cli/cli/command/network"
|
||||
"github.com/docker/cli/cli/command/volume"
|
||||
"github.com/docker/cli/opts"
|
||||
)
|
||||
|
||||
// RunContainerPrune executes a prune command for containers
|
||||
func RunContainerPrune(dockerCli command.Cli, filter opts.FilterOpt) (uint64, string, error) {
|
||||
return container.RunPrune(dockerCli, filter)
|
||||
}
|
||||
|
||||
// RunVolumePrune executes a prune command for volumes
|
||||
func RunVolumePrune(dockerCli command.Cli, filter opts.FilterOpt) (uint64, string, error) {
|
||||
return volume.RunPrune(dockerCli, filter)
|
||||
}
|
||||
|
||||
// RunImagePrune executes a prune command for images
|
||||
func RunImagePrune(dockerCli command.Cli, all bool, filter opts.FilterOpt) (uint64, string, error) {
|
||||
return image.RunPrune(dockerCli, all, filter)
|
||||
}
|
||||
|
||||
// RunNetworkPrune executes a prune command for networks
|
||||
func RunNetworkPrune(dockerCli command.Cli, filter opts.FilterOpt) (uint64, string, error) {
|
||||
return network.RunPrune(dockerCli, filter)
|
||||
}
|
||||
@ -12,6 +12,7 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/net/context"
|
||||
"vbom.ml/util/sortorder"
|
||||
)
|
||||
|
||||
type listOptions struct {
|
||||
@ -60,7 +61,7 @@ type byName []*formatter.Stack
|
||||
|
||||
func (n byName) Len() int { return len(n) }
|
||||
func (n byName) Swap(i, j int) { n[i], n[j] = n[j], n[i] }
|
||||
func (n byName) Less(i, j int) bool { return n[i].Name < n[j].Name }
|
||||
func (n byName) Less(i, j int) bool { return sortorder.NaturalLess(n[i].Name, n[j].Name) }
|
||||
|
||||
func getStacks(ctx context.Context, apiclient client.APIClient) ([]*formatter.Stack, error) {
|
||||
services, err := apiclient.ServiceList(
|
||||
|
||||
@ -98,10 +98,13 @@ func TestListWithoutFormat(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestListOrder(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newListCommand(test.NewFakeCliWithOutput(&fakeClient{
|
||||
serviceListFunc: func(options types.ServiceListOptions) ([]swarm.Service, error) {
|
||||
return []swarm.Service{
|
||||
usecases := []struct {
|
||||
golden string
|
||||
swarmServices []swarm.Service
|
||||
}{
|
||||
{
|
||||
golden: "stack-list-sort.golden",
|
||||
swarmServices: []swarm.Service{
|
||||
*Service(
|
||||
ServiceLabels(map[string]string{
|
||||
"com.docker.stack.namespace": "service-name-foo",
|
||||
@ -112,11 +115,40 @@ func TestListOrder(t *testing.T) {
|
||||
"com.docker.stack.namespace": "service-name-bar",
|
||||
}),
|
||||
),
|
||||
}, nil
|
||||
},
|
||||
},
|
||||
}, buf))
|
||||
assert.NoError(t, cmd.Execute())
|
||||
actual := buf.String()
|
||||
expected := golden.Get(t, []byte(actual), "stack-list-sort.golden")
|
||||
testutil.EqualNormalizedString(t, testutil.RemoveSpace, actual, string(expected))
|
||||
{
|
||||
golden: "stack-list-sort-natural.golden",
|
||||
swarmServices: []swarm.Service{
|
||||
*Service(
|
||||
ServiceLabels(map[string]string{
|
||||
"com.docker.stack.namespace": "service-name-1-foo",
|
||||
}),
|
||||
),
|
||||
*Service(
|
||||
ServiceLabels(map[string]string{
|
||||
"com.docker.stack.namespace": "service-name-10-foo",
|
||||
}),
|
||||
),
|
||||
*Service(
|
||||
ServiceLabels(map[string]string{
|
||||
"com.docker.stack.namespace": "service-name-2-foo",
|
||||
}),
|
||||
),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, uc := range usecases {
|
||||
buf := new(bytes.Buffer)
|
||||
cmd := newListCommand(test.NewFakeCliWithOutput(&fakeClient{
|
||||
serviceListFunc: func(options types.ServiceListOptions) ([]swarm.Service, error) {
|
||||
return uc.swarmServices, nil
|
||||
},
|
||||
}, buf))
|
||||
assert.NoError(t, cmd.Execute())
|
||||
actual := buf.String()
|
||||
expected := golden.Get(t, []byte(actual), uc.golden)
|
||||
testutil.EqualNormalizedString(t, testutil.RemoveSpace, actual, string(expected))
|
||||
}
|
||||
}
|
||||
|
||||
4
components/cli/cli/command/stack/testdata/stack-list-sort-natural.golden
vendored
Normal file
4
components/cli/cli/command/stack/testdata/stack-list-sort-natural.golden
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
NAME SERVICES
|
||||
service-name-1-foo 1
|
||||
service-name-2-foo 1
|
||||
service-name-10-foo 1
|
||||
15
components/cli/cli/command/system/client_test.go
Normal file
15
components/cli/cli/command/system/client_test.go
Normal file
@ -0,0 +1,15 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"github.com/docker/docker/client"
|
||||
)
|
||||
|
||||
type fakeClient struct {
|
||||
client.Client
|
||||
|
||||
version string
|
||||
}
|
||||
|
||||
func (cli *fakeClient) ClientVersion() string {
|
||||
return cli.version
|
||||
}
|
||||
@ -18,8 +18,8 @@ func NewSystemCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
cmd.AddCommand(
|
||||
NewEventsCommand(dockerCli),
|
||||
NewInfoCommand(dockerCli),
|
||||
NewDiskUsageCommand(dockerCli),
|
||||
NewPruneCommand(dockerCli),
|
||||
newDiskUsageCommand(dockerCli),
|
||||
newPruneCommand(dockerCli),
|
||||
)
|
||||
|
||||
return cmd
|
||||
|
||||
@ -15,8 +15,8 @@ type diskUsageOptions struct {
|
||||
format string
|
||||
}
|
||||
|
||||
// NewDiskUsageCommand creates a new cobra.Command for `docker df`
|
||||
func NewDiskUsageCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
// newDiskUsageCommand creates a new cobra.Command for `docker df`
|
||||
func newDiskUsageCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
var opts diskUsageOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
|
||||
@ -7,23 +7,28 @@ import (
|
||||
|
||||
"github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/command/prune"
|
||||
"github.com/docker/cli/cli/command/container"
|
||||
"github.com/docker/cli/cli/command/image"
|
||||
"github.com/docker/cli/cli/command/network"
|
||||
"github.com/docker/cli/cli/command/volume"
|
||||
"github.com/docker/cli/opts"
|
||||
"github.com/docker/docker/api/types/versions"
|
||||
units "github.com/docker/go-units"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
type pruneOptions struct {
|
||||
force bool
|
||||
all bool
|
||||
pruneVolumes bool
|
||||
filter opts.FilterOpt
|
||||
force bool
|
||||
all bool
|
||||
pruneBuildCache bool
|
||||
pruneVolumes bool
|
||||
filter opts.FilterOpt
|
||||
}
|
||||
|
||||
// NewPruneCommand creates a new cobra.Command for `docker prune`
|
||||
func NewPruneCommand(dockerCli command.Cli) *cobra.Command {
|
||||
options := pruneOptions{filter: opts.NewFilterOpt()}
|
||||
// newPruneCommand creates a new cobra.Command for `docker prune`
|
||||
func newPruneCommand(dockerCli command.Cli) *cobra.Command {
|
||||
options := pruneOptions{filter: opts.NewFilterOpt(), pruneBuildCache: true}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "prune [OPTIONS]",
|
||||
@ -52,20 +57,38 @@ const confirmationTemplate = `WARNING! This will remove:
|
||||
{{- end }}
|
||||
Are you sure you want to continue?`
|
||||
|
||||
// runBuildCachePrune executes a prune command for build cache
|
||||
func runBuildCachePrune(dockerCli command.Cli, _ opts.FilterOpt) (uint64, string, error) {
|
||||
report, err := dockerCli.Client().BuildCachePrune(context.Background())
|
||||
if err != nil {
|
||||
return 0, "", err
|
||||
}
|
||||
return report.SpaceReclaimed, "", nil
|
||||
}
|
||||
|
||||
func runPrune(dockerCli command.Cli, options pruneOptions) error {
|
||||
if versions.LessThan(dockerCli.Client().ClientVersion(), "1.31") {
|
||||
options.pruneBuildCache = false
|
||||
}
|
||||
if !options.force && !command.PromptForConfirmation(dockerCli.In(), dockerCli.Out(), confirmationMessage(options)) {
|
||||
return nil
|
||||
}
|
||||
|
||||
var spaceReclaimed uint64
|
||||
imagePrune := func(dockerCli command.Cli, filter opts.FilterOpt) (uint64, string, error) {
|
||||
return image.RunPrune(dockerCli, options.all, options.filter)
|
||||
}
|
||||
pruneFuncs := []func(dockerCli command.Cli, filter opts.FilterOpt) (uint64, string, error){
|
||||
prune.RunContainerPrune,
|
||||
prune.RunNetworkPrune,
|
||||
container.RunPrune,
|
||||
network.RunPrune,
|
||||
}
|
||||
if options.pruneVolumes {
|
||||
pruneFuncs = append(pruneFuncs, prune.RunVolumePrune)
|
||||
pruneFuncs = append(pruneFuncs, volume.RunPrune)
|
||||
}
|
||||
pruneFuncs = append(pruneFuncs, imagePrune)
|
||||
if options.pruneBuildCache {
|
||||
pruneFuncs = append(pruneFuncs, runBuildCachePrune)
|
||||
}
|
||||
|
||||
var spaceReclaimed uint64
|
||||
for _, pruneFn := range pruneFuncs {
|
||||
spc, output, err := pruneFn(dockerCli, options.filter)
|
||||
if err != nil {
|
||||
@ -77,21 +100,6 @@ func runPrune(dockerCli command.Cli, options pruneOptions) error {
|
||||
}
|
||||
}
|
||||
|
||||
spc, output, err := prune.RunImagePrune(dockerCli, options.all, options.filter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if spc > 0 {
|
||||
spaceReclaimed += spc
|
||||
fmt.Fprintln(dockerCli.Out(), output)
|
||||
}
|
||||
|
||||
report, err := dockerCli.Client().BuildCachePrune(context.Background())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
spaceReclaimed += report.SpaceReclaimed
|
||||
|
||||
fmt.Fprintln(dockerCli.Out(), "Total reclaimed space:", units.HumanSize(float64(spaceReclaimed)))
|
||||
|
||||
return nil
|
||||
@ -113,7 +121,9 @@ func confirmationMessage(options pruneOptions) string {
|
||||
} else {
|
||||
warnings = append(warnings, "all dangling images")
|
||||
}
|
||||
warnings = append(warnings, "all build cache")
|
||||
if options.pruneBuildCache {
|
||||
warnings = append(warnings, "all build cache")
|
||||
}
|
||||
|
||||
var buffer bytes.Buffer
|
||||
t.Execute(&buffer, &warnings)
|
||||
|
||||
15
components/cli/cli/command/system/prune_test.go
Normal file
15
components/cli/cli/command/system/prune_test.go
Normal file
@ -0,0 +1,15 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/docker/cli/cli/internal/test"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestPrunePromptPre131(t *testing.T) {
|
||||
cli := test.NewFakeCli(&fakeClient{version: "1.30"})
|
||||
cmd := newPruneCommand(cli)
|
||||
assert.NoError(t, cmd.Execute())
|
||||
assert.NotContains(t, cli.OutBuffer().String(), "all build cache")
|
||||
}
|
||||
@ -1,4 +1,13 @@
|
||||
#!/usr/bin/env bash
|
||||
# shellcheck disable=SC2016,SC2119,SC2155
|
||||
#
|
||||
# Shellcheck ignore list:
|
||||
# - SC2016: Expressions don't expand in single quotes, use double quotes for that.
|
||||
# - SC2119: Use foo "$@" if function's $1 should mean script's $1.
|
||||
# - SC2155: Declare and assign separately to avoid masking return values.
|
||||
#
|
||||
# You can find more details for each warning at the following page:
|
||||
# https://github.com/koalaman/shellcheck/wiki/<SCXXXX>
|
||||
#
|
||||
# bash completion file for core docker commands
|
||||
#
|
||||
@ -101,6 +110,7 @@ __docker_complete_containers_all() {
|
||||
__docker_complete_containers "$@" --all
|
||||
}
|
||||
|
||||
# shellcheck disable=SC2120
|
||||
__docker_complete_containers_removable() {
|
||||
__docker_complete_containers "$@" --filter status=created --filter status=exited
|
||||
}
|
||||
@ -109,10 +119,12 @@ __docker_complete_containers_running() {
|
||||
__docker_complete_containers "$@" --filter status=running
|
||||
}
|
||||
|
||||
# shellcheck disable=SC2120
|
||||
__docker_complete_containers_stopped() {
|
||||
__docker_complete_containers "$@" --filter status=exited
|
||||
}
|
||||
|
||||
# shellcheck disable=SC2120
|
||||
__docker_complete_containers_unpauseable() {
|
||||
__docker_complete_containers "$@" --filter status=paused
|
||||
}
|
||||
@ -213,6 +225,7 @@ __docker_complete_networks() {
|
||||
COMPREPLY=( $(compgen -W "$(__docker_networks "$@")" -- "$current") )
|
||||
}
|
||||
|
||||
# shellcheck disable=SC2128,SC2178
|
||||
__docker_complete_containers_in_network() {
|
||||
local containers=$(__docker_q network inspect -f '{{range $i, $c := .Containers}}{{$i}} {{$c.Name}} {{end}}' "$1")
|
||||
COMPREPLY=( $(compgen -W "$containers" -- "$cur") )
|
||||
@ -271,6 +284,7 @@ __docker_plugins_bundled() {
|
||||
for del in "${remove[@]}" ; do
|
||||
plugins=(${plugins[@]/$del/})
|
||||
done
|
||||
# shellcheck disable=SC2145
|
||||
echo "${plugins[@]} ${add[@]}"
|
||||
}
|
||||
|
||||
@ -414,7 +428,7 @@ __docker_nodes() {
|
||||
esac
|
||||
done
|
||||
|
||||
echo $(__docker_q node ls "$@" | tr -d '*' | awk "NR>1 {print $fields}") "${add[@]}"
|
||||
echo "$(__docker_q node ls "$@" | tr -d '*' | awk "NR>1 {print $fields}")" "${add[@]}"
|
||||
}
|
||||
|
||||
# __docker_complete_nodes applies completion of nodes based on the current
|
||||
@ -469,6 +483,7 @@ __docker_tasks() {
|
||||
}
|
||||
|
||||
# __docker_complete_services_and_tasks applies completion of services and task IDs.
|
||||
# shellcheck disable=SC2120
|
||||
__docker_complete_services_and_tasks() {
|
||||
COMPREPLY=( $(compgen -W "$(__docker_services "$@") $(__docker_tasks)" -- "$cur") )
|
||||
}
|
||||
@ -509,7 +524,7 @@ __docker_pos_first_nonflag() {
|
||||
local argument_flags=$1
|
||||
|
||||
local counter=$((${subcommand_pos:-${command_pos}} + 1))
|
||||
while [ $counter -le $cword ]; do
|
||||
while [ "$counter" -le "$cword" ]; do
|
||||
if [ -n "$argument_flags" ] && eval "case '${words[$counter]}' in $argument_flags) true ;; *) false ;; esac"; then
|
||||
(( counter++ ))
|
||||
# eat "=" in case of --option=arg syntax
|
||||
@ -569,10 +584,10 @@ __docker_value_of_option() {
|
||||
local option_extglob=$(__docker_to_extglob "$1")
|
||||
|
||||
local counter=$((command_pos + 1))
|
||||
while [ $counter -lt $cword ]; do
|
||||
while [ "$counter" -lt "$cword" ]; do
|
||||
case ${words[$counter]} in
|
||||
$option_extglob )
|
||||
echo ${words[$counter + 1]}
|
||||
echo "${words[$counter + 1]}"
|
||||
break
|
||||
;;
|
||||
esac
|
||||
@ -609,14 +624,14 @@ __docker_to_extglob() {
|
||||
__docker_subcommands() {
|
||||
local subcommands="$1"
|
||||
|
||||
local counter=$(($command_pos + 1))
|
||||
while [ $counter -lt $cword ]; do
|
||||
local counter=$((command_pos + 1))
|
||||
while [ "$counter" -lt "$cword" ]; do
|
||||
case "${words[$counter]}" in
|
||||
$(__docker_to_extglob "$subcommands") )
|
||||
subcommand_pos=$counter
|
||||
local subcommand=${words[$counter]}
|
||||
local completions_func=_docker_${command}_${subcommand//-/_}
|
||||
declare -F $completions_func >/dev/null && $completions_func
|
||||
declare -F "$completions_func" >/dev/null && "$completions_func"
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
@ -951,7 +966,7 @@ __docker_complete_signals() {
|
||||
SIGUSR1
|
||||
SIGUSR2
|
||||
)
|
||||
COMPREPLY=( $( compgen -W "${signals[*]} ${signals[*]#SIG}" -- "$( echo $cur | tr '[:lower:]' '[:upper:]')" ) )
|
||||
COMPREPLY=( $( compgen -W "${signals[*]} ${signals[*]#SIG}" -- "$( echo "$cur" | tr '[:lower:]' '[:upper:]')" ) )
|
||||
}
|
||||
|
||||
__docker_complete_user_group() {
|
||||
@ -991,7 +1006,7 @@ _docker_docker() {
|
||||
;;
|
||||
*)
|
||||
local counter=$( __docker_pos_first_nonflag "$(__docker_to_extglob "$global_options_with_args")" )
|
||||
if [ $cword -eq $counter ]; then
|
||||
if [ "$cword" -eq "$counter" ]; then
|
||||
__docker_daemon_is_experimental && commands+=(${experimental_commands[*]})
|
||||
COMPREPLY=( $( compgen -W "${commands[*]} help" -- "$cur" ) )
|
||||
fi
|
||||
@ -1044,7 +1059,7 @@ _docker_checkpoint_create() {
|
||||
;;
|
||||
*)
|
||||
local counter=$(__docker_pos_first_nonflag '--checkpoint-dir')
|
||||
if [ $cword -eq $counter ]; then
|
||||
if [ "$cword" -eq "$counter" ]; then
|
||||
__docker_complete_containers_running
|
||||
fi
|
||||
;;
|
||||
@ -1065,7 +1080,7 @@ _docker_checkpoint_ls() {
|
||||
;;
|
||||
*)
|
||||
local counter=$(__docker_pos_first_nonflag '--checkpoint-dir')
|
||||
if [ $cword -eq $counter ]; then
|
||||
if [ "$cword" -eq "$counter" ]; then
|
||||
__docker_complete_containers_all
|
||||
fi
|
||||
;;
|
||||
@ -1086,9 +1101,9 @@ _docker_checkpoint_rm() {
|
||||
;;
|
||||
*)
|
||||
local counter=$(__docker_pos_first_nonflag '--checkpoint-dir')
|
||||
if [ $cword -eq $counter ]; then
|
||||
if [ "$cword" -eq "$counter" ]; then
|
||||
__docker_complete_containers_all
|
||||
elif [ $cword -eq $(($counter + 1)) ]; then
|
||||
elif [ "$cword" -eq "$((counter + 1))" ]; then
|
||||
COMPREPLY=( $( compgen -W "$(__docker_q checkpoint ls "$prev" | sed 1d)" -- "$cur" ) )
|
||||
fi
|
||||
;;
|
||||
@ -1149,7 +1164,7 @@ _docker_container_attach() {
|
||||
;;
|
||||
*)
|
||||
local counter=$(__docker_pos_first_nonflag '--detach-keys')
|
||||
if [ $cword -eq $counter ]; then
|
||||
if [ "$cword" -eq "$counter" ]; then
|
||||
__docker_complete_containers_running
|
||||
fi
|
||||
;;
|
||||
@ -1170,13 +1185,13 @@ _docker_container_commit() {
|
||||
*)
|
||||
local counter=$(__docker_pos_first_nonflag '--author|-a|--change|-c|--message|-m')
|
||||
|
||||
if [ $cword -eq $counter ]; then
|
||||
if [ "$cword" -eq "$counter" ]; then
|
||||
__docker_complete_containers_all
|
||||
return
|
||||
fi
|
||||
(( counter++ ))
|
||||
|
||||
if [ $cword -eq $counter ]; then
|
||||
if [ "$cword" -eq "$counter" ]; then
|
||||
__docker_complete_image_repos_and_tags
|
||||
return
|
||||
fi
|
||||
@ -1191,7 +1206,7 @@ _docker_container_cp() {
|
||||
;;
|
||||
*)
|
||||
local counter=$(__docker_pos_first_nonflag)
|
||||
if [ $cword -eq $counter ]; then
|
||||
if [ "$cword" -eq "$counter" ]; then
|
||||
case "$cur" in
|
||||
*:)
|
||||
return
|
||||
@ -1206,6 +1221,7 @@ _docker_container_cp() {
|
||||
local containers=( ${COMPREPLY[@]} )
|
||||
|
||||
COMPREPLY=( $( compgen -W "${files[*]} ${containers[*]}" -- "$cur" ) )
|
||||
# shellcheck disable=SC2128
|
||||
if [[ "$COMPREPLY" == *: ]]; then
|
||||
__docker_nospace
|
||||
fi
|
||||
@ -1215,7 +1231,7 @@ _docker_container_cp() {
|
||||
fi
|
||||
(( counter++ ))
|
||||
|
||||
if [ $cword -eq $counter ]; then
|
||||
if [ "$cword" -eq "$counter" ]; then
|
||||
if [ -e "$prev" ]; then
|
||||
__docker_complete_containers_all
|
||||
COMPREPLY=( $( compgen -W "${COMPREPLY[*]}" -S ':' ) )
|
||||
@ -1240,7 +1256,7 @@ _docker_container_diff() {
|
||||
;;
|
||||
*)
|
||||
local counter=$(__docker_pos_first_nonflag)
|
||||
if [ $cword -eq $counter ]; then
|
||||
if [ "$cword" -eq "$counter" ]; then
|
||||
__docker_complete_containers_all
|
||||
fi
|
||||
;;
|
||||
@ -1252,7 +1268,7 @@ _docker_container_exec() {
|
||||
|
||||
case "$prev" in
|
||||
--env|-e)
|
||||
# we do not append a "=" here because "-e VARNAME" is legal systax, too
|
||||
# we do not append a "=" here because "-e VARNAME" is legal syntax, too
|
||||
COMPREPLY=( $( compgen -e -- "$cur" ) )
|
||||
__docker_nospace
|
||||
return
|
||||
@ -1287,7 +1303,7 @@ _docker_container_export() {
|
||||
;;
|
||||
*)
|
||||
local counter=$(__docker_pos_first_nonflag)
|
||||
if [ $cword -eq $counter ]; then
|
||||
if [ "$cword" -eq "$counter" ]; then
|
||||
__docker_complete_containers_all
|
||||
fi
|
||||
;;
|
||||
@ -1329,7 +1345,7 @@ _docker_container_logs() {
|
||||
;;
|
||||
*)
|
||||
local counter=$(__docker_pos_first_nonflag '--since|--tail')
|
||||
if [ $cword -eq $counter ]; then
|
||||
if [ "$cword" -eq "$counter" ]; then
|
||||
__docker_complete_containers_all
|
||||
fi
|
||||
;;
|
||||
@ -1425,7 +1441,7 @@ _docker_container_port() {
|
||||
;;
|
||||
*)
|
||||
local counter=$(__docker_pos_first_nonflag)
|
||||
if [ $cword -eq $counter ]; then
|
||||
if [ "$cword" -eq "$counter" ]; then
|
||||
__docker_complete_containers_all
|
||||
fi
|
||||
;;
|
||||
@ -1459,7 +1475,7 @@ _docker_container_rename() {
|
||||
;;
|
||||
*)
|
||||
local counter=$(__docker_pos_first_nonflag)
|
||||
if [ $cword -eq $counter ]; then
|
||||
if [ "$cword" -eq "$counter" ]; then
|
||||
__docker_complete_containers_all
|
||||
fi
|
||||
;;
|
||||
@ -1608,7 +1624,7 @@ _docker_container_run_and_create() {
|
||||
--tty -t
|
||||
"
|
||||
|
||||
if [ "$command" = "run" -o "$subcommand" = "run" ] ; then
|
||||
if [ "$command" = "run" ] || [ "$subcommand" = "run" ] ; then
|
||||
options_with_args="$options_with_args
|
||||
--detach-keys
|
||||
"
|
||||
@ -1686,7 +1702,7 @@ _docker_container_run_and_create() {
|
||||
return
|
||||
;;
|
||||
--env|-e)
|
||||
# we do not append a "=" here because "-e VARNAME" is legal systax, too
|
||||
# we do not append a "=" here because "-e VARNAME" is legal syntax, too
|
||||
COMPREPLY=( $( compgen -e -- "$cur" ) )
|
||||
__docker_nospace
|
||||
return
|
||||
@ -1699,6 +1715,7 @@ _docker_container_run_and_create() {
|
||||
;;
|
||||
*)
|
||||
COMPREPLY=( $( compgen -W 'host container:' -- "$cur" ) )
|
||||
# shellcheck disable=SC2128
|
||||
if [ "$COMPREPLY" = "container:" ]; then
|
||||
__docker_nospace
|
||||
fi
|
||||
@ -1753,6 +1770,7 @@ _docker_container_run_and_create() {
|
||||
;;
|
||||
*)
|
||||
COMPREPLY=( $( compgen -W 'host container:' -- "$cur" ) )
|
||||
# shellcheck disable=SC2128
|
||||
if [ "$COMPREPLY" = "container:" ]; then
|
||||
__docker_nospace
|
||||
fi
|
||||
@ -1806,8 +1824,8 @@ _docker_container_run_and_create() {
|
||||
COMPREPLY=( $( compgen -W "$all_options" -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
local counter=$( __docker_pos_first_nonflag $( __docker_to_alternatives "$options_with_args" ) )
|
||||
if [ $cword -eq $counter ]; then
|
||||
local counter=$( __docker_pos_first_nonflag "$( __docker_to_alternatives "$options_with_args" )" )
|
||||
if [ "$cword" -eq "$counter" ]; then
|
||||
__docker_complete_images
|
||||
fi
|
||||
;;
|
||||
@ -1816,7 +1834,7 @@ _docker_container_run_and_create() {
|
||||
|
||||
_docker_container_start() {
|
||||
__docker_complete_detach_keys && return
|
||||
|
||||
# shellcheck disable=SC2078
|
||||
case "$prev" in
|
||||
--checkpoint)
|
||||
if [ __docker_daemon_is_experimental ] ; then
|
||||
@ -1884,7 +1902,7 @@ _docker_container_top() {
|
||||
;;
|
||||
*)
|
||||
local counter=$(__docker_pos_first_nonflag)
|
||||
if [ $cword -eq $counter ]; then
|
||||
if [ "$cword" -eq "$counter" ]; then
|
||||
__docker_complete_containers_running
|
||||
fi
|
||||
;;
|
||||
@ -1898,7 +1916,7 @@ _docker_container_unpause() {
|
||||
;;
|
||||
*)
|
||||
local counter=$(__docker_pos_first_nonflag)
|
||||
if [ $cword -eq $counter ]; then
|
||||
if [ "$cword" -eq "$counter" ]; then
|
||||
__docker_complete_containers_unpauseable
|
||||
fi
|
||||
;;
|
||||
@ -2098,7 +2116,7 @@ _docker_daemon() {
|
||||
return
|
||||
;;
|
||||
--storage-driver|-s)
|
||||
COMPREPLY=( $( compgen -W "aufs btrfs devicemapper overlay overlay2 vfs zfs" -- "$(echo $cur | tr '[:upper:]' '[:lower:]')" ) )
|
||||
COMPREPLY=( $( compgen -W "aufs btrfs devicemapper overlay overlay2 vfs zfs" -- "$(echo "$cur" | tr '[:upper:]' '[:lower:]')" ) )
|
||||
return
|
||||
;;
|
||||
--storage-opt)
|
||||
@ -2211,7 +2229,7 @@ _docker_export() {
|
||||
|
||||
_docker_help() {
|
||||
local counter=$(__docker_pos_first_nonflag)
|
||||
if [ $cword -eq $counter ]; then
|
||||
if [ "$cword" -eq "$counter" ]; then
|
||||
COMPREPLY=( $( compgen -W "${commands[*]}" -- "$cur" ) )
|
||||
fi
|
||||
}
|
||||
@ -2348,8 +2366,8 @@ _docker_image_build() {
|
||||
COMPREPLY=( $( compgen -W "$all_options" -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
local counter=$( __docker_pos_first_nonflag $( __docker_to_alternatives "$options_with_args" ) )
|
||||
if [ $cword -eq $counter ]; then
|
||||
local counter=$( __docker_pos_first_nonflag "$( __docker_to_alternatives "$options_with_args" )" )
|
||||
if [ "$cword" -eq "$counter" ]; then
|
||||
_filedir -d
|
||||
fi
|
||||
;;
|
||||
@ -2369,7 +2387,7 @@ _docker_image_history() {
|
||||
;;
|
||||
*)
|
||||
local counter=$(__docker_pos_first_nonflag)
|
||||
if [ $cword -eq $counter ]; then
|
||||
if [ "$cword" -eq "$counter" ]; then
|
||||
__docker_complete_images
|
||||
fi
|
||||
;;
|
||||
@ -2393,12 +2411,12 @@ _docker_image_import() {
|
||||
;;
|
||||
*)
|
||||
local counter=$(__docker_pos_first_nonflag '--change|-c|--message|-m')
|
||||
if [ $cword -eq $counter ]; then
|
||||
if [ "$cword" -eq "$counter" ]; then
|
||||
return
|
||||
fi
|
||||
(( counter++ ))
|
||||
|
||||
if [ $cword -eq $counter ]; then
|
||||
if [ "$cword" -eq "$counter" ]; then
|
||||
__docker_complete_image_repos_and_tags
|
||||
return
|
||||
fi
|
||||
@ -2493,7 +2511,7 @@ _docker_image_pull() {
|
||||
;;
|
||||
*)
|
||||
local counter=$(__docker_pos_first_nonflag)
|
||||
if [ $cword -eq $counter ]; then
|
||||
if [ "$cword" -eq "$counter" ]; then
|
||||
for arg in "${COMP_WORDS[@]}"; do
|
||||
case "$arg" in
|
||||
--all-tags|-a)
|
||||
@ -2515,7 +2533,7 @@ _docker_image_push() {
|
||||
;;
|
||||
*)
|
||||
local counter=$(__docker_pos_first_nonflag)
|
||||
if [ $cword -eq $counter ]; then
|
||||
if [ "$cword" -eq "$counter" ]; then
|
||||
__docker_complete_image_repos_and_tags
|
||||
fi
|
||||
;;
|
||||
@ -2567,13 +2585,13 @@ _docker_image_tag() {
|
||||
*)
|
||||
local counter=$(__docker_pos_first_nonflag)
|
||||
|
||||
if [ $cword -eq $counter ]; then
|
||||
if [ "$cword" -eq "$counter" ]; then
|
||||
__docker_complete_image_repos_and_tags
|
||||
return
|
||||
fi
|
||||
(( counter++ ))
|
||||
|
||||
if [ $cword -eq $counter ]; then
|
||||
if [ "$cword" -eq "$counter" ]; then
|
||||
__docker_complete_image_repos_and_tags
|
||||
return
|
||||
fi
|
||||
@ -2737,10 +2755,10 @@ _docker_network_connect() {
|
||||
COMPREPLY=( $( compgen -W "$boolean_options $options_with_args" -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
local counter=$( __docker_pos_first_nonflag $( __docker_to_alternatives "$options_with_args" ) )
|
||||
if [ $cword -eq $counter ]; then
|
||||
local counter=$( __docker_pos_first_nonflag "$( __docker_to_alternatives "$options_with_args" )" )
|
||||
if [ "$cword" -eq "$counter" ]; then
|
||||
__docker_complete_networks
|
||||
elif [ $cword -eq $(($counter + 1)) ]; then
|
||||
elif [ "$cword" -eq "$((counter + 1))" ]; then
|
||||
__docker_complete_containers_all
|
||||
fi
|
||||
;;
|
||||
@ -2788,9 +2806,9 @@ _docker_network_disconnect() {
|
||||
;;
|
||||
*)
|
||||
local counter=$(__docker_pos_first_nonflag)
|
||||
if [ $cword -eq $counter ]; then
|
||||
if [ "$cword" -eq "$counter" ]; then
|
||||
__docker_complete_networks
|
||||
elif [ $cword -eq $(($counter + 1)) ]; then
|
||||
elif [ "$cword" -eq "$((counter + 1))" ]; then
|
||||
__docker_complete_containers_in_network "$prev"
|
||||
fi
|
||||
;;
|
||||
@ -2969,7 +2987,7 @@ _docker_service_logs() {
|
||||
;;
|
||||
*)
|
||||
local counter=$(__docker_pos_first_nonflag '--since|--tail')
|
||||
if [ $cword -eq $counter ]; then
|
||||
if [ "$cword" -eq "$counter" ]; then
|
||||
__docker_complete_services_and_tasks
|
||||
fi
|
||||
;;
|
||||
@ -3076,7 +3094,7 @@ _docker_service_ps() {
|
||||
;;
|
||||
*)
|
||||
local counter=$(__docker_pos_first_nonflag '--filter|-f')
|
||||
if [ $cword -eq $counter ]; then
|
||||
if [ "$cword" -eq "$counter" ]; then
|
||||
__docker_complete_services
|
||||
fi
|
||||
;;
|
||||
@ -3091,6 +3109,7 @@ _docker_service_update() {
|
||||
# and `docker service update`
|
||||
_docker_service_update_and_create() {
|
||||
local options_with_args="
|
||||
--credential-spec
|
||||
--endpoint-mode
|
||||
--entrypoint
|
||||
--env -e
|
||||
@ -3321,13 +3340,13 @@ _docker_service_update_and_create() {
|
||||
COMPREPLY=( $( compgen -W "$boolean_options $options_with_args" -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
local counter=$( __docker_pos_first_nonflag $( __docker_to_alternatives "$options_with_args" ) )
|
||||
local counter=$( __docker_pos_first_nonflag "$( __docker_to_alternatives "$options_with_args" )" )
|
||||
if [ "$subcommand" = "update" ] ; then
|
||||
if [ $cword -eq $counter ]; then
|
||||
if [ "$cword" -eq "$counter" ]; then
|
||||
__docker_complete_services
|
||||
fi
|
||||
else
|
||||
if [ $cword -eq $counter ]; then
|
||||
if [ "$cword" -eq "$counter" ]; then
|
||||
__docker_complete_images
|
||||
fi
|
||||
fi
|
||||
@ -3466,7 +3485,7 @@ _docker_swarm_join_token() {
|
||||
;;
|
||||
*)
|
||||
local counter=$( __docker_pos_first_nonflag )
|
||||
if [ $cword -eq $counter ]; then
|
||||
if [ "$cword" -eq "$counter" ]; then
|
||||
COMPREPLY=( $( compgen -W "manager worker" -- "$cur" ) )
|
||||
fi
|
||||
;;
|
||||
@ -3685,7 +3704,7 @@ _docker_node_update() {
|
||||
;;
|
||||
*)
|
||||
local counter=$(__docker_pos_first_nonflag '--availability|--label-add|--label-rm|--role')
|
||||
if [ $cword -eq $counter ]; then
|
||||
if [ "$cword" -eq "$counter" ]; then
|
||||
__docker_complete_nodes
|
||||
fi
|
||||
;;
|
||||
@ -3732,10 +3751,10 @@ _docker_plugin_create() {
|
||||
;;
|
||||
*)
|
||||
local counter=$(__docker_pos_first_nonflag)
|
||||
if [ $cword -eq $counter ]; then
|
||||
if [ "$cword" -eq "$counter" ]; then
|
||||
# reponame
|
||||
return
|
||||
elif [ $cword -eq $((counter + 1)) ]; then
|
||||
elif [ "$cword" -eq "$((counter + 1))" ]; then
|
||||
_filedir -d
|
||||
fi
|
||||
;;
|
||||
@ -3749,7 +3768,7 @@ _docker_plugin_disable() {
|
||||
;;
|
||||
*)
|
||||
local counter=$(__docker_pos_first_nonflag)
|
||||
if [ $cword -eq $counter ]; then
|
||||
if [ "$cword" -eq "$counter" ]; then
|
||||
__docker_complete_plugins_installed --filter enabled=true
|
||||
fi
|
||||
;;
|
||||
@ -3769,7 +3788,7 @@ _docker_plugin_enable() {
|
||||
;;
|
||||
*)
|
||||
local counter=$(__docker_pos_first_nonflag '--timeout')
|
||||
if [ $cword -eq $counter ]; then
|
||||
if [ "$cword" -eq "$counter" ]; then
|
||||
__docker_complete_plugins_installed --filter enabled=false
|
||||
fi
|
||||
;;
|
||||
@ -3849,7 +3868,7 @@ _docker_plugin_push() {
|
||||
;;
|
||||
*)
|
||||
local counter=$(__docker_pos_first_nonflag)
|
||||
if [ $cword -eq $counter ]; then
|
||||
if [ "$cword" -eq "$counter" ]; then
|
||||
__docker_complete_plugins_installed
|
||||
fi
|
||||
;;
|
||||
@ -3878,7 +3897,7 @@ _docker_plugin_set() {
|
||||
;;
|
||||
*)
|
||||
local counter=$(__docker_pos_first_nonflag)
|
||||
if [ $cword -eq $counter ]; then
|
||||
if [ "$cword" -eq "$counter" ]; then
|
||||
__docker_complete_plugins_installed
|
||||
fi
|
||||
;;
|
||||
@ -3892,10 +3911,10 @@ _docker_plugin_upgrade() {
|
||||
;;
|
||||
*)
|
||||
local counter=$(__docker_pos_first_nonflag)
|
||||
if [ $cword -eq $counter ]; then
|
||||
if [ "$cword" -eq "$counter" ]; then
|
||||
__docker_complete_plugins_installed
|
||||
__ltrim_colon_completions "$cur"
|
||||
elif [ $cword -eq $((counter + 1)) ]; then
|
||||
elif [ "$cword" -eq "$((counter + 1))" ]; then
|
||||
local plugin_images="$(__docker_plugins_installed)"
|
||||
COMPREPLY=( $(compgen -S : -W "${plugin_images%:*}" -- "$cur") )
|
||||
__docker_nospace
|
||||
@ -4189,7 +4208,7 @@ _docker_stack_ps() {
|
||||
;;
|
||||
*)
|
||||
local counter=$(__docker_pos_first_nonflag '--filter|-f')
|
||||
if [ $cword -eq $counter ]; then
|
||||
if [ "$cword" -eq "$counter" ]; then
|
||||
__docker_complete_stacks
|
||||
fi
|
||||
;;
|
||||
@ -4243,7 +4262,7 @@ _docker_stack_services() {
|
||||
;;
|
||||
*)
|
||||
local counter=$(__docker_pos_first_nonflag '--filter|-f|--format')
|
||||
if [ $cword -eq $counter ]; then
|
||||
if [ "$cword" -eq "$counter" ]; then
|
||||
__docker_complete_stacks
|
||||
fi
|
||||
;;
|
||||
@ -4685,7 +4704,7 @@ _docker() {
|
||||
|
||||
local command='docker' command_pos=0 subcommand_pos
|
||||
local counter=1
|
||||
while [ $counter -lt $cword ]; do
|
||||
while [ "$counter" -lt "$cword" ]; do
|
||||
case "${words[$counter]}" in
|
||||
# save host so that completion can use custom daemon
|
||||
--host|-H)
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
DEV_DOCKER_IMAGE_NAME = docker-cli-dev
|
||||
LINTER_IMAGE_NAME = docker-cli-lint
|
||||
CROSS_IMAGE_NAME = docker-cli-cross
|
||||
VALIDATE_IMAGE_NAME = docker-cli-shell-validate
|
||||
MOUNTS = -v "$(CURDIR)":/go/src/github.com/docker/cli
|
||||
VERSION = $(shell cat VERSION)
|
||||
ENVVARS = -e VERSION=$(VERSION) -e GITCOMMIT
|
||||
@ -25,6 +26,9 @@ build_linter_image:
|
||||
build_cross_image:
|
||||
docker build ${DOCKER_BUILD_ARGS} -t $(CROSS_IMAGE_NAME) -f ./dockerfiles/Dockerfile.cross .
|
||||
|
||||
.PHONY: build_shell_validate_image
|
||||
build_shell_validate_image:
|
||||
docker build -t $(VALIDATE_IMAGE_NAME) -f ./dockerfiles/Dockerfile.shellcheck .
|
||||
|
||||
# build executable using a container
|
||||
binary: build_docker_image
|
||||
@ -80,3 +84,7 @@ manpages: build_docker_image
|
||||
.PHONY: yamldocs
|
||||
yamldocs: build_docker_image
|
||||
docker run -ti --rm $(MOUNTS) $(DEV_DOCKER_IMAGE_NAME) make yamldocs
|
||||
|
||||
.PHONY: shellcheck
|
||||
shellcheck: build_shell_validate_image
|
||||
docker run -ti --rm $(MOUNTS) $(VALIDATE_IMAGE_NAME) make shellcheck
|
||||
|
||||
@ -4,21 +4,21 @@ FROM golang:1.8.3-alpine
|
||||
RUN apk add -U git make bash coreutils
|
||||
|
||||
ARG VNDR_SHA=9909bb2b8a0b7ea464527b376dc50389c90df587
|
||||
RUN go get github.com/LK4D4/vndr && \
|
||||
RUN go get -d github.com/LK4D4/vndr && \
|
||||
cd /go/src/github.com/LK4D4/vndr && \
|
||||
git checkout -q "$VNDR_SHA" && \
|
||||
go build -v -o /usr/bin/vndr . && \
|
||||
rm -rf /go/src/* /go/pkg/* /go/bin/*
|
||||
|
||||
ARG BINDATA_SHA=a0ff2567cfb70903282db057e799fd826784d41d
|
||||
RUN go get github.com/jteeuwen/go-bindata/go-bindata && \
|
||||
RUN go get -d github.com/jteeuwen/go-bindata/go-bindata && \
|
||||
cd /go/src/github.com/jteeuwen/go-bindata/go-bindata && \
|
||||
git checkout -q "$BINDATA_SHA" && \
|
||||
go build -v -o /usr/bin/go-bindata . && \
|
||||
rm -rf /go/src/* /go/pkg/* /go/bin/*
|
||||
|
||||
ARG FILEWATCHER_SHA=2e12ea42f6c8c089b19e992145bb94e8adaecedb
|
||||
RUN go get github.com/dnephin/filewatcher && \
|
||||
RUN go get -d github.com/dnephin/filewatcher && \
|
||||
cd /go/src/github.com/dnephin/filewatcher && \
|
||||
git checkout -q "$FILEWATCHER_SHA" && \
|
||||
go build -v -o /usr/bin/filewatcher . && \
|
||||
|
||||
@ -3,7 +3,7 @@ FROM golang:1.8.3-alpine
|
||||
RUN apk add -U git
|
||||
|
||||
ARG GOMETALINTER_SHA=4306381615a2ba2a207f8fcea02c08c6b2b0803f
|
||||
RUN go get github.com/alecthomas/gometalinter && \
|
||||
RUN go get -d github.com/alecthomas/gometalinter && \
|
||||
cd /go/src/github.com/alecthomas/gometalinter && \
|
||||
git checkout -q "$GOMETALINTER_SHA" && \
|
||||
go build -v -o /usr/local/bin/gometalinter . && \
|
||||
|
||||
9
components/cli/dockerfiles/Dockerfile.shellcheck
Normal file
9
components/cli/dockerfiles/Dockerfile.shellcheck
Normal file
@ -0,0 +1,9 @@
|
||||
FROM debian:stretch-slim
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get -y install make shellcheck && \
|
||||
apt-get clean
|
||||
|
||||
WORKDIR /go/src/github.com/docker/cli
|
||||
|
||||
CMD bash
|
||||
@ -530,15 +530,16 @@ FROM extras:${CODE_VERSION}
|
||||
CMD /code/run-extras
|
||||
```
|
||||
|
||||
To use the default value of an `ARG` declared before the first `FROM` use an
|
||||
`ARG` instruction without a value:
|
||||
An `ARG` declared before a `FROM` is outside of a build stage, so it
|
||||
can't be used in any instruction after a `FROM`. To use the default value of
|
||||
an `ARG` declared before the first `FROM` use an `ARG` instruction without
|
||||
a value inside of a build stage:
|
||||
|
||||
```Dockerfile
|
||||
ARG SETTINGS=default
|
||||
|
||||
FROM busybox
|
||||
ARG SETTINGS
|
||||
|
||||
ARG VERSION=latest
|
||||
FROM busybox:$VERSION
|
||||
ARG VERSION
|
||||
RUN echo $VERSION > image_version
|
||||
```
|
||||
|
||||
## RUN
|
||||
@ -1364,8 +1365,8 @@ defined in the Dockerfile, the build outputs a warning.
|
||||
[Warning] One or more build-args [foo] were not consumed.
|
||||
```
|
||||
|
||||
The Dockerfile author can define a single variable by specifying `ARG` once or many
|
||||
variables by specifying `ARG` more than once. For example, a valid Dockerfile:
|
||||
A Dockerfile may include one or more `ARG` instructions. For example,
|
||||
the following is a valid Dockerfile:
|
||||
|
||||
```
|
||||
FROM busybox
|
||||
@ -1374,7 +1375,13 @@ ARG buildno
|
||||
...
|
||||
```
|
||||
|
||||
A Dockerfile author may optionally specify a default value for an `ARG` instruction:
|
||||
> **Warning:** It is not recommended to use build-time variables for
|
||||
> passing secrets like github keys, user credentials etc. Build-time variable
|
||||
> values are visible to any user of the image with the `docker history` command.
|
||||
|
||||
### Default values
|
||||
|
||||
An `ARG` instruction can optionally include a default value:
|
||||
|
||||
```
|
||||
FROM busybox
|
||||
@ -1383,8 +1390,10 @@ ARG buildno=1
|
||||
...
|
||||
```
|
||||
|
||||
If an `ARG` value has a default and if there is no value passed at build-time, the
|
||||
builder uses the default.
|
||||
If an `ARG` instruction has a default value and if there is no value passed
|
||||
at build-time, the builder uses the default.
|
||||
|
||||
### Scope
|
||||
|
||||
An `ARG` variable definition comes into effect from the line on which it is
|
||||
defined in the `Dockerfile` not from the argument's use on the command-line or
|
||||
@ -1408,9 +1417,21 @@ subsequent line 3. The `USER` at line 4 evaluates to `what_user` as `user` is
|
||||
defined and the `what_user` value was passed on the command line. Prior to its definition by an
|
||||
`ARG` instruction, any use of a variable results in an empty string.
|
||||
|
||||
> **Warning:** It is not recommended to use build-time variables for
|
||||
> passing secrets like github keys, user credentials etc. Build-time variable
|
||||
> values are visible to any user of the image with the `docker history` command.
|
||||
An `ARG` instruction goes out of scope at the end of the build
|
||||
stage where it was defined. To use an arg in multiple stages, each stage must
|
||||
include the `ARG` instruction.
|
||||
|
||||
```
|
||||
FROM busybox
|
||||
ARG SETTINGS
|
||||
RUN ./run/setup $SETTINGS
|
||||
|
||||
FROM busybox
|
||||
ARG SETTINGS
|
||||
RUN ./run/other $SETTINGS
|
||||
```
|
||||
|
||||
### Using ARG variables
|
||||
|
||||
You can use an `ARG` or an `ENV` instruction to specify variables that are
|
||||
available to the `RUN` instruction. Environment variables defined using the
|
||||
@ -1459,6 +1480,8 @@ from the command line and persist them in the final image by leveraging the
|
||||
`ENV` instruction. Variable expansion is only supported for [a limited set of
|
||||
Dockerfile instructions.](#environment-replacement)
|
||||
|
||||
### Predefined ARGs
|
||||
|
||||
Docker has a set of predefined `ARG` variables that you can use without a
|
||||
corresponding `ARG` instruction in the Dockerfile.
|
||||
|
||||
|
||||
@ -80,9 +80,9 @@ Docker images report the following events:
|
||||
|
||||
Docker plugins report the following events:
|
||||
|
||||
- `install`
|
||||
- `enable`
|
||||
- `disable`
|
||||
- `install`
|
||||
- `remove`
|
||||
|
||||
#### Volumes
|
||||
@ -90,9 +90,9 @@ Docker plugins report the following events:
|
||||
Docker volumes report the following events:
|
||||
|
||||
- `create`
|
||||
- `destroy`
|
||||
- `mount`
|
||||
- `unmount`
|
||||
- `destroy`
|
||||
|
||||
#### Networks
|
||||
|
||||
@ -100,8 +100,9 @@ Docker networks report the following events:
|
||||
|
||||
- `create`
|
||||
- `connect`
|
||||
- `disconnect`
|
||||
- `destroy`
|
||||
- `disconnect`
|
||||
- `remove`
|
||||
|
||||
#### Daemons
|
||||
|
||||
@ -109,6 +110,38 @@ Docker daemons report the following events:
|
||||
|
||||
- `reload`
|
||||
|
||||
#### Services
|
||||
|
||||
Docker services report the following events:
|
||||
|
||||
- `create`
|
||||
- `remove`
|
||||
- `update`
|
||||
|
||||
#### Nodes
|
||||
|
||||
Docker nodes report the following events:
|
||||
|
||||
- `create`
|
||||
- `remove`
|
||||
- `update`
|
||||
|
||||
#### Secrets
|
||||
|
||||
Docker secrets report the following events:
|
||||
|
||||
- `create`
|
||||
- `remove`
|
||||
- `update`
|
||||
|
||||
#### Configs
|
||||
|
||||
Docker configs report the following events:
|
||||
|
||||
- `create`
|
||||
- `remove`
|
||||
- `update`
|
||||
|
||||
### Limiting, filtering, and formatting the output
|
||||
|
||||
#### Limit events by time
|
||||
@ -149,7 +182,8 @@ The currently supported filters are:
|
||||
* label (`label=<key>` or `label=<key>=<value>`)
|
||||
* network (`network=<name or id>`)
|
||||
* plugin (`plugin=<name or id>`)
|
||||
* type (`type=<container or image or volume or network or daemon or plugin>`)
|
||||
* scope (`scope=<local or swarm>`)
|
||||
* type (`type=<container or image or volume or network or daemon or plugin or service or node or secret or config>`)
|
||||
* volume (`volume=<name or id>`)
|
||||
|
||||
#### Format
|
||||
@ -317,6 +351,29 @@ $ docker events --filter 'type=plugin'
|
||||
|
||||
2016-07-25T17:30:14.825557616Z plugin pull ec7b87f2ce84330fe076e666f17dfc049d2d7ae0b8190763de94e1f2d105993f (name=tiborvass/sample-volume-plugin:latest)
|
||||
2016-07-25T17:30:14.888127370Z plugin enable ec7b87f2ce84330fe076e666f17dfc049d2d7ae0b8190763de94e1f2d105993f (name=tiborvass/sample-volume-plugin:latest)
|
||||
|
||||
$ docker events -f type=service
|
||||
|
||||
2017-07-12T06:34:07.999446625Z service create wj64st89fzgchxnhiqpn8p4oj (name=reverent_albattani)
|
||||
2017-07-12T06:34:21.405496207Z service remove wj64st89fzgchxnhiqpn8p4oj (name=reverent_albattani)
|
||||
|
||||
$ docker events -f type=node
|
||||
|
||||
2017-07-12T06:21:51.951586759Z node update 3xyz5ttp1a253q74z1thwywk9 (name=ip-172-31-23-42, state.new=ready, state.old=unknown)
|
||||
|
||||
$ docker events -f type=secret
|
||||
|
||||
2017-07-12T06:32:13.915704367Z secret create s8o6tmlnndrgzbmdilyy5ymju (name=new_secret)
|
||||
2017-07-12T06:32:37.052647783Z secret remove s8o6tmlnndrgzbmdilyy5ymju (name=new_secret)
|
||||
|
||||
$ docker events -f type=config
|
||||
2017-07-12T06:44:13.349037127Z config create u96zlvzdfsyb9sg4mhyxfh3rl (name=abc)
|
||||
2017-07-12T06:44:36.327694184Z config remove u96zlvzdfsyb9sg4mhyxfh3rl (name=abc)
|
||||
|
||||
$ docker events --filter 'scope=swarm'
|
||||
|
||||
2017-07-10T07:46:50.250024503Z service create m8qcxu8081woyof7w3jaax6gk (name=affectionate_wilson)
|
||||
2017-07-10T07:47:31.093797134Z secret create 6g5pufzsv438p9tbvl9j94od4 (name=new_secret)
|
||||
```
|
||||
|
||||
### Format the output
|
||||
|
||||
@ -94,7 +94,6 @@ radial/busyboxplus Full-chain, Internet enabled, busybox made from scratch. Co
|
||||
The flag `--limit` is the maximum number of results returned by a search. This value could
|
||||
be in the range between 1 and 100. The default value of `--limit` is 25.
|
||||
|
||||
|
||||
### Filtering
|
||||
|
||||
The filtering flag (`-f` or `--filter`) format is a `key=value` pair. If there is more
|
||||
@ -103,9 +102,8 @@ than one filter, then pass multiple flags (e.g. `--filter "foo=bar" --filter "bi
|
||||
The currently supported filters are:
|
||||
|
||||
* stars (int - number of stars the image has)
|
||||
* is-automated (true|false) - is the image automated or not
|
||||
* is-official (true|false) - is the image official or not
|
||||
|
||||
* is-automated (boolean - true or false) - is the image automated or not
|
||||
* is-official (boolean - true or false) - is the image official or not
|
||||
|
||||
#### stars
|
||||
|
||||
@ -121,7 +119,6 @@ progrium/busybox 50
|
||||
radial/busyboxplus Full-chain, Internet enabled, busybox made... 8 [OK]
|
||||
```
|
||||
|
||||
|
||||
#### is-automated
|
||||
|
||||
This example displays images with a name containing 'busybox'
|
||||
|
||||
@ -23,6 +23,7 @@ Create a new service
|
||||
Options:
|
||||
--constraint list Placement constraints
|
||||
--container-label list Container labels
|
||||
--credential-spec Credential spec for managed service account (Windows only)
|
||||
-d, --detach Exit immediately instead of waiting for the service to converge (default true)
|
||||
--dns list Set custom DNS servers
|
||||
--dns-option list Set DNS options
|
||||
@ -779,6 +780,24 @@ $ docker service create --name dns-cache -p 53:53/tcp -p 53:53/udp dns-cache
|
||||
$ docker service create --name dns-cache -p 53:53/udp dns-cache
|
||||
```
|
||||
|
||||
### Provide credential specs for managed service accounts (Windows only)
|
||||
|
||||
This option is only used for services using Windows containers. The
|
||||
`--credential-spec` must be in the format `file://<filename>` or
|
||||
`registry://<value-name>`.
|
||||
|
||||
When using the `file://<filename>` format, the referenced file must be
|
||||
present in the `CredentialSpecs` subdirectory in the docker data directory,
|
||||
which defaults to `C:\ProgramData\Docker\` on Windows. For example,
|
||||
specifying `file://spec.json` loads `C:\ProgramData\Docker\CredentialSpecs\spec.json`.
|
||||
|
||||
When using the `registry://<value-name>` format, the credential spec is
|
||||
read from the Windows registry on the daemon's host. The specified
|
||||
registry value must be located in:
|
||||
|
||||
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Virtualization\Containers\CredentialSpecs
|
||||
|
||||
|
||||
### Create services using templates
|
||||
|
||||
You can use templates for some flags of `service create`, using the syntax
|
||||
|
||||
@ -26,6 +26,7 @@ Options:
|
||||
--constraint-rm list Remove a constraint
|
||||
--container-label-add list Add or update a container label
|
||||
--container-label-rm list Remove a container label by its key
|
||||
--credential-spec Credential spec for managed service account (Windows only)
|
||||
-d, --detach Exit immediately instead of waiting for the service to converge (default true)
|
||||
--dns-add list Add or update a custom DNS server
|
||||
--dns-option-add list Add or update a DNS option
|
||||
|
||||
11
components/cli/scripts/validate/shellcheck
Executable file
11
components/cli/scripts/validate/shellcheck
Executable file
@ -0,0 +1,11 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
# Maintain an array of files to shellcheck not the best solution but will do for the time being
|
||||
FILES=()
|
||||
FILES+=("contrib/completion/bash/docker")
|
||||
FILES+=("scripts/validate/shellcheck")
|
||||
|
||||
for f in "${FILES[@]}"; do
|
||||
shellcheck "$f"
|
||||
done
|
||||
@ -52,3 +52,4 @@ gopkg.in/yaml.v2 4c78c975fe7c825c6d1466c42be594d1d6f3aba6
|
||||
github.com/tonistiigi/fsutil 0ac4c11b053b9c5c7c47558f81f96c7100ce50fb
|
||||
github.com/stevvooe/continuity cd7a8e21e2b6f84799f5dd4b65faf49c8d3ee02d
|
||||
golang.org/x/sync de49d9dcd27d4f764488181bea099dfe6179bcf0
|
||||
vbom.ml/util 928aaa586d7718c70f4090ddf83f2b34c16fdc8d
|
||||
|
||||
17
components/cli/vendor/vbom.ml/util/LICENSE
vendored
Normal file
17
components/cli/vendor/vbom.ml/util/LICENSE
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
The MIT License (MIT)
|
||||
Copyright (c) 2015 Frits van Bommel
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
5
components/cli/vendor/vbom.ml/util/README.md
vendored
Normal file
5
components/cli/vendor/vbom.ml/util/README.md
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
## util [](https://godoc.org/vbom.ml/util)
|
||||
|
||||
import "vbom.ml/util"
|
||||
|
||||
Go utility packages.
|
||||
5
components/cli/vendor/vbom.ml/util/sortorder/README.md
vendored
Normal file
5
components/cli/vendor/vbom.ml/util/sortorder/README.md
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
## sortorder [](https://godoc.org/vbom.ml/util/sortorder)
|
||||
|
||||
import "vbom.ml/util/sortorder"
|
||||
|
||||
Sort orders and comparison functions.
|
||||
5
components/cli/vendor/vbom.ml/util/sortorder/doc.go
vendored
Normal file
5
components/cli/vendor/vbom.ml/util/sortorder/doc.go
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
// Package sortorder implements sort orders and comparison functions.
|
||||
//
|
||||
// Currently, it only implements so-called "natural order", where integers
|
||||
// embedded in strings are compared by value.
|
||||
package sortorder // import "vbom.ml/util/sortorder"
|
||||
76
components/cli/vendor/vbom.ml/util/sortorder/natsort.go
vendored
Normal file
76
components/cli/vendor/vbom.ml/util/sortorder/natsort.go
vendored
Normal file
@ -0,0 +1,76 @@
|
||||
package sortorder
|
||||
|
||||
// Natural implements sort.Interface to sort strings in natural order. This
|
||||
// means that e.g. "abc2" < "abc12".
|
||||
//
|
||||
// Non-digit sequences and numbers are compared separately. The former are
|
||||
// compared bytewise, while the latter are compared numerically (except that
|
||||
// the number of leading zeros is used as a tie-breaker, so e.g. "2" < "02")
|
||||
//
|
||||
// Limitation: only ASCII digits (0-9) are considered.
|
||||
type Natural []string
|
||||
|
||||
func (n Natural) Len() int { return len(n) }
|
||||
func (n Natural) Swap(i, j int) { n[i], n[j] = n[j], n[i] }
|
||||
func (n Natural) Less(i, j int) bool { return NaturalLess(n[i], n[j]) }
|
||||
|
||||
func isdigit(b byte) bool { return '0' <= b && b <= '9' }
|
||||
|
||||
// NaturalLess compares two strings using natural ordering. This means that e.g.
|
||||
// "abc2" < "abc12".
|
||||
//
|
||||
// Non-digit sequences and numbers are compared separately. The former are
|
||||
// compared bytewise, while the latter are compared numerically (except that
|
||||
// the number of leading zeros is used as a tie-breaker, so e.g. "2" < "02")
|
||||
//
|
||||
// Limitation: only ASCII digits (0-9) are considered.
|
||||
func NaturalLess(str1, str2 string) bool {
|
||||
idx1, idx2 := 0, 0
|
||||
for idx1 < len(str1) && idx2 < len(str2) {
|
||||
c1, c2 := str1[idx1], str2[idx2]
|
||||
dig1, dig2 := isdigit(c1), isdigit(c2)
|
||||
switch {
|
||||
case dig1 != dig2: // Digits before other characters.
|
||||
return dig1 // True if LHS is a digit, false if the RHS is one.
|
||||
case !dig1: // && !dig2, because dig1 == dig2
|
||||
// UTF-8 compares bytewise-lexicographically, no need to decode
|
||||
// codepoints.
|
||||
if c1 != c2 {
|
||||
return c1 < c2
|
||||
}
|
||||
idx1++
|
||||
idx2++
|
||||
default: // Digits
|
||||
// Eat zeros.
|
||||
for ; idx1 < len(str1) && str1[idx1] == '0'; idx1++ {
|
||||
}
|
||||
for ; idx2 < len(str2) && str2[idx2] == '0'; idx2++ {
|
||||
}
|
||||
// Eat all digits.
|
||||
nonZero1, nonZero2 := idx1, idx2
|
||||
for ; idx1 < len(str1) && isdigit(str1[idx1]); idx1++ {
|
||||
}
|
||||
for ; idx2 < len(str2) && isdigit(str2[idx2]); idx2++ {
|
||||
}
|
||||
// If lengths of numbers with non-zero prefix differ, the shorter
|
||||
// one is less.
|
||||
if len1, len2 := idx1-nonZero1, idx2-nonZero2; len1 != len2 {
|
||||
return len1 < len2
|
||||
}
|
||||
// If they're not equal, string comparison is correct.
|
||||
if nr1, nr2 := str1[nonZero1:idx1], str2[nonZero2:idx2]; nr1 != nr2 {
|
||||
return nr1 < nr2
|
||||
}
|
||||
// Otherwise, the one with less zeros is less.
|
||||
// Because everything up to the number is equal, comparing the index
|
||||
// after the zeros is sufficient.
|
||||
if nonZero1 != nonZero2 {
|
||||
return nonZero1 < nonZero2
|
||||
}
|
||||
}
|
||||
// They're identical so far, so continue comparing.
|
||||
}
|
||||
// So far they are identical. At least one is ended. If the other continues,
|
||||
// it sorts last.
|
||||
return len(str1) < len(str2)
|
||||
}
|
||||
Reference in New Issue
Block a user