|
98 | 98 | HarnessSelfHostedGcpJsonKey string // Harness self hosted gcp json region |
99 | 99 | BuildxOptions []string // Generic buildx options passed directly to the buildx command |
100 | 100 | BuildxOptionsSemicolon string // Buildx options separated by semicolons instead of commas |
| 101 | + // Buildx Bake (opt-in) |
| 102 | + BakeFile string // Buildx Bake definition file (HCL/JSON/Compose). If set, Bake mode is active |
| 103 | + BakeOptions string // Semicolon-delimited Bake options and/or target names |
101 | 104 | } |
102 | 105 |
|
103 | 106 | // Plugin defines the Docker plugin parameters. |
@@ -199,8 +202,18 @@ func (p Plugin) Exec() error { |
199 | 202 | os.MkdirAll(dockerHome, 0600) |
200 | 203 |
|
201 | 204 | path := filepath.Join(dockerHome, "config.json") |
202 | | - err := os.WriteFile(path, []byte(p.Login.Config), 0600) |
203 | | - if err != nil { |
| 205 | + var content []byte |
| 206 | + // If PLUGIN_CONFIG starts with '/' or './', treat it as a file path |
| 207 | + if strings.HasPrefix(p.Login.Config, "/") || strings.HasPrefix(p.Login.Config, "./") { |
| 208 | + data, err := os.ReadFile(p.Login.Config) |
| 209 | + if err != nil { |
| 210 | + return fmt.Errorf("Error reading docker config file %s: %s", p.Login.Config, err) |
| 211 | + } |
| 212 | + content = data |
| 213 | + } else { |
| 214 | + content = []byte(p.Login.Config) |
| 215 | + } |
| 216 | + if err := os.WriteFile(path, content, 0600); err != nil { |
204 | 217 | return fmt.Errorf("Error writing config.json: %s", err) |
205 | 218 | } |
206 | 219 | } |
@@ -257,7 +270,8 @@ func (p Plugin) Exec() error { |
257 | 270 | } |
258 | 271 |
|
259 | 272 | // cache export feature is currently not supported for docker driver hence we have to create docker-container driver |
260 | | - if len(p.Build.CacheTo) > 0 && (p.Builder.Driver == "" || p.Builder.Driver == defaultDriver) { |
| 273 | + // NOTE: skip this auto-switch when Bake mode is active |
| 274 | + if p.Build.BakeFile == "" && len(p.Build.CacheTo) > 0 && (p.Builder.Driver == "" || p.Builder.Driver == defaultDriver) { |
261 | 275 | p.Builder.Driver = dockerContainerDriver |
262 | 276 | } |
263 | 277 |
|
@@ -359,21 +373,38 @@ func (p Plugin) Exec() error { |
359 | 373 | }() |
360 | 374 | } |
361 | 375 |
|
| 376 | + // Enforce mutual exclusivity: Bake mode and Push-only mode cannot be used together |
| 377 | + if p.Build.BakeFile != "" && p.PushOnly { |
| 378 | + return fmt.Errorf("conflict: Bake mode (PLUGIN_BAKE_FILE) and Push-only mode (PLUGIN_PUSH_ONLY) cannot be used together") |
| 379 | + } |
| 380 | + |
362 | 381 | // Handle push-only mode if requested |
363 | 382 | if p.PushOnly { |
364 | 383 | return p.pushOnly() |
365 | 384 | } |
366 | 385 |
|
367 | | - // add proxy build args |
368 | | - addProxyBuildArgs(&p.Build) |
369 | | - |
370 | 386 | var cmds []*exec.Cmd |
371 | 387 |
|
372 | 388 | cmds = append(cmds, commandVersion()) // docker version |
373 | 389 | cmds = append(cmds, commandInfo()) // docker info |
374 | 390 |
|
375 | | - // Command to build, tag and push |
376 | | - cmds = append(cmds, commandBuildx(p.Build, p.Builder, p.Dryrun, p.MetadataFile, p.TarPath)) // docker build |
| 391 | + // Determine execution path: Bake mode vs Classic buildx build |
| 392 | + if p.Build.BakeFile != "" { |
| 393 | + // Inform about ignored classic cache settings |
| 394 | + if len(p.Build.CacheFrom) > 0 || len(p.Build.CacheTo) > 0 || p.Build.NoCache { |
| 395 | + fmt.Println("Bake mode: ignoring PLUGIN_CACHE_*; define cache in the bake file.") |
| 396 | + } |
| 397 | + // Tar export is not applied in Bake mode |
| 398 | + if p.TarPath != "" { |
| 399 | + fmt.Println("Bake mode: ignoring PLUGIN_TAR_PATH; define outputs in the bake file.") |
| 400 | + } |
| 401 | + // Command to run buildx bake |
| 402 | + cmds = append(cmds, commandBuildxBake(p.Build, p.Builder, p.Dryrun, p.MetadataFile)) |
| 403 | + } else { |
| 404 | + // Classic path: add proxy build args and run buildx build |
| 405 | + addProxyBuildArgs(&p.Build) |
| 406 | + cmds = append(cmds, commandBuildx(p.Build, p.Builder, p.Dryrun, p.MetadataFile, p.TarPath)) // docker build |
| 407 | + } |
377 | 408 |
|
378 | 409 | // execute all commands in batch mode. |
379 | 410 | for _, cmd := range cmds { |
@@ -425,7 +456,7 @@ func (p Plugin) Exec() error { |
425 | 456 | } |
426 | 457 | } |
427 | 458 |
|
428 | | - if p.TarPath != "" && p.Dryrun { |
| 459 | + if p.Build.BakeFile == "" && p.TarPath != "" && p.Dryrun { |
429 | 460 | if len(p.Build.Tags) > 0 { |
430 | 461 | tag := p.Build.Tags[0] |
431 | 462 | fullImageName := fmt.Sprintf("%s:%s", p.Build.Repo, tag) |
@@ -457,11 +488,13 @@ func (p Plugin) Exec() error { |
457 | 488 | } |
458 | 489 | } |
459 | 490 |
|
460 | | - // output the adaptive card |
461 | | - if p.Builder.Driver == defaultDriver { |
| 491 | + // output the adaptive card (skipped in Bake mode) |
| 492 | + if p.Build.BakeFile == "" && p.Builder.Driver == defaultDriver { |
462 | 493 | if err := p.writeCard(); err != nil { |
463 | 494 | fmt.Printf("Could not create adaptive card. %s\n", err) |
464 | 495 | } |
| 496 | + } else if p.Build.BakeFile != "" { |
| 497 | + fmt.Println("Bake mode: skipping adaptive card output.") |
465 | 498 | } |
466 | 499 |
|
467 | 500 | // write to artifact file |
@@ -729,6 +762,44 @@ func commandBuildx(build Build, builder Builder, dryrun bool, metadataFile strin |
729 | 762 | return exec.Command(dockerExe, args...) |
730 | 763 | } |
731 | 764 |
|
| 765 | +// helper function to create the docker buildx bake command. |
| 766 | +func commandBuildxBake(build Build, builder Builder, dryrun bool, metadataFile string) *exec.Cmd { |
| 767 | + args := []string{"buildx", "bake"} |
| 768 | + |
| 769 | + if build.BakeFile != "" { |
| 770 | + args = append(args, "-f", build.BakeFile) |
| 771 | + } |
| 772 | + if builder.Name != "" { |
| 773 | + args = append(args, "--builder", builder.Name) |
| 774 | + } |
| 775 | + |
| 776 | + if dryrun { |
| 777 | + args = append(args, "--load") |
| 778 | + } else { |
| 779 | + args = append(args, "--push") |
| 780 | + } |
| 781 | + |
| 782 | + if metadataFile != "" { |
| 783 | + args = append(args, "--metadata-file", metadataFile) |
| 784 | + } |
| 785 | + |
| 786 | + if build.BakeOptions != "" { |
| 787 | + tokens := strings.Split(build.BakeOptions, ";") |
| 788 | + for _, t := range tokens { |
| 789 | + t = strings.TrimSpace(t) |
| 790 | + if t == "" { |
| 791 | + continue |
| 792 | + } |
| 793 | + if t == "--push" || t == "--load" { |
| 794 | + continue |
| 795 | + } |
| 796 | + args = append(args, t) |
| 797 | + } |
| 798 | + } |
| 799 | + |
| 800 | + return exec.Command(dockerExe, args...) |
| 801 | +} |
| 802 | + |
732 | 803 | func sanitizeCacheCommand(build *Build) { |
733 | 804 | // Helper function to sanitize cache arguments |
734 | 805 | sanitizeCacheArgs := func(args []string) []string { |
|
0 commit comments