Skip to content

bake: add file-relative path opt-in#3935

Open
crazy-max wants to merge 3 commits into
docker:masterfrom
crazy-max:bake-file-relative-paths
Open

bake: add file-relative path opt-in#3935
crazy-max wants to merge 3 commits into
docker:masterfrom
crazy-max:bake-file-relative-paths

Conversation

@crazy-max

@crazy-max crazy-max commented Jul 6, 2026

Copy link
Copy Markdown
Member

fixes #1028
fixes #197

This adds an opt-in Bake mode that resolves local directory paths in target.context and target.contexts relative to the Bake or Compose definition that owns those paths when BUILDX_BAKE_FILE_RELATIVE_PATHS=1 is set.

Bake now accepts a parser option that records the definition file for local build context paths and rebases them after HCL or Compose files are parsed. The buildx bake command enables this option from BUILDX_BAKE_FILE_RELATIVE_PATHS, while cwd:// remains available for paths that should stay relative to the invocation directory.

The opt-in also applies to targets that omit context, so the implicit default . is resolved from the target definition file instead of the invocation directory.

Compose files follow Compose project semantics for this opt-in, so local build contexts from Compose input are resolved from the directory of the first local Compose file rather than from override file locations.

@crazy-max

Copy link
Copy Markdown
Member Author

@crazy-max crazy-max force-pushed the bake-file-relative-paths branch from fc54769 to 4ce75f7 Compare July 6, 2026 12:16
@crazy-max crazy-max requested a review from tonistiigi July 6, 2026 12:24
@crazy-max crazy-max marked this pull request as ready for review July 6, 2026 12:33

@tonistiigi tonistiigi left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like default . does not atm take the relative mode into account


	t.Run("default-context-in-subdirectory", func(t *testing.T) {
		bakefile := []byte(`
target "default" {
  dockerfile-inline = <<EOT
FROM scratch
COPY marker /marker
EOT
}
`)

		dir := tmpdir(
			t,
			fstest.CreateDir("definitions", 0700),
			fstest.CreateFile("definitions/docker-bake.hcl", bakefile, 0600),
			fstest.CreateFile("definitions/marker", []byte("marker"), 0600),
		)
		dirDest := t.TempDir()

		out, err := bakeCmd(
			sb,
			withDir(dir),
			withArgs("--file", "definitions/docker-bake.hcl", "--set", "*.output=type=local,dest="+dirDest),
			withEnv("BUILDX_BAKE_FILE_RELATIVE_PATHS=1"),
		)
		require.NoError(t, err, out)
		require.FileExists(t, filepath.Join(dirDest, "marker"))
	})

Comment thread docs/reference/buildx_bake.md
@crazy-max

Copy link
Copy Markdown
Member Author

@tonistiigi Updated so file-relative mode is no longer based on the first Bake file.

The parser now tracks the definition file that contributed each path. For HCL targets, it records the source block filename while decoding the target. For Compose files, it parses files separately in this mode so each generated target keeps the Compose file directory before normal Bake merging happens.

The rebase now uses that metadata per field. An explicit context is rebased from the file that defined that context. Each named context in contexts is rebased from the file that defined that entry. If context is omitted, it now treats the implicit default . as coming from the target definition file, which fixes the default-context case you pointed out.

cwd:// remains the escape hatch for paths that should stay relative to the invocation directory, and special context forms such as target:, docker-image://, oci-layout://, remote URLs, and absolute paths are still left untouched.

Also added coverage for the implicit default context case, for multi-file definitions using the defining file instead of the first file, and for the Compose subdirectory case.

@crazy-max crazy-max force-pushed the bake-file-relative-paths branch 2 times, most recently from 9e98fbe to 61f0bf5 Compare July 10, 2026 14:23
@crazy-max

Copy link
Copy Markdown
Member Author

I adjusted the Compose side in the last commit so it follows Compose project directory semantics instead of the HCL Bake definition-file model.

For HCL Bake files, file-relative mode still resolves each path from the Bake file that defines that path. For Compose files, the base is now the first Compose file directory, which better matches what users expect when comparing docker compose --build with docker buildx bake -f compose.yml.

This also keeps Compose override files from accidentally changing the base directory for paths they add. I added a regression test that covers a second Compose file adding additional_contexts and another build target, and both resolve from the first Compose file directory.

crazy-max and others added 2 commits July 10, 2026 16:36
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
@crazy-max crazy-max force-pushed the bake-file-relative-paths branch from e48541b to 4a3ed37 Compare July 10, 2026 14:39
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
@crazy-max crazy-max force-pushed the bake-file-relative-paths branch from 4a3ed37 to 90119d8 Compare July 10, 2026 14:44
@crazy-max crazy-max requested a review from tonistiigi July 10, 2026 14:55

@tonistiigi tonistiigi left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

failing tests for the inherits case

func TestInheritedContextRebase(t *testing.T) {
	t.Run("same file", func(t *testing.T) {
		fp := File{
			Name: filepath.Join("subdir", "docker-bake.hcl"),
			Data: []byte(`
target "base" {
  context = "basectx"
}

target "app" {
  inherits = ["base"]
  tags = ["app:latest"]
}`),
		}

		m, _, err := ReadTargets(context.TODO(), []File{fp}, []string{"app"}, nil, nil, nil, &EntitlementConf{}, ParseOpt{
			FileRelativePaths: true,
		})
		require.NoError(t, err)

		// the context path is defined in subdir/docker-bake.hcl, so the
		// inherited value must resolve relative to that file
		require.Equal(t, filepath.ToSlash(filepath.Clean("subdir/basectx")), *m["app"].Context)
	})

	t.Run("cross file", func(t *testing.T) {
		fp1 := File{
			Name: filepath.Join("one", "docker-bake.hcl"),
			Data: []byte(`
target "base" {
  context = "basectx"
}`),
		}
		fp2 := File{
			Name: filepath.Join("two", "docker-bake.hcl"),
			Data: []byte(`
target "app" {
  inherits = ["base"]
  tags = ["app:latest"]
}`),
		}

		m, _, err := ReadTargets(context.TODO(), []File{fp1, fp2}, []string{"app"}, nil, nil, nil, &EntitlementConf{}, ParseOpt{
			FileRelativePaths: true,
		})
		require.NoError(t, err)

		// the context path is defined in one/docker-bake.hcl, so the
		// inherited value must resolve relative to that file, not to the
		// file that defines the inheriting target
		require.Equal(t, filepath.ToSlash(filepath.Clean("one/basectx")), *m["app"].Context)
	})
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

buildx bake: Specifying --file in another directory leads to failure Relative paths in compose file not resolved correctly?

2 participants