diff --git a/.changeset/fair-rocks-dream.md b/.changeset/fair-rocks-dream.md new file mode 100644 index 0000000000..d1f7f3f0f7 --- /dev/null +++ b/.changeset/fair-rocks-dream.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-backend': patch +--- + +Fixed an issue where invalid expressions or non-object values in `step.each` caused an error. diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.test.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.test.ts index e368bfb07d..2239583cb7 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.test.ts @@ -991,6 +991,27 @@ describe('NunjucksWorkflowRunner', () => { ); expect(fakeActionHandler).not.toHaveBeenCalled(); }); + + it('should validate each parameter renders to a valid value', async () => { + const task = createMockTaskWithSpec({ + apiVersion: 'scaffolder.backstage.io/v1beta3', + steps: [ + { + id: 'test', + name: 'name', + each: '${{parameters.data}}', + action: 'jest-validated-action', + input: { foo: '${{each.value}}' }, + }, + ], + output: {}, + parameters: {}, + }); + await expect(runner.execute(task)).rejects.toThrow( + 'Invalid value on action jest-validated-action.each parameter, "${{parameters.data}}" cannot be resolved to a value', + ); + expect(fakeActionHandler).not.toHaveBeenCalled(); + }); }); describe('secrets', () => { diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.ts index fbe87780c7..63d4172659 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.ts @@ -310,13 +310,21 @@ export class NunjucksWorkflowRunner implements WorkflowRunner { return; } } + + const resolvedEach = + step.each && this.render(step.each, context, renderTemplate); + + if (step.each && !resolvedEach) { + throw new InputError( + `Invalid value on action ${action.id}.each parameter, "${step.each}" cannot be resolved to a value`, + ); + } + const iterations = ( - step.each - ? Object.entries(this.render(step.each, context, renderTemplate)).map( - ([key, value]) => ({ - each: { key, value }, - }), - ) + resolvedEach + ? Object.entries(resolvedEach).map(([key, value]) => ({ + each: { key, value }, + })) : [{}] ).map(i => ({ ...i,