Merge pull request #27640 from backstage/blam/retries

scaffolder: move checkpoints to objects notation instead
This commit is contained in:
Patrik Oldsberg
2024-11-19 12:15:33 +01:00
committed by GitHub
5 changed files with 62 additions and 22 deletions
+12
View File
@@ -0,0 +1,12 @@
---
'@backstage/plugin-scaffolder-backend': minor
'@backstage/plugin-scaffolder-node': minor
---
BREAKING EXPERIMENTAL: The `checkpoint` method now takes an object instead of previous arguments.
```ts
await ctx.checkpoint({ key: 'repo.create', fn: () => ockokit.repo.create({...})})
```
You can also now return `void` from the checkpoint if the method returns `void` inside the `checkpoint` handler.
@@ -152,19 +152,37 @@ describe('NunjucksWorkflowRunner', () => {
id: 'checkpoints-action',
description: 'Mock action with checkpoints',
handler: async ctx => {
const key1 = await ctx.checkpoint('key1', async () => {
return 'updated';
const key1 = await ctx.checkpoint({
key: 'key1',
fn: async () => 'updated',
});
const key2 = await ctx.checkpoint('key2', async () => {
return 'updated';
const key2 = await ctx.checkpoint({
key: 'key2',
fn: async () => 'updated',
});
const key3 = await ctx.checkpoint('key3', async () => {
return 'updated';
const key3 = await ctx.checkpoint({
key: 'key3',
fn: async () => 'updated',
});
const key4 = await ctx.checkpoint({
key: 'key4',
fn: () => {},
});
const key5 = await ctx.checkpoint({
key: 'key5',
fn: async () => {},
});
ctx.output('key1', key1);
ctx.output('key2', key2);
ctx.output('key3', key3);
// @ts-expect-error - this is void return
ctx.output('key4', key4);
// @ts-expect-error - this is void return
ctx.output('key5', key5);
},
});
@@ -632,6 +650,9 @@ describe('NunjucksWorkflowRunner', () => {
key1: '${{steps.test.output.key1}}',
key2: '${{steps.test.output.key2}}',
key3: '${{steps.test.output.key3}}',
key4: '${{steps.test.output.key4}}',
key5: '${{steps.test.output.key5}}',
key6: '${{steps.test.output.key6}}',
},
}),
getTaskState: (): Promise<
@@ -661,6 +682,8 @@ describe('NunjucksWorkflowRunner', () => {
expect(result.output.key1).toEqual('initial');
expect(result.output.key2).toEqual('updated');
expect(result.output.key3).toEqual('updated');
expect(result.output.key4).toEqual(undefined);
expect(result.output.key5).toEqual(undefined);
});
it('should template the output from simple actions', async () => {
@@ -364,6 +364,7 @@ export class NunjucksWorkflowRunner implements WorkflowRunner {
)}`,
);
}
await action.handler({
input: iteration.input,
secrets: task.secrets ?? {},
@@ -371,21 +372,25 @@ export class NunjucksWorkflowRunner implements WorkflowRunner {
logger: loggerToWinstonLogger(taskLogger),
logStream: streamLogger,
workspacePath,
async checkpoint<U extends JsonValue>(
keySuffix: string,
fn: () => Promise<U>,
) {
const key = `v1.task.checkpoint.${step.id}.${keySuffix}`;
async checkpoint<T extends JsonValue | void>(opts: {
key?: string;
fn: () => Promise<T> | T;
}) {
const { key: checkpointKey, fn } = opts;
const key = `v1.task.checkpoint.${step.id}.${checkpointKey}`;
try {
let prevValue: U | undefined;
let prevValue: T | undefined;
if (prevTaskState) {
const prevState = (
prevTaskState.state?.checkpoints as {
[key: string]: CheckpointState;
}
)?.[key];
if (prevState && prevState.status === 'success') {
prevValue = prevState.value as U;
prevValue = prevState.value as T;
}
}
@@ -395,7 +400,7 @@ export class NunjucksWorkflowRunner implements WorkflowRunner {
task.updateCheckpoint?.({
key,
status: 'success',
value,
value: value ?? {},
});
}
return value;
+4 -4
View File
@@ -31,10 +31,10 @@ export type ActionContext<
secrets?: TaskSecrets;
workspacePath: string;
input: TActionInput;
checkpoint<U extends JsonValue>(
key: string,
fn: () => Promise<U>,
): Promise<U>;
checkpoint<T extends JsonValue | void>(opts: {
key: string;
fn: () => Promise<T> | T;
}): Promise<T>;
output(
name: keyof TActionOutput,
value: TActionOutput[keyof TActionOutput],
+4 -4
View File
@@ -38,10 +38,10 @@ export type ActionContext<
secrets?: TaskSecrets;
workspacePath: string;
input: TActionInput;
checkpoint<U extends JsonValue>(
key: string,
fn: () => Promise<U>,
): Promise<U>;
checkpoint<T extends JsonValue | void>(opts: {
key: string;
fn: () => Promise<T> | T;
}): Promise<T>;
output(
name: keyof TActionOutput,
value: TActionOutput[keyof TActionOutput],