Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions cmd/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ func addBuildFlags(cmd *cobra.Command) {
cmd.Flags().String("coverage-output-path", "", "Output path where test coverage file will be copied after running tests")
cmd.Flags().Bool("disable-coverage", false, "Disable test coverage collection (defaults to false)")
cmd.Flags().Bool("enable-test-tracing", false, "Enable per-test OpenTelemetry span creation (defaults to false)")
cmd.Flags().StringToString("docker-build-options", nil, "Options passed to all 'docker build' commands")
cmd.Flags().StringArray("docker-build-options", nil, "Options passed to all 'docker build' commands (can be repeated, e.g., --docker-build-options=cache-to=type=gha,mode=max)")
cmd.Flags().Bool("slsa-cache-verification", false, "Enable SLSA verification for cached artifacts")
cmd.Flags().String("slsa-source-uri", "", "Expected source URI for SLSA verification (required when verification enabled)")
cmd.Flags().Bool("slsa-require-attestation", false, "Require SLSA attestations (missing/invalid → build locally)")
Expand Down Expand Up @@ -367,11 +367,11 @@ func getBuildOpts(cmd *cobra.Command) ([]leeway.BuildOption, cache.LocalCache) {
disableCoverage, _ := cmd.Flags().GetBool("disable-coverage")
enableTestTracing, _ := cmd.Flags().GetBool("enable-test-tracing")

var dockerBuildOptions leeway.DockerBuildOptions
dockerBuildOptions, err = cmd.Flags().GetStringToString("docker-build-options")
dockerBuildOptionsSlice, err := cmd.Flags().GetStringArray("docker-build-options")
if err != nil {
log.Fatal(err)
}
dockerBuildOptions := leeway.DockerBuildOptions(dockerBuildOptionsSlice)

jailedExecution, err := cmd.Flags().GetBool("jailed-execution")
if err != nil {
Expand Down
9 changes: 5 additions & 4 deletions pkg/leeway/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -495,8 +495,9 @@ type buildOptions struct {
context *buildContext
}

// DockerBuildOptions are options passed to "docker build"
type DockerBuildOptions map[string]string
// DockerBuildOptions are options passed to "docker build".
// Each entry is a "key=value" string that becomes "--key=value" in the docker command.
type DockerBuildOptions []string

// BuildOption configures the build behaviour
type BuildOption func(*buildOptions) error
Expand Down Expand Up @@ -2408,8 +2409,8 @@ func (p *Package) buildDocker(buildctx *buildContext, wd, result string) (res *p
buildcmd = append(buildcmd, "--squash")
}
if buildctx.DockerBuildOptions != nil {
for opt, v := range *buildctx.DockerBuildOptions {
buildcmd = append(buildcmd, fmt.Sprintf("--%s=%s", opt, v))
for _, opt := range *buildctx.DockerBuildOptions {
buildcmd = append(buildcmd, fmt.Sprintf("--%s", opt))
}
}
buildcmd = append(buildcmd, ".")
Expand Down
54 changes: 54 additions & 0 deletions pkg/leeway/build_internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -333,3 +333,57 @@ func TestYarnAppExtraction_ScopedPackage(t *testing.T) {
})
}
}

func TestDockerBuildOptions_CommaInValue(t *testing.T) {
// DockerBuildOptions should preserve comma-containing values intact.
// This is the fix for: https://github.com/gitpod-io/leeway/issues/XXX
// Previously, using StringToString flag type would split "cache-to=type=gha,mode=max"
// into two separate options, breaking docker buildx cache options.

tests := []struct {
name string
opts DockerBuildOptions
expected []string
}{
{
name: "single option with comma in value",
opts: DockerBuildOptions{"cache-to=type=gha,mode=max"},
expected: []string{"--cache-to=type=gha,mode=max"},
},
{
name: "multiple options with commas",
opts: DockerBuildOptions{"cache-to=type=gha,mode=max", "cache-from=type=gha"},
expected: []string{"--cache-to=type=gha,mode=max", "--cache-from=type=gha"},
},
{
name: "simple option without comma",
opts: DockerBuildOptions{"no-cache=true"},
expected: []string{"--no-cache=true"},
},
{
name: "empty options",
opts: DockerBuildOptions{},
expected: []string{},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var result []string
for _, opt := range tt.opts {
result = append(result, fmt.Sprintf("--%s", opt))
}

if len(result) != len(tt.expected) {
t.Errorf("got %d options, want %d", len(result), len(tt.expected))
return
}

for i, got := range result {
if got != tt.expected[i] {
t.Errorf("option %d: got %q, want %q", i, got, tt.expected[i])
}
}
})
}
}
Loading