diff --git a/commands/bake.go b/commands/bake.go index 2b23032b6b98..391db4c925e6 100644 --- a/commands/bake.go +++ b/commands/bake.go @@ -17,6 +17,7 @@ import ( "text/tabwriter" "github.com/containerd/console" + "github.com/containerd/containerd/v2/pkg/epoch" "github.com/containerd/platforms" "github.com/docker/buildx/bake" "github.com/docker/buildx/bake/hclparser" @@ -241,16 +242,20 @@ func runBake(ctx context.Context, dockerCli command.Cli, targets []string, in ba return err } - if v := os.Getenv("SOURCE_DATE_EPOCH"); v != "" { - // TODO: extract env var parsing to a method easily usable by library consumers - for _, t := range tgts { - if _, ok := t.Args["SOURCE_DATE_EPOCH"]; ok { - continue - } + var sourceDateEpoch *string + for _, t := range tgts { + if _, ok := t.Args[epoch.SourceDateEpochEnv]; ok { + continue + } + + v := os.Getenv(epoch.SourceDateEpochEnv) + sourceDateEpoch = &v + + if *sourceDateEpoch != "" { if t.Args == nil { t.Args = map[string]*string{} } - t.Args["SOURCE_DATE_EPOCH"] = &v + t.Args[epoch.SourceDateEpochEnv] = sourceDateEpoch } } diff --git a/commands/build.go b/commands/build.go index aa58a6e04d71..455d8143e1e8 100644 --- a/commands/build.go +++ b/commands/build.go @@ -18,6 +18,7 @@ import ( "time" "github.com/containerd/console" + "github.com/containerd/containerd/v2/pkg/epoch" "github.com/docker/buildx/build" "github.com/docker/buildx/builder" "github.com/docker/buildx/store" @@ -139,10 +140,9 @@ func (o *buildOptions) toOptions() (*BuildOptions, error) { ExportLoad: o.exportLoad, } - // TODO: extract env var parsing to a method easily usable by library consumers - if v := os.Getenv("SOURCE_DATE_EPOCH"); v != "" { - if _, ok := opts.BuildArgs["SOURCE_DATE_EPOCH"]; !ok { - opts.BuildArgs["SOURCE_DATE_EPOCH"] = v + if _, ok := opts.BuildArgs[epoch.SourceDateEpochEnv]; !ok { + if v := os.Getenv(epoch.SourceDateEpochEnv); v != "" { + opts.BuildArgs[epoch.SourceDateEpochEnv] = v } } diff --git a/commands/history/inspect.go b/commands/history/inspect.go index a045d1ec724c..6930a25da4de 100644 --- a/commands/history/inspect.go +++ b/commands/history/inspect.go @@ -19,6 +19,7 @@ import ( "github.com/containerd/containerd/v2/core/content" "github.com/containerd/containerd/v2/core/content/proxy" "github.com/containerd/containerd/v2/core/images" + "github.com/containerd/containerd/v2/pkg/epoch" "github.com/containerd/platforms" "github.com/docker/buildx/localstate" "github.com/docker/buildx/util/cobrautil/completion" @@ -392,7 +393,7 @@ workers0: readAttr(attrs, "ulimit", &out.Config.Ulimit, nil) readAttr(attrs, "build-arg:BUILDKIT_CACHE_MOUNT_NS", &out.Config.CacheMountNS, nil) readAttr(attrs, "build-arg:BUILDKIT_DOCKERFILE_CHECK", &out.Config.DockerfileCheckConfig, nil) - readAttr(attrs, "build-arg:SOURCE_DATE_EPOCH", &out.Config.SourceDateEpoch, nil) + readAttr(attrs, "build-arg:"+epoch.SourceDateEpochEnv, &out.Config.SourceDateEpoch, nil) readAttr(attrs, "build-arg:SANDBOX_HOSTNAME", &out.Config.SandboxHostname, nil) var unusedAttrs []keyValueOutput diff --git a/vendor/github.com/containerd/containerd/v2/pkg/epoch/context.go b/vendor/github.com/containerd/containerd/v2/pkg/epoch/context.go new file mode 100644 index 000000000000..fd16f951969f --- /dev/null +++ b/vendor/github.com/containerd/containerd/v2/pkg/epoch/context.go @@ -0,0 +1,41 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package epoch + +import ( + "context" + "time" +) + +type ( + epochKey struct{} +) + +// WithSourceDateEpoch associates the context with the epoch. +func WithSourceDateEpoch(ctx context.Context, tm *time.Time) context.Context { + return context.WithValue(ctx, epochKey{}, tm) +} + +// FromContext returns the epoch associated with the context. +// FromContext does not fall back to read the SOURCE_DATE_EPOCH env var. +func FromContext(ctx context.Context) *time.Time { + v := ctx.Value(epochKey{}) + if v == nil { + return nil + } + return v.(*time.Time) +} diff --git a/vendor/github.com/containerd/containerd/v2/pkg/epoch/epoch.go b/vendor/github.com/containerd/containerd/v2/pkg/epoch/epoch.go new file mode 100644 index 000000000000..2b6718fa62bd --- /dev/null +++ b/vendor/github.com/containerd/containerd/v2/pkg/epoch/epoch.go @@ -0,0 +1,67 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +// Package epoch provides SOURCE_DATE_EPOCH utilities. +package epoch + +import ( + "fmt" + "os" + "strconv" + "time" +) + +// SourceDateEpochEnv is the SOURCE_DATE_EPOCH env var. +// See https://reproducible-builds.org/docs/source-date-epoch/ +const SourceDateEpochEnv = "SOURCE_DATE_EPOCH" + +// SourceDateEpoch returns the SOURCE_DATE_EPOCH env var as *time.Time. +// If the env var is not set, SourceDateEpoch returns nil without an error. +func SourceDateEpoch() (*time.Time, error) { + v, ok := os.LookupEnv(SourceDateEpochEnv) + if !ok || v == "" { + return nil, nil // not an error + } + t, err := ParseSourceDateEpoch(v) + if err != nil { + return nil, fmt.Errorf("invalid %s value: %w", SourceDateEpochEnv, err) + } + return t, nil +} + +// ParseSourceDateEpoch parses the given source date epoch, as *time.Time. +// It returns an error if sourceDateEpoch is empty or not well-formatted. +func ParseSourceDateEpoch(sourceDateEpoch string) (*time.Time, error) { + if sourceDateEpoch == "" { + return nil, fmt.Errorf("value is empty") + } + i64, err := strconv.ParseInt(sourceDateEpoch, 10, 64) + if err != nil { + return nil, fmt.Errorf("invalid value: %w", err) + } + unix := time.Unix(i64, 0).UTC() + return &unix, nil +} + +// SetSourceDateEpoch sets the SOURCE_DATE_EPOCH env var. +func SetSourceDateEpoch(tm time.Time) { + _ = os.Setenv(SourceDateEpochEnv, fmt.Sprintf("%d", tm.Unix())) +} + +// UnsetSourceDateEpoch unsets the SOURCE_DATE_EPOCH env var. +func UnsetSourceDateEpoch() { + _ = os.Unsetenv(SourceDateEpochEnv) +} diff --git a/vendor/modules.txt b/vendor/modules.txt index d928774265f9..2168ef6c0ffd 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -222,6 +222,7 @@ github.com/containerd/containerd/v2/internal/fsverity github.com/containerd/containerd/v2/internal/lazyregexp github.com/containerd/containerd/v2/internal/randutil github.com/containerd/containerd/v2/pkg/archive/compression +github.com/containerd/containerd/v2/pkg/epoch github.com/containerd/containerd/v2/pkg/filters github.com/containerd/containerd/v2/pkg/identifiers github.com/containerd/containerd/v2/pkg/kernelversion