Merge pull request #29353 from kurtaking/checkpoint-types
refactor: experimental checkpoint types
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder-node-test-utils': patch
|
||||
'@backstage/plugin-scaffolder-backend': patch
|
||||
'@backstage/plugin-scaffolder-node': patch
|
||||
---
|
||||
|
||||
An internal refactor which adds additional types to experimental checkpoints
|
||||
@@ -14,7 +14,6 @@ import { Duration } from 'luxon';
|
||||
import { EventsService } from '@backstage/plugin-events-node';
|
||||
import { HumanDuration } from '@backstage/types';
|
||||
import { JsonObject } from '@backstage/types';
|
||||
import { JsonValue } from '@backstage/types';
|
||||
import { Knex } from 'knex';
|
||||
import { LoggerService } from '@backstage/backend-plugin-api';
|
||||
import { PermissionEvaluator } from '@backstage/plugin-permission-common';
|
||||
@@ -38,6 +37,7 @@ import { TemplateEntityStepV1beta3 } from '@backstage/plugin-scaffolder-common';
|
||||
import { TemplateFilter } from '@backstage/plugin-scaffolder-node';
|
||||
import { TemplateGlobal } from '@backstage/plugin-scaffolder-node';
|
||||
import { TemplateParametersV1beta3 } from '@backstage/plugin-scaffolder-common';
|
||||
import { UpdateTaskCheckpointOptions } from '@backstage/plugin-scaffolder-node/alpha';
|
||||
import { UrlReaderService } from '@backstage/backend-plugin-api';
|
||||
import { WorkspaceProvider } from '@backstage/plugin-scaffolder-node/alpha';
|
||||
|
||||
@@ -442,19 +442,7 @@ export class TaskManager implements TaskContext {
|
||||
// (undocumented)
|
||||
get spec(): TaskSpecV1beta3;
|
||||
// (undocumented)
|
||||
updateCheckpoint?(
|
||||
options:
|
||||
| {
|
||||
key: string;
|
||||
status: 'success';
|
||||
value: JsonValue;
|
||||
}
|
||||
| {
|
||||
key: string;
|
||||
status: 'failed';
|
||||
reason: string;
|
||||
},
|
||||
): Promise<void>;
|
||||
updateCheckpoint?(options: UpdateTaskCheckpointOptions): Promise<void>;
|
||||
}
|
||||
|
||||
// @public @deprecated
|
||||
|
||||
@@ -60,6 +60,10 @@ import { scaffolderActionRules } from '../../service/rules';
|
||||
import { createCounterMetric, createHistogramMetric } from '../../util/metrics';
|
||||
import { BackstageLoggerTransport, WinstonLogger } from './logger';
|
||||
import { convertFiltersToRecord } from '../../util/templating';
|
||||
import {
|
||||
CheckpointState,
|
||||
CheckpointContext,
|
||||
} from '@backstage/plugin-scaffolder-node/alpha';
|
||||
|
||||
type NunjucksWorkflowRunnerOptions = {
|
||||
workingDirectory: string;
|
||||
@@ -91,16 +95,6 @@ type TemplateContext = {
|
||||
};
|
||||
};
|
||||
|
||||
type CheckpointState =
|
||||
| {
|
||||
status: 'failed';
|
||||
reason: string;
|
||||
}
|
||||
| {
|
||||
status: 'success';
|
||||
value: JsonValue;
|
||||
};
|
||||
|
||||
const isValidTaskSpec = (taskSpec: TaskSpec): taskSpec is TaskSpecV1beta3 => {
|
||||
return taskSpec.apiVersion === 'scaffolder.backstage.io/v1beta3';
|
||||
};
|
||||
@@ -384,10 +378,9 @@ export class NunjucksWorkflowRunner implements WorkflowRunner {
|
||||
secrets: task.secrets ?? {},
|
||||
logger: taskLogger,
|
||||
workspacePath,
|
||||
async checkpoint<T extends JsonValue | void>(opts: {
|
||||
key?: string;
|
||||
fn: () => Promise<T> | T;
|
||||
}) {
|
||||
async checkpoint<T extends JsonValue | void>(
|
||||
opts: CheckpointContext<T>,
|
||||
) {
|
||||
const { key: checkpointKey, fn } = opts;
|
||||
const key = `v1.task.checkpoint.${step.id}.${checkpointKey}`;
|
||||
|
||||
@@ -396,9 +389,7 @@ export class NunjucksWorkflowRunner implements WorkflowRunner {
|
||||
|
||||
if (prevTaskState) {
|
||||
const prevState = (
|
||||
prevTaskState.state?.checkpoints as {
|
||||
[key: string]: CheckpointState;
|
||||
}
|
||||
prevTaskState.state?.checkpoints as CheckpointState
|
||||
)?.[key];
|
||||
|
||||
if (prevState && prevState.status === 'success') {
|
||||
|
||||
@@ -32,30 +32,19 @@ import {
|
||||
TaskSecrets,
|
||||
TaskStatus,
|
||||
} from '@backstage/plugin-scaffolder-node';
|
||||
import { WorkspaceProvider } from '@backstage/plugin-scaffolder-node/alpha';
|
||||
import {
|
||||
JsonObject,
|
||||
JsonValue,
|
||||
Observable,
|
||||
createDeferred,
|
||||
} from '@backstage/types';
|
||||
CheckpointState,
|
||||
WorkspaceProvider,
|
||||
UpdateTaskCheckpointOptions,
|
||||
} from '@backstage/plugin-scaffolder-node/alpha';
|
||||
import { JsonObject, Observable, createDeferred } from '@backstage/types';
|
||||
import ObservableImpl from 'zen-observable';
|
||||
import { DefaultWorkspaceService, WorkspaceService } from './WorkspaceService';
|
||||
import { readDuration } from './helper';
|
||||
import { InternalTaskSecrets, TaskStore } from './types';
|
||||
|
||||
type TaskState = {
|
||||
checkpoints: {
|
||||
[key: string]:
|
||||
| {
|
||||
status: 'failed';
|
||||
reason: string;
|
||||
}
|
||||
| {
|
||||
status: 'success';
|
||||
value: JsonValue;
|
||||
};
|
||||
};
|
||||
checkpoints: CheckpointState;
|
||||
};
|
||||
/**
|
||||
* TaskManager
|
||||
@@ -152,20 +141,9 @@ export class TaskManager implements TaskContext {
|
||||
return this.storage.getTaskState?.({ taskId: this.task.taskId });
|
||||
}
|
||||
|
||||
async updateCheckpoint?(
|
||||
options:
|
||||
| {
|
||||
key: string;
|
||||
status: 'success';
|
||||
value: JsonValue;
|
||||
}
|
||||
| {
|
||||
key: string;
|
||||
status: 'failed';
|
||||
reason: string;
|
||||
},
|
||||
): Promise<void> {
|
||||
async updateCheckpoint?(options: UpdateTaskCheckpointOptions): Promise<void> {
|
||||
const { key, ...value } = options;
|
||||
|
||||
if (this.task.state) {
|
||||
(this.task.state as TaskState).checkpoints[key] = value;
|
||||
} else {
|
||||
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
} from '@backstage/backend-test-utils';
|
||||
import { JsonObject, JsonValue } from '@backstage/types';
|
||||
import { ActionContext } from '@backstage/plugin-scaffolder-node';
|
||||
import { CheckpointContext } from '@backstage/plugin-scaffolder-node/alpha';
|
||||
import { loggerToWinstonLogger } from './loggerToWinstonLogger';
|
||||
|
||||
/**
|
||||
@@ -44,10 +45,9 @@ export function createMockActionContext<
|
||||
output: jest.fn(),
|
||||
createTemporaryDirectory: jest.fn(),
|
||||
input: {} as TActionInput,
|
||||
async checkpoint<T extends JsonValue | void>(opts: {
|
||||
key: string;
|
||||
fn: () => Promise<T> | T;
|
||||
}): Promise<T> {
|
||||
async checkpoint<T extends JsonValue | void>(
|
||||
opts: CheckpointContext<T>,
|
||||
): Promise<T> {
|
||||
return opts.fn();
|
||||
},
|
||||
getInitiatorCredentials: () => Promise.resolve(credentials),
|
||||
|
||||
@@ -27,6 +27,31 @@ export type AutocompleteHandler = ({
|
||||
}[];
|
||||
}>;
|
||||
|
||||
// @alpha
|
||||
export type CheckpointContext<T extends JsonValue | void = JsonValue> = {
|
||||
key: string;
|
||||
fn: () => Promise<T> | T;
|
||||
};
|
||||
|
||||
// @alpha
|
||||
export type CheckpointState = {
|
||||
[key: string]: CheckpointStateValue;
|
||||
};
|
||||
|
||||
// @alpha
|
||||
export type CheckpointStateValue<T extends JsonValue = JsonValue> =
|
||||
| {
|
||||
status: 'failed';
|
||||
reason: string;
|
||||
}
|
||||
| {
|
||||
status: 'success';
|
||||
value: T;
|
||||
};
|
||||
|
||||
// @alpha
|
||||
export type CheckpointStatus = 'failed' | 'success';
|
||||
|
||||
// @alpha (undocumented)
|
||||
export type CreatedTemplateFilter<
|
||||
TFunctionArgs extends [z.ZodTypeAny, ...z.ZodTypeAny[]],
|
||||
@@ -187,6 +212,11 @@ export type TemplateGlobalFunctionExample = {
|
||||
notes?: string;
|
||||
};
|
||||
|
||||
// @alpha
|
||||
export type UpdateTaskCheckpointOptions = {
|
||||
key: string;
|
||||
} & CheckpointStateValue;
|
||||
|
||||
// @alpha
|
||||
export interface WorkspaceProvider {
|
||||
// (undocumented)
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
```ts
|
||||
import { BackstageCredentials } from '@backstage/backend-plugin-api';
|
||||
import { CheckpointContext } from '@backstage/plugin-scaffolder-node/alpha';
|
||||
import { Expand } from '@backstage/types';
|
||||
import { JsonObject } from '@backstage/types';
|
||||
import { JsonValue } from '@backstage/types';
|
||||
@@ -15,6 +16,7 @@ import { ScmIntegrations } from '@backstage/integration';
|
||||
import { SpawnOptionsWithoutStdio } from 'child_process';
|
||||
import { TaskSpec } from '@backstage/plugin-scaffolder-common';
|
||||
import { TemplateInfo } from '@backstage/plugin-scaffolder-common';
|
||||
import { UpdateTaskCheckpointOptions } from '@backstage/plugin-scaffolder-node/alpha';
|
||||
import { UrlReaderService } from '@backstage/backend-plugin-api';
|
||||
import { UserEntity } from '@backstage/catalog-model';
|
||||
import { Writable } from 'stream';
|
||||
@@ -30,10 +32,9 @@ export type ActionContext<
|
||||
secrets?: TaskSecrets;
|
||||
workspacePath: string;
|
||||
input: TActionInput;
|
||||
checkpoint<T extends JsonValue | void>(opts: {
|
||||
key: string;
|
||||
fn: () => Promise<T> | T;
|
||||
}): Promise<T>;
|
||||
checkpoint<T extends JsonValue | void>(
|
||||
opts: CheckpointContext<T>,
|
||||
): Promise<T>;
|
||||
output(
|
||||
name: keyof TActionOutput,
|
||||
value: TActionOutput[keyof TActionOutput],
|
||||
@@ -446,19 +447,7 @@ export interface TaskContext {
|
||||
// (undocumented)
|
||||
taskId?: string;
|
||||
// (undocumented)
|
||||
updateCheckpoint?(
|
||||
options:
|
||||
| {
|
||||
key: string;
|
||||
status: 'success';
|
||||
value: JsonValue;
|
||||
}
|
||||
| {
|
||||
key: string;
|
||||
status: 'failed';
|
||||
reason: string;
|
||||
},
|
||||
): Promise<void>;
|
||||
updateCheckpoint?(options: UpdateTaskCheckpointOptions): Promise<void>;
|
||||
}
|
||||
|
||||
// @public
|
||||
|
||||
@@ -23,6 +23,8 @@ import {
|
||||
BackstageCredentials,
|
||||
LoggerService,
|
||||
} from '@backstage/backend-plugin-api';
|
||||
import { CheckpointContext } from '@backstage/plugin-scaffolder-node/alpha';
|
||||
|
||||
/**
|
||||
* ActionContext is passed into scaffolder actions.
|
||||
* @public
|
||||
@@ -36,10 +38,9 @@ export type ActionContext<
|
||||
secrets?: TaskSecrets;
|
||||
workspacePath: string;
|
||||
input: TActionInput;
|
||||
checkpoint<T extends JsonValue | void>(opts: {
|
||||
key: string;
|
||||
fn: () => Promise<T> | T;
|
||||
}): Promise<T>;
|
||||
checkpoint<T extends JsonValue | void>(
|
||||
opts: CheckpointContext<T>,
|
||||
): Promise<T>;
|
||||
output(
|
||||
name: keyof TActionOutput,
|
||||
value: TActionOutput[keyof TActionOutput],
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright 2025 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
export * from './types';
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2025 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { JsonValue } from '@backstage/types';
|
||||
|
||||
/**
|
||||
* The status of a checkpoint, indicating whether it succeeded or failed.
|
||||
*
|
||||
* @alpha
|
||||
*/
|
||||
export type CheckpointStatus = 'failed' | 'success';
|
||||
|
||||
/**
|
||||
* Represents the union of all possible checkpoint state values.
|
||||
*
|
||||
* @alpha
|
||||
*/
|
||||
export type CheckpointStateValue<T extends JsonValue = JsonValue> =
|
||||
| { status: 'failed'; reason: string }
|
||||
| { status: 'success'; value: T };
|
||||
|
||||
/**
|
||||
* A map of checkpoint keys to their states.
|
||||
*
|
||||
* @alpha
|
||||
*/
|
||||
export type CheckpointState = {
|
||||
[key: string]: CheckpointStateValue;
|
||||
};
|
||||
|
||||
/**
|
||||
* Context for checkpoint function invocation.
|
||||
*
|
||||
* @alpha
|
||||
*/
|
||||
export type CheckpointContext<T extends JsonValue | void = JsonValue> = {
|
||||
/**
|
||||
* Unique key for the checkpoint
|
||||
*/
|
||||
key: string;
|
||||
/**
|
||||
* Function to execute for the checkpoint
|
||||
*/
|
||||
fn: () => Promise<T> | T;
|
||||
};
|
||||
@@ -28,6 +28,7 @@ export * from '../tasks/alpha';
|
||||
export * from './filters';
|
||||
export * from './globals';
|
||||
export * from './types';
|
||||
export * from './checkpoints';
|
||||
|
||||
/**
|
||||
* Extension point for managing scaffolder actions.
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { CheckpointStateValue } from '../alpha';
|
||||
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
@@ -14,3 +16,12 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
export * from './serializer';
|
||||
|
||||
/**
|
||||
* Options for updating a checkpoint in a task.
|
||||
*
|
||||
* @alpha
|
||||
*/
|
||||
export type UpdateTaskCheckpointOptions = {
|
||||
key: string;
|
||||
} & CheckpointStateValue;
|
||||
|
||||
@@ -16,7 +16,8 @@
|
||||
|
||||
import { BackstageCredentials } from '@backstage/backend-plugin-api';
|
||||
import { TaskSpec } from '@backstage/plugin-scaffolder-common';
|
||||
import { JsonObject, JsonValue, Observable } from '@backstage/types';
|
||||
import { JsonObject, Observable } from '@backstage/types';
|
||||
import { UpdateTaskCheckpointOptions } from '@backstage/plugin-scaffolder-node/alpha';
|
||||
|
||||
/**
|
||||
* TaskSecrets
|
||||
@@ -129,19 +130,7 @@ export interface TaskContext {
|
||||
| undefined
|
||||
>;
|
||||
|
||||
updateCheckpoint?(
|
||||
options:
|
||||
| {
|
||||
key: string;
|
||||
status: 'success';
|
||||
value: JsonValue;
|
||||
}
|
||||
| {
|
||||
key: string;
|
||||
status: 'failed';
|
||||
reason: string;
|
||||
},
|
||||
): Promise<void>;
|
||||
updateCheckpoint?(options: UpdateTaskCheckpointOptions): Promise<void>;
|
||||
|
||||
serializeWorkspace?(options: { path: string }): Promise<void>;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user