bake: add file-relative path opt-in

Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
This commit is contained in:
CrazyMax
2026-07-21 11:25:55 +02:00
committed by CrazyMax
parent 9618d82eff
commit b38d1004e3
6 changed files with 250 additions and 5 deletions
+53 -3
View File
@@ -23,6 +23,7 @@ import (
"github.com/docker/buildx/bake/hclparser"
"github.com/docker/buildx/build"
"github.com/docker/buildx/util/buildflags"
"github.com/docker/buildx/util/osutil"
"github.com/docker/buildx/util/platformutil"
"github.com/docker/buildx/util/progress"
"github.com/docker/buildx/util/urlutil"
@@ -46,6 +47,10 @@ type File struct {
Data []byte
}
type ParseOpt struct {
FileRelativePaths bool
}
type Override struct {
Value string
ArrValue []string
@@ -197,8 +202,8 @@ func ListTargets(files []File) ([]string, error) {
return dedupSlice(targets), nil
}
func ReadTargets(ctx context.Context, files []File, targets, overrides []string, defaults, vars map[string]string, ent *EntitlementConf) (map[string]*Target, map[string]*Group, error) {
c, _, err := ParseFiles(files, defaults, vars)
func ReadTargets(ctx context.Context, files []File, targets, overrides []string, defaults, vars map[string]string, ent *EntitlementConf, opts ...ParseOpt) (map[string]*Target, map[string]*Group, error) {
c, _, err := ParseFiles(files, defaults, vars, opts...)
if err != nil {
return nil, nil, err
}
@@ -337,7 +342,7 @@ func (c Config) matchNames(pattern string) ([]string, error) {
return names, nil
}
func ParseFiles(files []File, defaults, vars map[string]string) (_ *Config, _ *hclparser.ParseMeta, err error) {
func ParseFiles(files []File, defaults, vars map[string]string, opts ...ParseOpt) (_ *Config, _ *hclparser.ParseMeta, err error) {
defer func() {
err = formatHCLError(err, files)
}()
@@ -413,9 +418,54 @@ func ParseFiles(files []File, defaults, vars map[string]string) (_ *Config, _ *h
pm = *res
}
for _, opt := range opts {
if opt.FileRelativePaths {
rebaseContextPaths(&c, files)
break
}
}
return &c, &pm, nil
}
func rebaseContextPaths(c *Config, files []File) {
base, ok := firstLocalFileDir(files)
if !ok {
return
}
for _, t := range c.Targets {
if t.Context != nil {
contextPath := rebaseContextPath(base, *t.Context)
t.Context = &contextPath
}
for k, v := range t.Contexts {
t.Contexts[k] = rebaseContextPath(base, v)
}
}
}
func firstLocalFileDir(files []File) (string, bool) {
if len(files) == 0 || files[0].Name == "-" || urlutil.IsRemoteURL(files[0].Name) {
return "", false
}
return filepath.Dir(files[0].Name), true
}
func rebaseContextPath(base, p string) string {
if p == "" || isSpecialContextPath(p) || filepath.IsAbs(p) {
return p
}
return osutil.SanitizePath(filepath.Join(base, filepath.FromSlash(p)))
}
func isSpecialContextPath(p string) bool {
return strings.HasPrefix(p, "cwd://") ||
strings.HasPrefix(p, "target:") ||
strings.HasPrefix(p, "docker-image:") ||
strings.HasPrefix(p, "oci-layout://") ||
urlutil.IsRemoteURL(p)
}
func dedupeConfig(c Config) Config {
c2 := c
c2.Groups = make([]*Group, 0, len(c2.Groups))
+93
View File
@@ -763,6 +763,99 @@ func TestHCLDockerfileCwdPrefix(t *testing.T) {
assert.Equal(t, ".", bo["app"].Inputs.ContextPath)
}
func TestContextPathRebase(t *testing.T) {
fp := File{
Name: filepath.Join("subdir", "docker-bake.hcl"),
Data: []byte(`
target "base" {
context = "base"
}
target "app" {
context = "."
dockerfile = "Dockerfile.app"
contexts = {
shared = "../shared"
cwd = "cwd://local"
linked = "target:base"
image = "docker-image://alpine:latest"
layout = "oci-layout://layout"
}
}`),
}
m, _, err := ReadTargets(context.TODO(), []File{fp}, []string{"app"}, nil, nil, nil, &EntitlementConf{}, ParseOpt{
FileRelativePaths: true,
})
require.NoError(t, err)
require.Equal(t, filepath.ToSlash(filepath.Clean("subdir")), *m["app"].Context)
require.Equal(t, "Dockerfile.app", *m["app"].Dockerfile)
require.Equal(t, filepath.ToSlash(filepath.Clean("shared")), m["app"].Contexts["shared"])
require.Equal(t, "cwd://local", m["app"].Contexts["cwd"])
require.Equal(t, "target:base", m["app"].Contexts["linked"])
require.Equal(t, "docker-image://alpine:latest", m["app"].Contexts["image"])
require.Equal(t, "oci-layout://layout", m["app"].Contexts["layout"])
require.Equal(t, filepath.ToSlash(filepath.Clean("subdir/base")), *m["base"].Context)
bo, err := TargetsToBuildOpt(m, &Input{})
require.NoError(t, err)
require.Equal(t, filepath.ToSlash(filepath.Clean("subdir")), bo["app"].Inputs.ContextPath)
require.Equal(t, filepath.Join("subdir", "Dockerfile.app"), bo["app"].Inputs.DockerfilePath)
require.Equal(t, filepath.ToSlash(filepath.Clean("shared")), bo["app"].Inputs.NamedContexts["shared"].Path)
}
func TestOverridesNotRebased(t *testing.T) {
fp := File{
Name: filepath.Join("subdir", "docker-bake.hcl"),
Data: []byte(`
target "app" {
context = "."
contexts = {
shared = "../shared"
}
}`),
}
m, _, err := ReadTargets(context.TODO(), []File{fp}, []string{"app"}, []string{
"app.context=override",
"app.contexts.shared=override-shared",
}, nil, nil, &EntitlementConf{}, ParseOpt{
FileRelativePaths: true,
})
require.NoError(t, err)
require.Equal(t, "override", *m["app"].Context)
require.Equal(t, "override-shared", m["app"].Contexts["shared"])
}
func TestComposePathRebase(t *testing.T) {
fp := File{
Name: filepath.Join("tests", "docker-compose.yml"),
Data: []byte(`
services:
debian:
build:
context: ./dockerfiles/debian
additional_contexts:
shared: ../shared
`),
}
m, _, err := ReadTargets(context.TODO(), []File{fp}, []string{"debian"}, nil, nil, nil, &EntitlementConf{}, ParseOpt{
FileRelativePaths: true,
})
require.NoError(t, err)
require.Equal(t, filepath.ToSlash(filepath.Clean("tests/dockerfiles/debian")), *m["debian"].Context)
require.Equal(t, filepath.ToSlash(filepath.Clean("shared")), m["debian"].Contexts["shared"])
bo, err := TargetsToBuildOpt(m, &Input{})
require.NoError(t, err)
require.Equal(t, filepath.ToSlash(filepath.Clean("tests/dockerfiles/debian")), bo["debian"].Inputs.ContextPath)
require.Equal(t, filepath.Join("tests", "dockerfiles", "debian", "Dockerfile"), bo["debian"].Inputs.DockerfilePath)
}
func TestOverrideMerge(t *testing.T) {
fp := File{
Name: "docker-bake.hcl",