Merge pull request #26607 from acierto/scaffolder-retry

Make it possible to manually retry the scaffolder template from the failed step (based on checkpoints)
This commit is contained in:
Ben Lambert
2024-09-24 10:34:48 +02:00
committed by GitHub
19 changed files with 316 additions and 87 deletions
+23 -19
View File
@@ -235,6 +235,7 @@ export interface ScaffolderApi {
listTasks?(options: { filterByOwnership: 'owned' | 'all' }): Promise<{
tasks: ScaffolderTask[];
}>;
retry?(taskId: string): Promise<void>;
scaffold(
options: ScaffolderScaffoldOptions,
): Promise<ScaffolderScaffoldResponse>;
@@ -454,6 +455,8 @@ export interface ScaffolderStreamLogsOptions {
// (undocumented)
after?: number;
// (undocumented)
isTaskRecoverable?: boolean;
// (undocumented)
taskId: string;
}
@@ -562,25 +565,26 @@ export const useTemplateSecrets: () => ScaffolderUseTemplateSecrets;
// src/api/types.d.ts:105:5 - (ae-undocumented) Missing documentation for "taskId".
// src/api/types.d.ts:113:5 - (ae-undocumented) Missing documentation for "allowedHosts".
// src/api/types.d.ts:121:5 - (ae-undocumented) Missing documentation for "integrations".
// src/api/types.d.ts:133:5 - (ae-undocumented) Missing documentation for "taskId".
// src/api/types.d.ts:134:5 - (ae-undocumented) Missing documentation for "after".
// src/api/types.d.ts:137:1 - (ae-undocumented) Missing documentation for "ScaffolderDryRunOptions".
// src/api/types.d.ts:138:5 - (ae-undocumented) Missing documentation for "template".
// src/api/types.d.ts:139:5 - (ae-undocumented) Missing documentation for "values".
// src/api/types.d.ts:140:5 - (ae-undocumented) Missing documentation for "secrets".
// src/api/types.d.ts:141:5 - (ae-undocumented) Missing documentation for "directoryContents".
// src/api/types.d.ts:147:1 - (ae-undocumented) Missing documentation for "ScaffolderDryRunResponse".
// src/api/types.d.ts:148:5 - (ae-undocumented) Missing documentation for "directoryContents".
// src/api/types.d.ts:153:5 - (ae-undocumented) Missing documentation for "log".
// src/api/types.d.ts:154:5 - (ae-undocumented) Missing documentation for "steps".
// src/api/types.d.ts:155:5 - (ae-undocumented) Missing documentation for "output".
// src/api/types.d.ts:163:5 - (ae-undocumented) Missing documentation for "getTemplateParameterSchema".
// src/api/types.d.ts:171:5 - (ae-undocumented) Missing documentation for "getTask".
// src/api/types.d.ts:178:5 - (ae-undocumented) Missing documentation for "listTasks".
// src/api/types.d.ts:183:5 - (ae-undocumented) Missing documentation for "getIntegrationsList".
// src/api/types.d.ts:188:5 - (ae-undocumented) Missing documentation for "streamLogs".
// src/api/types.d.ts:189:5 - (ae-undocumented) Missing documentation for "dryRun".
// src/api/types.d.ts:190:5 - (ae-undocumented) Missing documentation for "autocomplete".
// src/api/types.d.ts:133:5 - (ae-undocumented) Missing documentation for "isTaskRecoverable".
// src/api/types.d.ts:134:5 - (ae-undocumented) Missing documentation for "taskId".
// src/api/types.d.ts:135:5 - (ae-undocumented) Missing documentation for "after".
// src/api/types.d.ts:138:1 - (ae-undocumented) Missing documentation for "ScaffolderDryRunOptions".
// src/api/types.d.ts:139:5 - (ae-undocumented) Missing documentation for "template".
// src/api/types.d.ts:140:5 - (ae-undocumented) Missing documentation for "values".
// src/api/types.d.ts:141:5 - (ae-undocumented) Missing documentation for "secrets".
// src/api/types.d.ts:142:5 - (ae-undocumented) Missing documentation for "directoryContents".
// src/api/types.d.ts:148:1 - (ae-undocumented) Missing documentation for "ScaffolderDryRunResponse".
// src/api/types.d.ts:149:5 - (ae-undocumented) Missing documentation for "directoryContents".
// src/api/types.d.ts:154:5 - (ae-undocumented) Missing documentation for "log".
// src/api/types.d.ts:155:5 - (ae-undocumented) Missing documentation for "steps".
// src/api/types.d.ts:156:5 - (ae-undocumented) Missing documentation for "output".
// src/api/types.d.ts:164:5 - (ae-undocumented) Missing documentation for "getTemplateParameterSchema".
// src/api/types.d.ts:172:5 - (ae-undocumented) Missing documentation for "getTask".
// src/api/types.d.ts:185:5 - (ae-undocumented) Missing documentation for "listTasks".
// src/api/types.d.ts:190:5 - (ae-undocumented) Missing documentation for "getIntegrationsList".
// src/api/types.d.ts:195:5 - (ae-undocumented) Missing documentation for "streamLogs".
// src/api/types.d.ts:196:5 - (ae-undocumented) Missing documentation for "dryRun".
// src/api/types.d.ts:197:5 - (ae-undocumented) Missing documentation for "autocomplete".
// src/components/types.d.ts:7:1 - (ae-undocumented) Missing documentation for "TemplateGroupFilter".
// src/extensions/types.d.ts:13:5 - (ae-undocumented) Missing documentation for "uiSchema".
// src/extensions/types.d.ts:30:5 - (ae-undocumented) Missing documentation for ""ui:options"".
@@ -161,6 +161,7 @@ export interface ScaffolderGetIntegrationsListResponse {
* @public
*/
export interface ScaffolderStreamLogsOptions {
isTaskRecoverable?: boolean;
taskId: string;
after?: number;
}
@@ -213,6 +214,13 @@ export interface ScaffolderApi {
*/
cancelTask(taskId: string): Promise<void>;
/**
* Starts the task again from the point where it failed.
*
* @param taskId - the id of the task
*/
retry?(taskId: string): Promise<void>;
listTasks?(options: {
filterByOwnership: 'owned' | 'all';
}): Promise<{ tasks: ScaffolderTask[] }>;
@@ -143,6 +143,11 @@ function reducer(draft: TaskStream, action: ReducerAction) {
}
case 'RECOVERED': {
draft.cancelled = false;
draft.completed = false;
draft.output = undefined;
draft.error = undefined;
for (const stepId in draft.steps) {
if (draft.steps.hasOwnProperty(stepId)) {
draft.steps[stepId].startedAt = undefined;
@@ -185,12 +190,16 @@ export const useTaskEventStream = (taskId: string): TaskStream => {
let subscription: Subscription | undefined;
let logPusher: NodeJS.Timeout | undefined;
let retryCount = 1;
let isTaskRecoverable = false;
const startStreamLogProcess = () =>
scaffolderApi.getTask(taskId).then(
task => {
if (didCancel) {
return;
}
isTaskRecoverable =
task.spec.EXPERIMENTAL_recovery?.EXPERIMENTAL_strategy ===
'startOver';
dispatch({ type: 'INIT', data: task });
// TODO(blam): Use a normal fetch to fetch the current log for the event stream
@@ -199,7 +208,10 @@ export const useTaskEventStream = (taskId: string): TaskStream => {
// stream logs. Without this, if you have a lot of logs, it can look like the
// task is being rebuilt on load as it progresses through the steps at a slower
// rate whilst it builds the status from the event logs
const observable = scaffolderApi.streamLogs({ taskId });
const observable = scaffolderApi.streamLogs({
isTaskRecoverable,
taskId,
});
const collectedLogEvents = new Array<LogEvent>();
@@ -270,12 +282,14 @@ export const useTaskEventStream = (taskId: string): TaskStream => {
);
void startStreamLogProcess();
return () => {
didCancel = true;
if (subscription) {
subscription.unsubscribe();
}
if (logPusher) {
clearInterval(logPusher);
if (!isTaskRecoverable) {
didCancel = true;
if (subscription) {
subscription.unsubscribe();
}
if (logPusher) {
clearInterval(logPusher);
}
}
};
}, [scaffolderApi, dispatch, taskId]);