diff --git a/frontend/dockerfile/builder/build.go b/frontend/dockerfile/builder/build.go index 76777eea..6af3bab3 100644 --- a/frontend/dockerfile/builder/build.go +++ b/frontend/dockerfile/builder/build.go @@ -8,6 +8,7 @@ import ( "encoding/json" "fmt" "net" + "path" "regexp" "strconv" "strings" @@ -46,6 +47,7 @@ const ( keyOverrideCopyImage = "override-copy-image" // remove after CopyOp implemented keyNameContext = "contextkey" keyNameDockerfile = "dockerfilekey" + keyContextSubDir = "contextsubdir" ) var httpPrefix = regexp.MustCompile("^https?://") @@ -122,6 +124,8 @@ func Build(ctx context.Context, c client.Client) (*client.Result, error) { dockerfile2llb.WithInternalName(name), ) + fileop := useFileOp(opts, &caps) + var buildContext *llb.State isScratchContext := false if st, ok := detectGitContext(opts[localNameContext]); ok { @@ -157,7 +161,6 @@ func Build(ctx context.Context, c client.Client) (*client.Result, error) { return nil, errors.Errorf("failed to read downloaded context") } if isArchive(dt) { - fileop := useFileOp(opts, &caps) if fileop { bc := llb.Scratch().File(llb.Copy(httpContext, "/context", "/", &llb.CopyInfo{ AttemptUnpack: true, @@ -190,6 +193,12 @@ func Build(ctx context.Context, c client.Client) (*client.Result, error) { } } + if buildContext != nil { + if sub, ok := opts[keyContextSubDir]; ok { + buildContext = scopeToSubDir(buildContext, fileop, sub) + } + } + def, err := src.Marshal(marshalOpts...) if err != nil { return nil, errors.Wrapf(err, "failed to marshal local source") @@ -561,3 +570,17 @@ func useFileOp(args map[string]string, caps *apicaps.CapSet) bool { } return enabled && caps != nil && caps.Supports(pb.CapFileBase) == nil } + +func scopeToSubDir(c *llb.State, fileop bool, dir string) *llb.State { + if fileop { + bc := llb.Scratch().File(llb.Copy(*c, dir, "/", &llb.CopyInfo{ + CopyDirContentsOnly: true, + })) + return &bc + } + unpack := llb.Image(dockerfile2llb.DefaultCopyImage, dockerfile2llb.WithInternalName("helper image for file operations")). + Run(llb.Shlexf("copy %s/. /out/", path.Join("/src", dir)), llb.ReadonlyRootFS(), dockerfile2llb.WithInternalName("filtering build context")) + unpack.AddMount("/src", *c, llb.Readonly) + bc := unpack.AddMount("/out", llb.Scratch()) + return &bc +} diff --git a/frontend/dockerfile/dockerfile_test.go b/frontend/dockerfile/dockerfile_test.go index cf261a9e..7f71cf95 100644 --- a/frontend/dockerfile/dockerfile_test.go +++ b/frontend/dockerfile/dockerfile_test.go @@ -3844,7 +3844,7 @@ func testTarContextExternalDockerfile(t *testing.T, sb integration.Sandbox) { buf := bytes.NewBuffer(nil) tw := tar.NewWriter(buf) err := tw.WriteHeader(&tar.Header{ - Name: "foo", + Name: "sub/dir/foo", Typeflag: tar.TypeReg, Size: int64(len(foo)), Mode: 0644, @@ -3882,6 +3882,7 @@ COPY foo bar "build-arg:BUILDKIT_DISABLE_FILEOP": strconv.FormatBool(!isFileOp), "context": url, "dockerfilekey": builder.DefaultLocalNameDockerfile, + "contextsubdir": "sub/dir", }, Session: []session.Attachable{up}, LocalDirs: map[string]string{