diff --git a/build/build.go b/build/build.go index 9df07a90..05fced84 100644 --- a/build/build.go +++ b/build/build.go @@ -219,7 +219,7 @@ func BuildWithResultHandler(ctx context.Context, nodes []builder.Node, opt map[s if err != nil { return nil, err } - so, release, err := toSolveOpt(ctx, np.Node(), multiDriver, opt, gatewayOpts, configDir, w, docker) + so, release, err := toSolveOpt(ctx, np.Node(), multiDriver, opt, gatewayOpts, configDir, addVCSLocalDir, w, docker) if err != nil { return nil, err } @@ -229,9 +229,6 @@ func BuildWithResultHandler(ctx context.Context, nodes []builder.Node, opt map[s for k, v := range gitattrs { so.FrontendAttrs[k] = v } - if addVCSLocalDir != nil { - addVCSLocalDir(so) - } defers = append(defers, release) reqn = append(reqn, &reqForNode{ resolvedNode: np, diff --git a/build/git.go b/build/git.go index 3655bfa3..8ee1875c 100644 --- a/build/git.go +++ b/build/git.go @@ -17,7 +17,7 @@ import ( const DockerfileLabel = "com.docker.image.source.entrypoint" -func getGitAttributes(ctx context.Context, contextPath string, dockerfilePath string) (map[string]string, func(*client.SolveOpt), error) { +func getGitAttributes(ctx context.Context, contextPath string, dockerfilePath string) (map[string]string, func(key, dir string, so *client.SolveOpt), error) { res := make(map[string]string) if contextPath == "" { return nil, nil, nil @@ -112,26 +112,20 @@ func getGitAttributes(ctx context.Context, contextPath string, dockerfilePath st } } - return res, func(so *client.SolveOpt) { + return res, func(key, dir string, so *client.SolveOpt) { if !setGitInfo || root == "" { return } - for k, dir := range so.LocalDirs { - dir, err = filepath.EvalSymlinks(dir) - if err != nil { - continue - } - dir, err = filepath.Abs(dir) - if err != nil { - continue - } - if lp, err := osutil.GetLongPathName(dir); err == nil { - dir = lp - } - dir = osutil.SanitizePath(dir) - if r, err := filepath.Rel(root, dir); err == nil && !strings.HasPrefix(r, "..") { - so.FrontendAttrs["vcs:localdir:"+k] = r - } + dir, err := filepath.Abs(dir) + if err != nil { + return + } + if lp, err := osutil.GetLongPathName(dir); err == nil { + dir = lp + } + dir = osutil.SanitizePath(dir) + if r, err := filepath.Rel(root, dir); err == nil && !strings.HasPrefix(r, "..") { + so.FrontendAttrs["vcs:localdir:"+key] = r } }, nil } diff --git a/build/git_test.go b/build/git_test.go index da954ddb..d8698507 100644 --- a/build/git_test.go +++ b/build/git_test.go @@ -161,19 +161,17 @@ func TestLocalDirs(t *testing.T) { so := &client.SolveOpt{ FrontendAttrs: map[string]string{}, - LocalDirs: map[string]string{ - "context": ".", - "dockerfile": ".", - }, } _, addVCSLocalDir, err := getGitAttributes(context.Background(), ".", "Dockerfile") require.NoError(t, err) require.NotNil(t, addVCSLocalDir) - addVCSLocalDir(so) + require.NoError(t, setLocalMount("context", ".", so, addVCSLocalDir)) require.Contains(t, so.FrontendAttrs, "vcs:localdir:context") assert.Equal(t, ".", so.FrontendAttrs["vcs:localdir:context"]) + + require.NoError(t, setLocalMount("dockerfile", ".", so, addVCSLocalDir)) require.Contains(t, so.FrontendAttrs, "vcs:localdir:dockerfile") assert.Equal(t, ".", so.FrontendAttrs["vcs:localdir:dockerfile"]) } @@ -195,19 +193,17 @@ func TestLocalDirsSub(t *testing.T) { so := &client.SolveOpt{ FrontendAttrs: map[string]string{}, - LocalDirs: map[string]string{ - "context": ".", - "dockerfile": "app", - }, } _, addVCSLocalDir, err := getGitAttributes(context.Background(), ".", "app/Dockerfile") require.NoError(t, err) require.NotNil(t, addVCSLocalDir) - addVCSLocalDir(so) + require.NoError(t, setLocalMount("context", ".", so, addVCSLocalDir)) require.Contains(t, so.FrontendAttrs, "vcs:localdir:context") assert.Equal(t, ".", so.FrontendAttrs["vcs:localdir:context"]) + + require.NoError(t, setLocalMount("dockerfile", "app", so, addVCSLocalDir)) require.Contains(t, so.FrontendAttrs, "vcs:localdir:dockerfile") assert.Equal(t, "app", so.FrontendAttrs["vcs:localdir:dockerfile"]) } diff --git a/build/opt.go b/build/opt.go index a51003ba..0750f4dd 100644 --- a/build/opt.go +++ b/build/opt.go @@ -32,9 +32,10 @@ import ( "github.com/moby/buildkit/util/entitlements" "github.com/opencontainers/go-digest" "github.com/pkg/errors" + "github.com/tonistiigi/fsutil" ) -func toSolveOpt(ctx context.Context, node builder.Node, multiDriver bool, opt Options, bopts gateway.BuildOpts, configDir string, pw progress.Writer, docker *dockerutil.Client) (solveOpt *client.SolveOpt, release func(), err error) { +func toSolveOpt(ctx context.Context, node builder.Node, multiDriver bool, opt Options, bopts gateway.BuildOpts, configDir string, addVCSLocalDir func(key, dir string, so *client.SolveOpt), pw progress.Writer, docker *dockerutil.Client) (_ *client.SolveOpt, release func(), err error) { nodeDriver := node.Driver defers := make([]func(), 0, 2) releaseF := func() { @@ -97,7 +98,7 @@ func toSolveOpt(ctx context.Context, node builder.Node, multiDriver bool, opt Op Ref: opt.Ref, Frontend: "dockerfile.v0", FrontendAttrs: map[string]string{}, - LocalDirs: map[string]string{}, + LocalMounts: map[string]fsutil.FS{}, CacheExports: cacheTo, CacheImports: cacheFrom, AllowedEntitlements: opt.Allow, @@ -262,7 +263,7 @@ func toSolveOpt(ctx context.Context, node builder.Node, multiDriver bool, opt Op so.Exports = opt.Exports so.Session = opt.Session - releaseLoad, err := loadInputs(ctx, nodeDriver, opt.Inputs, pw, &so) + releaseLoad, err := loadInputs(ctx, nodeDriver, opt.Inputs, addVCSLocalDir, pw, &so) if err != nil { return nil, nil, err } @@ -352,7 +353,7 @@ func toSolveOpt(ctx context.Context, node builder.Node, multiDriver bool, opt Op return &so, releaseF, nil } -func loadInputs(ctx context.Context, d *driver.DriverHandle, inp Inputs, pw progress.Writer, target *client.SolveOpt) (func(), error) { +func loadInputs(ctx context.Context, d *driver.DriverHandle, inp Inputs, addVCSLocalDir func(key, dir string, so *client.SolveOpt), pw progress.Writer, target *client.SolveOpt) (func(), error) { if inp.ContextPath == "" { return nil, errors.New("please specify build context (e.g. \".\" for the current directory)") } @@ -398,11 +399,15 @@ func loadInputs(ctx context.Context, d *driver.DriverHandle, inp Inputs, pw prog dockerfileReader = buf inp.ContextPath, _ = os.MkdirTemp("", "empty-dir") toRemove = append(toRemove, inp.ContextPath) - target.LocalDirs["context"] = inp.ContextPath + if err := setLocalMount("context", inp.ContextPath, target, addVCSLocalDir); err != nil { + return nil, err + } } } case osutil.IsLocalDir(inp.ContextPath): - target.LocalDirs["context"] = inp.ContextPath + if err := setLocalMount("context", inp.ContextPath, target, addVCSLocalDir); err != nil { + return nil, err + } switch inp.DockerfilePath { case "-": dockerfileReader = inp.InStream @@ -454,7 +459,9 @@ func loadInputs(ctx context.Context, d *driver.DriverHandle, inp Inputs, pw prog } if dockerfileDir != "" { - target.LocalDirs["dockerfile"] = dockerfileDir + if err := setLocalMount("dockerfile", dockerfileDir, target, addVCSLocalDir); err != nil { + return nil, err + } dockerfileName = handleLowercaseDockerfile(dockerfileDir, dockerfileName) } @@ -549,7 +556,9 @@ func loadInputs(ctx context.Context, d *driver.DriverHandle, inp Inputs, pw prog if k == "context" || k == "dockerfile" { localName = "_" + k // underscore to avoid collisions } - target.LocalDirs[localName] = v.Path + if err := setLocalMount(localName, v.Path, target, addVCSLocalDir); err != nil { + return nil, err + } target.FrontendAttrs["context:"+k] = "local:" + localName } @@ -561,6 +570,25 @@ func loadInputs(ctx context.Context, d *driver.DriverHandle, inp Inputs, pw prog return release, nil } +func setLocalMount(name, root string, so *client.SolveOpt, addVCSLocalDir func(key, dir string, so *client.SolveOpt)) error { + lm, err := fsutil.NewFS(root) + if err != nil { + return err + } + root, err = filepath.EvalSymlinks(root) // keep same behavior as fsutil.NewFS + if err != nil { + return err + } + if so.LocalMounts == nil { + so.LocalMounts = map[string]fsutil.FS{} + } + so.LocalMounts[name] = lm + if addVCSLocalDir != nil { + addVCSLocalDir(name, root, so) + } + return nil +} + func createTempDockerfile(r io.Reader) (string, error) { dir, err := os.MkdirTemp("", "dockerfile") if err != nil { diff --git a/go.mod b/go.mod index 7cfc48bb..01ea50ff 100644 --- a/go.mod +++ b/go.mod @@ -38,6 +38,7 @@ require ( github.com/spf13/cobra v1.8.0 github.com/spf13/pflag v1.0.5 github.com/stretchr/testify v1.8.4 + github.com/tonistiigi/fsutil v0.0.0-20240301111122-7525a1af2bb5 github.com/zclconf/go-cty v1.14.1 go.opentelemetry.io/otel v1.21.0 go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.42.0 @@ -140,7 +141,6 @@ require ( github.com/secure-systems-lab/go-securesystemslib v0.4.0 // indirect github.com/shibumi/go-pathspec v1.3.0 // indirect github.com/theupdateframework/notary v0.7.0 // indirect - github.com/tonistiigi/fsutil v0.0.0-20240301111122-7525a1af2bb5 // indirect github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea // indirect github.com/tonistiigi/vt100 v0.0.0-20230623042737-f9a4f7ef6531 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect