diff --git a/bake/bake.go b/bake/bake.go index 5cb6c7564dfc..df9c559e6ffd 100644 --- a/bake/bake.go +++ b/bake/bake.go @@ -4,6 +4,7 @@ import ( "context" "encoding" "io" + "maps" "os" "path" "path/filepath" @@ -1104,9 +1105,7 @@ func (t *Target) GetEvalContexts(ectx *hcl.EvalContext, block *hcl.Block, loadDe e2 := ectx.NewChild() e2.Variables = make(map[string]cty.Value) if e != ectx { - for k, v := range e.Variables { - e2.Variables[k] = v - } + maps.Copy(e2.Variables, e.Variables) } e2.Variables[k] = v ectxs2 = append(ectxs2, e2) diff --git a/bake/compose.go b/bake/compose.go index 87cd5fa3c1e2..ccf8098cdc8e 100644 --- a/bake/compose.go +++ b/bake/compose.go @@ -3,6 +3,7 @@ package bake import ( "context" "fmt" + "maps" "os" "path/filepath" "slices" @@ -91,9 +92,7 @@ func ParseCompose(cfgs []composetypes.ConfigFile, envs map[string]string) (*Conf var additionalContexts map[string]string if s.Build.AdditionalContexts != nil { additionalContexts = map[string]string{} - for k, v := range s.Build.AdditionalContexts { - additionalContexts[k] = v - } + maps.Copy(additionalContexts, s.Build.AdditionalContexts) } var shmSize *string @@ -214,7 +213,7 @@ func validateComposeFile(dt []byte, fn string) (bool, error) { } func validateCompose(dt []byte, envs map[string]string) error { - _, err := loader.Load(composetypes.ConfigDetails{ + _, err := loader.LoadWithContext(context.Background(), composetypes.ConfigDetails{ ConfigFiles: []composetypes.ConfigFile{ { Content: dt, diff --git a/build/build.go b/build/build.go index 96f850694821..95d658b3c6fb 100644 --- a/build/build.go +++ b/build/build.go @@ -8,6 +8,7 @@ import ( "encoding/json" "fmt" "io" + "maps" "os" "slices" "strconv" @@ -431,9 +432,7 @@ func BuildWithResultHandler(ctx context.Context, nodes []builder.Node, opts map[ FrontendInputs: frontendInputs, FrontendOpt: make(map[string]string), } - for k, v := range so.FrontendAttrs { - req.FrontendOpt[k] = v - } + maps.Copy(req.FrontendOpt, so.FrontendAttrs) so.Frontend = "" so.FrontendInputs = nil diff --git a/build/git.go b/build/git.go index 0b7aba10407d..4672860ce133 100644 --- a/build/git.go +++ b/build/git.go @@ -2,6 +2,7 @@ package build import ( "context" + "maps" "os" "path" "path/filepath" @@ -127,9 +128,7 @@ func getGitAttributes(ctx context.Context, contextPath, dockerfilePath string) ( if so.FrontendAttrs == nil { so.FrontendAttrs = make(map[string]string) } - for k, v := range res { - so.FrontendAttrs[k] = v - } + maps.Copy(so.FrontendAttrs, res) if !setGitInfo || root == "" { return diff --git a/build/provenance.go b/build/provenance.go index 5fd8d08c3704..b6cca56f495c 100644 --- a/build/provenance.go +++ b/build/provenance.go @@ -5,6 +5,7 @@ import ( "encoding/base64" "encoding/json" "io" + "maps" "strings" "sync" @@ -40,9 +41,7 @@ func setRecordProvenance(ctx context.Context, c *client.Client, sr *client.Solve if err != nil { return err } - for k, v := range res { - sr.ExporterResponse[k] = v - } + maps.Copy(sr.ExporterResponse, res) return nil }) } diff --git a/commands/ls.go b/commands/ls.go index ffd4c10d42a9..8c247679d05d 100644 --- a/commands/ls.go +++ b/commands/ls.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "fmt" + "maps" "sort" "strings" "time" @@ -409,9 +410,7 @@ func truncPlatforms(pfs []string, max int) truncatedPlatforms { left[ppf] = append(left[ppf], pf) } } - for k, v := range left { - res[k] = v - } + maps.Copy(res, left) return truncatedPlatforms{ res: res, input: pfs, diff --git a/controller/pb/cache.go b/controller/pb/cache.go index 4b7c2b7546e8..f363a41902a4 100644 --- a/controller/pb/cache.go +++ b/controller/pb/cache.go @@ -1,6 +1,10 @@ package pb -import "github.com/moby/buildkit/client" +import ( + "maps" + + "github.com/moby/buildkit/client" +) func CreateCaches(entries []*CacheOptionsEntry) []client.CacheOptionsEntry { var outs []client.CacheOptionsEntry @@ -12,9 +16,7 @@ func CreateCaches(entries []*CacheOptionsEntry) []client.CacheOptionsEntry { Type: entry.Type, Attrs: map[string]string{}, } - for k, v := range entry.Attrs { - out.Attrs[k] = v - } + maps.Copy(out.Attrs, entry.Attrs) outs = append(outs, out) } return outs diff --git a/controller/pb/export.go b/controller/pb/export.go index 8df341b30346..c7eef8c9fa9e 100644 --- a/controller/pb/export.go +++ b/controller/pb/export.go @@ -2,6 +2,7 @@ package pb import ( "io" + "maps" "os" "strconv" @@ -26,9 +27,7 @@ func CreateExports(entries []*ExportEntry) ([]client.ExportEntry, []string, erro Type: entry.Type, Attrs: map[string]string{}, } - for k, v := range entry.Attrs { - out.Attrs[k] = v - } + maps.Copy(out.Attrs, entry.Attrs) supportFile := false supportDir := false diff --git a/go.mod b/go.mod index c09539430e3d..3eb47868276a 100644 --- a/go.mod +++ b/go.mod @@ -1,12 +1,12 @@ module github.com/docker/buildx -go 1.22.0 +go 1.23.0 require ( github.com/Masterminds/semver/v3 v3.2.1 github.com/Microsoft/go-winio v0.6.2 github.com/aws/aws-sdk-go-v2/config v1.27.27 - github.com/compose-spec/compose-go/v2 v2.4.8 + github.com/compose-spec/compose-go/v2 v2.4.9 github.com/containerd/console v1.0.4 github.com/containerd/containerd/v2 v2.0.3 github.com/containerd/continuity v0.4.5 @@ -159,6 +159,7 @@ require ( github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonschema v1.2.0 // indirect + github.com/xhit/go-str2duration/v2 v2.1.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.56.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.56.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 // indirect diff --git a/go.sum b/go.sum index 20556210d884..19b4dcd1ddee 100644 --- a/go.sum +++ b/go.sum @@ -77,8 +77,8 @@ github.com/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004 h1:lkAMpLVBDaj17e github.com/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004/go.mod h1:yMWuSON2oQp+43nFtAV/uvKQIFpSPerB57DCt9t8sSA= github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb h1:EDmT6Q9Zs+SbUoc7Ik9EfrFqcylYqgPZ9ANSbTAntnE= github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb/go.mod h1:ZjrT6AXHbDs86ZSdt/osfBi5qfexBrKUdONk989Wnk4= -github.com/compose-spec/compose-go/v2 v2.4.8 h1:7Myl8wDRl/4mRz77S+eyDJymGGEHu0diQdGSSeyq90A= -github.com/compose-spec/compose-go/v2 v2.4.8/go.mod h1:lFN0DrMxIncJGYAXTfWuajfwj5haBJqrBkarHcnjJKc= +github.com/compose-spec/compose-go/v2 v2.4.9 h1:2K4TDw+1ba2idiR6empXHKRXvWYpnvAKoNQy93/sSOs= +github.com/compose-spec/compose-go/v2 v2.4.9/go.mod h1:6k5l/0TxCg0/2uLEhRVEsoBWBprS2uvZi32J7xub3lo= github.com/containerd/cgroups/v3 v3.0.5 h1:44na7Ud+VwyE7LIoJ8JTNQOa549a8543BmzaJHo6Bzo= github.com/containerd/cgroups/v3 v3.0.5/go.mod h1:SA5DLYnXO8pTGYiAHXz94qvLQTKfVM5GEVisn4jpins= github.com/containerd/console v1.0.4 h1:F2g4+oChYvBTsASRTz8NP6iIAi97J3TtSAsLbIFn4ro= @@ -459,6 +459,8 @@ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHo github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= +github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc= +github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/zclconf/go-cty v1.4.0/go.mod h1:nHzOclRkoj++EU9ZjSrZvRG0BXIWt8c7loYc0qXAFGQ= diff --git a/store/nodegroup.go b/store/nodegroup.go index 06ca070ee9ae..9d986d17b78a 100644 --- a/store/nodegroup.go +++ b/store/nodegroup.go @@ -2,6 +2,7 @@ package store import ( "fmt" + "maps" "slices" "time" @@ -93,9 +94,7 @@ func (ng *NodeGroup) Update(name, endpoint string, platforms []string, endpoints needsRestart = true } if buildkitdConfigFile != "" { - for k, v := range files { - n.Files[k] = v - } + maps.Copy(n.Files, files) needsRestart = true } if needsRestart { @@ -147,9 +146,7 @@ func (n *Node) Copy() *Node { buildkitdFlags := []string{} copy(buildkitdFlags, n.BuildkitdFlags) driverOpts := map[string]string{} - for k, v := range n.DriverOpts { - driverOpts[k] = v - } + maps.Copy(driverOpts, n.DriverOpts) files := map[string][]byte{} for k, v := range n.Files { vv := []byte{} diff --git a/util/imagetools/create.go b/util/imagetools/create.go index 6fdcac96d125..9b1ca6b48f53 100644 --- a/util/imagetools/create.go +++ b/util/imagetools/create.go @@ -107,9 +107,7 @@ func (r *Resolver) Combine(ctx context.Context, srcs []*Source, ann map[exptypes if old.Annotations == nil { old.Annotations = map[string]string{} } - for k, v := range d.Annotations { - old.Annotations[k] = v - } + maps.Copy(old.Annotations, d.Annotations) newDescs[idx] = old } else { m[d.Digest] = len(newDescs) diff --git a/util/imagetools/loader.go b/util/imagetools/loader.go index 457e684e5743..eaba4d11e0f2 100644 --- a/util/imagetools/loader.go +++ b/util/imagetools/loader.go @@ -6,6 +6,7 @@ import ( "context" "encoding/base64" "encoding/json" + "maps" "regexp" "sort" "strings" @@ -126,13 +127,9 @@ func (l *loader) Load(ctx context.Context, ref string) (*result, error) { } var a asset - annotations := make(map[string]string, len(mfst.manifest.Annotations)+len(mfst.desc.Annotations)) - for k, v := range mfst.desc.Annotations { - annotations[k] = v - } - for k, v := range mfst.manifest.Annotations { - annotations[k] = v - } + annotations := map[string]string{} + maps.Copy(annotations, mfst.desc.Annotations) + maps.Copy(annotations, mfst.manifest.Annotations) if err := l.scanConfig(ctx, fetcher, mfst.manifest.Config, &a); err != nil { return nil, err diff --git a/vendor/github.com/compose-spec/compose-go/v2/cli/options.go b/vendor/github.com/compose-spec/compose-go/v2/cli/options.go index 162e1ea74609..091a2aae9db0 100644 --- a/vendor/github.com/compose-spec/compose-go/v2/cli/options.go +++ b/vendor/github.com/compose-spec/compose-go/v2/cli/options.go @@ -18,7 +18,6 @@ package cli import ( "context" - "fmt" "io" "os" "path/filepath" @@ -30,7 +29,6 @@ import ( "github.com/compose-spec/compose-go/v2/consts" "github.com/compose-spec/compose-go/v2/dotenv" - "github.com/compose-spec/compose-go/v2/errdefs" "github.com/compose-spec/compose-go/v2/loader" "github.com/compose-spec/compose-go/v2/types" "github.com/compose-spec/compose-go/v2/utils" @@ -551,14 +549,6 @@ func withListeners(options *ProjectOptions) func(*loader.Options) { } } -// getConfigPaths retrieves the config files for project based on project options -func (o *ProjectOptions) getConfigPaths() ([]string, error) { - if len(o.ConfigPaths) != 0 { - return absolutePaths(o.ConfigPaths) - } - return nil, fmt.Errorf("no configuration file provided: %w", errdefs.ErrNotFound) -} - func findFiles(names []string, pwd string) []string { candidates := []string{} for _, n := range names { diff --git a/vendor/github.com/compose-spec/compose-go/v2/dotenv/format.go b/vendor/github.com/compose-spec/compose-go/v2/dotenv/format.go index c583d2126e02..7b744bc09241 100644 --- a/vendor/github.com/compose-spec/compose-go/v2/dotenv/format.go +++ b/vendor/github.com/compose-spec/compose-go/v2/dotenv/format.go @@ -21,7 +21,17 @@ import ( "io" ) -var formats = map[string]Parser{} +const DotEnv = ".env" + +var formats = map[string]Parser{ + DotEnv: func(r io.Reader, filename string, lookup func(key string) (string, bool)) (map[string]string, error) { + m, err := ParseWithLookup(r, lookup) + if err != nil { + return nil, fmt.Errorf("failed to read %s: %w", filename, err) + } + return m, nil + }, +} type Parser func(r io.Reader, filename string, lookup func(key string) (string, bool)) (map[string]string, error) @@ -30,9 +40,12 @@ func RegisterFormat(format string, p Parser) { } func ParseWithFormat(r io.Reader, filename string, resolve LookupFn, format string) (map[string]string, error) { - parser, ok := formats[format] + if format == "" { + format = DotEnv + } + fn, ok := formats[format] if !ok { return nil, fmt.Errorf("unsupported env_file format %q", format) } - return parser(r, filename, resolve) + return fn(r, filename, resolve) } diff --git a/vendor/github.com/compose-spec/compose-go/v2/dotenv/godotenv.go b/vendor/github.com/compose-spec/compose-go/v2/dotenv/godotenv.go index 76907249ba5f..215e86f780c6 100644 --- a/vendor/github.com/compose-spec/compose-go/v2/dotenv/godotenv.go +++ b/vendor/github.com/compose-spec/compose-go/v2/dotenv/godotenv.go @@ -30,7 +30,7 @@ var startsWithDigitRegex = regexp.MustCompile(`^\s*\d.*`) // Keys starting with // LookupFn represents a lookup function to resolve variables from type LookupFn func(string) (string, bool) -var noLookupFn = func(s string) (string, bool) { +var noLookupFn = func(_ string) (string, bool) { return "", false } diff --git a/vendor/github.com/compose-spec/compose-go/v2/dotenv/parser.go b/vendor/github.com/compose-spec/compose-go/v2/dotenv/parser.go index 85dda7385263..2db7b9072485 100644 --- a/vendor/github.com/compose-spec/compose-go/v2/dotenv/parser.go +++ b/vendor/github.com/compose-spec/compose-go/v2/dotenv/parser.go @@ -115,7 +115,7 @@ loop: switch rune { case '=', ':', '\n': // library also supports yaml-style value declaration - key = string(src[0:i]) + key = src[0:i] offset = i + 1 inherited = rune == '\n' break loop @@ -157,7 +157,7 @@ func (p *parser) extractVarValue(src string, envMap map[string]string, lookupFn // Remove inline comments on unquoted lines value, _, _ = strings.Cut(value, " #") value = strings.TrimRightFunc(value, unicode.IsSpace) - retVal, err := expandVariables(string(value), envMap, lookupFn) + retVal, err := expandVariables(value, envMap, lookupFn) return retVal, rest, err } diff --git a/vendor/github.com/compose-spec/compose-go/v2/graph/traversal.go b/vendor/github.com/compose-spec/compose-go/v2/graph/traversal.go index de85d1fceca1..f0ee6c09e507 100644 --- a/vendor/github.com/compose-spec/compose-go/v2/graph/traversal.go +++ b/vendor/github.com/compose-spec/compose-go/v2/graph/traversal.go @@ -63,9 +63,9 @@ func newTraversal[S, T any](fn CollectorFn[S, T]) *traversal[S, T] { } // WithMaxConcurrency configure traversal to limit concurrency walking graph nodes -func WithMaxConcurrency(max int) func(*Options) { +func WithMaxConcurrency(concurrency int) func(*Options) { return func(o *Options) { - o.maxConcurrency = max + o.maxConcurrency = concurrency } } diff --git a/vendor/github.com/compose-spec/compose-go/v2/loader/extends.go b/vendor/github.com/compose-spec/compose-go/v2/loader/extends.go index 4a04654a13e5..f3c8f96faa43 100644 --- a/vendor/github.com/compose-spec/compose-go/v2/loader/extends.go +++ b/vendor/github.com/compose-spec/compose-go/v2/loader/extends.go @@ -113,11 +113,14 @@ func applyServiceExtends(ctx context.Context, name string, services map[string]a source := deepClone(base).(map[string]any) for _, processor := range post { - processor.Apply(map[string]any{ + err = processor.Apply(map[string]any{ "services": map[string]any{ name: source, }, }) + if err != nil { + return nil, err + } } merged, err := override.ExtendService(source, service) if err != nil { diff --git a/vendor/github.com/compose-spec/compose-go/v2/loader/interpolate.go b/vendor/github.com/compose-spec/compose-go/v2/loader/interpolate.go index 481c66b5b110..491de5bdca2d 100644 --- a/vendor/github.com/compose-spec/compose-go/v2/loader/interpolate.go +++ b/vendor/github.com/compose-spec/compose-go/v2/loader/interpolate.go @@ -27,7 +27,6 @@ import ( ) var interpolateTypeCastMapping = map[tree.Path]interp.Cast{ - servicePath("configs", tree.PathMatchList, "mode"): toInt, servicePath("cpu_count"): toInt64, servicePath("cpu_percent"): toFloat, servicePath("cpu_period"): toInt64, @@ -53,7 +52,6 @@ var interpolateTypeCastMapping = map[tree.Path]interp.Cast{ servicePath("privileged"): toBoolean, servicePath("read_only"): toBoolean, servicePath("scale"): toInt, - servicePath("secrets", tree.PathMatchList, "mode"): toInt, servicePath("stdin_open"): toBoolean, servicePath("tty"): toBoolean, servicePath("ulimits", tree.PathMatchAll): toInt, diff --git a/vendor/github.com/compose-spec/compose-go/v2/loader/loader.go b/vendor/github.com/compose-spec/compose-go/v2/loader/loader.go index 612b91caff45..d413cba06dcc 100644 --- a/vendor/github.com/compose-spec/compose-go/v2/loader/loader.go +++ b/vendor/github.com/compose-spec/compose-go/v2/loader/loader.go @@ -257,15 +257,6 @@ func WithProfiles(profiles []string) func(*Options) { } } -// ParseYAML reads the bytes from a file, parses the bytes into a mapping -// structure, and returns it. -func ParseYAML(source []byte) (map[string]interface{}, error) { - r := bytes.NewReader(source) - decoder := yaml.NewDecoder(r) - m, _, err := parseYAML(decoder) - return m, err -} - // PostProcessor is used to tweak compose model based on metadata extracted during yaml Unmarshal phase // that hardly can be implemented using go-yaml and mapstructure type PostProcessor interface { @@ -275,32 +266,6 @@ type PostProcessor interface { Apply(interface{}) error } -func parseYAML(decoder *yaml.Decoder) (map[string]interface{}, PostProcessor, error) { - var cfg interface{} - processor := ResetProcessor{target: &cfg} - - if err := decoder.Decode(&processor); err != nil { - return nil, nil, err - } - stringMap, ok := cfg.(map[string]interface{}) - if ok { - converted, err := convertToStringKeysRecursive(stringMap, "") - if err != nil { - return nil, nil, err - } - return converted.(map[string]interface{}), &processor, nil - } - cfgMap, ok := cfg.(map[interface{}]interface{}) - if !ok { - return nil, nil, errors.New("Top-level object must be a mapping") - } - converted, err := convertToStringKeysRecursive(cfgMap, "") - if err != nil { - return nil, nil, err - } - return converted.(map[string]interface{}), &processor, nil -} - // LoadConfigFiles ingests config files with ResourceLoader and returns config details with paths to local copies func LoadConfigFiles(ctx context.Context, configFiles []string, workingDir string, options ...func(*Options)) (*types.ConfigDetails, error) { if len(configFiles) < 1 { @@ -353,12 +318,6 @@ func LoadConfigFiles(ctx context.Context, configFiles []string, workingDir strin return config, nil } -// Load reads a ConfigDetails and returns a fully loaded configuration. -// Deprecated: use LoadWithContext. -func Load(configDetails types.ConfigDetails, options ...func(*Options)) (*types.Project, error) { - return LoadWithContext(context.Background(), configDetails, options...) -} - // LoadWithContext reads a ConfigDetails and returns a fully loaded configuration as a compose-go Project func LoadWithContext(ctx context.Context, configDetails types.ConfigDetails, options ...func(*Options)) (*types.Project, error) { opts := toOptions(&configDetails, options) @@ -448,7 +407,15 @@ func loadYamlModel(ctx context.Context, config types.ConfigDetails, opts *Option return dict, nil } -func loadYamlFile(ctx context.Context, file types.ConfigFile, opts *Options, workingDir string, environment types.Mapping, ct *cycleTracker, dict map[string]interface{}, included []string) (map[string]interface{}, PostProcessor, error) { +func loadYamlFile(ctx context.Context, + file types.ConfigFile, + opts *Options, + workingDir string, + environment types.Mapping, + ct *cycleTracker, + dict map[string]interface{}, + included []string, +) (map[string]interface{}, PostProcessor, error) { ctx = context.WithValue(ctx, consts.ComposeFileKey{}, file.Filename) if file.Content == nil && file.Config == nil { content, err := os.ReadFile(file.Filename) @@ -565,7 +532,6 @@ func load(ctx context.Context, configDetails types.ConfigDetails, opts *Options, return nil, fmt.Errorf("include cycle detected:\n%s\n include %s", loaded[0], strings.Join(loaded[1:], "\n include ")) } } - loaded = append(loaded, mainFile) dict, err := loadYamlModel(ctx, configDetails, opts, &cycleTracker{}, nil) if err != nil { @@ -576,7 +542,7 @@ func load(ctx context.Context, configDetails types.ConfigDetails, opts *Options, return nil, errors.New("empty compose file") } - if opts.projectName == "" { + if !opts.SkipValidation && opts.projectName == "" { return nil, errors.New("project name must not be empty") } diff --git a/vendor/github.com/compose-spec/compose-go/v2/loader/omitEmpty.go b/vendor/github.com/compose-spec/compose-go/v2/loader/omitEmpty.go index bc1cb1a55a49..eef6be8c56ce 100644 --- a/vendor/github.com/compose-spec/compose-go/v2/loader/omitEmpty.go +++ b/vendor/github.com/compose-spec/compose-go/v2/loader/omitEmpty.go @@ -19,7 +19,8 @@ package loader import "github.com/compose-spec/compose-go/v2/tree" var omitempty = []tree.Path{ - "services.*.dns"} + "services.*.dns", +} // OmitEmpty removes empty attributes which are irrelevant when unset func OmitEmpty(yaml map[string]any) map[string]any { diff --git a/vendor/github.com/compose-spec/compose-go/v2/loader/paths.go b/vendor/github.com/compose-spec/compose-go/v2/loader/paths.go index 102ff036e066..c03126a83a7d 100644 --- a/vendor/github.com/compose-spec/compose-go/v2/loader/paths.go +++ b/vendor/github.com/compose-spec/compose-go/v2/loader/paths.go @@ -17,9 +17,7 @@ package loader import ( - "os" "path/filepath" - "strings" "github.com/compose-spec/compose-go/v2/types" ) @@ -40,17 +38,6 @@ func ResolveRelativePaths(project *types.Project) error { return nil } -func absPath(workingDir string, filePath string) string { - if strings.HasPrefix(filePath, "~") { - home, _ := os.UserHomeDir() - return filepath.Join(home, filePath[1:]) - } - if filepath.IsAbs(filePath) { - return filePath - } - return filepath.Join(workingDir, filePath) -} - func absComposeFiles(composeFiles []string) ([]string, error) { for i, composeFile := range composeFiles { absComposefile, err := filepath.Abs(composeFile) @@ -61,14 +48,3 @@ func absComposeFiles(composeFiles []string) ([]string, error) { } return composeFiles, nil } - -func resolvePaths(basePath string, in types.StringList) types.StringList { - if in == nil { - return nil - } - ret := make(types.StringList, len(in)) - for i := range in { - ret[i] = absPath(basePath, in[i]) - } - return ret -} diff --git a/vendor/github.com/compose-spec/compose-go/v2/loader/validate.go b/vendor/github.com/compose-spec/compose-go/v2/loader/validate.go index 0feb2a967f38..aa570888ccea 100644 --- a/vendor/github.com/compose-spec/compose-go/v2/loader/validate.go +++ b/vendor/github.com/compose-spec/compose-go/v2/loader/validate.go @@ -27,7 +27,7 @@ import ( ) // checkConsistency validate a compose model is consistent -func checkConsistency(project *types.Project) error { +func checkConsistency(project *types.Project) error { //nolint:gocyclo for name, s := range project.Services { if s.Build == nil && s.Image == "" { return fmt.Errorf("service %q has neither an image nor a build context specified: %w", s.Name, errdefs.ErrInvalid) @@ -171,7 +171,6 @@ func checkConsistency(project *types.Project) error { return fmt.Errorf("services.%s.develop.watch: target is required for non-rebuild actions: %w", s.Name, errdefs.ErrInvalid) } } - } } diff --git a/vendor/github.com/compose-spec/compose-go/v2/paths/windows_path.go b/vendor/github.com/compose-spec/compose-go/v2/paths/windows_path.go index 746aefd15e35..968d8ed7bb91 100644 --- a/vendor/github.com/compose-spec/compose-go/v2/paths/windows_path.go +++ b/vendor/github.com/compose-spec/compose-go/v2/paths/windows_path.go @@ -44,7 +44,6 @@ func isWindowsAbs(path string) (b bool) { // volumeNameLen returns length of the leading volume name on Windows. // It returns 0 elsewhere. -// nolint: gocyclo func volumeNameLen(path string) int { if len(path) < 2 { return 0 diff --git a/vendor/github.com/compose-spec/compose-go/v2/schema/compose-spec.json b/vendor/github.com/compose-spec/compose-go/v2/schema/compose-spec.json index 1da7f228b0ad..4b6df9a8ac19 100644 --- a/vendor/github.com/compose-spec/compose-go/v2/schema/compose-spec.json +++ b/vendor/github.com/compose-spec/compose-go/v2/schema/compose-spec.json @@ -370,9 +370,10 @@ "pre_stop": {"type": "array", "items": {"$ref": "#/definitions/service_hook"}}, "privileged": {"type": ["boolean", "string"]}, "profiles": {"$ref": "#/definitions/list_of_strings"}, - "pull_policy": {"type": "string", "enum": [ - "always", "never", "if_not_present", "build", "missing" - ]}, + "pull_policy": {"type": "string", + "pattern": "always|never|build|if_not_present|missing|refresh|daily|weekly|every_([0-9]+[wdhms])+" + }, + "pull_refresh_after": {"type": "string"}, "read_only": {"type": ["boolean", "string"]}, "restart": {"type": "string"}, "runtime": { @@ -490,7 +491,8 @@ "type": "object", "required": ["path", "action"], "properties": { - "ignore": {"type": "array", "items": {"type": "string"}}, + "ignore": {"$ref": "#/definitions/string_or_list"}, + "include": {"$ref": "#/definitions/string_or_list"}, "path": {"type": "string"}, "action": {"type": "string", "enum": ["rebuild", "sync", "restart", "sync+restart", "sync+exec"]}, "target": {"type": "string"}, @@ -837,7 +839,8 @@ "environment": {"$ref": "#/definitions/list_or_dict"} }, "additionalProperties": false, - "patternProperties": {"^x-": {}} + "patternProperties": {"^x-": {}}, + "required": ["command"] }, "env_file": { diff --git a/vendor/github.com/compose-spec/compose-go/v2/template/template.go b/vendor/github.com/compose-spec/compose-go/v2/template/template.go index d9483cbdbcea..2d48188dd759 100644 --- a/vendor/github.com/compose-spec/compose-go/v2/template/template.go +++ b/vendor/github.com/compose-spec/compose-go/v2/template/template.go @@ -26,25 +26,28 @@ import ( "github.com/sirupsen/logrus" ) -var delimiter = "\\$" -var substitutionNamed = "[_a-z][_a-z0-9]*" -var substitutionBraced = "[_a-z][_a-z0-9]*(?::?[-+?](.*))?" - -var groupEscaped = "escaped" -var groupNamed = "named" -var groupBraced = "braced" -var groupInvalid = "invalid" - -var patternString = fmt.Sprintf( - "%s(?i:(?P<%s>%s)|(?P<%s>%s)|{(?:(?P<%s>%s)}|(?P<%s>)))", - delimiter, - groupEscaped, delimiter, - groupNamed, substitutionNamed, - groupBraced, substitutionBraced, - groupInvalid, +const ( + delimiter = "\\$" + substitutionNamed = "[_a-z][_a-z0-9]*" + substitutionBraced = "[_a-z][_a-z0-9]*(?::?[-+?](.*))?" + groupEscaped = "escaped" + groupNamed = "named" + groupBraced = "braced" + groupInvalid = "invalid" ) -var DefaultPattern = regexp.MustCompile(patternString) +var ( + patternString = fmt.Sprintf( + "%s(?i:(?P<%s>%s)|(?P<%s>%s)|{(?:(?P<%s>%s)}|(?P<%s>)))", + delimiter, + groupEscaped, delimiter, + groupNamed, substitutionNamed, + groupBraced, substitutionBraced, + groupInvalid, + ) + + DefaultPattern = regexp.MustCompile(patternString) +) // InvalidTemplateError is returned when a variable template is not in a valid // format diff --git a/vendor/github.com/compose-spec/compose-go/v2/transform/canonical.go b/vendor/github.com/compose-spec/compose-go/v2/transform/canonical.go index d37eb1e29246..3f65d117be0a 100644 --- a/vendor/github.com/compose-spec/compose-go/v2/transform/canonical.go +++ b/vendor/github.com/compose-spec/compose-go/v2/transform/canonical.go @@ -44,6 +44,8 @@ func init() { transformers["services.*.build.ssh"] = transformSSH transformers["services.*.ulimits.*"] = transformUlimits transformers["services.*.build.ulimits.*"] = transformUlimits + transformers["services.*.develop.watch.*.ignore"] = transformStringOrList + transformers["services.*.develop.watch.*.include"] = transformStringOrList transformers["volumes.*"] = transformMaybeExternal transformers["networks.*"] = transformMaybeExternal transformers["secrets.*"] = transformMaybeExternal diff --git a/vendor/github.com/compose-spec/compose-go/v2/transform/devices.go b/vendor/github.com/compose-spec/compose-go/v2/transform/devices.go index 3ce7fa0045be..5de0613c748b 100644 --- a/vendor/github.com/compose-spec/compose-go/v2/transform/devices.go +++ b/vendor/github.com/compose-spec/compose-go/v2/transform/devices.go @@ -28,8 +28,8 @@ func deviceRequestDefaults(data any, p tree.Path, _ bool) (any, error) { return data, fmt.Errorf("%s: invalid type %T for device request", p, v) } _, hasCount := v["count"] - _, hasIds := v["device_ids"] - if !hasCount && !hasIds { + _, hasIDs := v["device_ids"] + if !hasCount && !hasIDs { v["count"] = "all" } return v, nil diff --git a/vendor/github.com/compose-spec/compose-go/v2/types/config.go b/vendor/github.com/compose-spec/compose-go/v2/types/config.go index d73d2b9fa76c..4c0d00a8d434 100644 --- a/vendor/github.com/compose-spec/compose-go/v2/types/config.go +++ b/vendor/github.com/compose-spec/compose-go/v2/types/config.go @@ -24,10 +24,8 @@ import ( "github.com/go-viper/mapstructure/v2" ) -var ( - // isCaseInsensitiveEnvVars is true on platforms where environment variable names are treated case-insensitively. - isCaseInsensitiveEnvVars = (runtime.GOOS == "windows") -) +// isCaseInsensitiveEnvVars is true on platforms where environment variable names are treated case-insensitively. +var isCaseInsensitiveEnvVars = (runtime.GOOS == "windows") // ConfigDetails are the details about a group of ConfigFiles type ConfigDetails struct { diff --git a/vendor/github.com/compose-spec/compose-go/v2/types/derived.gen.go b/vendor/github.com/compose-spec/compose-go/v2/types/derived.gen.go index 445d1cd3bccc..fd8e059e1be6 100644 --- a/vendor/github.com/compose-spec/compose-go/v2/types/derived.gen.go +++ b/vendor/github.com/compose-spec/compose-go/v2/types/derived.gen.go @@ -1605,7 +1605,7 @@ func deriveDeepCopy_31(dst, src *ServiceConfigObjConfig) { if src.Mode == nil { dst.Mode = nil } else { - dst.Mode = new(uint32) + dst.Mode = new(FileMode) *dst.Mode = *src.Mode } if src.Extensions != nil { @@ -1812,6 +1812,7 @@ func deriveDeepCopy_38(dst, src *DeviceRequest) { // deriveDeepCopy_39 recursively copies the contents of src into dst. func deriveDeepCopy_39(dst, src *ServiceNetworkConfig) { dst.Priority = src.Priority + dst.GatewayPriority = src.GatewayPriority if src.Aliases == nil { dst.Aliases = nil } else { @@ -1891,7 +1892,7 @@ func deriveDeepCopy_41(dst, src *ServiceSecretConfig) { if src.Mode == nil { dst.Mode = nil } else { - dst.Mode = new(uint32) + dst.Mode = new(FileMode) *dst.Mode = *src.Mode } if src.Extensions != nil { @@ -2024,6 +2025,24 @@ func deriveDeepCopy_46(dst, src *Trigger) { deriveDeepCopy_44(field, &src.Exec) dst.Exec = *field }() + if src.Include == nil { + dst.Include = nil + } else { + if dst.Include != nil { + if len(src.Include) > len(dst.Include) { + if cap(dst.Include) >= len(src.Include) { + dst.Include = (dst.Include)[:len(src.Include)] + } else { + dst.Include = make([]string, len(src.Include)) + } + } else if len(src.Include) < len(dst.Include) { + dst.Include = (dst.Include)[:len(src.Include)] + } + } else { + dst.Include = make([]string, len(src.Include)) + } + copy(dst.Include, src.Include) + } if src.Ignore == nil { dst.Ignore = nil } else { diff --git a/vendor/github.com/compose-spec/compose-go/v2/types/develop.go b/vendor/github.com/compose-spec/compose-go/v2/types/develop.go index 8f7c8fa553bb..fa306152e091 100644 --- a/vendor/github.com/compose-spec/compose-go/v2/types/develop.go +++ b/vendor/github.com/compose-spec/compose-go/v2/types/develop.go @@ -37,6 +37,7 @@ type Trigger struct { Action WatchAction `yaml:"action" json:"action"` Target string `yaml:"target,omitempty" json:"target,omitempty"` Exec ServiceHook `yaml:"exec,omitempty" json:"exec,omitempty"` + Include []string `yaml:"include,omitempty" json:"include,omitempty"` Ignore []string `yaml:"ignore,omitempty" json:"ignore,omitempty"` Extensions Extensions `yaml:"#extensions,inline,omitempty" json:"-"` } diff --git a/vendor/github.com/compose-spec/compose-go/v2/types/duration.go b/vendor/github.com/compose-spec/compose-go/v2/types/duration.go index 95f562a7cf2b..c1c39730dd73 100644 --- a/vendor/github.com/compose-spec/compose-go/v2/types/duration.go +++ b/vendor/github.com/compose-spec/compose-go/v2/types/duration.go @@ -21,6 +21,8 @@ import ( "fmt" "strings" "time" + + "github.com/xhit/go-str2duration/v2" ) // Duration is a thin wrapper around time.Duration with improved JSON marshalling @@ -31,7 +33,7 @@ func (d Duration) String() string { } func (d *Duration) DecodeMapstructure(value interface{}) error { - v, err := time.ParseDuration(fmt.Sprint(value)) + v, err := str2duration.ParseDuration(fmt.Sprint(value)) if err != nil { return err } diff --git a/vendor/github.com/compose-spec/compose-go/v2/types/labels.go b/vendor/github.com/compose-spec/compose-go/v2/types/labels.go index 713c28f945b2..7ea5edc41014 100644 --- a/vendor/github.com/compose-spec/compose-go/v2/types/labels.go +++ b/vendor/github.com/compose-spec/compose-go/v2/types/labels.go @@ -55,7 +55,6 @@ func (l Labels) AsList() []string { func (l Labels) ToMappingWithEquals() MappingWithEquals { mapping := MappingWithEquals{} for k, v := range l { - v := v mapping[k] = &v } return mapping diff --git a/vendor/github.com/compose-spec/compose-go/v2/types/mapping.go b/vendor/github.com/compose-spec/compose-go/v2/types/mapping.go index 63f6e58b8967..1fc2aea19f37 100644 --- a/vendor/github.com/compose-spec/compose-go/v2/types/mapping.go +++ b/vendor/github.com/compose-spec/compose-go/v2/types/mapping.go @@ -20,6 +20,7 @@ import ( "fmt" "sort" "strings" + "unicode" ) // MappingWithEquals is a mapping type that can be converted from a list of @@ -94,6 +95,9 @@ func (m *MappingWithEquals) DecodeMapstructure(value interface{}) error { mapping := make(MappingWithEquals, len(v)) for _, s := range v { k, e, ok := strings.Cut(fmt.Sprint(s), "=") + if unicode.IsSpace(rune(k[len(k)-1])) { + return fmt.Errorf("environment variable %s is declared with a trailing space", k) + } if !ok { mapping[k] = nil } else { @@ -157,7 +161,6 @@ func (m Mapping) Values() []string { func (m Mapping) ToMappingWithEquals() MappingWithEquals { mapping := MappingWithEquals{} for k, v := range m { - v := v mapping[k] = &v } return mapping diff --git a/vendor/github.com/compose-spec/compose-go/v2/types/project.go b/vendor/github.com/compose-spec/compose-go/v2/types/project.go index d8005c0ad786..e28554ab4204 100644 --- a/vendor/github.com/compose-spec/compose-go/v2/types/project.go +++ b/vendor/github.com/compose-spec/compose-go/v2/types/project.go @@ -380,12 +380,7 @@ func (p *Project) WithServicesEnabled(names ...string) (*Project, error) { service := p.DisabledServices[name] profiles = append(profiles, service.Profiles...) } - newProject, err := newProject.WithProfiles(profiles) - if err != nil { - return newProject, err - } - - return newProject.WithServicesEnvironmentResolved(true) + return newProject.WithProfiles(profiles) } // WithoutUnnecessaryResources drops networks/volumes/secrets/configs that are not referenced by active services @@ -477,7 +472,7 @@ func (p *Project) WithSelectedServices(names []string, options ...DependencyOpti } set := utils.NewSet[string]() - err := p.ForEachService(names, func(name string, service *ServiceConfig) error { + err := p.ForEachService(names, func(name string, _ *ServiceConfig) error { set.Add(name) return nil }, options...) @@ -535,7 +530,7 @@ func (p *Project) WithServicesDisabled(names ...string) *Project { // WithImagesResolved updates services images to include digest computed by a resolver function // It returns a new Project instance with the changes and keep the original Project unchanged func (p *Project) WithImagesResolved(resolver func(named reference.Named) (godigest.Digest, error)) (*Project, error) { - return p.WithServicesTransform(func(name string, service ServiceConfig) (ServiceConfig, error) { + return p.WithServicesTransform(func(_ string, service ServiceConfig) (ServiceConfig, error) { if service.Image == "" { return service, nil } @@ -725,14 +720,9 @@ func loadMappingFile(path string, format string, resolve dotenv.LookupFn) (Mappi if err != nil { return nil, err } - defer file.Close() //nolint:errcheck + defer file.Close() - var fileVars map[string]string - if format != "" { - fileVars, err = dotenv.ParseWithFormat(file, path, resolve, format) - } else { - fileVars, err = dotenv.ParseWithLookup(file, resolve) - } + fileVars, err := dotenv.ParseWithFormat(file, path, resolve, format) if err != nil { return nil, err } @@ -746,7 +736,6 @@ func (p *Project) deepCopy() *Project { n := &Project{} deriveDeepCopyProject(n, p) return n - } // WithServicesTransform applies a transformation to project services and return a new project with transformation results diff --git a/vendor/github.com/compose-spec/compose-go/v2/types/types.go b/vendor/github.com/compose-spec/compose-go/v2/types/types.go index 4c7baa46ed33..fe95444d7e5e 100644 --- a/vendor/github.com/compose-spec/compose-go/v2/types/types.go +++ b/vendor/github.com/compose-spec/compose-go/v2/types/types.go @@ -20,9 +20,12 @@ import ( "encoding/json" "fmt" "sort" + "strconv" "strings" + "time" "github.com/docker/go-connections/nat" + "github.com/xhit/go-str2duration/v2" ) // ServiceConfig is the configuration of one service @@ -215,6 +218,8 @@ const ( PullPolicyMissing = "missing" // PullPolicyBuild force building images PullPolicyBuild = "build" + // PullPolicyRefresh checks if image needs to be updated + PullPolicyRefresh = "refresh" ) const ( @@ -268,6 +273,27 @@ func (s ServiceConfig) GetDependents(p *Project) []string { return dependent } +func (s ServiceConfig) GetPullPolicy() (string, time.Duration, error) { + switch s.PullPolicy { + case PullPolicyAlways, PullPolicyNever, PullPolicyIfNotPresent, PullPolicyMissing, PullPolicyBuild: + return s.PullPolicy, 0, nil + case "daily": + return PullPolicyRefresh, 24 * time.Hour, nil + case "weekly": + return PullPolicyRefresh, 7 * 24 * time.Hour, nil + default: + if strings.HasPrefix(s.PullPolicy, "every_") { + delay := s.PullPolicy[6:] + duration, err := str2duration.ParseDuration(delay) + if err != nil { + return "", 0, err + } + return PullPolicyRefresh, duration, nil + } + return PullPolicyMissing, 0, nil + } +} + // BuildConfig is a type for build type BuildConfig struct { Context string `yaml:"context,omitempty" json:"context,omitempty"` @@ -479,16 +505,13 @@ func ParsePortConfig(value string) ([]ServicePortConfig, error) { for _, key := range keys { port := nat.Port(key) - converted, err := convertPortToPortConfig(port, portBindings) - if err != nil { - return nil, err - } + converted := convertPortToPortConfig(port, portBindings) portConfigs = append(portConfigs, converted...) } return portConfigs, nil } -func convertPortToPortConfig(port nat.Port, portBindings map[nat.Port][]nat.PortBinding) ([]ServicePortConfig, error) { +func convertPortToPortConfig(port nat.Port, portBindings map[nat.Port][]nat.PortBinding) []ServicePortConfig { var portConfigs []ServicePortConfig for _, binding := range portBindings[port] { portConfigs = append(portConfigs, ServicePortConfig{ @@ -499,7 +522,7 @@ func convertPortToPortConfig(port nat.Port, portBindings map[nat.Port][]nat.Port Mode: "ingress", }) } - return portConfigs, nil + return portConfigs } // ServiceVolumeConfig are references to a volume used by a service @@ -604,17 +627,51 @@ type ServiceVolumeTmpfs struct { Extensions Extensions `yaml:"#extensions,inline,omitempty" json:"-"` } +type FileMode int64 + // FileReferenceConfig for a reference to a swarm file object type FileReferenceConfig struct { - Source string `yaml:"source,omitempty" json:"source,omitempty"` - Target string `yaml:"target,omitempty" json:"target,omitempty"` - UID string `yaml:"uid,omitempty" json:"uid,omitempty"` - GID string `yaml:"gid,omitempty" json:"gid,omitempty"` - Mode *uint32 `yaml:"mode,omitempty" json:"mode,omitempty"` + Source string `yaml:"source,omitempty" json:"source,omitempty"` + Target string `yaml:"target,omitempty" json:"target,omitempty"` + UID string `yaml:"uid,omitempty" json:"uid,omitempty"` + GID string `yaml:"gid,omitempty" json:"gid,omitempty"` + Mode *FileMode `yaml:"mode,omitempty" json:"mode,omitempty"` Extensions Extensions `yaml:"#extensions,inline,omitempty" json:"-"` } +func (f *FileMode) DecodeMapstructure(value interface{}) error { + switch v := value.(type) { + case *FileMode: + return nil + case string: + i, err := strconv.ParseInt(v, 8, 64) + if err != nil { + return err + } + *f = FileMode(i) + case int: + *f = FileMode(v) + default: + return fmt.Errorf("unexpected value type %T for mode", value) + } + return nil +} + +// MarshalYAML makes FileMode implement yaml.Marshaller +func (f *FileMode) MarshalYAML() (interface{}, error) { + return f.String(), nil +} + +// MarshalJSON makes FileMode implement json.Marshaller +func (f *FileMode) MarshalJSON() ([]byte, error) { + return []byte("\"" + f.String() + "\""), nil +} + +func (f *FileMode) String() string { + return fmt.Sprintf("0%o", int64(*f)) +} + // ServiceConfigObjConfig is the config obj configuration for a service type ServiceConfigObjConfig FileReferenceConfig diff --git a/vendor/github.com/compose-spec/compose-go/v2/utils/pathutils.go b/vendor/github.com/compose-spec/compose-go/v2/utils/pathutils.go index fd2a635ec8cb..211e2999356e 100644 --- a/vendor/github.com/compose-spec/compose-go/v2/utils/pathutils.go +++ b/vendor/github.com/compose-spec/compose-go/v2/utils/pathutils.go @@ -41,7 +41,6 @@ func ResolveSymbolicLink(path string) (string, error) { return path, nil } return strings.Replace(path, part, sym, 1), nil - } // getSymbolinkLink parses all parts of the path and returns the diff --git a/vendor/github.com/compose-spec/compose-go/v2/validation/validation.go b/vendor/github.com/compose-spec/compose-go/v2/validation/validation.go index 707f247e36a3..793c1930147f 100644 --- a/vendor/github.com/compose-spec/compose-go/v2/validation/validation.go +++ b/vendor/github.com/compose-spec/compose-go/v2/validation/validation.go @@ -65,7 +65,6 @@ func check(value any, p tree.Path) error { func checkFileObject(keys ...string) checkerFunc { return func(value any, p tree.Path) error { - v := value.(map[string]any) count := 0 for _, s := range keys { @@ -100,8 +99,8 @@ func checkPath(value any, p tree.Path) error { func checkDeviceRequest(value any, p tree.Path) error { v := value.(map[string]any) _, hasCount := v["count"] - _, hasIds := v["device_ids"] - if hasCount && hasIds { + _, hasIDs := v["device_ids"] + if hasCount && hasIDs { return fmt.Errorf(`%s: "count" and "device_ids" attributes are exclusive`, p) } return nil diff --git a/vendor/github.com/xhit/go-str2duration/v2/LICENSE b/vendor/github.com/xhit/go-str2duration/v2/LICENSE new file mode 100644 index 000000000000..ea5ea8986925 --- /dev/null +++ b/vendor/github.com/xhit/go-str2duration/v2/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/vendor/github.com/xhit/go-str2duration/v2/README.md b/vendor/github.com/xhit/go-str2duration/v2/README.md new file mode 100644 index 000000000000..03263737a284 --- /dev/null +++ b/vendor/github.com/xhit/go-str2duration/v2/README.md @@ -0,0 +1,88 @@ +# Go String To Duration (go-str2duration) + +This package allows to get a time.Duration from a string. The string can be a string retorned for time.Duration or a similar string with weeks or days too!. + +Go Report Card +go.dev + +## Download + +```bash +go get github.com/xhit/go-str2duration/v2 +``` + +## Features + +Go String To Duration supports this strings conversions to duration: +- All strings returned in time.Duration String. +- A string more readable like 1w2d6h3ns (1 week 2 days 6 hours and 3 nanoseconds). +- `µs` and `us` are microsecond. + +It's the same `time.ParseDuration` standard function in Go, but with days and week support. + +**Note**: a day is 24 hour. + +If you don't need days and weeks, use [`time.ParseDuration`](https://golang.org/pkg/time/#ParseDuration). + +## Usage + +```go +package main + +import ( + "fmt" + str2duration "github.com/xhit/go-str2duration/v2" + "os" + "time" +) + +func main() { + + for i, tt := range []struct { + dur string + expected time.Duration + }{ + //This times are returned with time.Duration string + {"1h", time.Duration(time.Hour)}, + {"1m", time.Duration(time.Minute)}, + {"1s", time.Duration(time.Second)}, + {"1ms", time.Duration(time.Millisecond)}, + {"1µs", time.Duration(time.Microsecond)}, + {"1us", time.Duration(time.Microsecond)}, + {"1ns", time.Duration(time.Nanosecond)}, + {"4.000000001s", time.Duration(4*time.Second + time.Nanosecond)}, + {"1h0m4.000000001s", time.Duration(time.Hour + 4*time.Second + time.Nanosecond)}, + {"1h1m0.01s", time.Duration(61*time.Minute + 10*time.Millisecond)}, + {"1h1m0.123456789s", time.Duration(61*time.Minute + 123456789*time.Nanosecond)}, + {"1.00002ms", time.Duration(time.Millisecond + 20*time.Nanosecond)}, + {"1.00000002s", time.Duration(time.Second + 20*time.Nanosecond)}, + {"693ns", time.Duration(693 * time.Nanosecond)}, + + //This times aren't returned with time.Duration string, but are easily readable and can be parsed too! + {"1ms1ns", time.Duration(time.Millisecond + 1*time.Nanosecond)}, + {"1s20ns", time.Duration(time.Second + 20*time.Nanosecond)}, + {"60h8ms", time.Duration(60*time.Hour + 8*time.Millisecond)}, + {"96h63s", time.Duration(96*time.Hour + 63*time.Second)}, + + //And works with days and weeks! + {"2d3s96ns", time.Duration(48*time.Hour + 3*time.Second + 96*time.Nanosecond)}, + {"1w2d3s96ns", time.Duration(168*time.Hour + 48*time.Hour + 3*time.Second + 96*time.Nanosecond)}, + + {"10s1us693ns", time.Duration(10*time.Second + time.Microsecond + 693*time.Nanosecond)}, + + } { + durationFromString, err := str2duration.ParseDuration(tt.dur) + if err != nil { + panic(err) + + //Check if expected time is the time returned by the parser + } else if tt.expected != durationFromString { + fmt.Println(fmt.Sprintf("index %d -> in: %s returned: %s\tnot equal to %s", i, tt.dur, durationFromString.String(), tt.expected.String())) + }else{ + fmt.Println(fmt.Sprintf("index %d -> in: %s parsed succesfully", i, tt.dur)) + } + } +} +``` + +Also, you can convert to string the duration using `String(t time.Duration)` function. This support weeks and days and not return the ugly decimals from golang standard `t.String()` function. Units with 0 values aren't returned. For example: `1d1ms` means 1 day 1 millisecond. \ No newline at end of file diff --git a/vendor/github.com/xhit/go-str2duration/v2/str2duration.go b/vendor/github.com/xhit/go-str2duration/v2/str2duration.go new file mode 100644 index 000000000000..51631db5e796 --- /dev/null +++ b/vendor/github.com/xhit/go-str2duration/v2/str2duration.go @@ -0,0 +1,331 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in https://raw.githubusercontent.com/golang/go/master/LICENSE + +package str2duration + +import ( + "errors" + "time" +) + +var unitMap = map[string]int64{ + "ns": int64(time.Nanosecond), + "us": int64(time.Microsecond), + "µs": int64(time.Microsecond), // U+00B5 = micro symbol + "μs": int64(time.Microsecond), // U+03BC = Greek letter mu + "ms": int64(time.Millisecond), + "s": int64(time.Second), + "m": int64(time.Minute), + "h": int64(time.Hour), + "d": int64(time.Hour) * 24, + "w": int64(time.Hour) * 168, +} + +// ParseDuration parses a duration string. +// A duration string is a possibly signed sequence of +// decimal numbers, each with optional fraction and a unit suffix, +// such as "300ms", "-1.5h" or "2h45m". +// Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h", "d", "w". +func ParseDuration(s string) (time.Duration, error) { + // [-+]?([0-9]*(\.[0-9]*)?[a-z]+)+ + orig := s + var d int64 + neg := false + + // Consume [-+]? + if s != "" { + c := s[0] + if c == '-' || c == '+' { + neg = c == '-' + s = s[1:] + } + } + // Special case: if all that is left is "0", this is zero. + if s == "0" { + return 0, nil + } + if s == "" { + return 0, errors.New("time: invalid duration " + quote(orig)) + } + for s != "" { + var ( + v, f int64 // integers before, after decimal point + scale float64 = 1 // value = v + f/scale + ) + + var err error + + // The next character must be [0-9.] + if !(s[0] == '.' || '0' <= s[0] && s[0] <= '9') { + return 0, errors.New("time: invalid duration " + quote(orig)) + } + // Consume [0-9]* + pl := len(s) + v, s, err = leadingInt(s) + if err != nil { + return 0, errors.New("time: invalid duration " + quote(orig)) + } + pre := pl != len(s) // whether we consumed anything before a period + + // Consume (\.[0-9]*)? + post := false + if s != "" && s[0] == '.' { + s = s[1:] + pl := len(s) + f, scale, s = leadingFraction(s) + post = pl != len(s) + } + if !pre && !post { + // no digits (e.g. ".s" or "-.s") + return 0, errors.New("time: invalid duration " + quote(orig)) + } + + // Consume unit. + i := 0 + for ; i < len(s); i++ { + c := s[i] + if c == '.' || '0' <= c && c <= '9' { + break + } + } + if i == 0 { + return 0, errors.New("time: missing unit in duration " + quote(orig)) + } + u := s[:i] + s = s[i:] + unit, ok := unitMap[u] + if !ok { + return 0, errors.New("time: unknown unit " + quote(u) + " in duration " + quote(orig)) + } + if v > (1<<63-1)/unit { + // overflow + return 0, errors.New("time: invalid duration " + quote(orig)) + } + v *= unit + if f > 0 { + // float64 is needed to be nanosecond accurate for fractions of hours. + // v >= 0 && (f*unit/scale) <= 3.6e+12 (ns/h, h is the largest unit) + v += int64(float64(f) * (float64(unit) / scale)) + if v < 0 { + // overflow + return 0, errors.New("time: invalid duration " + quote(orig)) + } + } + d += v + if d < 0 { + // overflow + return 0, errors.New("time: invalid duration " + quote(orig)) + } + } + + if neg { + d = -d + } + return time.Duration(d), nil +} + +func quote(s string) string { + return "\"" + s + "\"" +} + +var errLeadingInt = errors.New("time: bad [0-9]*") // never printed + +// leadingInt consumes the leading [0-9]* from s. +func leadingInt(s string) (x int64, rem string, err error) { + i := 0 + for ; i < len(s); i++ { + c := s[i] + if c < '0' || c > '9' { + break + } + if x > (1<<63-1)/10 { + // overflow + return 0, "", errLeadingInt + } + x = x*10 + int64(c) - '0' + if x < 0 { + // overflow + return 0, "", errLeadingInt + } + } + return x, s[i:], nil +} + +// leadingFraction consumes the leading [0-9]* from s. +// It is used only for fractions, so does not return an error on overflow, +// it just stops accumulating precision. +func leadingFraction(s string) (x int64, scale float64, rem string) { + i := 0 + scale = 1 + overflow := false + for ; i < len(s); i++ { + c := s[i] + if c < '0' || c > '9' { + break + } + if overflow { + continue + } + if x > (1<<63-1)/10 { + // It's possible for overflow to give a positive number, so take care. + overflow = true + continue + } + y := x*10 + int64(c) - '0' + if y < 0 { + overflow = true + continue + } + x = y + scale *= 10 + } + return x, scale, s[i:] +} + +// String returns a string representing the duration in the form "1w4d2h3m5s". +// Units with 0 values aren't returned, for example: 1d1ms is 1 day 1 milliseconds +func String(d time.Duration) string { + if d == 0 { + return "0s" + } + + // Largest time is 15250w1d23h47m16s854ms775us807ns + var buf [32]byte + w := len(buf) + var sign string + + u := uint64(d) + neg := d < 0 + if neg { + u = -u + sign = "-" + } + + // u is nanoseconds (ns) + if u > 0 { + w-- + + if u%1000 > 0 { + buf[w] = 's' + w-- + buf[w] = 'n' + w = fmtInt(buf[:w], u%1000) + } else { + w++ + } + + u /= 1000 + + // u is now integer microseconds (us) + if u > 0 { + w-- + if u%1000 > 0 { + buf[w] = 's' + w-- + buf[w] = 'u' + w = fmtInt(buf[:w], u%1000) + } else { + w++ + } + u /= 1000 + + // u is now integer milliseconds (ms) + if u > 0 { + w-- + if u%1000 > 0 { + buf[w] = 's' + w-- + buf[w] = 'm' + w = fmtInt(buf[:w], u%1000) + } else { + w++ + } + u /= 1000 + + // u is now integer seconds (s) + if u > 0 { + w-- + if u%60 > 0 { + buf[w] = 's' + w = fmtInt(buf[:w], u%60) + } else { + w++ + } + u /= 60 + + // u is now integer minutes (m) + if u > 0 { + w-- + + if u%60 > 0 { + buf[w] = 'm' + w = fmtInt(buf[:w], u%60) + } else { + w++ + } + + u /= 60 + + // u is now integer hours (h) + if u > 0 { + w-- + + if u%24 > 0 { + buf[w] = 'h' + w = fmtInt(buf[:w], u%24) + } else { + w++ + } + + u /= 24 + + // u is now integer days (d) + if u > 0 { + w-- + + if u%7 > 0 { + buf[w] = 'd' + w = fmtInt(buf[:w], u%7) + } else { + w++ + } + + u /= 7 + + // u is now integer weeks (w) + if u > 0 { + w-- + buf[w] = 'w' + w = fmtInt(buf[:w], u) + } + + } + + } + } + } + } + } + + } + + return sign + string(buf[w:]) +} + +// fmtInt formats v into the tail of buf. +// It returns the index where the output begins. +func fmtInt(buf []byte, v uint64) int { + w := len(buf) + if v == 0 { + w-- + buf[w] = '0' + } else { + for v > 0 { + w-- + buf[w] = byte(v%10) + '0' + v /= 10 + } + } + return w +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 2d453226a84e..3f242f52b470 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -130,8 +130,8 @@ github.com/cenkalti/backoff/v4 # github.com/cespare/xxhash/v2 v2.3.0 ## explicit; go 1.11 github.com/cespare/xxhash/v2 -# github.com/compose-spec/compose-go/v2 v2.4.8 -## explicit; go 1.21 +# github.com/compose-spec/compose-go/v2 v2.4.9 +## explicit; go 1.23 github.com/compose-spec/compose-go/v2/cli github.com/compose-spec/compose-go/v2/consts github.com/compose-spec/compose-go/v2/dotenv @@ -761,6 +761,9 @@ github.com/xeipuuv/gojsonreference # github.com/xeipuuv/gojsonschema v1.2.0 ## explicit github.com/xeipuuv/gojsonschema +# github.com/xhit/go-str2duration/v2 v2.1.0 +## explicit; go 1.13 +github.com/xhit/go-str2duration/v2 # github.com/zclconf/go-cty v1.16.0 ## explicit; go 1.18 github.com/zclconf/go-cty/cty