Skip to content

Commit c2644cb

Browse files
authored
feat(azure-devops): Add option to fail action if pipeline is unsuccessful (#5971)
* Add option failIfNotSuccessful Signed-off-by: Oskar Jiang <[email protected]> * Add changeset Signed-off-by: Oskar Jiang <[email protected]> * chore: add unit test Signed-off-by: Oskar Jiang <[email protected]> * fix: throw if pollingInterval is not set Signed-off-by: Oskar Jiang <[email protected]> * doc(azure-devops): add examples for failIfNotSuccessful Signed-off-by: Oskar Jiang <[email protected]> --------- Signed-off-by: Oskar Jiang <[email protected]>
1 parent 3c0a6e4 commit c2644cb

File tree

5 files changed

+104
-0
lines changed

5 files changed

+104
-0
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@backstage-community/plugin-scaffolder-backend-module-azure-devops': minor
3+
---
4+
5+
Add option that marks action as failed if pipeline result is not successful

workspaces/azure-devops/plugins/scaffolder-backend-module-azure-devops/README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,21 @@ spec:
201201
**pipeline output:** `${{ steps['runAzurePipeline'].output.pipelineOutput['myOutputVar'].value }}` }}
202202
```
203203

204+
### Example running a pipeline, waiting for it to complete, and failing the action if the pipeline is unsuccessful using the `azure:pipeline:run` action
205+
206+
```yaml
207+
spec:
208+
steps:
209+
- id: runAzurePipeline
210+
name: Run Pipeline
211+
action: azure:pipeline:run
212+
input:
213+
#[...]
214+
pollingInterval: 10 # Poll for pipeline run status every 10 seconds
215+
pipelineTimeout: 300 # Timeout after 5 minutes
216+
failIfNotSuccessful: true # Fail the scaffolder step when the run is not successful
217+
```
218+
204219
### Example: Create Azure Pipeline
205220
206221
```yaml

workspaces/azure-devops/plugins/scaffolder-backend-module-azure-devops/src/actions/devopsRunPipeline.examples.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,4 +129,25 @@ export const examples: TemplateExample[] = [
129129
],
130130
}),
131131
},
132+
{
133+
description:
134+
'Run Azure Pipeline, wait for completion, and fail if unsuccessful',
135+
example: yaml.stringify({
136+
steps: [
137+
{
138+
id: 'runAzurePipeline',
139+
action: 'azure:pipeline:run',
140+
name: 'Run Azure Devops Pipeline and fail if unsuccessful',
141+
input: {
142+
organization: 'organization',
143+
pipelineId: 'pipelineId',
144+
project: 'project',
145+
pollingInterval: 10,
146+
pipelineTimeout: 300,
147+
failIfNotSuccessful: true,
148+
},
149+
},
150+
],
151+
}),
152+
},
132153
];

workspaces/azure-devops/plugins/scaffolder-backend-module-azure-devops/src/actions/devopsRunPipeline.test.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,4 +377,48 @@ describe('publish:azure', () => {
377377
expect.objectContaining({ var2: { isSecret: true, value: 'bar' } }),
378378
);
379379
});
380+
381+
it('should fail if pipeline failIfNotSuccessful is true and no pollingInterval is set', async () => {
382+
await expect(
383+
action.handler({
384+
...mockContext,
385+
input: {
386+
host: 'dev.azure.com',
387+
organization: 'org',
388+
pipelineId: '1',
389+
project: 'project',
390+
token: 'input-token',
391+
failIfNotSuccessful: true,
392+
},
393+
}),
394+
).rejects.toThrow(
395+
'The parameter failIfNotSuccessful option requires pollingInterval to be set',
396+
);
397+
});
398+
399+
it('should fail if pipeline not successful and failIfNotSuccessful is true', async () => {
400+
mockPipelineClient.runPipeline.mockImplementation(() => ({
401+
_links: { web: { href: 'http://pipeline-run-url.com' } },
402+
}));
403+
mockPipelineClient.getRun.mockImplementation(() => ({
404+
_links: { web: { href: 'http://pipeline-run-url.com' } },
405+
result: RunResult.Failed,
406+
state: RunState.Completed,
407+
}));
408+
409+
await expect(
410+
action.handler({
411+
...mockContext,
412+
input: {
413+
host: 'dev.azure.com',
414+
organization: 'org',
415+
pipelineId: '1',
416+
project: 'project',
417+
token: 'input-token',
418+
failIfNotSuccessful: true,
419+
pollingInterval: 1,
420+
},
421+
}),
422+
).rejects.toThrow('Pipeline run was not successful');
423+
});
380424
});

workspaces/azure-devops/plugins/scaffolder-backend-module-azure-devops/src/actions/devopsRunPipeline.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { WebApi } from 'azure-devops-node-api';
2121
import {
2222
RunPipelineParameters,
2323
RunState,
24+
RunResult,
2425
} from 'azure-devops-node-api/interfaces/PipelinesInterfaces';
2526
import { getAuthHandler } from './helpers';
2627
/**
@@ -76,6 +77,13 @@ export function createAzureDevopsRunPipelineAction(options: {
7677
'Azure DevOps pipeline template parameters in key-value pairs.',
7778
)
7879
.optional(),
80+
failIfNotSuccessful: d =>
81+
d
82+
.boolean()
83+
.describe(
84+
'The action should fail if the pipeline run was not successful.',
85+
)
86+
.optional(),
7987
},
8088
output: {
8189
pipelineRunUrl: d => d.string().describe('Url of the pipeline'),
@@ -114,8 +122,15 @@ export function createAzureDevopsRunPipelineAction(options: {
114122
templateParameters,
115123
pollingInterval,
116124
pipelineTimeout,
125+
failIfNotSuccessful,
117126
} = ctx.input;
118127

128+
if (failIfNotSuccessful && !pollingInterval) {
129+
throw new Error(
130+
'The parameter failIfNotSuccessful option requires pollingInterval to be set',
131+
);
132+
}
133+
119134
const url = `https://${host}/${organization}`;
120135
const authHandler = await getAuthHandler(
121136
integrations,
@@ -201,6 +216,10 @@ export function createAzureDevopsRunPipelineAction(options: {
201216
);
202217
}
203218

219+
if (failIfNotSuccessful && pipelineRun.result !== RunResult.Succeeded) {
220+
throw new Error('Pipeline run was not successful');
221+
}
222+
204223
// Log the entire pipeline run object for debugging purposes
205224
ctx.logger.debug(
206225
`Pipeline run details: ${JSON.stringify(pipelineRun, null, 2)}`,

0 commit comments

Comments
 (0)