From c7b089a5aeb2817e91543dee1db571fe7e66d7d3 Mon Sep 17 00:00:00 2001 From: blam Date: Tue, 28 Sep 2021 04:10:33 +0200 Subject: [PATCH] chore: adding the new if syntax and using the power of nunjucks Signed-off-by: blam --- .../tasks/DefaultWorkflowRunner.test.ts | 72 ++++++++++++++++++- .../scaffolder/tasks/DefaultWorkflowRunner.ts | 15 ++++ 2 files changed, 86 insertions(+), 1 deletion(-) diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/DefaultWorkflowRunner.test.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/DefaultWorkflowRunner.test.ts index dea94e9b52..c249606658 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/DefaultWorkflowRunner.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/DefaultWorkflowRunner.test.ts @@ -60,6 +60,7 @@ describe('DefaultWorkflowRunner', () => { description: 'Mock action for testing', handler: async ctx => { ctx.output('mock', 'backstage'); + ctx.output('shouldRun', true); }, }); @@ -85,7 +86,76 @@ describe('DefaultWorkflowRunner', () => { }); describe('validation', () => {}); - describe('running', () => {}); + describe('conditionals', () => { + it('should execute steps conditionally', async () => { + const task = createMockTaskWithSpec({ + apiVersion: 'backstage.io/v1beta3', + steps: [ + { id: 'test', name: 'test', action: 'output-action' }, + { + id: 'conditional', + name: 'conditional', + action: 'output-action', + if: '${{ steps.test.output.shouldRun }}', + }, + ], + output: { + result: '${{ steps.conditional.output.mock }}', + }, + parameters: {}, + }); + + const { output } = await runner.execute(task); + + expect(output.result).toBe('backstage'); + }); + + it('should skips steps conditionally', async () => { + const task = createMockTaskWithSpec({ + apiVersion: 'backstage.io/v1beta3', + steps: [ + { id: 'test', name: 'test', action: 'output-action' }, + { + id: 'conditional', + name: 'conditional', + action: 'output-action', + if: '${{ not steps.test.output.shouldRun}}', + }, + ], + output: { + result: '${{ steps.conditional.output.mock }}', + }, + parameters: {}, + }); + + const { output } = await runner.execute(task); + + expect(output.result).toBeUndefined(); + }); + + it('should skips steps using the negating equals operator', async () => { + const task = createMockTaskWithSpec({ + apiVersion: 'backstage.io/v1beta3', + steps: [ + { id: 'test', name: 'test', action: 'output-action' }, + { + id: 'conditional', + name: 'conditional', + action: 'output-action', + if: '${{ steps.test.output.mock !== "backstage"}}', + }, + ], + output: { + result: '${{ steps.conditional.output.mock }}', + }, + parameters: {}, + }); + + const { output } = await runner.execute(task); + + expect(output.result).toBeUndefined(); + }); + }); describe('templating', () => { it('should template the input to an action', async () => { diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/DefaultWorkflowRunner.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/DefaultWorkflowRunner.ts index c8f77f066a..889048d04a 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/DefaultWorkflowRunner.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/DefaultWorkflowRunner.ts @@ -30,6 +30,7 @@ import path from 'path'; import { JsonObject, JsonValue } from '@backstage/config'; import { InputError } from '@backstage/errors'; import { PassThrough } from 'stream'; +import { isTruthy } from './helper'; type Options = { workingDirectory: string; @@ -92,6 +93,9 @@ export class DefaultWorkflowRunner implements WorkflowRunner { try { if (typeof value === 'string') { const templated = this.nunjucks.renderString(value, context); + if (templated === '') { + return undefined; + } try { return JSON.parse(templated); } catch { @@ -147,6 +151,16 @@ export class DefaultWorkflowRunner implements WorkflowRunner { for (const step of task.spec.steps) { try { + if (step.if) { + const ifResult = await this.render(step.if, context); + if (!isTruthy(ifResult)) { + await task.emitLog( + `Skipping step ${step.id} because it's if condition was false`, + ); + continue; + } + } + const action = this.options.actionRegistry.get(step.action); const { taskLogger, streamLogger } = createStepLogger({ task, step }); @@ -189,6 +203,7 @@ export class DefaultWorkflowRunner implements WorkflowRunner { stepId: step.id, status: 'failed', }); + throw err; } }