Skip to content

Commit 0bed0b5

Browse files
authored
Merge pull request #3242 from rrjjvv/new-bakefile-env-var
Allow bake files to be specified via environment variable
2 parents b034cff + d44ffb4 commit 0bed0b5

3 files changed

Lines changed: 219 additions & 5 deletions

File tree

commands/bake.go

Lines changed: 54 additions & 5 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
@@ -62,7 +67,7 @@ type bakeOptions struct {
6267
listVars bool
6368
}
6469

65-
func runBake(ctx context.Context, dockerCli command.Cli, targets []string, in bakeOptions, cFlags commonFlags) (err error) {
70+
func runBake(ctx context.Context, dockerCli command.Cli, targets []string, in bakeOptions, cFlags commonFlags, filesFromEnv bool) (err error) {
6671
mp := dockerCli.MeterProvider()
6772

6873
ctx, end, err := tracing.TraceCurrentCommand(ctx, append([]string{"bake"}, targets...),
@@ -186,7 +191,7 @@ func runBake(ctx context.Context, dockerCli command.Cli, targets []string, in ba
186191
return err
187192
}
188193

189-
files, inp, err := readBakeFiles(ctx, nodes, url, in.files, dockerCli.In(), printer)
194+
files, inp, err := readBakeFiles(ctx, nodes, url, in.files, dockerCli.In(), printer, filesFromEnv)
190195
if err != nil {
191196
return err
192197
}
@@ -458,6 +463,15 @@ func bakeCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
458463
Aliases: []string{"f"},
459464
Short: "Build from a file",
460465
RunE: func(cmd *cobra.Command, args []string) error {
466+
filesFromEnv := false
467+
if len(options.files) == 0 {
468+
envFiles, err := bakeEnvFiles(os.LookupEnv)
469+
if err != nil {
470+
return err
471+
}
472+
options.files = envFiles
473+
filesFromEnv = true
474+
}
461475
// reset to nil to avoid override is unset
462476
if !cmd.Flags().Lookup("no-cache").Changed {
463477
cFlags.noCache = nil
@@ -475,7 +489,7 @@ func bakeCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
475489
options.builder = rootOpts.builder
476490
options.metadataFile = cFlags.metadataFile
477491
// Other common flags (noCache, pull and progress) are processed in runBake function.
478-
return runBake(cmd.Context(), dockerCli, args, options, cFlags)
492+
return runBake(cmd.Context(), dockerCli, args, options, cFlags, filesFromEnv)
479493
},
480494
ValidArgsFunction: completion.BakeTargets(options.files),
481495
}
@@ -510,6 +524,37 @@ func bakeCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
510524
return cmd
511525
}
512526

527+
func bakeEnvFiles(lookup func(string string) (string, bool)) ([]string, error) {
528+
sep, _ := lookup(bakeEnvFileSeparator)
529+
if sep == "" {
530+
sep = string(os.PathListSeparator)
531+
}
532+
f, ok := lookup(bakeEnvFilePath)
533+
if ok {
534+
return cleanPaths(strings.Split(f, sep))
535+
}
536+
return []string{}, nil
537+
}
538+
539+
func cleanPaths(p []string) ([]string, error) {
540+
var paths []string
541+
for _, f := range p {
542+
f = strings.TrimSpace(f)
543+
if f == "" {
544+
continue
545+
}
546+
if f == "-" {
547+
paths = append(paths, f)
548+
continue
549+
}
550+
if _, err := os.Stat(f); err != nil {
551+
return nil, err
552+
}
553+
paths = append(paths, f)
554+
}
555+
return paths, nil
556+
}
557+
513558
func saveLocalStateGroup(dockerCli command.Cli, in bakeOptions, targets []string, bo map[string]build.Options) error {
514559
l, err := localstate.New(confutil.NewConfig(dockerCli))
515560
if err != nil {
@@ -559,7 +604,7 @@ func bakeArgs(args []string) (url, cmdContext string, targets []string) {
559604
return url, cmdContext, targets
560605
}
561606

562-
func readBakeFiles(ctx context.Context, nodes []builder.Node, url string, names []string, stdin io.Reader, pw progress.Writer) (files []bake.File, inp *bake.Input, err error) {
607+
func readBakeFiles(ctx context.Context, nodes []builder.Node, url string, names []string, stdin io.Reader, pw progress.Writer, filesFromEnv bool) (files []bake.File, inp *bake.Input, err error) {
563608
var lnames []string // local
564609
var rnames []string // remote
565610
var anames []string // both
@@ -584,7 +629,11 @@ func readBakeFiles(ctx context.Context, nodes []builder.Node, url string, names
584629

585630
if len(lnames) > 0 || url == "" {
586631
var lfiles []bake.File
587-
progress.Wrap("[internal] load local bake definitions", pw.Write, func(sub progress.SubLogger) error {
632+
where := ""
633+
if filesFromEnv {
634+
where = " from " + bakeEnvFilePath + " env"
635+
}
636+
progress.Wrap("[internal] load local bake definitions"+where, pw.Write, func(sub progress.SubLogger) error {
588637
if url != "" {
589638
lfiles, err = bake.ReadLocalFiles(lnames, stdin, sub)
590639
} else {

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) {
@@ -2191,6 +2192,165 @@ target "default" {
21912192
require.NoError(t, err, out)
21922193
}
21932194

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

0 commit comments

Comments
 (0)