vendor: github.com/moby/buildkit@master ed6dc749ce40b9fdc308676d10343fd00f56c717

Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
This commit is contained in:
CrazyMax
2026-01-07 11:45:01 -08:00
committed by Tonis Tiigi
parent 752e0b2227
commit c3514fea5d
21 changed files with 512 additions and 10480 deletions
+27 -2
View File
@@ -236,6 +236,9 @@ func (c *Client) solve(ctx context.Context, def *llb.Definition, runGateway runG
frontendAttrs := maps.Clone(opt.FrontendAttrs)
maps.Copy(frontendAttrs, cacheOpt.frontendAttrs)
const statusInactivityTimeout = 5 * time.Second
statusActivity := make(chan struct{}, 1)
solveCtx, cancelSolve := context.WithCancelCause(ctx)
var res *SolveResponse
eg.Go(func() error {
@@ -244,8 +247,21 @@ func (c *Client) solve(ctx context.Context, def *llb.Definition, runGateway runG
defer func() { // make sure the Status ends cleanly on build errors
go func() {
<-time.After(3 * time.Second)
cancelStatus(errors.WithStack(context.Canceled))
// Start inactivity monitoring after solve completes
statusInactivityTimer := time.NewTimer(statusInactivityTimeout)
defer statusInactivityTimer.Stop()
for {
select {
case <-statusContext.Done():
return
case <-statusActivity:
// Reset timer on activity
statusInactivityTimer.Reset(statusInactivityTimeout)
case <-statusInactivityTimer.C:
cancelStatus(errors.WithStack(context.Canceled))
return
}
}
}()
if !opt.SessionPreInitialized {
bklog.G(ctx).Debugf("stopping session")
@@ -345,8 +361,17 @@ func (c *Client) solve(ctx context.Context, def *llb.Definition, runGateway runG
if errors.Is(err, io.EOF) {
return nil
}
// Ignore context canceled, triggered after inactivity timeout
if errors.Is(err, context.Canceled) || statusContext.Err() != nil {
return nil
}
return errors.Wrap(err, "failed to receive status")
}
// Signal activity (non-blocking)
select {
case statusActivity <- struct{}{}:
default:
}
if statusChan != nil {
statusChan <- NewSolveStatus(resp)
}
+56 -5
View File
@@ -2,6 +2,7 @@ package linter
import (
"fmt"
"maps"
"strconv"
"strings"
@@ -19,7 +20,7 @@ type Config struct {
}
type Linter struct {
CalledRules []string
CalledRules *[]string
ExperimentalAll bool
ExperimentalRules map[string]struct{}
ReturnAsError bool
@@ -32,7 +33,7 @@ func New(config *Config) *Linter {
toret := &Linter{
SkippedRules: map[string]struct{}{},
ExperimentalRules: map[string]struct{}{},
CalledRules: []string{},
CalledRules: new([]string),
Warn: config.Warn,
}
toret.SkipAll = config.SkipAll
@@ -65,20 +66,70 @@ func (lc *Linter) Run(rule LinterRuleI, location []parser.Range, txt ...string)
}
}
lc.CalledRules = append(lc.CalledRules, rulename)
*lc.CalledRules = append(*lc.CalledRules, rulename)
rule.Run(lc.Warn, location, txt...)
}
func (lc *Linter) WithMergedConfig(other *Config) *Linter {
cloned := *lc
if other.ExperimentalAll {
cloned.ExperimentalAll = true
}
if len(other.ExperimentalRules) > 0 {
cloned.ExperimentalRules = maps.Clone(cloned.ExperimentalRules)
for _, rulename := range other.ExperimentalRules {
cloned.ExperimentalRules[rulename] = struct{}{}
}
}
if other.SkipAll {
cloned.SkipAll = true
}
if len(other.SkipRules) > 0 {
cloned.SkippedRules = maps.Clone(cloned.SkippedRules)
for _, rulename := range other.SkipRules {
cloned.SkippedRules[rulename] = struct{}{}
}
}
return &cloned
}
func (lc *Linter) WithMergedConfigFromComments(comments []string) *Linter {
if comments == nil {
return lc
}
for _, comment := range comments {
p := parser.DirectiveParser{}
p.SetComment("")
d, _ := p.ParseLine([]byte(comment))
if d == nil || d.Name != "check" {
continue
}
v, _, _ := strings.Cut(d.Value, " ")
lintConfig, err := ParseLintOptions(v)
if err != nil {
return lc
}
return lc.WithMergedConfig(lintConfig)
}
return lc
}
func (lc *Linter) Error() error {
if lc == nil || !lc.ReturnAsError {
return nil
}
if len(lc.CalledRules) == 0 {
if len(*lc.CalledRules) == 0 {
return nil
}
var rules []string
uniqueRules := map[string]struct{}{}
for _, r := range lc.CalledRules {
for _, r := range *lc.CalledRules {
uniqueRules[r] = struct{}{}
}
for r := range uniqueRules {
+23 -11
View File
@@ -4,9 +4,10 @@ import (
"bufio"
"bytes"
"encoding/json"
"fmt"
"regexp"
"strings"
"sync"
"unicode"
"github.com/pkg/errors"
)
@@ -32,14 +33,18 @@ type Directive struct {
// DirectiveParser is a parser for Dockerfile directives that enforces the
// quirks of the directive parser.
type DirectiveParser struct {
line int
regexp *regexp.Regexp
seen map[string]struct{}
done bool
line int
comment *string
seen map[string]struct{}
done bool
}
func (d *DirectiveParser) setComment(comment string) {
d.regexp = regexp.MustCompile(fmt.Sprintf(`^%s\s*([a-zA-Z][a-zA-Z0-9]*)\s*=\s*(.+?)\s*$`, comment))
var directiveRegexp = sync.OnceValue(func() *regexp.Regexp {
return regexp.MustCompile(`^([a-zA-Z][a-zA-Z0-9]*)\s*=\s*(.+?)\s*$`)
})
func (d *DirectiveParser) SetComment(comment string) {
d.comment = &comment
}
func (d *DirectiveParser) ParseLine(line []byte) (*Directive, error) {
@@ -47,11 +52,18 @@ func (d *DirectiveParser) ParseLine(line []byte) (*Directive, error) {
if d.done {
return nil, nil
}
if d.regexp == nil {
d.setComment("#")
if d.comment == nil {
d.SetComment("#")
}
match := d.regexp.FindSubmatch(line)
line, ok := bytes.CutPrefix(line, []byte(*d.comment))
if !ok {
d.done = true
return nil, nil
}
line = bytes.TrimLeftFunc(line, unicode.IsSpace)
match := directiveRegexp().FindSubmatch(line)
if len(match) == 0 {
d.done = true
return nil, nil
@@ -142,7 +154,7 @@ func parseDirective(key string, dt []byte, anyFormat bool) (string, string, []Ra
// use directive with different comment prefix, and search for //key=
directiveParser = DirectiveParser{line: line}
directiveParser.setComment("//")
directiveParser.SetComment("//")
if syntax, cmdline, loc, ok := detectDirectiveFromParser(key, dt, directiveParser); ok {
return syntax, cmdline, loc, true
}
@@ -0,0 +1,58 @@
package authprovider
import (
"context"
"sync"
"time"
"github.com/docker/cli/cli/config/configfile"
"github.com/docker/cli/cli/config/types"
)
func LoadAuthConfig(config *configfile.ConfigFile) AuthConfigProvider {
acp := &authConfigProvider{
config: config,
authConfigCache: map[string]authConfigCacheEntry{},
}
return acp.load
}
type authConfigProvider struct {
config *configfile.ConfigFile
authConfigCache map[string]authConfigCacheEntry
mu sync.Mutex
}
func (ap *authConfigProvider) load(ctx context.Context, host string, scopes []string, cacheExpireCheck ExpireCachedAuthCheck) (types.AuthConfig, error) {
ap.mu.Lock()
defer ap.mu.Unlock()
entry, exists := ap.authConfigCache[host]
if exists && (cacheExpireCheck == nil || !cacheExpireCheck(entry.Created, host)) {
return *entry.Auth, nil
}
hostKey := host
if host == DockerHubRegistryHost {
hostKey = DockerHubConfigfileKey
}
ac, err := ap.config.GetAuthConfig(hostKey)
if err != nil {
return types.AuthConfig{}, err
}
entry = authConfigCacheEntry{
Created: time.Now(),
Auth: &ac,
}
ap.authConfigCache[host] = entry
return ac, nil
}
type authConfigCacheEntry struct {
Created time.Time
Auth *types.AuthConfig
}
+44 -61
View File
@@ -19,7 +19,6 @@ import (
authutil "github.com/containerd/containerd/v2/core/remotes/docker/auth"
remoteserrors "github.com/containerd/containerd/v2/core/remotes/errors"
"github.com/docker/cli/cli/config"
"github.com/docker/cli/cli/config/configfile"
"github.com/docker/cli/cli/config/types"
cleanhttp "github.com/hashicorp/go-cleanhttp"
"github.com/moby/buildkit/session"
@@ -36,13 +35,17 @@ import (
const (
defaultExpiration = 60
dockerHubConfigfileKey = "https://index.docker.io/v1/"
dockerHubRegistryHost = "registry-1.docker.io"
DockerHubConfigfileKey = "https://index.docker.io/v1/"
DockerHubRegistryHost = "registry-1.docker.io"
)
type AuthConfigProvider func(ctx context.Context, host string, scope []string, cacheCheck ExpireCachedAuthCheck) (types.AuthConfig, error)
type ExpireCachedAuthCheck func(created time.Time, serverURL string) bool
type DockerAuthProviderConfig struct {
// ConfigFile is the docker config file
ConfigFile *configfile.ConfigFile
// AuthConfigProvider is a function that provides auth config for a given host and scope
AuthConfigProvider AuthConfigProvider
// TLSConfigs is a map of host to TLS config
TLSConfigs map[string]*AuthTLSConfig
// ExpireCachedAuth is a function that returns true auth config should be refreshed
@@ -50,12 +53,7 @@ type DockerAuthProviderConfig struct {
// If nil then the cached result will expire after 4 minutes and 50 seconds.
// The function is called with the time the cached auth config was created
// and the server URL the auth config is for.
ExpireCachedAuth func(created time.Time, serverURL string) bool
}
type authConfigCacheEntry struct {
Created time.Time
Auth *types.AuthConfig
ExpireCachedAuth ExpireCachedAuthCheck
}
func NewDockerAuthProvider(cfg DockerAuthProviderConfig) session.Attachable {
@@ -66,23 +64,21 @@ func NewDockerAuthProvider(cfg DockerAuthProviderConfig) session.Attachable {
}
}
return &authProvider{
authConfigCache: map[string]authConfigCacheEntry{},
expireAc: cfg.ExpireCachedAuth,
config: cfg.ConfigFile,
seeds: &tokenSeeds{dir: config.Dir()},
loggerCache: map[string]struct{}{},
tlsConfigs: cfg.TLSConfigs,
expireAc: cfg.ExpireCachedAuth,
provider: cfg.AuthConfigProvider,
seeds: &tokenSeeds{dir: config.Dir()},
loggerCache: map[string]struct{}{},
tlsConfigs: cfg.TLSConfigs,
}
}
type authProvider struct {
authConfigCache map[string]authConfigCacheEntry
expireAc func(time.Time, string) bool
config *configfile.ConfigFile
seeds *tokenSeeds
logger progresswriter.Logger
loggerCache map[string]struct{}
tlsConfigs map[string]*AuthTLSConfig
expireAc func(time.Time, string) bool
provider AuthConfigProvider
seeds *tokenSeeds
logger progresswriter.Logger
loggerCache map[string]struct{}
tlsConfigs map[string]*AuthTLSConfig
// The need for this mutex is not well understood.
// Without it, the docker cli on OS X hangs when
@@ -102,7 +98,7 @@ func (ap *authProvider) Register(server *grpc.Server) {
}
func (ap *authProvider) FetchToken(ctx context.Context, req *auth.FetchTokenRequest) (rr *auth.FetchTokenResponse, err error) {
ac, err := ap.getAuthConfig(ctx, req.Host)
ac, err := ap.getAuthConfig(ctx, req.Host, req.Scopes)
if err != nil {
return nil, err
}
@@ -112,11 +108,7 @@ func (ap *authProvider) FetchToken(ctx context.Context, req *auth.FetchTokenRequ
return toTokenResponse(ac.RegistryToken, time.Time{}, 0), nil
}
creds, err := ap.credentials(ctx, req.Host)
if err != nil {
return nil, err
}
creds := toCredentials(*ac)
to := authutil.TokenOptions{
Realm: req.Realm,
Service: req.Service,
@@ -215,11 +207,7 @@ func (ap *authProvider) tlsConfig(host string) (*tls.Config, error) {
return tc, nil
}
func (ap *authProvider) credentials(ctx context.Context, host string) (*auth.CredentialsResponse, error) {
ac, err := ap.getAuthConfig(ctx, host)
if err != nil {
return nil, err
}
func toCredentials(ac types.AuthConfig) *auth.CredentialsResponse {
res := &auth.CredentialsResponse{}
if ac.IdentityToken != "" {
res.Secret = ac.IdentityToken
@@ -227,12 +215,16 @@ func (ap *authProvider) credentials(ctx context.Context, host string) (*auth.Cre
res.Username = ac.Username
res.Secret = ac.Password
}
return res, nil
return res
}
func (ap *authProvider) Credentials(ctx context.Context, req *auth.CredentialsRequest) (*auth.CredentialsResponse, error) {
resp, err := ap.credentials(ctx, req.Host)
if err != nil || resp.Secret != "" {
ac, err := ap.getAuthConfig(ctx, req.Host, nil)
if err != nil {
return nil, err
}
resp := toCredentials(*ac)
if resp.Secret != "" {
ap.mu.Lock()
defer ap.mu.Unlock()
_, ok := ap.loggerCache[req.Host]
@@ -267,33 +259,22 @@ func (ap *authProvider) VerifyTokenAuthority(ctx context.Context, req *auth.Veri
return &auth.VerifyTokenAuthorityResponse{Signed: sign.Sign(nil, req.Payload, priv)}, nil
}
func (ap *authProvider) getAuthConfig(ctx context.Context, host string) (*types.AuthConfig, error) {
func (ap *authProvider) getAuthConfig(ctx context.Context, host string, scopes []string) (*types.AuthConfig, error) {
ap.mu.Lock()
defer ap.mu.Unlock()
if host == dockerHubRegistryHost {
host = dockerHubConfigfileKey
var ac types.AuthConfig
if ap.provider != nil {
span, _ := tracing.StartSpan(ctx, fmt.Sprintf("load credentials for %s", host))
res, err := ap.provider(ctx, host, scopes, ap.expireAc)
tracing.FinishWithError(span, err)
if err != nil {
return nil, err
}
ac = res
}
entry, exists := ap.authConfigCache[host]
if exists && !ap.expireAc(entry.Created, host) {
return entry.Auth, nil
}
span, _ := tracing.StartSpan(ctx, fmt.Sprintf("load credentials for %s", host))
ac, err := ap.config.GetAuthConfig(host)
tracing.FinishWithError(span, err)
if err != nil {
return nil, err
}
entry = authConfigCacheEntry{
Created: time.Now(),
Auth: &ac,
}
ap.authConfigCache[host] = entry
return entry.Auth, nil
return &ac, nil
}
func (ap *authProvider) getAuthorityKey(ctx context.Context, host string, salt []byte) (ed25519.PrivateKey, error) {
@@ -301,10 +282,12 @@ func (ap *authProvider) getAuthorityKey(ctx context.Context, host string, salt [
return nil, status.Errorf(codes.Unavailable, "client side tokens disabled")
}
creds, err := ap.credentials(ctx, host)
ac, err := ap.getAuthConfig(ctx, host, nil)
if err != nil {
return nil, err
}
creds := toCredentials(*ac)
seed, err := ap.seeds.getSeed(host)
if err != nil {
return nil, err
+7
View File
@@ -95,7 +95,14 @@ func (b *buffer) Writer(ctx context.Context, opts ...content.WriterOpt) (content
}
}
b.mu.Lock()
if wOpts.Desc.Digest != "" {
if _, ok := b.buffers[wOpts.Desc.Digest]; ok {
b.mu.Unlock()
return nil, errors.Wrapf(cerrdefs.ErrAlreadyExists, "content %v already exists", wOpts.Desc.Digest)
}
}
if _, ok := b.refs[wOpts.Ref]; ok {
b.mu.Unlock()
return nil, errors.Wrapf(cerrdefs.ErrUnavailable, "ref %s locked", wOpts.Ref)
}
b.mu.Unlock()
+229
View File
@@ -0,0 +1,229 @@
package contentutil
import (
"context"
"encoding/json"
"slices"
"strings"
"sync"
"github.com/containerd/containerd/v2/core/content"
"github.com/containerd/containerd/v2/core/remotes"
cerrdefs "github.com/containerd/errdefs"
digest "github.com/opencontainers/go-digest"
ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
)
func ReferrersProviderWithBuffer(p ReferrersProvider, buffer Buffer, name string) *ReferrersProviderBuffer {
return &ReferrersProviderBuffer{
p: p,
cache: buffer,
name: name,
}
}
var _ ReferrersProvider = &ReferrersProviderBuffer{}
type ReferrersProviderBuffer struct {
p ReferrersProvider
cache Buffer
name string
mu sync.Mutex
blobs map[digest.Digest]ocispecs.Descriptor
refs map[digest.Digest][]ocispecs.Descriptor
}
func (p *ReferrersProviderBuffer) ReaderAt(ctx context.Context, desc ocispecs.Descriptor) (content.ReaderAt, error) {
cw, err := content.OpenWriter(ctx, p.cache, content.WithDescriptor(desc), content.WithRef(desc.Digest.String()))
if err != nil {
if cerrdefs.IsAlreadyExists(err) {
ra, err := p.cache.ReaderAt(ctx, desc)
if err != nil {
return nil, err
}
p.mu.Lock()
if p.blobs == nil {
p.blobs = make(map[digest.Digest]ocispecs.Descriptor)
}
p.blobs[desc.Digest] = desc
p.mu.Unlock()
return ra, nil
}
return nil, err
}
ra, err := p.p.ReaderAt(ctx, desc)
if err != nil {
cw.Close()
return nil, err
}
if err := content.CopyReaderAt(cw, ra, ra.Size()); err != nil {
cw.Close()
return nil, err
}
if err := cw.Commit(ctx, desc.Size, desc.Digest); err != nil {
cw.Close()
return nil, err
}
ra, err = p.cache.ReaderAt(ctx, desc)
if err != nil {
return nil, err
}
p.mu.Lock()
if p.blobs == nil {
p.blobs = make(map[digest.Digest]ocispecs.Descriptor)
}
p.blobs[desc.Digest] = desc
p.mu.Unlock()
return ra, nil
}
func (p *ReferrersProviderBuffer) FetchReferrers(ctx context.Context, dgst digest.Digest, opts ...remotes.FetchReferrersOpt) ([]ocispecs.Descriptor, error) {
cfg := remotes.FetchReferrersConfig{}
for _, o := range opts {
if err := o(ctx, &cfg); err != nil {
return nil, err
}
}
info, err := p.cache.Info(ctx, dgst)
if err == nil && len(info.Labels) != 0 {
refs := []ocispecs.Descriptor{}
for l, v := range info.Labels {
if !strings.HasPrefix(l, "containerd.io/gc.ref.content.buildkit.refs.") {
continue
}
dgst, err := digest.Parse(v)
if err != nil {
continue
}
dt, err := content.ReadBlob(ctx, p.cache, ocispecs.Descriptor{Digest: dgst})
if err != nil {
continue
}
desc := ocispecs.Descriptor{
Digest: dgst,
Size: int64(len(dt)),
ArtifactType: readArtifactType(dt),
}
refs = append(refs, desc)
}
refs = filterRefs(refs, &cfg)
if len(refs) > 0 {
return refs, nil
}
v, ok := info.Labels["buildkit/refs.null"]
if ok {
for name := range strings.SplitSeq(v, ",") {
if name == p.name {
return nil, nil
}
}
}
}
refs, err := p.p.FetchReferrers(ctx, dgst, opts...)
if err != nil {
return nil, err
}
refs = filterRefs(refs, &cfg)
p.mu.Lock()
if p.refs == nil {
p.refs = make(map[digest.Digest][]ocispecs.Descriptor)
}
p.refs[dgst] = append(p.refs[dgst], refs...)
p.mu.Unlock()
return refs, nil
}
func (p *ReferrersProviderBuffer) SetGCLabels(ctx context.Context, root ocispecs.Descriptor) error {
labels := map[string]string{}
fieldpaths := []string{}
p.mu.Lock()
for _, desc := range p.blobs {
shaPrefix := desc.Digest.Hex()[:12]
key := "containerd.io/gc.ref.content.buildkit." + shaPrefix
labels[key] = desc.Digest.String()
fieldpaths = append(fieldpaths, "labels."+key)
}
p.mu.Unlock()
_, err := p.cache.Update(ctx, content.Info{
Digest: root.Digest,
Labels: labels,
}, fieldpaths...)
if err != nil {
return err
}
for dgst, refs := range p.refs {
info, err := p.cache.Info(ctx, dgst)
if err != nil {
continue
}
labels := map[string]string{}
fieldpaths := []string{}
for _, ref := range refs {
shaPrefix := ref.Digest.Hex()[:12]
key := "containerd.io/gc.ref.content.buildkit.refs." + shaPrefix
labels[key] = ref.Digest.String()
fieldpaths = append(fieldpaths, "labels."+key)
}
if len(refs) == 0 {
key := "buildkit/refs.null"
labels[key] = addName(info.Labels[key], p.name)
fieldpaths = append(fieldpaths, "labels."+key)
}
if len(labels) == 0 {
continue
}
_, err = p.cache.Update(ctx, content.Info{
Digest: dgst,
Labels: labels,
}, fieldpaths...)
if err != nil {
return err
}
}
return nil
}
func filterRefs(refs []ocispecs.Descriptor, cfg *remotes.FetchReferrersConfig) []ocispecs.Descriptor {
if len(cfg.ArtifactTypes) == 0 {
return refs
}
out := []ocispecs.Descriptor{}
for _, ref := range refs {
if slices.Contains(cfg.ArtifactTypes, ref.ArtifactType) {
out = append(out, ref)
}
}
return out
}
func addName(existing, name string) string {
if existing == "" {
return name
}
m := map[string]struct{}{}
for n := range strings.SplitSeq(existing, ",") {
m[n] = struct{}{}
}
m[name] = struct{}{}
var names []string
for n := range m {
names = append(names, n)
}
slices.Sort(names)
return strings.Join(names, ",")
}
func readArtifactType(dt []byte) string {
var mfst ocispecs.Manifest
if err := json.Unmarshal(dt, &mfst); err != nil {
return ""
}
return mfst.ArtifactType
}
+1 -1
View File
@@ -12,7 +12,7 @@ import (
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/sdk"
"go.opentelemetry.io/otel/sdk/resource"
semconv "go.opentelemetry.io/otel/semconv/v1.26.0"
semconv "go.opentelemetry.io/otel/semconv/v1.37.0"
)
var (
+2 -3
View File
@@ -11,10 +11,9 @@ import (
"github.com/pkg/errors"
"go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace"
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/propagation"
semconv "go.opentelemetry.io/otel/semconv/v1.26.0"
semconv "go.opentelemetry.io/otel/semconv/v1.37.0"
"go.opentelemetry.io/otel/trace"
"go.opentelemetry.io/otel/trace/noop"
)
@@ -43,7 +42,7 @@ func FinishWithError(span trace.Span, err error) {
if err != nil {
span.RecordError(err)
if hasStacktrace(err) {
span.SetAttributes(attribute.String(string(semconv.ExceptionStacktraceKey), fmt.Sprintf("%+v", stack.Formatter(err))))
span.SetAttributes(semconv.ExceptionStacktrace(fmt.Sprintf("%+v", stack.Formatter(err))))
}
span.SetStatus(codes.Error, err.Error())
}
-40
View File
@@ -36,10 +36,6 @@ import (
const ImpliedDirectoryMode = 0o755
type (
// Compression is the state represents if compressed or not.
//
// Deprecated: use [compression.Compression].
Compression = compression.Compression
// WhiteoutFormat is the format of whiteouts unpacked
WhiteoutFormat int
@@ -95,14 +91,6 @@ func NewDefaultArchiver() *Archiver {
// in order for the test to pass.
type breakoutError error
const (
Uncompressed = compression.None // Deprecated: use [compression.None].
Bzip2 = compression.Bzip2 // Deprecated: use [compression.Bzip2].
Gzip = compression.Gzip // Deprecated: use [compression.Gzip].
Xz = compression.Xz // Deprecated: use [compression.Xz].
Zstd = compression.Zstd // Deprecated: use [compression.Zstd].
)
const (
AUFSWhiteoutFormat WhiteoutFormat = 0 // AUFSWhiteoutFormat is the default format for whiteouts
OverlayWhiteoutFormat WhiteoutFormat = 1 // OverlayWhiteoutFormat formats whiteout according to the overlay standard.
@@ -126,27 +114,6 @@ func IsArchivePath(path string) bool {
return err == nil
}
// DetectCompression detects the compression algorithm of the source.
//
// Deprecated: use [compression.Detect].
func DetectCompression(source []byte) compression.Compression {
return compression.Detect(source)
}
// DecompressStream decompresses the archive and returns a ReaderCloser with the decompressed archive.
//
// Deprecated: use [compression.DecompressStream].
func DecompressStream(archive io.Reader) (io.ReadCloser, error) {
return compression.DecompressStream(archive)
}
// CompressStream compresses the dest with specified compression algorithm.
//
// Deprecated: use [compression.CompressStream].
func CompressStream(dest io.Writer, comp compression.Compression) (io.WriteCloser, error) {
return compression.CompressStream(dest, comp)
}
// TarModifierFunc is a function that can be passed to ReplaceFileTarWrapper to
// modify the contents or header of an entry in the archive. If the file already
// exists in the archive the TarModifierFunc will be called with the Header and
@@ -235,13 +202,6 @@ func ReplaceFileTarWrapper(inputTarStream io.ReadCloser, mods map[string]TarModi
return pipeReader
}
// FileInfoHeaderNoLookups creates a partially-populated tar.Header from fi.
//
// Deprecated: use [tarheader.FileInfoHeaderNoLookups].
func FileInfoHeaderNoLookups(fi os.FileInfo, link string) (*tar.Header, error) {
return tarheader.FileInfoHeaderNoLookups(fi, link)
}
// FileInfoHeader creates a populated Header from fi.
//
// Compared to the archive/tar package, this function fills in less information
+1 -1
View File
@@ -1,4 +1,4 @@
//go:build !linux && !windows
//go:build darwin || freebsd || netbsd
package archive
+52 -10
View File
@@ -111,6 +111,7 @@ func Copy(ctx context.Context, srcRoot, src, dstRoot, dst string, opts ...Opt) e
if err != nil {
return err
}
c.testHookLstat = ci.testHookLstat
srcs := []string{src}
if ci.AllowWildcards {
@@ -206,6 +207,9 @@ type CopyInfo struct {
// replace any existing symlink or file)
AlwaysReplaceExistingDestPaths bool
ChangeFunc fsutil.ChangeFunc
// testHookLstat is called before each os.Lstat if non-nil (for testing only)
testHookLstat func(path string)
}
type Opt func(*CopyInfo)
@@ -272,6 +276,7 @@ type copier struct {
changefn fsutil.ChangeFunc
root string
alwaysReplaceExistingDestPaths bool
testHookLstat func(string)
}
type parentDir struct {
@@ -317,6 +322,7 @@ func newCopier(root string, chown Chowner, tm *time.Time, mode *int, modeSet *mo
excludePatternMatcher: excludePatternMatcher,
changefn: changeFunc,
alwaysReplaceExistingDestPaths: alwaysReplaceExistingDestPaths,
testHookLstat: nil,
}, nil
}
@@ -328,16 +334,10 @@ func (c *copier) copy(ctx context.Context, src, srcComponents, target string, ov
default:
}
fi, err := os.Lstat(src)
if err != nil {
return errors.Wrapf(err, "failed to stat %s", src)
}
targetFi, err := os.Lstat(target)
if err != nil && !os.IsNotExist(err) {
return errors.Wrapf(err, "failed to stat %s", src)
}
// Check exclude patterns BEFORE calling os.Lstat to avoid permission errors
// on inaccessible files/directories (e.g., protected Windows system folders)
include := true
excluded := false
var (
includeMatchInfo patternmatcher.MatchInfo
excludeMatchInfo patternmatcher.MatchInfo
@@ -345,6 +345,7 @@ func (c *copier) copy(ctx context.Context, src, srcComponents, target string, ov
if srcComponents != "" {
matchesIncludePattern := false
matchesExcludePattern := false
var err error
matchesIncludePattern, includeMatchInfo, err = c.include(srcComponents, parentIncludeMatchInfo)
if err != nil {
return err
@@ -357,7 +358,40 @@ func (c *copier) copy(ctx context.Context, src, srcComponents, target string, ov
}
if matchesExcludePattern {
include = false
excluded = true
}
// Optimization: Skip os.Lstat() for excluded paths when safe to do so.
// We can skip Lstat if:
// 1. The path is explicitly excluded
// 2. There are no include patterns (no need to check children)
// 3. There are no negation patterns in excludes (no exceptions to exclusions)
//
// This prevents "Access is denied" errors on Windows protected folders
// like "System Volume Information" and "WcSandboxState".
canSkip := !include && c.includePatternMatcher == nil &&
(c.excludePatternMatcher == nil || !c.excludePatternMatcher.Exclusions())
if canSkip {
return nil
}
}
if c.testHookLstat != nil {
c.testHookLstat(src)
}
fi, err := os.Lstat(src)
if err != nil {
return errors.Wrapf(err, "failed to stat %s", src)
}
// After Lstat, if this item is excluded and is NOT a directory, skip it
if !include && !fi.IsDir() {
return nil
}
targetFi, err := os.Lstat(target)
if err != nil && !os.IsNotExist(err) {
return errors.Wrapf(err, "failed to stat %s", target)
}
if include {
@@ -388,7 +422,7 @@ func (c *copier) copy(ctx context.Context, src, srcComponents, target string, ov
case fi.IsDir():
if created, err := c.copyDirectory(
ctx, src, srcComponents, target, fi, overwriteTargetMetadata,
include, includeMatchInfo, excludeMatchInfo,
include, excluded, includeMatchInfo, excludeMatchInfo,
); err != nil {
return err
} else if !overwriteTargetMetadata {
@@ -539,6 +573,7 @@ func (c *copier) copyDirectory(
stat os.FileInfo,
overwriteTargetMetadata bool,
include bool,
excluded bool,
includeMatchInfo patternmatcher.MatchInfo,
excludeMatchInfo patternmatcher.MatchInfo,
) (bool, error) {
@@ -577,6 +612,13 @@ func (c *copier) copyDirectory(
c.parentDirs = c.parentDirs[:len(c.parentDirs)-1]
}()
// Skip reading directory contents if explicitly excluded AND no negation patterns exist.
// If negation patterns exist (e.g., "!bar/baz"), we must traverse excluded directories
// to find children that might be un-excluded by the negation.
if excluded && (c.excludePatternMatcher == nil || !c.excludePatternMatcher.Exclusions()) {
return false, nil
}
fis, err := os.ReadDir(src)
if err != nil {
return false, errors.Wrapf(err, "failed to read %s", src)
-3
View File
@@ -1,3 +0,0 @@
# Semconv v1.26.0
[![PkgGoDev](https://pkg.go.dev/badge/go.opentelemetry.io/otel/semconv/v1.26.0)](https://pkg.go.dev/go.opentelemetry.io/otel/semconv/v1.26.0)
File diff suppressed because it is too large Load Diff
-9
View File
@@ -1,9 +0,0 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
// Package semconv implements OpenTelemetry semantic conventions.
//
// OpenTelemetry semantic conventions are agreed standardized naming
// patterns for OpenTelemetry things. This package represents the v1.26.0
// version of the OpenTelemetry semantic conventions.
package semconv // import "go.opentelemetry.io/otel/semconv/v1.26.0"
-9
View File
@@ -1,9 +0,0 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
package semconv // import "go.opentelemetry.io/otel/semconv/v1.26.0"
const (
// ExceptionEventName is the name of the Span event representing an exception.
ExceptionEventName = "exception"
)
File diff suppressed because it is too large Load Diff
-9
View File
@@ -1,9 +0,0 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
package semconv // import "go.opentelemetry.io/otel/semconv/v1.26.0"
// SchemaURL is the schema URL that matches the version of the semantic conventions
// that this package defines. Semconv packages starting from v1.4.0 must declare
// non-empty schema URL in the form https://opentelemetry.io/schemas/<version>
const SchemaURL = "https://opentelemetry.io/schemas/1.26.0"
+3 -4
View File
@@ -421,7 +421,7 @@ github.com/mitchellh/go-wordwrap
# github.com/mitchellh/hashstructure/v2 v2.0.2
## explicit; go 1.14
github.com/mitchellh/hashstructure/v2
# github.com/moby/buildkit v0.26.2
# github.com/moby/buildkit v0.26.1-0.20260106154623-ed6dc749ce40
## explicit; go 1.24.3
github.com/moby/buildkit/api/services/control
github.com/moby/buildkit/api/types
@@ -514,7 +514,7 @@ github.com/moby/buildkit/version
# github.com/moby/docker-image-spec v1.3.1
## explicit; go 1.18
github.com/moby/docker-image-spec/specs-go/v1
# github.com/moby/go-archive v0.1.0
# github.com/moby/go-archive v0.2.0
## explicit; go 1.23.0
github.com/moby/go-archive
github.com/moby/go-archive/compression
@@ -670,7 +670,7 @@ github.com/stretchr/testify/require
# github.com/tonistiigi/dchapes-mode v0.0.0-20250318174251-73d941a28323
## explicit; go 1.21
github.com/tonistiigi/dchapes-mode
# github.com/tonistiigi/fsutil v0.0.0-20250605211040-586307ad452f
# github.com/tonistiigi/fsutil v0.0.0-20251211185533-a2aa163d723f
## explicit; go 1.21
github.com/tonistiigi/fsutil
github.com/tonistiigi/fsutil/copy
@@ -732,7 +732,6 @@ go.opentelemetry.io/otel/codes
go.opentelemetry.io/otel/internal/baggage
go.opentelemetry.io/otel/internal/global
go.opentelemetry.io/otel/propagation
go.opentelemetry.io/otel/semconv/v1.26.0
go.opentelemetry.io/otel/semconv/v1.37.0
go.opentelemetry.io/otel/semconv/v1.37.0/httpconv
go.opentelemetry.io/otel/semconv/v1.37.0/otelconv