scaffolder-backend: add dry-run support for actions and runner
Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
@@ -39,12 +39,19 @@ export type ActionContext<Input extends JsonObject> = {
|
||||
createTemporaryDirectory(): Promise<string>;
|
||||
|
||||
templateInfo?: TemplateInfo;
|
||||
|
||||
/**
|
||||
* Whether this action invocation is a dry-run or not.
|
||||
* This will only ever be true if the actions as marked as supporting dry-runs.
|
||||
*/
|
||||
isDryRun?: boolean;
|
||||
};
|
||||
|
||||
/** @public */
|
||||
export type TemplateAction<Input extends JsonObject> = {
|
||||
id: string;
|
||||
description?: string;
|
||||
supportsDryRun?: boolean;
|
||||
schema?: {
|
||||
input?: Schema;
|
||||
output?: Schema;
|
||||
|
||||
@@ -23,7 +23,7 @@ import nunjucks from 'nunjucks';
|
||||
import { JsonObject, JsonValue } from '@backstage/types';
|
||||
import { InputError } from '@backstage/errors';
|
||||
import { PassThrough } from 'stream';
|
||||
import { isTruthy } from './helper';
|
||||
import { generateExampleOutput, isTruthy } from './helper';
|
||||
import { validate as validateJsonSchema } from 'jsonschema';
|
||||
import { parseRepoUrl } from '../actions/builtin/publish/util';
|
||||
import { TemplateActionRegistry } from '../actions';
|
||||
@@ -236,6 +236,23 @@ export class NunjucksWorkflowRunner implements WorkflowRunner {
|
||||
const action = this.options.actionRegistry.get(step.action);
|
||||
const { taskLogger, streamLogger } = createStepLogger({ task, step });
|
||||
|
||||
if (task.isDryRun && !action.supportsDryRun) {
|
||||
task.emitLog(
|
||||
`Skipping because ${action.id} does not support dry-run`,
|
||||
);
|
||||
const outputSchema = action.schema?.output;
|
||||
if (outputSchema) {
|
||||
context.steps[step.id] = {
|
||||
output: generateExampleOutput(outputSchema) as {
|
||||
[name in string]: JsonValue;
|
||||
},
|
||||
};
|
||||
} else {
|
||||
context.steps[step.id] = { output: {} };
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Secrets are only passed when templating the input to actions for security reasons
|
||||
const input =
|
||||
(step.input &&
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
|
||||
import { isArray } from 'lodash';
|
||||
import { Schema } from 'jsonschema';
|
||||
|
||||
/**
|
||||
* Returns true if the input is not `false`, `undefined`, `null`, `""`, `0`, or
|
||||
@@ -24,3 +25,31 @@ import { isArray } from 'lodash';
|
||||
export function isTruthy(value: any): boolean {
|
||||
return isArray(value) ? value.length > 0 : !!value;
|
||||
}
|
||||
|
||||
export function generateExampleOutput(schema: Schema): unknown {
|
||||
const { examples } = schema as { examples?: unknown };
|
||||
if (examples && Array.isArray(examples)) {
|
||||
return examples[0];
|
||||
}
|
||||
if (schema.type === 'object') {
|
||||
return Object.fromEntries(
|
||||
Object.entries(schema.properties ?? {}).map(([key, value]) => [
|
||||
key,
|
||||
generateExampleOutput(value),
|
||||
]),
|
||||
);
|
||||
} else if (schema.type === 'array') {
|
||||
const [firstSchema] = [schema.items]?.flat();
|
||||
if (firstSchema) {
|
||||
return [generateExampleOutput(firstSchema)];
|
||||
}
|
||||
return [];
|
||||
} else if (schema.type === 'string') {
|
||||
return '<example>';
|
||||
} else if (schema.type === 'number') {
|
||||
return 0;
|
||||
} else if (schema.type === 'boolean') {
|
||||
return false;
|
||||
}
|
||||
return '<unknown>';
|
||||
}
|
||||
|
||||
@@ -111,6 +111,7 @@ export interface TaskContext {
|
||||
secrets?: TaskSecrets;
|
||||
createdBy?: string;
|
||||
done: boolean;
|
||||
isDryRun?: boolean;
|
||||
emitLog(message: string, logMetadata?: JsonObject): Promise<void>;
|
||||
complete(result: TaskCompletionState, metadata?: JsonObject): Promise<void>;
|
||||
getWorkspaceName(): Promise<string>;
|
||||
|
||||
Reference in New Issue
Block a user