Skip to content

Commit cb54ddb

Browse files
committed
Allow bake files to be specified via environment variable
The environment variable `BUILDX_BAKE_FILE` (and optional variable `BUILDX_BAKE_FILE_SEPARATOR`) can be used to specify one or more bake files (similar to `compose`). This is mutually exclusive with`--file` (which takes precedence). This is done very early to ensure the values are treated just like `--file`, e.g., participate in telemetry. This includes leaving relative paths as-is, which deviates from `compose` (which makes them absolute). Signed-off-by: Roberto Villarreal <rrjjvv@yahoo.com>
1 parent eb43f4c commit cb54ddb

3 files changed

Lines changed: 208 additions & 0 deletions

File tree

commands/bake.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,11 @@ import (
4040
"go.opentelemetry.io/otel/attribute"
4141
)
4242

43+
const (
44+
bakeEnvFileSeparator = "BUILDX_BAKE_PATH_SEPARATOR"
45+
bakeEnvFilePath = "BUILDX_BAKE_FILE"
46+
)
47+
4348
type bakeOptions struct {
4449
files []string
4550
overrides []string
@@ -452,6 +457,13 @@ func bakeCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
452457
Aliases: []string{"f"},
453458
Short: "Build from a file",
454459
RunE: func(cmd *cobra.Command, args []string) error {
460+
if len(options.files) == 0 {
461+
envFiles, err := bakeEnvFiles(os.LookupEnv)
462+
if err != nil {
463+
return err
464+
}
465+
options.files = envFiles
466+
}
455467
// reset to nil to avoid override is unset
456468
if !cmd.Flags().Lookup("no-cache").Changed {
457469
cFlags.noCache = nil
@@ -504,6 +516,37 @@ func bakeCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
504516
return cmd
505517
}
506518

519+
func bakeEnvFiles(lookup func(string string) (string, bool)) ([]string, error) {
520+
sep, _ := lookup(bakeEnvFileSeparator)
521+
if sep == "" {
522+
sep = string(os.PathListSeparator)
523+
}
524+
f, ok := lookup(bakeEnvFilePath)
525+
if ok {
526+
return cleanPaths(strings.Split(f, sep))
527+
}
528+
return []string{}, nil
529+
}
530+
531+
func cleanPaths(p []string) ([]string, error) {
532+
var paths []string
533+
for _, f := range p {
534+
f = strings.TrimSpace(f)
535+
if f == "" {
536+
continue
537+
}
538+
if f == "-" {
539+
paths = append(paths, f)
540+
continue
541+
}
542+
if _, err := os.Stat(f); err != nil {
543+
return nil, err
544+
}
545+
paths = append(paths, f)
546+
}
547+
return paths, nil
548+
}
549+
507550
func saveLocalStateGroup(dockerCli command.Cli, in bakeOptions, targets []string, bo map[string]build.Options) error {
508551
l, err := localstate.New(confutil.NewConfig(dockerCli))
509552
if err != nil {

docs/reference/buildx_bake.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,11 @@ Use the `-f` / `--file` option to specify the build definition file to use.
143143
The file can be an HCL, JSON or Compose file. If multiple files are specified,
144144
all are read and the build configurations are combined.
145145

146+
Alternatively, the environment variable `BUILDX_BAKE_FILE` can be used to specify the build definition to use.
147+
This is mutually exclusive with `-f` / `--file`; if both are specified, the environment variable is ignored.
148+
Multiple definitions can be specified by separating them with the system's path separator
149+
(typically `;` on Windows and `:` elsewhere), but can be changed with `BUILDX_BAKE_PATH_SEPARATOR`.
150+
146151
You can pass the names of the targets to build, to build only specific target(s).
147152
The following example builds the `db` and `webapp-release` targets that are
148153
defined in the `docker-bake.dev.hcl` file:

tests/bake.go

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ var bakeTests = []func(t *testing.T, sb integration.Sandbox){
8181
testBakeMultiPlatform,
8282
testBakeCheckCallOutput,
8383
testBakeExtraHosts,
84+
testBakeFileFromEnvironment,
8485
}
8586

8687
func testBakePrint(t *testing.T, sb integration.Sandbox) {
@@ -2188,6 +2189,165 @@ target "default" {
21882189
require.NoError(t, err, out)
21892190
}
21902191

2192+
func testBakeFileFromEnvironment(t *testing.T, sb integration.Sandbox) {
2193+
bakeFileFirst := []byte(`
2194+
target "first" {
2195+
dockerfile-inline = "FROM scratch\nCOPY first /"
2196+
}
2197+
`)
2198+
bakeFileSecond := []byte(`
2199+
target "second" {
2200+
dockerfile-inline = "FROM scratch\nCOPY second /"
2201+
}
2202+
`)
2203+
2204+
t.Run("single file", func(t *testing.T) {
2205+
dir := tmpdir(t,
2206+
fstest.CreateFile("first.hcl", bakeFileFirst, 0600),
2207+
fstest.CreateFile("first", []byte("first"), 0600),
2208+
)
2209+
cmd := buildxCmd(sb,
2210+
withDir(dir),
2211+
withArgs("bake", "--progress=plain", "first"),
2212+
withEnv("BUILDX_BAKE_FILE=first.hcl"))
2213+
2214+
dt, err := cmd.CombinedOutput()
2215+
require.NoError(t, err, string(dt))
2216+
require.Contains(t, string(dt), `#1 [internal] load local bake definitions`)
2217+
require.Contains(t, string(dt), `#1 reading first.hcl`)
2218+
})
2219+
2220+
t.Run("single file, default ignored if present", func(t *testing.T) {
2221+
dir := tmpdir(t,
2222+
fstest.CreateFile("first.hcl", bakeFileFirst, 0600),
2223+
fstest.CreateFile("first", []byte("first"), 0600),
2224+
fstest.CreateFile("docker-bake.hcl", []byte("invalid bake file"), 0600),
2225+
)
2226+
cmd := buildxCmd(sb,
2227+
withDir(dir),
2228+
withArgs("bake", "--progress=plain", "first"),
2229+
withEnv("BUILDX_BAKE_FILE=first.hcl"))
2230+
2231+
dt, err := cmd.CombinedOutput()
2232+
require.NoError(t, err, string(dt))
2233+
require.Contains(t, string(dt), `#1 [internal] load local bake definitions`)
2234+
require.Contains(t, string(dt), `#1 reading first.hcl`)
2235+
require.NotContains(t, string(dt), "docker-bake.hcl")
2236+
})
2237+
2238+
t.Run("multiple files", func(t *testing.T) {
2239+
dir := tmpdir(t,
2240+
fstest.CreateFile("first.hcl", bakeFileFirst, 0600),
2241+
fstest.CreateFile("first", []byte("first"), 0600),
2242+
fstest.CreateFile("second.hcl", bakeFileSecond, 0600),
2243+
fstest.CreateFile("second", []byte("second"), 0600),
2244+
)
2245+
2246+
cmd := buildxCmd(sb,
2247+
withDir(dir),
2248+
withArgs("bake", "--progress=plain", "second", "first"),
2249+
withEnv("BUILDX_BAKE_FILE=first.hcl"+string(os.PathListSeparator)+"second.hcl"))
2250+
dt, err := cmd.CombinedOutput()
2251+
require.NoError(t, err, string(dt))
2252+
require.Contains(t, string(dt), `#1 [internal] load local bake definitions`)
2253+
require.Contains(t, string(dt), `#1 reading first.hcl`)
2254+
require.Contains(t, string(dt), `#1 reading second.hcl`)
2255+
})
2256+
2257+
t.Run("multiple files, custom separator", func(t *testing.T) {
2258+
dir := tmpdir(t,
2259+
fstest.CreateFile("first.hcl", bakeFileFirst, 0600),
2260+
fstest.CreateFile("first", []byte("first"), 0600),
2261+
fstest.CreateFile("second.hcl", bakeFileSecond, 0600),
2262+
fstest.CreateFile("second", []byte("second"), 0600),
2263+
)
2264+
2265+
cmd := buildxCmd(sb,
2266+
withDir(dir),
2267+
withArgs("bake", "--progress=plain", "second", "first"),
2268+
withEnv("BUILDX_BAKE_PATH_SEPARATOR=@", "BUILDX_BAKE_FILE=first.hcl@second.hcl"))
2269+
2270+
dt, err := cmd.CombinedOutput()
2271+
require.NoError(t, err, string(dt))
2272+
require.Contains(t, string(dt), `#1 [internal] load local bake definitions`)
2273+
require.Contains(t, string(dt), `#1 reading first.hcl`)
2274+
require.Contains(t, string(dt), `#1 reading second.hcl`)
2275+
})
2276+
2277+
t.Run("multiple files, one STDIN", func(t *testing.T) {
2278+
dir := tmpdir(t,
2279+
fstest.CreateFile("first.hcl", bakeFileFirst, 0600),
2280+
fstest.CreateFile("first", []byte("first"), 0600),
2281+
fstest.CreateFile("second", []byte("second"), 0600),
2282+
)
2283+
2284+
cmd := buildxCmd(sb,
2285+
withDir(dir),
2286+
withArgs("bake", "--progress=plain", "second", "first"),
2287+
withEnv("BUILDX_BAKE_FILE=first.hcl"+string(os.PathListSeparator)+"-"))
2288+
w, err := cmd.StdinPipe()
2289+
require.NoError(t, err)
2290+
go func() {
2291+
defer w.Close()
2292+
w.Write(bakeFileSecond)
2293+
}()
2294+
2295+
dt, err := cmd.CombinedOutput()
2296+
require.NoError(t, err, string(dt))
2297+
require.Contains(t, string(dt), `#1 [internal] load local bake definitions`)
2298+
require.Contains(t, string(dt), `#1 reading first.hcl`)
2299+
require.Contains(t, string(dt), `#1 reading from stdin`)
2300+
})
2301+
2302+
t.Run("env ignored if file arg passed", func(t *testing.T) {
2303+
dir := tmpdir(t,
2304+
fstest.CreateFile("first.hcl", bakeFileFirst, 0600),
2305+
fstest.CreateFile("first", []byte("first"), 0600),
2306+
fstest.CreateFile("second.hcl", bakeFileSecond, 0600),
2307+
)
2308+
cmd := buildxCmd(sb,
2309+
withDir(dir),
2310+
withArgs("bake", "--progress=plain", "-f", "first.hcl", "first", "second"),
2311+
withEnv("BUILDX_BAKE_FILE=second.hcl"))
2312+
2313+
dt, err := cmd.CombinedOutput()
2314+
require.Error(t, err, string(dt))
2315+
require.Contains(t, string(dt), "failed to find target second")
2316+
})
2317+
2318+
t.Run("file does not exist", func(t *testing.T) {
2319+
dir := tmpdir(t,
2320+
fstest.CreateFile("first.hcl", bakeFileFirst, 0600),
2321+
fstest.CreateFile("first", []byte("first"), 0600),
2322+
)
2323+
cmd := buildxCmd(sb,
2324+
withDir(dir),
2325+
withArgs("bake", "--progress=plain", "first"),
2326+
withEnv("BUILDX_BAKE_FILE=wrong.hcl"))
2327+
2328+
dt, err := cmd.CombinedOutput()
2329+
require.Error(t, err, string(dt))
2330+
require.Contains(t, string(dt), "wrong.hcl: no such file or directory")
2331+
})
2332+
2333+
for kind, val := range map[string]string{"missing": "", "whitespace": " "} {
2334+
t.Run(kind+" value ignored", func(t *testing.T) {
2335+
dir := tmpdir(t,
2336+
fstest.CreateFile("first.hcl", bakeFileFirst, 0600),
2337+
fstest.CreateFile("first", []byte("first"), 0600),
2338+
)
2339+
cmd := buildxCmd(sb,
2340+
withDir(dir),
2341+
withArgs("bake", "--progress=plain", "first"),
2342+
withEnv(fmt.Sprintf("BUILDX_BAKE_FILE=%s", val)))
2343+
2344+
dt, err := cmd.CombinedOutput()
2345+
require.Error(t, err, string(dt))
2346+
require.Contains(t, string(dt), "couldn't find a bake definition")
2347+
})
2348+
}
2349+
}
2350+
21912351
func writeTempPrivateKey(fp string) error {
21922352
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
21932353
if err != nil {

0 commit comments

Comments
 (0)