mirror of
https://github.com/GoogleContainerTools/kaniko
synced 2026-10-19 05:22:06 +02:00
Updated vendor, for container-diff
This commit is contained in:
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2017 Google, Inc. All rights reserved.
|
||||
Copyright 2018 Google, Inc. All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
||||
+31
-12
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2017 Google, Inc. All rights reserved.
|
||||
Copyright 2018 Google, Inc. All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -20,13 +20,14 @@ import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"encoding/json"
|
||||
img "github.com/containers/image/image"
|
||||
"github.com/containers/image/types"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/containers/image/manifest"
|
||||
"github.com/containers/image/types"
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
@@ -76,28 +77,46 @@ func (m *MutableSource) GetManifest(_ *digest.Digest) ([]byte, string, error) {
|
||||
|
||||
// populateManifestAndConfig parses the raw manifest and configs, storing them on the struct.
|
||||
func (m *MutableSource) populateManifestAndConfig() error {
|
||||
mfstBytes, _, err := m.ProxySource.GetManifest(nil)
|
||||
context := &types.SystemContext{
|
||||
OSChoice: "linux",
|
||||
ArchitectureChoice: "amd64",
|
||||
}
|
||||
image, err := m.ProxySource.Ref.NewImage(context)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer image.Close()
|
||||
// First get manifest
|
||||
mfstBytes, mfstType, err := image.Manifest()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if mfstType == manifest.DockerV2ListMediaType {
|
||||
// We need to select a manifest digest from the manifest list
|
||||
unparsedImage := img.UnparsedInstance(m.ImageSource, nil)
|
||||
|
||||
mfstDigest, err := img.ChooseManifestInstanceFromManifestList(context, unparsedImage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mfstBytes, _, err = m.ProxySource.GetManifest(&mfstDigest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
m.mfst, err = manifest.Schema2FromManifest(mfstBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
bi := types.BlobInfo{Digest: m.mfst.ConfigDescriptor.Digest}
|
||||
r, _, err := m.GetBlob(bi)
|
||||
// Now, get config
|
||||
configBlob, err := image.ConfigBlob()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cfgBytes, err := ioutil.ReadAll(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return json.Unmarshal(cfgBytes, &m.cfg)
|
||||
return json.Unmarshal(configBlob, &m.cfg)
|
||||
}
|
||||
|
||||
// GetBlob first checks the stored "extra" blobs, then proxies the call to the original source.
|
||||
|
||||
+2
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2017 Google, Inc. All rights reserved.
|
||||
Copyright 2018 Google, Inc. All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -32,6 +32,7 @@ func NewProxySource(ref types.ImageReference) (*ProxySource, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer src.Close()
|
||||
img, err := ref.NewImage(nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
+10
-6
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2017 Google, Inc. All rights reserved.
|
||||
Copyright 2018 Google, Inc. All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -32,21 +32,25 @@ type CloudPrepper struct {
|
||||
ImageSource types.ImageSource
|
||||
}
|
||||
|
||||
func (p CloudPrepper) Name() string {
|
||||
func (p *CloudPrepper) Name() string {
|
||||
return "Cloud Registry"
|
||||
}
|
||||
|
||||
func (p CloudPrepper) GetSource() string {
|
||||
func (p *CloudPrepper) GetSource() string {
|
||||
return p.Source
|
||||
}
|
||||
|
||||
func (p CloudPrepper) GetImage() (Image, error) {
|
||||
func (p *CloudPrepper) SetSource(source string) {
|
||||
p.Source = source
|
||||
}
|
||||
|
||||
func (p *CloudPrepper) GetImage() (Image, error) {
|
||||
image, err := getImage(p)
|
||||
image.Type = ImageTypeCloud
|
||||
return image, err
|
||||
}
|
||||
|
||||
func (p CloudPrepper) GetFileSystem() (string, error) {
|
||||
func (p *CloudPrepper) GetFileSystem() (string, error) {
|
||||
ref, err := docker.ParseReference("//" + p.Source)
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -62,7 +66,7 @@ func (p CloudPrepper) GetFileSystem() (string, error) {
|
||||
return path, GetFileSystemFromReference(ref, p.ImageSource, path, nil)
|
||||
}
|
||||
|
||||
func (p CloudPrepper) GetConfig() (ConfigSchema, error) {
|
||||
func (p *CloudPrepper) GetConfig() (ConfigSchema, error) {
|
||||
ref, err := docker.ParseReference("//" + p.Source)
|
||||
if err != nil {
|
||||
return ConfigSchema{}, err
|
||||
|
||||
+12
-7
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2017 Google, Inc. All rights reserved.
|
||||
Copyright 2018 Google, Inc. All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -32,21 +32,25 @@ type DaemonPrepper struct {
|
||||
Client *client.Client
|
||||
}
|
||||
|
||||
func (p DaemonPrepper) Name() string {
|
||||
func (p *DaemonPrepper) Name() string {
|
||||
return "Local Daemon"
|
||||
}
|
||||
|
||||
func (p DaemonPrepper) GetSource() string {
|
||||
func (p *DaemonPrepper) GetSource() string {
|
||||
return p.Source
|
||||
}
|
||||
|
||||
func (p DaemonPrepper) GetImage() (Image, error) {
|
||||
func (p *DaemonPrepper) SetSource(source string) {
|
||||
p.Source = source
|
||||
}
|
||||
|
||||
func (p *DaemonPrepper) GetImage() (Image, error) {
|
||||
image, err := getImage(p)
|
||||
image.Type = ImageTypeDaemon
|
||||
return image, err
|
||||
}
|
||||
|
||||
func (p DaemonPrepper) GetFileSystem() (string, error) {
|
||||
func (p *DaemonPrepper) GetFileSystem() (string, error) {
|
||||
ref, err := daemon.ParseReference(p.Source)
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -56,6 +60,7 @@ func (p DaemonPrepper) GetFileSystem() (string, error) {
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer src.Close()
|
||||
|
||||
sanitizedName := strings.Replace(p.Source, ":", "", -1)
|
||||
sanitizedName = strings.Replace(sanitizedName, "/", "", -1)
|
||||
@@ -67,7 +72,7 @@ func (p DaemonPrepper) GetFileSystem() (string, error) {
|
||||
return path, GetFileSystemFromReference(ref, src, path, nil)
|
||||
}
|
||||
|
||||
func (p DaemonPrepper) GetConfig() (ConfigSchema, error) {
|
||||
func (p *DaemonPrepper) GetConfig() (ConfigSchema, error) {
|
||||
ref, err := daemon.ParseReference(p.Source)
|
||||
if err != nil {
|
||||
return ConfigSchema{}, err
|
||||
@@ -75,7 +80,7 @@ func (p DaemonPrepper) GetConfig() (ConfigSchema, error) {
|
||||
return getConfigFromReference(ref, p.Source)
|
||||
}
|
||||
|
||||
func (p DaemonPrepper) GetHistory() []ImageHistoryItem {
|
||||
func (p *DaemonPrepper) GetHistory() []ImageHistoryItem {
|
||||
history, err := p.Client.ImageHistory(context.Background(), p.Source)
|
||||
if err != nil {
|
||||
logrus.Errorf("Could not obtain image history for %s: %s", p.Source, err)
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2017 Google, Inc. All rights reserved.
|
||||
Copyright 2018 Google, Inc. All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2017 Google, Inc. All rights reserved.
|
||||
Copyright 2018 Google, Inc. All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
||||
Generated
Vendored
+83
-9
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2017 Google, Inc. All rights reserved.
|
||||
Copyright 2018 Google, Inc. All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -20,8 +20,10 @@ import (
|
||||
"archive/tar"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/GoogleCloudPlatform/container-diff/cmd/util/output"
|
||||
@@ -38,6 +40,7 @@ type Prepper interface {
|
||||
GetFileSystem() (string, error)
|
||||
GetImage() (Image, error)
|
||||
GetSource() string
|
||||
SetSource(string)
|
||||
}
|
||||
|
||||
type ImageType int
|
||||
@@ -84,13 +87,60 @@ type ImageHistoryItem struct {
|
||||
}
|
||||
|
||||
type ConfigObject struct {
|
||||
Env []string `json:"Env"`
|
||||
Entrypoint []string `json:"Entrypoint"`
|
||||
Hostname string
|
||||
Domainname string
|
||||
User string
|
||||
AttachStdin bool
|
||||
AttachStdout bool
|
||||
AttachStderr bool
|
||||
ExposedPorts map[string]struct{} `json:"ExposedPorts"`
|
||||
Cmd []string `json:"Cmd"`
|
||||
Volumes map[string]struct{} `json:"Volumes"`
|
||||
Workdir string `json:"WorkingDir"`
|
||||
Labels map[string]string `json:"Labels"`
|
||||
Tty bool
|
||||
OpenStdin bool
|
||||
StdinOnce bool
|
||||
Env []string `json:"Env"`
|
||||
Cmd []string `json:"Cmd"`
|
||||
// Healthcheck *HealthConfig
|
||||
ArgsEscaped bool `json:",omitempty"`
|
||||
Image string
|
||||
Volumes map[string]struct{} `json:"Volumes"`
|
||||
Workdir string `json:"WorkingDir"`
|
||||
Entrypoint []string `json:"Entrypoint"`
|
||||
NetworkDisabled bool `json:",omitempty"`
|
||||
MacAddress string `json:",omitempty"`
|
||||
OnBuild []string
|
||||
Labels map[string]string `json:"Labels"`
|
||||
StopSignal string `json:",omitempty"`
|
||||
StopTimeout *int `json:",omitempty"`
|
||||
Shell []string `json:",omitempty"`
|
||||
}
|
||||
|
||||
func (c ConfigObject) AsList() []string {
|
||||
return []string{
|
||||
fmt.Sprintf("Hostname: %s", c.Hostname),
|
||||
fmt.Sprintf("Domainname: %s", c.Domainname),
|
||||
fmt.Sprintf("User: %s", c.User),
|
||||
fmt.Sprintf("AttachStdin: %t", c.AttachStdin),
|
||||
fmt.Sprintf("AttachStdout: %t", c.AttachStdout),
|
||||
fmt.Sprintf("AttachStderr: %t", c.AttachStderr),
|
||||
fmt.Sprintf("ExposedPorts: %v", sortMap(c.ExposedPorts)),
|
||||
fmt.Sprintf("Tty: %t", c.Tty),
|
||||
fmt.Sprintf("OpenStdin: %t", c.OpenStdin),
|
||||
fmt.Sprintf("StdinOnce: %t", c.StdinOnce),
|
||||
fmt.Sprintf("Env: %s", strings.Join(c.Env, ",")),
|
||||
fmt.Sprintf("Cmd: %s", strings.Join(c.Cmd, ",")),
|
||||
fmt.Sprintf("ArgsEscaped: %t", c.ArgsEscaped),
|
||||
fmt.Sprintf("Image: %s", c.Image),
|
||||
fmt.Sprintf("Volumes: %v", sortMap(c.Volumes)),
|
||||
fmt.Sprintf("Workdir: %s", c.Workdir),
|
||||
fmt.Sprintf("Entrypoint: %s", strings.Join(c.Entrypoint, ",")),
|
||||
fmt.Sprintf("NetworkDisabled: %t", c.NetworkDisabled),
|
||||
fmt.Sprintf("MacAddress: %s", c.MacAddress),
|
||||
fmt.Sprintf("OnBuild: %s", strings.Join(c.OnBuild, ",")),
|
||||
fmt.Sprintf("Labels: %v", c.Labels),
|
||||
fmt.Sprintf("StopSignal: %s", c.StopSignal),
|
||||
fmt.Sprintf("StopTimeout: %d", c.StopTimeout),
|
||||
fmt.Sprintf("Shell: %s", strings.Join(c.Shell, ",")),
|
||||
}
|
||||
}
|
||||
|
||||
type ConfigSchema struct {
|
||||
@@ -99,10 +149,17 @@ type ConfigSchema struct {
|
||||
}
|
||||
|
||||
func getImage(p Prepper) (Image, error) {
|
||||
// see if the image name has tag provided, if not add latest as tag
|
||||
if !IsTar(p.GetSource()) && !HasTag(p.GetSource()) {
|
||||
p.SetSource(p.GetSource() + LatestTag)
|
||||
}
|
||||
output.PrintToStdErr("Retrieving image %s from source %s\n", p.GetSource(), p.Name())
|
||||
imgPath, err := p.GetFileSystem()
|
||||
if err != nil {
|
||||
return Image{}, err
|
||||
// return image with FSPath so it can be cleaned up
|
||||
return Image{
|
||||
FSPath: imgPath,
|
||||
}, err
|
||||
}
|
||||
|
||||
config, err := p.GetConfig()
|
||||
@@ -128,6 +185,14 @@ func getImageFromTar(tarPath string) (string, error) {
|
||||
}
|
||||
|
||||
func GetFileSystemFromReference(ref types.ImageReference, imgSrc types.ImageSource, path string, whitelist []string) error {
|
||||
var err error
|
||||
if imgSrc == nil {
|
||||
imgSrc, err = ref.NewImageSource(nil)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer imgSrc.Close()
|
||||
img, err := ref.NewImage(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -208,7 +273,16 @@ func CleanupImage(image Image) {
|
||||
if image.FSPath != "" {
|
||||
logrus.Infof("Removing image filesystem directory %s from system", image.FSPath)
|
||||
if err := os.RemoveAll(image.FSPath); err != nil {
|
||||
logrus.Error(err.Error())
|
||||
logrus.Warn(err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func sortMap(m map[string]struct{}) string {
|
||||
pairs := make([]string, 0)
|
||||
for key := range m {
|
||||
pairs = append(pairs, fmt.Sprintf("%s:%s", key, m[key]))
|
||||
}
|
||||
sort.Strings(pairs)
|
||||
return strings.Join(pairs, " ")
|
||||
}
|
||||
|
||||
+10
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2017 Google, Inc. All rights reserved.
|
||||
Copyright 2018 Google, Inc. All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -21,11 +21,14 @@ import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
|
||||
"github.com/docker/docker/pkg/system"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const LatestTag string = ":latest"
|
||||
|
||||
func GetImageLayers(pathToImage string) []string {
|
||||
layers := []string{}
|
||||
contents, err := ioutil.ReadDir(pathToImage)
|
||||
@@ -67,3 +70,9 @@ func copyToFile(outfile string, r io.Reader) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// checks to see if an image string contains a tag.
|
||||
func HasTag(image string) bool {
|
||||
tagRegex := regexp.MustCompile(".*:[^/]+$")
|
||||
return tagRegex.MatchString(image)
|
||||
}
|
||||
|
||||
+10
-6
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2017 Google, Inc. All rights reserved.
|
||||
Copyright 2018 Google, Inc. All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -32,25 +32,29 @@ type TarPrepper struct {
|
||||
Client *client.Client
|
||||
}
|
||||
|
||||
func (p TarPrepper) Name() string {
|
||||
func (p *TarPrepper) Name() string {
|
||||
return "Tar Archive"
|
||||
}
|
||||
|
||||
func (p TarPrepper) GetSource() string {
|
||||
func (p *TarPrepper) GetSource() string {
|
||||
return p.Source
|
||||
}
|
||||
|
||||
func (p TarPrepper) GetImage() (Image, error) {
|
||||
func (p *TarPrepper) SetSource(source string) {
|
||||
p.Source = source
|
||||
}
|
||||
|
||||
func (p *TarPrepper) GetImage() (Image, error) {
|
||||
image, err := getImage(p)
|
||||
image.Type = ImageTypeTar
|
||||
return image, err
|
||||
}
|
||||
|
||||
func (p TarPrepper) GetFileSystem() (string, error) {
|
||||
func (p *TarPrepper) GetFileSystem() (string, error) {
|
||||
return getImageFromTar(p.Source)
|
||||
}
|
||||
|
||||
func (p TarPrepper) GetConfig() (ConfigSchema, error) {
|
||||
func (p *TarPrepper) GetConfig() (ConfigSchema, error) {
|
||||
tempDir, err := ioutil.TempDir("", ".container-diff")
|
||||
if err != nil {
|
||||
return ConfigSchema{}, nil
|
||||
|
||||
+62
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2017 Google, Inc. All rights reserved.
|
||||
Copyright 2018 Google, Inc. All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -18,14 +18,26 @@ package util
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"github.com/sirupsen/logrus"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// Map of target:linkname
|
||||
var hardlinks = make(map[string]string)
|
||||
|
||||
type OriginalPerm struct {
|
||||
path string
|
||||
perm os.FileMode
|
||||
}
|
||||
|
||||
func unpackTar(tr *tar.Reader, path string, whitelist []string) error {
|
||||
originalPerms := make([]OriginalPerm, 0)
|
||||
for {
|
||||
header, err := tr.Next()
|
||||
if err == io.EOF {
|
||||
@@ -37,7 +49,7 @@ func unpackTar(tr *tar.Reader, path string, whitelist []string) error {
|
||||
return err
|
||||
}
|
||||
if strings.Contains(header.Name, ".wh.") {
|
||||
rmPath := filepath.Join(path, header.Name)
|
||||
rmPath := filepath.Clean(filepath.Join(path, header.Name))
|
||||
// Remove the .wh file if it was extracted.
|
||||
if _, err := os.Stat(rmPath); !os.IsNotExist(err) {
|
||||
if err := os.Remove(rmPath); err != nil {
|
||||
@@ -52,7 +64,7 @@ func unpackTar(tr *tar.Reader, path string, whitelist []string) error {
|
||||
}
|
||||
continue
|
||||
}
|
||||
target := filepath.Join(path, header.Name)
|
||||
target := filepath.Clean(filepath.Join(path, header.Name))
|
||||
// Make sure the target isn't part of the whitelist
|
||||
if checkWhitelist(target, whitelist) {
|
||||
continue
|
||||
@@ -63,6 +75,17 @@ func unpackTar(tr *tar.Reader, path string, whitelist []string) error {
|
||||
// if its a dir and it doesn't exist create it
|
||||
case tar.TypeDir:
|
||||
if _, err := os.Stat(target); os.IsNotExist(err) {
|
||||
if mode.Perm()&(1<<(uint(7))) == 0 {
|
||||
logrus.Debugf("Write permission bit not set on %s by default; setting manually", target)
|
||||
originalMode := mode
|
||||
mode = mode | (1 << uint(7))
|
||||
// keep track of original file permission to reset later
|
||||
originalPerms = append(originalPerms, OriginalPerm{
|
||||
path: target,
|
||||
perm: originalMode,
|
||||
})
|
||||
}
|
||||
logrus.Debugf("Creating directory %s with permissions %v", target, mode)
|
||||
if err := os.MkdirAll(target, mode); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -91,6 +114,7 @@ func unpackTar(tr *tar.Reader, path string, whitelist []string) error {
|
||||
}
|
||||
}
|
||||
|
||||
logrus.Debugf("Creating file %s with permissions %v", target, mode)
|
||||
currFile, err := os.Create(target)
|
||||
if err != nil {
|
||||
logrus.Errorf("Error creating file %s %s", target, err)
|
||||
@@ -119,8 +143,42 @@ func unpackTar(tr *tar.Reader, path string, whitelist []string) error {
|
||||
if err = os.Symlink(header.Linkname, target); err != nil {
|
||||
logrus.Errorf("Failed to create symlink between %s and %s: %s", header.Linkname, target, err)
|
||||
}
|
||||
case tar.TypeLink:
|
||||
linkname := filepath.Clean(filepath.Join(path, header.Linkname))
|
||||
// Check if the linkname already exists
|
||||
if _, err := os.Stat(linkname); !os.IsNotExist(err) {
|
||||
// If it exists, create the hard link
|
||||
resolveHardlink(linkname, target)
|
||||
} else {
|
||||
hardlinks[target] = linkname
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for target, linkname := range hardlinks {
|
||||
logrus.Info("Resolving hard links.")
|
||||
if _, err := os.Stat(linkname); !os.IsNotExist(err) {
|
||||
// If it exists, create the hard link
|
||||
if err := resolveHardlink(linkname, target); err != nil {
|
||||
return errors.Wrap(err, fmt.Sprintf("Unable to create hard link from %s to %s", linkname, target))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// reset all original file
|
||||
for _, perm := range originalPerms {
|
||||
if err := os.Chmod(perm.path, perm.perm); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func resolveHardlink(linkname, target string) error {
|
||||
if err := os.Link(linkname, target); err != nil {
|
||||
return err
|
||||
}
|
||||
logrus.Debugf("Created hard link from %s to %s", linkname, target)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
+29
-5
@@ -77,15 +77,20 @@ func NewHighBiased(epsilon float64) *Stream {
|
||||
// is guaranteed to be within (Quantile±Epsilon).
|
||||
//
|
||||
// See http://www.cs.rutgers.edu/~muthu/bquant.pdf for time, space, and error properties.
|
||||
func NewTargeted(targets map[float64]float64) *Stream {
|
||||
func NewTargeted(targetMap map[float64]float64) *Stream {
|
||||
// Convert map to slice to avoid slow iterations on a map.
|
||||
// ƒ is called on the hot path, so converting the map to a slice
|
||||
// beforehand results in significant CPU savings.
|
||||
targets := targetMapToSlice(targetMap)
|
||||
|
||||
ƒ := func(s *stream, r float64) float64 {
|
||||
var m = math.MaxFloat64
|
||||
var f float64
|
||||
for quantile, epsilon := range targets {
|
||||
if quantile*s.n <= r {
|
||||
f = (2 * epsilon * r) / quantile
|
||||
for _, t := range targets {
|
||||
if t.quantile*s.n <= r {
|
||||
f = (2 * t.epsilon * r) / t.quantile
|
||||
} else {
|
||||
f = (2 * epsilon * (s.n - r)) / (1 - quantile)
|
||||
f = (2 * t.epsilon * (s.n - r)) / (1 - t.quantile)
|
||||
}
|
||||
if f < m {
|
||||
m = f
|
||||
@@ -96,6 +101,25 @@ func NewTargeted(targets map[float64]float64) *Stream {
|
||||
return newStream(ƒ)
|
||||
}
|
||||
|
||||
type target struct {
|
||||
quantile float64
|
||||
epsilon float64
|
||||
}
|
||||
|
||||
func targetMapToSlice(targetMap map[float64]float64) []target {
|
||||
targets := make([]target, 0, len(targetMap))
|
||||
|
||||
for quantile, epsilon := range targetMap {
|
||||
t := target{
|
||||
quantile: quantile,
|
||||
epsilon: epsilon,
|
||||
}
|
||||
targets = append(targets, t)
|
||||
}
|
||||
|
||||
return targets
|
||||
}
|
||||
|
||||
// Stream computes quantiles for a stream of float64s. It is not thread-safe by
|
||||
// design. Take care when using across multiple goroutines.
|
||||
type Stream struct {
|
||||
|
||||
Generated
Vendored
+4
-4
@@ -45,13 +45,13 @@ type Manager interface {
|
||||
// to a backend.
|
||||
func NewSimpleManager() Manager {
|
||||
return &simpleManager{
|
||||
Challanges: make(map[string][]Challenge),
|
||||
Challenges: make(map[string][]Challenge),
|
||||
}
|
||||
}
|
||||
|
||||
type simpleManager struct {
|
||||
sync.RWMutex
|
||||
Challanges map[string][]Challenge
|
||||
Challenges map[string][]Challenge
|
||||
}
|
||||
|
||||
func normalizeURL(endpoint *url.URL) {
|
||||
@@ -64,7 +64,7 @@ func (m *simpleManager) GetChallenges(endpoint url.URL) ([]Challenge, error) {
|
||||
|
||||
m.RLock()
|
||||
defer m.RUnlock()
|
||||
challenges := m.Challanges[endpoint.String()]
|
||||
challenges := m.Challenges[endpoint.String()]
|
||||
return challenges, nil
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ func (m *simpleManager) AddResponse(resp *http.Response) error {
|
||||
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
m.Challanges[urlCopy.String()] = challenges
|
||||
m.Challenges[urlCopy.String()] = challenges
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -31,7 +31,7 @@ type unitMap map[string]int64
|
||||
var (
|
||||
decimalMap = unitMap{"k": KB, "m": MB, "g": GB, "t": TB, "p": PB}
|
||||
binaryMap = unitMap{"k": KiB, "m": MiB, "g": GiB, "t": TiB, "p": PiB}
|
||||
sizeRegex = regexp.MustCompile(`^(\d+(\.\d+)*) ?([kKmMgGtTpP])?[bB]?$`)
|
||||
sizeRegex = regexp.MustCompile(`^(\d+(\.\d+)*) ?([kKmMgGtTpP])?[iI]?[bB]?$`)
|
||||
)
|
||||
|
||||
var decimapAbbrs = []string{"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"}
|
||||
|
||||
+7
-7
@@ -31,8 +31,8 @@ func isExported(field reflect.StructField) bool {
|
||||
// Traverses recursively both values, assigning src's fields values to dst.
|
||||
// The map argument tracks comparisons that have already been seen, which allows
|
||||
// short circuiting on recursive types.
|
||||
func deepMap(dst, src reflect.Value, visited map[uintptr]*visit, depth int, config *config) (err error) {
|
||||
overwrite := config.overwrite
|
||||
func deepMap(dst, src reflect.Value, visited map[uintptr]*visit, depth int, config *Config) (err error) {
|
||||
overwrite := config.Overwrite
|
||||
if dst.CanAddr() {
|
||||
addr := dst.UnsafeAddr()
|
||||
h := 17 * addr
|
||||
@@ -128,23 +128,23 @@ func deepMap(dst, src reflect.Value, visited map[uintptr]*visit, depth int, conf
|
||||
// doesn't apply if dst is a map.
|
||||
// This is separated method from Merge because it is cleaner and it keeps sane
|
||||
// semantics: merging equal types, mapping different (restricted) types.
|
||||
func Map(dst, src interface{}, opts ...func(*config)) error {
|
||||
func Map(dst, src interface{}, opts ...func(*Config)) error {
|
||||
return _map(dst, src, opts...)
|
||||
}
|
||||
|
||||
// MapWithOverwrite will do the same as Map except that non-empty dst attributes will be overriden by
|
||||
// MapWithOverwrite will do the same as Map except that non-empty dst attributes will be overridden by
|
||||
// non-empty src attribute values.
|
||||
// Deprecated: Use Map(…) with WithOverride
|
||||
func MapWithOverwrite(dst, src interface{}, opts ...func(*config)) error {
|
||||
func MapWithOverwrite(dst, src interface{}, opts ...func(*Config)) error {
|
||||
return _map(dst, src, append(opts, WithOverride)...)
|
||||
}
|
||||
|
||||
func _map(dst, src interface{}, opts ...func(*config)) error {
|
||||
func _map(dst, src interface{}, opts ...func(*Config)) error {
|
||||
var (
|
||||
vDst, vSrc reflect.Value
|
||||
err error
|
||||
)
|
||||
config := &config{}
|
||||
config := &Config{}
|
||||
|
||||
for _, opt := range opts {
|
||||
opt(config)
|
||||
|
||||
+37
-23
@@ -8,7 +8,9 @@
|
||||
|
||||
package mergo
|
||||
|
||||
import "reflect"
|
||||
import (
|
||||
"reflect"
|
||||
)
|
||||
|
||||
func hasExportedField(dst reflect.Value) (exported bool) {
|
||||
for i, n := 0, dst.NumField(); i < n; i++ {
|
||||
@@ -22,20 +24,21 @@ func hasExportedField(dst reflect.Value) (exported bool) {
|
||||
return
|
||||
}
|
||||
|
||||
type config struct {
|
||||
overwrite bool
|
||||
transformers transformers
|
||||
type Config struct {
|
||||
Overwrite bool
|
||||
AppendSlice bool
|
||||
Transformers Transformers
|
||||
}
|
||||
|
||||
type transformers interface {
|
||||
type Transformers interface {
|
||||
Transformer(reflect.Type) func(dst, src reflect.Value) error
|
||||
}
|
||||
|
||||
// Traverses recursively both values, assigning src's fields values to dst.
|
||||
// The map argument tracks comparisons that have already been seen, which allows
|
||||
// short circuiting on recursive types.
|
||||
func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, config *config) (err error) {
|
||||
overwrite := config.overwrite
|
||||
func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, config *Config) (err error) {
|
||||
overwrite := config.Overwrite
|
||||
|
||||
if !src.IsValid() {
|
||||
return
|
||||
@@ -54,8 +57,8 @@ func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, co
|
||||
visited[h] = &visit{addr, typ, seen}
|
||||
}
|
||||
|
||||
if config.transformers != nil && !isEmptyValue(dst) {
|
||||
if fn := config.transformers.Transformer(dst.Type()); fn != nil {
|
||||
if config.Transformers != nil && !isEmptyValue(dst) {
|
||||
if fn := config.Transformers.Transformer(dst.Type()); fn != nil {
|
||||
err = fn(dst, src)
|
||||
return
|
||||
}
|
||||
@@ -75,9 +78,8 @@ func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, co
|
||||
}
|
||||
}
|
||||
case reflect.Map:
|
||||
if len(src.MapKeys()) == 0 && !src.IsNil() && len(dst.MapKeys()) == 0 {
|
||||
if dst.IsNil() && !src.IsNil() {
|
||||
dst.Set(reflect.MakeMap(dst.Type()))
|
||||
return
|
||||
}
|
||||
for _, key := range src.MapKeys() {
|
||||
srcElement := src.MapIndex(key)
|
||||
@@ -86,7 +88,7 @@ func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, co
|
||||
}
|
||||
dstElement := dst.MapIndex(key)
|
||||
switch srcElement.Kind() {
|
||||
case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Slice:
|
||||
case reflect.Chan, reflect.Func, reflect.Map, reflect.Interface, reflect.Slice:
|
||||
if srcElement.IsNil() {
|
||||
continue
|
||||
}
|
||||
@@ -122,7 +124,7 @@ func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, co
|
||||
continue
|
||||
}
|
||||
|
||||
if !isEmptyValue(srcElement) && (overwrite || (!dstElement.IsValid() || isEmptyValue(dst))) {
|
||||
if srcElement.IsValid() && (overwrite || (!dstElement.IsValid() || isEmptyValue(dst))) {
|
||||
if dst.IsNil() {
|
||||
dst.Set(reflect.MakeMap(dst.Type()))
|
||||
}
|
||||
@@ -130,7 +132,14 @@ func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, co
|
||||
}
|
||||
}
|
||||
case reflect.Slice:
|
||||
dst.Set(reflect.AppendSlice(dst, src))
|
||||
if !dst.CanSet() {
|
||||
break
|
||||
}
|
||||
if !isEmptyValue(src) && (overwrite || isEmptyValue(dst)) && !config.AppendSlice {
|
||||
dst.Set(src)
|
||||
} else {
|
||||
dst.Set(reflect.AppendSlice(dst, src))
|
||||
}
|
||||
case reflect.Ptr:
|
||||
fallthrough
|
||||
case reflect.Interface:
|
||||
@@ -174,36 +183,41 @@ func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, co
|
||||
// src attributes if they themselves are not empty. dst and src must be valid same-type structs
|
||||
// and dst must be a pointer to struct.
|
||||
// It won't merge unexported (private) fields and will do recursively any exported field.
|
||||
func Merge(dst, src interface{}, opts ...func(*config)) error {
|
||||
func Merge(dst, src interface{}, opts ...func(*Config)) error {
|
||||
return merge(dst, src, opts...)
|
||||
}
|
||||
|
||||
// MergeWithOverwrite will do the same as Merge except that non-empty dst attributes will be overriden by
|
||||
// non-empty src attribute values.
|
||||
// Deprecated: use Merge(…) with WithOverride
|
||||
func MergeWithOverwrite(dst, src interface{}, opts ...func(*config)) error {
|
||||
func MergeWithOverwrite(dst, src interface{}, opts ...func(*Config)) error {
|
||||
return merge(dst, src, append(opts, WithOverride)...)
|
||||
}
|
||||
|
||||
// WithTransformers adds transformers to merge, allowing to customize the merging of some types.
|
||||
func WithTransformers(transformers transformers) func(*config) {
|
||||
return func(config *config) {
|
||||
config.transformers = transformers
|
||||
func WithTransformers(transformers Transformers) func(*Config) {
|
||||
return func(config *Config) {
|
||||
config.Transformers = transformers
|
||||
}
|
||||
}
|
||||
|
||||
// WithOverride will make merge override non-empty dst attributes with non-empty src attributes values.
|
||||
func WithOverride(config *config) {
|
||||
config.overwrite = true
|
||||
func WithOverride(config *Config) {
|
||||
config.Overwrite = true
|
||||
}
|
||||
|
||||
func merge(dst, src interface{}, opts ...func(*config)) error {
|
||||
// WithAppendSlice will make merge append slices instead of overwriting it
|
||||
func WithAppendSlice(config *Config) {
|
||||
config.AppendSlice = true
|
||||
}
|
||||
|
||||
func merge(dst, src interface{}, opts ...func(*Config)) error {
|
||||
var (
|
||||
vDst, vSrc reflect.Value
|
||||
err error
|
||||
)
|
||||
|
||||
config := &config{}
|
||||
config := &Config{}
|
||||
|
||||
for _, opt := range opts {
|
||||
opt(config)
|
||||
|
||||
+1
-1
@@ -184,7 +184,7 @@ func (nd NetDev) parseLine(rawLine string) (*NetDevLine, error) {
|
||||
}
|
||||
|
||||
// Total aggregates the values across interfaces and returns a new NetDevLine.
|
||||
// The Name field will be a sorted comma seperated list of interface names.
|
||||
// The Name field will be a sorted comma separated list of interface names.
|
||||
func (nd NetDev) Total() NetDevLine {
|
||||
total := NetDevLine{}
|
||||
|
||||
|
||||
+10
-10
@@ -11,7 +11,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package nfsd implements parsing of /proc/net/rpc/nfsd.
|
||||
// Package nfs implements parsing of /proc/net/rpc/nfsd.
|
||||
// Fields are documented in https://www.svennd.be/nfsd-stats-explained-procnetrpcnfsd/
|
||||
package nfs
|
||||
|
||||
@@ -136,8 +136,8 @@ type ClientV4Stats struct {
|
||||
Setattr uint64
|
||||
FsInfo uint64
|
||||
Renew uint64
|
||||
SetClientId uint64
|
||||
SetClientIdConfirm uint64
|
||||
SetClientID uint64
|
||||
SetClientIDConfirm uint64
|
||||
Lock uint64
|
||||
Lockt uint64
|
||||
Locku uint64
|
||||
@@ -156,13 +156,13 @@ type ClientV4Stats struct {
|
||||
ReadDir uint64
|
||||
ServerCaps uint64
|
||||
DelegReturn uint64
|
||||
GetAcl uint64
|
||||
SetAcl uint64
|
||||
GetACL uint64
|
||||
SetACL uint64
|
||||
FsLocations uint64
|
||||
ReleaseLockowner uint64
|
||||
Secinfo uint64
|
||||
FsidPresent uint64
|
||||
ExchangeId uint64
|
||||
ExchangeID uint64
|
||||
CreateSession uint64
|
||||
DestroySession uint64
|
||||
Sequence uint64
|
||||
@@ -173,11 +173,11 @@ type ClientV4Stats struct {
|
||||
LayoutCommit uint64
|
||||
LayoutReturn uint64
|
||||
SecinfoNoName uint64
|
||||
TestStateId uint64
|
||||
FreeStateId uint64
|
||||
TestStateID uint64
|
||||
FreeStateID uint64
|
||||
GetDeviceList uint64
|
||||
BindConnToSession uint64
|
||||
DestroyClientId uint64
|
||||
DestroyClientID uint64
|
||||
Seek uint64
|
||||
Allocate uint64
|
||||
DeAllocate uint64
|
||||
@@ -238,7 +238,7 @@ type V4Ops struct {
|
||||
RelLockOwner uint64
|
||||
}
|
||||
|
||||
// RPCStats models all stats from /proc/net/rpc/nfs.
|
||||
// ClientRPCStats models all stats from /proc/net/rpc/nfs.
|
||||
type ClientRPCStats struct {
|
||||
Network Network
|
||||
ClientRPC ClientRPC
|
||||
|
||||
+8
-8
@@ -204,8 +204,8 @@ func parseClientV4Stats(v []uint64) (ClientV4Stats, error) {
|
||||
Setattr: v[10],
|
||||
FsInfo: v[11],
|
||||
Renew: v[12],
|
||||
SetClientId: v[13],
|
||||
SetClientIdConfirm: v[14],
|
||||
SetClientID: v[13],
|
||||
SetClientIDConfirm: v[14],
|
||||
Lock: v[15],
|
||||
Lockt: v[16],
|
||||
Locku: v[17],
|
||||
@@ -224,13 +224,13 @@ func parseClientV4Stats(v []uint64) (ClientV4Stats, error) {
|
||||
ReadDir: v[30],
|
||||
ServerCaps: v[31],
|
||||
DelegReturn: v[32],
|
||||
GetAcl: v[33],
|
||||
SetAcl: v[34],
|
||||
GetACL: v[33],
|
||||
SetACL: v[34],
|
||||
FsLocations: v[35],
|
||||
ReleaseLockowner: v[36],
|
||||
Secinfo: v[37],
|
||||
FsidPresent: v[38],
|
||||
ExchangeId: v[39],
|
||||
ExchangeID: v[39],
|
||||
CreateSession: v[40],
|
||||
DestroySession: v[41],
|
||||
Sequence: v[42],
|
||||
@@ -241,11 +241,11 @@ func parseClientV4Stats(v []uint64) (ClientV4Stats, error) {
|
||||
LayoutCommit: v[47],
|
||||
LayoutReturn: v[48],
|
||||
SecinfoNoName: v[49],
|
||||
TestStateId: v[50],
|
||||
FreeStateId: v[51],
|
||||
TestStateID: v[50],
|
||||
FreeStateID: v[51],
|
||||
GetDeviceList: v[52],
|
||||
BindConnToSession: v[53],
|
||||
DestroyClientId: v[54],
|
||||
DestroyClientID: v[54],
|
||||
Seek: v[55],
|
||||
Allocate: v[56],
|
||||
DeAllocate: v[57],
|
||||
|
||||
+30
-21
@@ -94,32 +94,16 @@ func (entry Entry) log(level Level, msg string) {
|
||||
entry.Level = level
|
||||
entry.Message = msg
|
||||
|
||||
entry.Logger.mu.Lock()
|
||||
err := entry.Logger.Hooks.Fire(level, &entry)
|
||||
entry.Logger.mu.Unlock()
|
||||
if err != nil {
|
||||
entry.Logger.mu.Lock()
|
||||
fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err)
|
||||
entry.Logger.mu.Unlock()
|
||||
}
|
||||
entry.fireHooks()
|
||||
|
||||
buffer = bufferPool.Get().(*bytes.Buffer)
|
||||
buffer.Reset()
|
||||
defer bufferPool.Put(buffer)
|
||||
entry.Buffer = buffer
|
||||
serialized, err := entry.Logger.Formatter.Format(&entry)
|
||||
|
||||
entry.write()
|
||||
|
||||
entry.Buffer = nil
|
||||
if err != nil {
|
||||
entry.Logger.mu.Lock()
|
||||
fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err)
|
||||
entry.Logger.mu.Unlock()
|
||||
} else {
|
||||
entry.Logger.mu.Lock()
|
||||
_, err = entry.Logger.Out.Write(serialized)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err)
|
||||
}
|
||||
entry.Logger.mu.Unlock()
|
||||
}
|
||||
|
||||
// To avoid Entry#log() returning a value that only would make sense for
|
||||
// panic() to use in Entry#Panic(), we avoid the allocation by checking
|
||||
@@ -129,6 +113,31 @@ func (entry Entry) log(level Level, msg string) {
|
||||
}
|
||||
}
|
||||
|
||||
// This function is not declared with a pointer value because otherwise
|
||||
// race conditions will occur when using multiple goroutines
|
||||
func (entry Entry) fireHooks() {
|
||||
entry.Logger.mu.Lock()
|
||||
defer entry.Logger.mu.Unlock()
|
||||
err := entry.Logger.Hooks.Fire(entry.Level, &entry)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (entry *Entry) write() {
|
||||
serialized, err := entry.Logger.Formatter.Format(entry)
|
||||
entry.Logger.mu.Lock()
|
||||
defer entry.Logger.mu.Unlock()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err)
|
||||
} else {
|
||||
_, err = entry.Logger.Out.Write(serialized)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (entry *Entry) Debug(args ...interface{}) {
|
||||
if entry.Logger.level() >= DebugLevel {
|
||||
entry.log(DebugLevel, fmt.Sprint(args...))
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
// +build darwin freebsd openbsd netbsd dragonfly
|
||||
// +build !appengine
|
||||
// +build !appengine,!gopherjs
|
||||
|
||||
package logrus
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// +build appengine
|
||||
// +build appengine gopherjs
|
||||
|
||||
package logrus
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// +build !appengine
|
||||
// +build !appengine,!gopherjs
|
||||
|
||||
package logrus
|
||||
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !appengine
|
||||
// +build !appengine,!gopherjs
|
||||
|
||||
package logrus
|
||||
|
||||
|
||||
+8
-17
@@ -16,14 +16,14 @@ func legacyArgs(cmd *Command, args []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// root command with subcommands, do subcommand checking
|
||||
// root command with subcommands, do subcommand checking.
|
||||
if !cmd.HasParent() && len(args) > 0 {
|
||||
return fmt.Errorf("unknown command %q for %q%s", args[0], cmd.CommandPath(), cmd.findSuggestions(args[0]))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NoArgs returns an error if any args are included
|
||||
// NoArgs returns an error if any args are included.
|
||||
func NoArgs(cmd *Command, args []string) error {
|
||||
if len(args) > 0 {
|
||||
return fmt.Errorf("unknown command %q for %q", args[0], cmd.CommandPath())
|
||||
@@ -31,7 +31,7 @@ func NoArgs(cmd *Command, args []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// OnlyValidArgs returns an error if any args are not in the list of ValidArgs
|
||||
// OnlyValidArgs returns an error if any args are not in the list of ValidArgs.
|
||||
func OnlyValidArgs(cmd *Command, args []string) error {
|
||||
if len(cmd.ValidArgs) > 0 {
|
||||
for _, v := range args {
|
||||
@@ -43,21 +43,12 @@ func OnlyValidArgs(cmd *Command, args []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func stringInSlice(a string, list []string) bool {
|
||||
for _, b := range list {
|
||||
if b == a {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ArbitraryArgs never returns an error
|
||||
// ArbitraryArgs never returns an error.
|
||||
func ArbitraryArgs(cmd *Command, args []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// MinimumNArgs returns an error if there is not at least N args
|
||||
// MinimumNArgs returns an error if there is not at least N args.
|
||||
func MinimumNArgs(n int) PositionalArgs {
|
||||
return func(cmd *Command, args []string) error {
|
||||
if len(args) < n {
|
||||
@@ -67,7 +58,7 @@ func MinimumNArgs(n int) PositionalArgs {
|
||||
}
|
||||
}
|
||||
|
||||
// MaximumNArgs returns an error if there are more than N args
|
||||
// MaximumNArgs returns an error if there are more than N args.
|
||||
func MaximumNArgs(n int) PositionalArgs {
|
||||
return func(cmd *Command, args []string) error {
|
||||
if len(args) > n {
|
||||
@@ -77,7 +68,7 @@ func MaximumNArgs(n int) PositionalArgs {
|
||||
}
|
||||
}
|
||||
|
||||
// ExactArgs returns an error if there are not exactly n args
|
||||
// ExactArgs returns an error if there are not exactly n args.
|
||||
func ExactArgs(n int) PositionalArgs {
|
||||
return func(cmd *Command, args []string) error {
|
||||
if len(args) != n {
|
||||
@@ -87,7 +78,7 @@ func ExactArgs(n int) PositionalArgs {
|
||||
}
|
||||
}
|
||||
|
||||
// RangeArgs returns an error if the number of args is not within the expected range
|
||||
// RangeArgs returns an error if the number of args is not within the expected range.
|
||||
func RangeArgs(min int, max int) PositionalArgs {
|
||||
return func(cmd *Command, args []string) error {
|
||||
if len(args) < min || len(args) > max {
|
||||
|
||||
+78
-60
@@ -21,8 +21,8 @@ const (
|
||||
|
||||
func writePreamble(buf *bytes.Buffer, name string) {
|
||||
buf.WriteString(fmt.Sprintf("# bash completion for %-36s -*- shell-script -*-\n", name))
|
||||
buf.WriteString(`
|
||||
__debug()
|
||||
buf.WriteString(fmt.Sprintf(`
|
||||
__%[1]s_debug()
|
||||
{
|
||||
if [[ -n ${BASH_COMP_DEBUG_FILE} ]]; then
|
||||
echo "$*" >> "${BASH_COMP_DEBUG_FILE}"
|
||||
@@ -31,13 +31,13 @@ __debug()
|
||||
|
||||
# Homebrew on Macs have version 1.3 of bash-completion which doesn't include
|
||||
# _init_completion. This is a very minimal version of that function.
|
||||
__my_init_completion()
|
||||
__%[1]s_init_completion()
|
||||
{
|
||||
COMPREPLY=()
|
||||
_get_comp_words_by_ref "$@" cur prev words cword
|
||||
}
|
||||
|
||||
__index_of_word()
|
||||
__%[1]s_index_of_word()
|
||||
{
|
||||
local w word=$1
|
||||
shift
|
||||
@@ -49,7 +49,7 @@ __index_of_word()
|
||||
index=-1
|
||||
}
|
||||
|
||||
__contains_word()
|
||||
__%[1]s_contains_word()
|
||||
{
|
||||
local w word=$1; shift
|
||||
for w in "$@"; do
|
||||
@@ -58,9 +58,9 @@ __contains_word()
|
||||
return 1
|
||||
}
|
||||
|
||||
__handle_reply()
|
||||
__%[1]s_handle_reply()
|
||||
{
|
||||
__debug "${FUNCNAME[0]}"
|
||||
__%[1]s_debug "${FUNCNAME[0]}"
|
||||
case $cur in
|
||||
-*)
|
||||
if [[ $(type -t compopt) = "builtin" ]]; then
|
||||
@@ -85,7 +85,7 @@ __handle_reply()
|
||||
|
||||
local index flag
|
||||
flag="${cur%%=*}"
|
||||
__index_of_word "${flag}" "${flags_with_completion[@]}"
|
||||
__%[1]s_index_of_word "${flag}" "${flags_with_completion[@]}"
|
||||
COMPREPLY=()
|
||||
if [[ ${index} -ge 0 ]]; then
|
||||
PREFIX=""
|
||||
@@ -103,7 +103,7 @@ __handle_reply()
|
||||
|
||||
# check if we are handling a flag with special work handling
|
||||
local index
|
||||
__index_of_word "${prev}" "${flags_with_completion[@]}"
|
||||
__%[1]s_index_of_word "${prev}" "${flags_with_completion[@]}"
|
||||
if [[ ${index} -ge 0 ]]; then
|
||||
${flags_completion[${index}]}
|
||||
return
|
||||
@@ -136,24 +136,30 @@ __handle_reply()
|
||||
if declare -F __ltrim_colon_completions >/dev/null; then
|
||||
__ltrim_colon_completions "$cur"
|
||||
fi
|
||||
|
||||
# If there is only 1 completion and it is a flag with an = it will be completed
|
||||
# but we don't want a space after the =
|
||||
if [[ "${#COMPREPLY[@]}" -eq "1" ]] && [[ $(type -t compopt) = "builtin" ]] && [[ "${COMPREPLY[0]}" == --*= ]]; then
|
||||
compopt -o nospace
|
||||
fi
|
||||
}
|
||||
|
||||
# The arguments should be in the form "ext1|ext2|extn"
|
||||
__handle_filename_extension_flag()
|
||||
__%[1]s_handle_filename_extension_flag()
|
||||
{
|
||||
local ext="$1"
|
||||
_filedir "@(${ext})"
|
||||
}
|
||||
|
||||
__handle_subdirs_in_dir_flag()
|
||||
__%[1]s_handle_subdirs_in_dir_flag()
|
||||
{
|
||||
local dir="$1"
|
||||
pushd "${dir}" >/dev/null 2>&1 && _filedir -d && popd >/dev/null 2>&1
|
||||
}
|
||||
|
||||
__handle_flag()
|
||||
__%[1]s_handle_flag()
|
||||
{
|
||||
__debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}"
|
||||
__%[1]s_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}"
|
||||
|
||||
# if a command required a flag, and we found it, unset must_have_one_flag()
|
||||
local flagname=${words[c]}
|
||||
@@ -164,27 +170,30 @@ __handle_flag()
|
||||
flagname=${flagname%%=*} # strip everything after the =
|
||||
flagname="${flagname}=" # but put the = back
|
||||
fi
|
||||
__debug "${FUNCNAME[0]}: looking for ${flagname}"
|
||||
if __contains_word "${flagname}" "${must_have_one_flag[@]}"; then
|
||||
__%[1]s_debug "${FUNCNAME[0]}: looking for ${flagname}"
|
||||
if __%[1]s_contains_word "${flagname}" "${must_have_one_flag[@]}"; then
|
||||
must_have_one_flag=()
|
||||
fi
|
||||
|
||||
# if you set a flag which only applies to this command, don't show subcommands
|
||||
if __contains_word "${flagname}" "${local_nonpersistent_flags[@]}"; then
|
||||
if __%[1]s_contains_word "${flagname}" "${local_nonpersistent_flags[@]}"; then
|
||||
commands=()
|
||||
fi
|
||||
|
||||
# keep flag value with flagname as flaghash
|
||||
if [ -n "${flagvalue}" ] ; then
|
||||
flaghash[${flagname}]=${flagvalue}
|
||||
elif [ -n "${words[ $((c+1)) ]}" ] ; then
|
||||
flaghash[${flagname}]=${words[ $((c+1)) ]}
|
||||
else
|
||||
flaghash[${flagname}]="true" # pad "true" for bool flag
|
||||
# flaghash variable is an associative array which is only supported in bash > 3.
|
||||
if [[ -z "${BASH_VERSION}" || "${BASH_VERSINFO[0]}" -gt 3 ]]; then
|
||||
if [ -n "${flagvalue}" ] ; then
|
||||
flaghash[${flagname}]=${flagvalue}
|
||||
elif [ -n "${words[ $((c+1)) ]}" ] ; then
|
||||
flaghash[${flagname}]=${words[ $((c+1)) ]}
|
||||
else
|
||||
flaghash[${flagname}]="true" # pad "true" for bool flag
|
||||
fi
|
||||
fi
|
||||
|
||||
# skip the argument to a two word flag
|
||||
if __contains_word "${words[c]}" "${two_word_flags[@]}"; then
|
||||
if __%[1]s_contains_word "${words[c]}" "${two_word_flags[@]}"; then
|
||||
c=$((c+1))
|
||||
# if we are looking for a flags value, don't show commands
|
||||
if [[ $c -eq $cword ]]; then
|
||||
@@ -196,13 +205,13 @@ __handle_flag()
|
||||
|
||||
}
|
||||
|
||||
__handle_noun()
|
||||
__%[1]s_handle_noun()
|
||||
{
|
||||
__debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}"
|
||||
__%[1]s_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}"
|
||||
|
||||
if __contains_word "${words[c]}" "${must_have_one_noun[@]}"; then
|
||||
if __%[1]s_contains_word "${words[c]}" "${must_have_one_noun[@]}"; then
|
||||
must_have_one_noun=()
|
||||
elif __contains_word "${words[c]}" "${noun_aliases[@]}"; then
|
||||
elif __%[1]s_contains_word "${words[c]}" "${noun_aliases[@]}"; then
|
||||
must_have_one_noun=()
|
||||
fi
|
||||
|
||||
@@ -210,45 +219,45 @@ __handle_noun()
|
||||
c=$((c+1))
|
||||
}
|
||||
|
||||
__handle_command()
|
||||
__%[1]s_handle_command()
|
||||
{
|
||||
__debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}"
|
||||
__%[1]s_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}"
|
||||
|
||||
local next_command
|
||||
if [[ -n ${last_command} ]]; then
|
||||
next_command="_${last_command}_${words[c]//:/__}"
|
||||
else
|
||||
if [[ $c -eq 0 ]]; then
|
||||
next_command="_$(basename "${words[c]//:/__}")"
|
||||
next_command="_%[1]s_root_command"
|
||||
else
|
||||
next_command="_${words[c]//:/__}"
|
||||
fi
|
||||
fi
|
||||
c=$((c+1))
|
||||
__debug "${FUNCNAME[0]}: looking for ${next_command}"
|
||||
__%[1]s_debug "${FUNCNAME[0]}: looking for ${next_command}"
|
||||
declare -F "$next_command" >/dev/null && $next_command
|
||||
}
|
||||
|
||||
__handle_word()
|
||||
__%[1]s_handle_word()
|
||||
{
|
||||
if [[ $c -ge $cword ]]; then
|
||||
__handle_reply
|
||||
__%[1]s_handle_reply
|
||||
return
|
||||
fi
|
||||
__debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}"
|
||||
__%[1]s_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}"
|
||||
if [[ "${words[c]}" == -* ]]; then
|
||||
__handle_flag
|
||||
elif __contains_word "${words[c]}" "${commands[@]}"; then
|
||||
__handle_command
|
||||
elif [[ $c -eq 0 ]] && __contains_word "$(basename "${words[c]}")" "${commands[@]}"; then
|
||||
__handle_command
|
||||
__%[1]s_handle_flag
|
||||
elif __%[1]s_contains_word "${words[c]}" "${commands[@]}"; then
|
||||
__%[1]s_handle_command
|
||||
elif [[ $c -eq 0 ]]; then
|
||||
__%[1]s_handle_command
|
||||
else
|
||||
__handle_noun
|
||||
__%[1]s_handle_noun
|
||||
fi
|
||||
__handle_word
|
||||
__%[1]s_handle_word
|
||||
}
|
||||
|
||||
`)
|
||||
`, name))
|
||||
}
|
||||
|
||||
func writePostscript(buf *bytes.Buffer, name string) {
|
||||
@@ -260,7 +269,7 @@ func writePostscript(buf *bytes.Buffer, name string) {
|
||||
if declare -F _init_completion >/dev/null 2>&1; then
|
||||
_init_completion -s || return
|
||||
else
|
||||
__my_init_completion -n "=" || return
|
||||
__%[1]s_init_completion -n "=" || return
|
||||
fi
|
||||
|
||||
local c=0
|
||||
@@ -269,13 +278,13 @@ func writePostscript(buf *bytes.Buffer, name string) {
|
||||
local local_nonpersistent_flags=()
|
||||
local flags_with_completion=()
|
||||
local flags_completion=()
|
||||
local commands=("%s")
|
||||
local commands=("%[1]s")
|
||||
local must_have_one_flag=()
|
||||
local must_have_one_noun=()
|
||||
local last_command
|
||||
local nouns=()
|
||||
|
||||
__handle_word
|
||||
__%[1]s_handle_word
|
||||
}
|
||||
|
||||
`, name))
|
||||
@@ -300,7 +309,7 @@ func writeCommands(buf *bytes.Buffer, cmd *Command) {
|
||||
buf.WriteString("\n")
|
||||
}
|
||||
|
||||
func writeFlagHandler(buf *bytes.Buffer, name string, annotations map[string][]string) {
|
||||
func writeFlagHandler(buf *bytes.Buffer, name string, annotations map[string][]string, cmd *Command) {
|
||||
for key, value := range annotations {
|
||||
switch key {
|
||||
case BashCompFilenameExt:
|
||||
@@ -308,7 +317,7 @@ func writeFlagHandler(buf *bytes.Buffer, name string, annotations map[string][]s
|
||||
|
||||
var ext string
|
||||
if len(value) > 0 {
|
||||
ext = "__handle_filename_extension_flag " + strings.Join(value, "|")
|
||||
ext = fmt.Sprintf("__%s_handle_filename_extension_flag ", cmd.Root().Name()) + strings.Join(value, "|")
|
||||
} else {
|
||||
ext = "_filedir"
|
||||
}
|
||||
@@ -326,7 +335,7 @@ func writeFlagHandler(buf *bytes.Buffer, name string, annotations map[string][]s
|
||||
|
||||
var ext string
|
||||
if len(value) == 1 {
|
||||
ext = "__handle_subdirs_in_dir_flag " + value[0]
|
||||
ext = fmt.Sprintf("__%s_handle_subdirs_in_dir_flag ", cmd.Root().Name()) + value[0]
|
||||
} else {
|
||||
ext = "_filedir -d"
|
||||
}
|
||||
@@ -335,7 +344,7 @@ func writeFlagHandler(buf *bytes.Buffer, name string, annotations map[string][]s
|
||||
}
|
||||
}
|
||||
|
||||
func writeShortFlag(buf *bytes.Buffer, flag *pflag.Flag) {
|
||||
func writeShortFlag(buf *bytes.Buffer, flag *pflag.Flag, cmd *Command) {
|
||||
name := flag.Shorthand
|
||||
format := " "
|
||||
if len(flag.NoOptDefVal) == 0 {
|
||||
@@ -343,10 +352,10 @@ func writeShortFlag(buf *bytes.Buffer, flag *pflag.Flag) {
|
||||
}
|
||||
format += "flags+=(\"-%s\")\n"
|
||||
buf.WriteString(fmt.Sprintf(format, name))
|
||||
writeFlagHandler(buf, "-"+name, flag.Annotations)
|
||||
writeFlagHandler(buf, "-"+name, flag.Annotations, cmd)
|
||||
}
|
||||
|
||||
func writeFlag(buf *bytes.Buffer, flag *pflag.Flag) {
|
||||
func writeFlag(buf *bytes.Buffer, flag *pflag.Flag, cmd *Command) {
|
||||
name := flag.Name
|
||||
format := " flags+=(\"--%s"
|
||||
if len(flag.NoOptDefVal) == 0 {
|
||||
@@ -354,7 +363,7 @@ func writeFlag(buf *bytes.Buffer, flag *pflag.Flag) {
|
||||
}
|
||||
format += "\")\n"
|
||||
buf.WriteString(fmt.Sprintf(format, name))
|
||||
writeFlagHandler(buf, "--"+name, flag.Annotations)
|
||||
writeFlagHandler(buf, "--"+name, flag.Annotations, cmd)
|
||||
}
|
||||
|
||||
func writeLocalNonPersistentFlag(buf *bytes.Buffer, flag *pflag.Flag) {
|
||||
@@ -380,9 +389,9 @@ func writeFlags(buf *bytes.Buffer, cmd *Command) {
|
||||
if nonCompletableFlag(flag) {
|
||||
return
|
||||
}
|
||||
writeFlag(buf, flag)
|
||||
writeFlag(buf, flag, cmd)
|
||||
if len(flag.Shorthand) > 0 {
|
||||
writeShortFlag(buf, flag)
|
||||
writeShortFlag(buf, flag, cmd)
|
||||
}
|
||||
if localNonPersistentFlags.Lookup(flag.Name) != nil {
|
||||
writeLocalNonPersistentFlag(buf, flag)
|
||||
@@ -392,9 +401,9 @@ func writeFlags(buf *bytes.Buffer, cmd *Command) {
|
||||
if nonCompletableFlag(flag) {
|
||||
return
|
||||
}
|
||||
writeFlag(buf, flag)
|
||||
writeFlag(buf, flag, cmd)
|
||||
if len(flag.Shorthand) > 0 {
|
||||
writeShortFlag(buf, flag)
|
||||
writeShortFlag(buf, flag, cmd)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -452,7 +461,13 @@ func gen(buf *bytes.Buffer, cmd *Command) {
|
||||
commandName := cmd.CommandPath()
|
||||
commandName = strings.Replace(commandName, " ", "_", -1)
|
||||
commandName = strings.Replace(commandName, ":", "__", -1)
|
||||
buf.WriteString(fmt.Sprintf("_%s()\n{\n", commandName))
|
||||
|
||||
if cmd.Root() == cmd {
|
||||
buf.WriteString(fmt.Sprintf("_%s_root_command()\n{\n", commandName))
|
||||
} else {
|
||||
buf.WriteString(fmt.Sprintf("_%s()\n{\n", commandName))
|
||||
}
|
||||
|
||||
buf.WriteString(fmt.Sprintf(" last_command=%q\n", commandName))
|
||||
writeCommands(buf, cmd)
|
||||
writeFlags(buf, cmd)
|
||||
@@ -491,17 +506,20 @@ func (c *Command) GenBashCompletionFile(filename string) error {
|
||||
return c.GenBashCompletion(outFile)
|
||||
}
|
||||
|
||||
// MarkFlagRequired adds the BashCompOneRequiredFlag annotation to the named flag, if it exists.
|
||||
// MarkFlagRequired adds the BashCompOneRequiredFlag annotation to the named flag if it exists,
|
||||
// and causes your command to report an error if invoked without the flag.
|
||||
func (c *Command) MarkFlagRequired(name string) error {
|
||||
return MarkFlagRequired(c.Flags(), name)
|
||||
}
|
||||
|
||||
// MarkPersistentFlagRequired adds the BashCompOneRequiredFlag annotation to the named persistent flag, if it exists.
|
||||
// MarkPersistentFlagRequired adds the BashCompOneRequiredFlag annotation to the named persistent flag if it exists,
|
||||
// and causes your command to report an error if invoked without the flag.
|
||||
func (c *Command) MarkPersistentFlagRequired(name string) error {
|
||||
return MarkFlagRequired(c.PersistentFlags(), name)
|
||||
}
|
||||
|
||||
// MarkFlagRequired adds the BashCompOneRequiredFlag annotation to the named flag in the flag set, if it exists.
|
||||
// MarkFlagRequired adds the BashCompOneRequiredFlag annotation to the named flag if it exists,
|
||||
// and causes your command to report an error if invoked without the flag.
|
||||
func MarkFlagRequired(flags *pflag.FlagSet, name string) error {
|
||||
return flags.SetAnnotation(name, BashCompOneRequiredFlag, []string{"true"})
|
||||
}
|
||||
|
||||
+11
-1
@@ -70,7 +70,8 @@ func AddTemplateFuncs(tmplFuncs template.FuncMap) {
|
||||
}
|
||||
}
|
||||
|
||||
// OnInitialize takes a series of func() arguments and appends them to a slice of func().
|
||||
// OnInitialize sets the passed functions to be run when each command's
|
||||
// Execute method is called.
|
||||
func OnInitialize(y ...func()) {
|
||||
initializers = append(initializers, y...)
|
||||
}
|
||||
@@ -188,3 +189,12 @@ func ld(s, t string, ignoreCase bool) int {
|
||||
}
|
||||
return d[len(s)][len(t)]
|
||||
}
|
||||
|
||||
func stringInSlice(a string, list []string) bool {
|
||||
for _, b := range list {
|
||||
if b == a {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
+109
-11
@@ -75,6 +75,11 @@ type Command struct {
|
||||
// group commands.
|
||||
Annotations map[string]string
|
||||
|
||||
// Version defines the version for this command. If this value is non-empty and the command does not
|
||||
// define a "version" flag, a "version" boolean flag will be added to the command and, if specified,
|
||||
// will print content of the "Version" variable.
|
||||
Version string
|
||||
|
||||
// The *Run functions are executed in the following order:
|
||||
// * PersistentPreRun()
|
||||
// * PreRun()
|
||||
@@ -118,6 +123,10 @@ type Command struct {
|
||||
// will be printed by generating docs for this command.
|
||||
DisableAutoGenTag bool
|
||||
|
||||
// DisableFlagsInUseLine will disable the addition of [flags] to the usage
|
||||
// line of a command when printing help or generating docs
|
||||
DisableFlagsInUseLine bool
|
||||
|
||||
// DisableSuggestions disables the suggestions based on Levenshtein distance
|
||||
// that go along with 'unknown command' messages.
|
||||
DisableSuggestions bool
|
||||
@@ -138,6 +147,11 @@ type Command struct {
|
||||
commandsMaxNameLen int
|
||||
// commandsAreSorted defines, if command slice are sorted or not.
|
||||
commandsAreSorted bool
|
||||
// commandCalledAs is the name or alias value used to call this command.
|
||||
commandCalledAs struct {
|
||||
name string
|
||||
called bool
|
||||
}
|
||||
|
||||
// args is actual args parsed from flags.
|
||||
args []string
|
||||
@@ -173,6 +187,8 @@ type Command struct {
|
||||
// helpCommand is command with usage 'help'. If it's not defined by user,
|
||||
// cobra uses default help command.
|
||||
helpCommand *Command
|
||||
// versionTemplate is the version template defined by user.
|
||||
versionTemplate string
|
||||
}
|
||||
|
||||
// SetArgs sets arguments for the command. It is set to os.Args[1:] by default, if desired, can be overridden
|
||||
@@ -218,6 +234,11 @@ func (c *Command) SetHelpTemplate(s string) {
|
||||
c.helpTemplate = s
|
||||
}
|
||||
|
||||
// SetVersionTemplate sets version template to be used. Application can use it to set custom template.
|
||||
func (c *Command) SetVersionTemplate(s string) {
|
||||
c.versionTemplate = s
|
||||
}
|
||||
|
||||
// SetGlobalNormalizationFunc sets a normalization function to all flag sets and also to child commands.
|
||||
// The user should not have a cyclic dependency on commands.
|
||||
func (c *Command) SetGlobalNormalizationFunc(n func(f *flag.FlagSet, name string) flag.NormalizedName) {
|
||||
@@ -407,6 +428,19 @@ func (c *Command) HelpTemplate() string {
|
||||
{{end}}{{if or .Runnable .HasSubCommands}}{{.UsageString}}{{end}}`
|
||||
}
|
||||
|
||||
// VersionTemplate return version template for the command.
|
||||
func (c *Command) VersionTemplate() string {
|
||||
if c.versionTemplate != "" {
|
||||
return c.versionTemplate
|
||||
}
|
||||
|
||||
if c.HasParent() {
|
||||
return c.parent.VersionTemplate()
|
||||
}
|
||||
return `{{with .Name}}{{printf "%s " .}}{{end}}{{printf "version %s" .Version}}
|
||||
`
|
||||
}
|
||||
|
||||
func hasNoOptDefVal(name string, fs *flag.FlagSet) bool {
|
||||
flag := fs.Lookup(name)
|
||||
if flag == nil {
|
||||
@@ -441,6 +475,9 @@ Loop:
|
||||
s := args[0]
|
||||
args = args[1:]
|
||||
switch {
|
||||
case s == "--":
|
||||
// "--" terminates the flags
|
||||
break Loop
|
||||
case strings.HasPrefix(s, "--") && !strings.Contains(s, "=") && !hasNoOptDefVal(s[2:], flags):
|
||||
// If '--flag arg' then
|
||||
// delete arg from args.
|
||||
@@ -528,6 +565,7 @@ func (c *Command) findNext(next string) *Command {
|
||||
matches := make([]*Command, 0)
|
||||
for _, cmd := range c.commands {
|
||||
if cmd.Name() == next || cmd.HasAlias(next) {
|
||||
cmd.commandCalledAs.name = next
|
||||
return cmd
|
||||
}
|
||||
if EnablePrefixMatching && cmd.hasNameOrAliasPrefix(next) {
|
||||
@@ -538,6 +576,7 @@ func (c *Command) findNext(next string) *Command {
|
||||
if len(matches) == 1 {
|
||||
return matches[0]
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -621,10 +660,8 @@ func (c *Command) Root() *Command {
|
||||
return c
|
||||
}
|
||||
|
||||
// ArgsLenAtDash will return the length of f.Args at the moment when a -- was
|
||||
// found during arg parsing. This allows your program to know which args were
|
||||
// before the -- and which came after. (Description from
|
||||
// https://godoc.org/github.com/spf13/pflag#FlagSet.ArgsLenAtDash).
|
||||
// ArgsLenAtDash will return the length of c.Flags().Args at the moment
|
||||
// when a -- was found during args parsing.
|
||||
func (c *Command) ArgsLenAtDash() int {
|
||||
return c.Flags().ArgsLenAtDash()
|
||||
}
|
||||
@@ -638,9 +675,10 @@ func (c *Command) execute(a []string) (err error) {
|
||||
c.Printf("Command %q is deprecated, %s\n", c.Name(), c.Deprecated)
|
||||
}
|
||||
|
||||
// initialize help flag as the last point possible to allow for user
|
||||
// initialize help and version flag at the last point possible to allow for user
|
||||
// overriding
|
||||
c.InitDefaultHelpFlag()
|
||||
c.InitDefaultVersionFlag()
|
||||
|
||||
err = c.ParseFlags(a)
|
||||
if err != nil {
|
||||
@@ -657,7 +695,27 @@ func (c *Command) execute(a []string) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
if helpVal || !c.Runnable() {
|
||||
if helpVal {
|
||||
return flag.ErrHelp
|
||||
}
|
||||
|
||||
// for back-compat, only add version flag behavior if version is defined
|
||||
if c.Version != "" {
|
||||
versionVal, err := c.Flags().GetBool("version")
|
||||
if err != nil {
|
||||
c.Println("\"version\" flag declared as non-bool. Please correct your code")
|
||||
return err
|
||||
}
|
||||
if versionVal {
|
||||
err := tmpl(c.OutOrStdout(), c.VersionTemplate(), c)
|
||||
if err != nil {
|
||||
c.Println(err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if !c.Runnable() {
|
||||
return flag.ErrHelp
|
||||
}
|
||||
|
||||
@@ -780,6 +838,11 @@ func (c *Command) ExecuteC() (cmd *Command, err error) {
|
||||
return c, err
|
||||
}
|
||||
|
||||
cmd.commandCalledAs.called = true
|
||||
if cmd.commandCalledAs.name == "" {
|
||||
cmd.commandCalledAs.name = cmd.Name()
|
||||
}
|
||||
|
||||
err = cmd.execute(flags)
|
||||
if err != nil {
|
||||
// Always show help if requested, even if SilenceErrors is in
|
||||
@@ -825,7 +888,7 @@ func (c *Command) validateRequiredFlags() error {
|
||||
})
|
||||
|
||||
if len(missingFlagNames) > 0 {
|
||||
return fmt.Errorf(`Required flag(s) "%s" have/has not been set`, strings.Join(missingFlagNames, `", "`))
|
||||
return fmt.Errorf(`required flag(s) "%s" not set`, strings.Join(missingFlagNames, `", "`))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -846,6 +909,27 @@ func (c *Command) InitDefaultHelpFlag() {
|
||||
}
|
||||
}
|
||||
|
||||
// InitDefaultVersionFlag adds default version flag to c.
|
||||
// It is called automatically by executing the c.
|
||||
// If c already has a version flag, it will do nothing.
|
||||
// If c.Version is empty, it will do nothing.
|
||||
func (c *Command) InitDefaultVersionFlag() {
|
||||
if c.Version == "" {
|
||||
return
|
||||
}
|
||||
|
||||
c.mergePersistentFlags()
|
||||
if c.Flags().Lookup("version") == nil {
|
||||
usage := "version for "
|
||||
if c.Name() == "" {
|
||||
usage += "this command"
|
||||
} else {
|
||||
usage += c.Name()
|
||||
}
|
||||
c.Flags().Bool("version", false, usage)
|
||||
}
|
||||
}
|
||||
|
||||
// InitDefaultHelpCmd adds default help command to c.
|
||||
// It is called automatically by executing the c or by calling help and usage.
|
||||
// If c already has help command or c has no subcommands, it will do nothing.
|
||||
@@ -877,7 +961,7 @@ Simply type ` + c.Name() + ` help [path to command] for full details.`,
|
||||
c.AddCommand(c.helpCommand)
|
||||
}
|
||||
|
||||
// ResetCommands used for testing.
|
||||
// ResetCommands delete parent, subcommand and help command from c.
|
||||
func (c *Command) ResetCommands() {
|
||||
c.parent = nil
|
||||
c.commands = nil
|
||||
@@ -996,6 +1080,9 @@ func (c *Command) UseLine() string {
|
||||
} else {
|
||||
useline = c.Use
|
||||
}
|
||||
if c.DisableFlagsInUseLine {
|
||||
return useline
|
||||
}
|
||||
if c.HasAvailableFlags() && !strings.Contains(useline, "[flags]") {
|
||||
useline += " [flags]"
|
||||
}
|
||||
@@ -1063,14 +1150,25 @@ func (c *Command) HasAlias(s string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// CalledAs returns the command name or alias that was used to invoke
|
||||
// this command or an empty string if the command has not been called.
|
||||
func (c *Command) CalledAs() string {
|
||||
if c.commandCalledAs.called {
|
||||
return c.commandCalledAs.name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// hasNameOrAliasPrefix returns true if the Name or any of aliases start
|
||||
// with prefix
|
||||
func (c *Command) hasNameOrAliasPrefix(prefix string) bool {
|
||||
if strings.HasPrefix(c.Name(), prefix) {
|
||||
c.commandCalledAs.name = c.Name()
|
||||
return true
|
||||
}
|
||||
for _, alias := range c.Aliases {
|
||||
if strings.HasPrefix(alias, prefix) {
|
||||
c.commandCalledAs.name = alias
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -1163,7 +1261,7 @@ func (c *Command) HasAvailableSubCommands() bool {
|
||||
}
|
||||
}
|
||||
|
||||
// the command either has no sub comamnds, or no available (non deprecated/help/hidden)
|
||||
// the command either has no sub commands, or no available (non deprecated/help/hidden)
|
||||
// sub commands
|
||||
return false
|
||||
}
|
||||
@@ -1173,7 +1271,7 @@ func (c *Command) HasParent() bool {
|
||||
return c.parent != nil
|
||||
}
|
||||
|
||||
// GlobalNormalizationFunc returns the global normalization function or nil if doesn't exists.
|
||||
// GlobalNormalizationFunc returns the global normalization function or nil if it doesn't exist.
|
||||
func (c *Command) GlobalNormalizationFunc() func(f *flag.FlagSet, name string) flag.NormalizedName {
|
||||
return c.globNormFunc
|
||||
}
|
||||
@@ -1273,7 +1371,7 @@ func (c *Command) PersistentFlags() *flag.FlagSet {
|
||||
return c.pflags
|
||||
}
|
||||
|
||||
// ResetFlags is used in testing.
|
||||
// ResetFlags deletes all flags from command.
|
||||
func (c *Command) ResetFlags() {
|
||||
c.flagErrorBuf = new(bytes.Buffer)
|
||||
c.flagErrorBuf.Reset()
|
||||
|
||||
Reference in New Issue
Block a user