Skip to content

Commit 36ecbb9

Browse files
authored
Merge pull request #3359 from buildkite/fix-3358
Fix regression in pipeline upload with no-interpolation
2 parents 0716b76 + e3308e3 commit 36ecbb9

2 files changed

Lines changed: 121 additions & 37 deletions

File tree

clicommand/pipeline_upload.go

Lines changed: 26 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -548,32 +548,36 @@ func searchForSecrets(
548548
func (cfg *PipelineUploadConfig) parseAndInterpolate(ctx context.Context, src string, input io.Reader, environ *env.Environment) iter.Seq2[*pipeline.Pipeline, error] {
549549
return func(yield func(*pipeline.Pipeline, error) bool) {
550550
for result, err := range pipeline.ParseAll(input) {
551-
if err != nil && !warning.Is(err) {
552-
if !yield(nil, fmt.Errorf("pipeline parsing of %q failed: %w", src, err)) {
553-
return
554-
}
555-
}
556-
if cfg.NoInterpolation {
551+
// Check the error, apply interpolation if needed.
552+
switch {
553+
case err != nil && !warning.Is(err):
554+
err = fmt.Errorf("pipeline parsing of %q failed: %w", src, err)
555+
// yield below
556+
557+
case cfg.NoInterpolation:
557558
// Note that err may be nil or a non-nil warning from pipeline.Parse
558-
if !yield(result, err) {
559-
return
560-
}
561-
}
562-
// Pass the trace context from our environment to the pipeline.
563-
if tracing, has := environ.Get(tracetools.EnvVarTraceContextKey); has {
564-
if result.Env == nil {
565-
result.Env = ordered.NewMap[string, string](1)
559+
// yield below
560+
561+
default: // yes, interpolation
562+
// Pass the trace context from our environment to the pipeline.
563+
if tracing, has := environ.Get(tracetools.EnvVarTraceContextKey); has {
564+
if result.Env == nil {
565+
result.Env = ordered.NewMap[string, string](1)
566+
}
567+
result.Env.Set(tracetools.EnvVarTraceContextKey, tracing)
566568
}
567-
result.Env.Set(tracetools.EnvVarTraceContextKey, tracing)
568-
}
569-
// Do the interpolation.
570-
preferRuntimeEnv := experiments.IsEnabled(ctx, experiments.InterpolationPrefersRuntimeEnv)
571-
if err := result.Interpolate(environ, preferRuntimeEnv); err != nil {
572-
if !yield(nil, fmt.Errorf("pipeline interpolation of %q failed: %w", src, err)) {
573-
return
569+
570+
// Do the interpolation.
571+
preferRuntimeEnv := experiments.IsEnabled(ctx, experiments.InterpolationPrefersRuntimeEnv)
572+
err = result.Interpolate(environ, preferRuntimeEnv)
573+
if err != nil {
574+
err = fmt.Errorf("pipeline interpolation of %q failed: %w", src, err)
574575
}
576+
// yield below
575577
}
576-
if !yield(result, nil) {
578+
579+
// Yield the result and error.
580+
if !yield(result, err) {
577581
return
578582
}
579583
}

clicommand/pipeline_upload_test.go

Lines changed: 95 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import (
1212
"github.com/buildkite/go-pipeline"
1313
"github.com/buildkite/go-pipeline/ordered"
1414
"github.com/google/go-cmp/cmp"
15-
"gotest.tools/v3/assert"
1615
)
1716

1817
func TestSearchForSecrets(t *testing.T) {
@@ -138,10 +137,15 @@ func TestSearchForSecrets(t *testing.T) {
138137
l := logger.NewBuffer()
139138
err := searchForSecrets(l, cfg, env.FromMap(test.environ), test.pipeline, "cat-o-matic.yaml")
140139
if len(test.wantLog) == 0 {
141-
assert.NilError(t, err)
140+
if err != nil {
141+
t.Errorf("searchForSecrets(l, %v, %v, %v, %q) = %v", cfg, test.environ, test.pipeline, "cat-o-matic.yaml", err)
142+
}
142143
return
143144
}
144-
assert.ErrorContains(t, err, test.wantLog)
145+
if !strings.Contains(err.Error(), test.wantLog) {
146+
t.Errorf("searchForSecrets(l, %v, %v, %v, %q) = %v, want error string containing %q",
147+
cfg, test.environ, test.pipeline, "cat-o-matic.yaml", err, test.wantLog)
148+
}
145149
})
146150
}
147151
}
@@ -167,29 +171,36 @@ steps:
167171
- command: echo $foo
168172
`
169173

170-
var expectedPipeline *pipeline.Pipeline
174+
var wantPipelines []*pipeline.Pipeline
171175
if runtime.GOOS == "windows" {
172-
expectedPipeline = &pipeline.Pipeline{
176+
wantPipelines = []*pipeline.Pipeline{{
173177
Steps: pipeline.Steps{
174178
&pipeline.CommandStep{
175179
Command: "echo bar",
176180
},
177181
},
178-
}
182+
}}
179183
} else {
180-
expectedPipeline = &pipeline.Pipeline{
184+
wantPipelines = []*pipeline.Pipeline{{
181185
Steps: pipeline.Steps{
182186
&pipeline.CommandStep{
183187
Command: "echo ",
184188
},
185189
},
186-
}
190+
}}
187191
}
188192
ctx := context.Background()
189193

194+
var gotPipelines []*pipeline.Pipeline
195+
190196
for p, err := range cfg.parseAndInterpolate(ctx, "test", strings.NewReader(pipelineYAML), environ) {
191-
assert.NilError(t, err, `cfg.parseAndInterpolate(ctx, "test", %q, %q) = %v; want nil`, pipelineYAML, environ, err)
192-
assert.DeepEqual(t, p, expectedPipeline, cmp.Comparer(ordered.EqualSA), cmp.Comparer(ordered.EqualSS))
197+
if err != nil {
198+
t.Errorf(`cfg.parseAndInterpolate(ctx, "test", %q, %v) = %v; want nil`, pipelineYAML, environ, err)
199+
}
200+
gotPipelines = append(gotPipelines, p)
201+
}
202+
if diff := cmp.Diff(gotPipelines, wantPipelines, cmp.Comparer(ordered.EqualSA), cmp.Comparer(ordered.EqualSS)); diff != "" {
203+
t.Errorf("pipelines diff (-got +want):\n%s", diff)
193204
}
194205
}
195206

@@ -199,17 +210,17 @@ func TestPipelineInterpolationRuntimeEnvPrecedence(t *testing.T) {
199210
tests := []struct {
200211
desc string
201212
preferRuntimeEnv bool
202-
expectedCommand string
213+
wantCommands []string
203214
}{
204215
{
205216
desc: "With experiment disabled",
206217
preferRuntimeEnv: false,
207-
expectedCommand: "echo Hi bob",
218+
wantCommands: []string{"echo Hi bob"},
208219
},
209220
{
210221
desc: "With experiment enabled",
211222
preferRuntimeEnv: true,
212-
expectedCommand: "echo Hi alice",
223+
wantCommands: []string{"echo Hi alice"},
213224
},
214225
}
215226

@@ -236,14 +247,83 @@ steps:
236247
ctx, _ = experiments.Enable(ctx, experiments.InterpolationPrefersRuntimeEnv)
237248
}
238249

250+
var gotCommands []string
251+
239252
for p, err := range cfg.parseAndInterpolate(ctx, "test", strings.NewReader(pipelineYAML), environ) {
240-
assert.NilError(t, err, `cfg.parseAndInterpolate(ctx, "test", %q, %q) = %v; want nil`, pipelineYAML, environ, err)
253+
if err != nil {
254+
t.Errorf(`cfg.parseAndInterpolate(ctx, "test", %q, %v) = %v; want nil`, pipelineYAML, environ, err)
255+
}
241256
s := p.Steps[len(p.Steps)-1]
242257
commandStep, ok := s.(*pipeline.CommandStep)
243258
if !ok {
244259
t.Errorf("Invalid pipeline step %v", s)
245260
}
246-
assert.Equal(t, commandStep.Command, test.expectedCommand)
261+
gotCommands = append(gotCommands, commandStep.Command)
262+
}
263+
264+
if diff := cmp.Diff(gotCommands, test.wantCommands); diff != "" {
265+
t.Errorf("commands diff (-got +want):\n%s", diff)
266+
}
267+
})
268+
}
269+
}
270+
271+
func TestPipelineInterpolation_Regression3358(t *testing.T) {
272+
t.Parallel()
273+
274+
tests := []struct {
275+
name string
276+
interpolation bool
277+
wantCommands []string
278+
}{
279+
{
280+
name: "with interpolation",
281+
interpolation: true,
282+
wantCommands: []string{"echo Hi bob"},
283+
},
284+
{
285+
name: "without interpolation",
286+
interpolation: false,
287+
wantCommands: []string{"echo $GREETING"},
288+
},
289+
}
290+
291+
environ := env.FromMap(map[string]string{
292+
"NAME": "alice",
293+
})
294+
295+
for _, test := range tests {
296+
t.Run(test.name, func(t *testing.T) {
297+
const pipelineYAML = `---
298+
env:
299+
NAME: bob
300+
GREETING: "Hi ${NAME:-}"
301+
steps:
302+
- command: echo $GREETING
303+
`
304+
cfg := &PipelineUploadConfig{
305+
NoInterpolation: !test.interpolation,
306+
RedactedVars: []string{},
307+
RejectSecrets: true,
308+
}
309+
ctx := context.Background()
310+
311+
var gotCommands []string
312+
313+
for p, err := range cfg.parseAndInterpolate(ctx, "test", strings.NewReader(pipelineYAML), environ) {
314+
if err != nil {
315+
t.Errorf(`cfg.parseAndInterpolate(ctx, "test", %q, %v) = %v; want nil`, pipelineYAML, environ, err)
316+
}
317+
s := p.Steps[len(p.Steps)-1]
318+
commandStep, ok := s.(*pipeline.CommandStep)
319+
if !ok {
320+
t.Errorf("Invalid pipeline step %v", s)
321+
}
322+
gotCommands = append(gotCommands, commandStep.Command)
323+
}
324+
325+
if diff := cmp.Diff(gotCommands, test.wantCommands); diff != "" {
326+
t.Errorf("commands diff (-got +want):\n%s", diff)
247327
}
248328
})
249329
}

0 commit comments

Comments
 (0)