Merge pull request #7780 from backstage/joonp/provide-scaffolder-template-metadata
Expose template metadata to custom action handler
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder-backend': patch
|
||||
'@backstage/plugin-scaffolder-common': patch
|
||||
---
|
||||
|
||||
Expose template metadata to custom action handler in Scaffolder.
|
||||
@@ -91,6 +91,8 @@ argument. It looks like the following:
|
||||
- `createTemporaryDirectory` a function to call to give you a temporary
|
||||
directory somewhere on the runner so you can store some files there rather
|
||||
than polluting the `workspacePath`
|
||||
- `ctx.metadata` - an object containing a `name` field, indicating the template
|
||||
name. More metadata fields may be added later.
|
||||
|
||||
### Registering Custom Actions
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ export type ActionContext<Input extends InputBase> = {
|
||||
input: Input;
|
||||
output(name: string, value: JsonValue): void;
|
||||
createTemporaryDirectory(): Promise<string>;
|
||||
metadata?: TemplateMetadata;
|
||||
};
|
||||
|
||||
// Warning: (ae-missing-release-tag) "CatalogEntityClient" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
@@ -432,6 +433,8 @@ export interface TaskSpecV1beta2 {
|
||||
// (undocumented)
|
||||
baseUrl?: string;
|
||||
// (undocumented)
|
||||
metadata?: TemplateMetadata;
|
||||
// (undocumented)
|
||||
output: {
|
||||
[name: string]: string;
|
||||
};
|
||||
@@ -454,6 +457,8 @@ export interface TaskSpecV1beta3 {
|
||||
// (undocumented)
|
||||
baseUrl?: string;
|
||||
// (undocumented)
|
||||
metadata?: TemplateMetadata;
|
||||
// (undocumented)
|
||||
output: {
|
||||
[name: string]: JsonValue;
|
||||
};
|
||||
@@ -558,4 +563,9 @@ export class TemplateActionRegistry {
|
||||
action: TemplateAction<Parameters>,
|
||||
): void;
|
||||
}
|
||||
|
||||
// @public
|
||||
export type TemplateMetadata = {
|
||||
name: string;
|
||||
};
|
||||
```
|
||||
|
||||
@@ -18,6 +18,7 @@ import { Logger } from 'winston';
|
||||
import { Writable } from 'stream';
|
||||
import { JsonValue, JsonObject } from '@backstage/types';
|
||||
import { Schema } from 'jsonschema';
|
||||
import { TemplateMetadata } from '../tasks/types';
|
||||
|
||||
type PartialJsonObject = Partial<JsonObject>;
|
||||
type PartialJsonValue = PartialJsonObject | JsonValue | undefined;
|
||||
@@ -44,6 +45,8 @@ export type ActionContext<Input extends InputBase> = {
|
||||
* Creates a temporary directory for use by the action, which is then cleaned up automatically.
|
||||
*/
|
||||
createTemporaryDirectory(): Promise<string>;
|
||||
|
||||
metadata?: TemplateMetadata;
|
||||
};
|
||||
|
||||
export type TemplateAction<Input extends InputBase> = {
|
||||
|
||||
@@ -146,6 +146,30 @@ describe('DefaultWorkflowRunner', () => {
|
||||
|
||||
expect(fakeActionHandler).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should pass metadata through', async () => {
|
||||
const templateName = 'template name';
|
||||
const task = createMockTaskWithSpec({
|
||||
apiVersion: 'scaffolder.backstage.io/v1beta3',
|
||||
parameters: {},
|
||||
output: {},
|
||||
steps: [
|
||||
{
|
||||
id: 'test',
|
||||
name: 'name',
|
||||
action: 'jest-validated-action',
|
||||
input: { foo: 1 },
|
||||
},
|
||||
],
|
||||
metadata: { name: templateName },
|
||||
});
|
||||
|
||||
await runner.execute(task);
|
||||
|
||||
expect(fakeActionHandler.mock.calls[0][0].metadata).toEqual({
|
||||
name: templateName,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('conditionals', () => {
|
||||
|
||||
@@ -225,6 +225,12 @@ export class HandlebarsWorkflowRunner implements WorkflowRunner {
|
||||
input: JSON.stringify(input, null, 2),
|
||||
});
|
||||
|
||||
if (!task.spec.metadata) {
|
||||
console.warn(
|
||||
'DEPRECATION NOTICE: metadata is undefined. metadata will be required in the future.',
|
||||
);
|
||||
}
|
||||
|
||||
await action.handler({
|
||||
baseUrl: task.spec.baseUrl,
|
||||
logger: taskLogger,
|
||||
@@ -242,6 +248,7 @@ export class HandlebarsWorkflowRunner implements WorkflowRunner {
|
||||
output(name: string, value: JsonValue) {
|
||||
stepOutputs[name] = value;
|
||||
},
|
||||
metadata: task.spec.metadata,
|
||||
});
|
||||
|
||||
// Remove all temporary directories that were created when executing the action
|
||||
|
||||
@@ -28,6 +28,7 @@ describe('LegacyWorkflowRunner', () => {
|
||||
let runner: HandlebarsWorkflowRunner;
|
||||
const logger = getVoidLogger();
|
||||
let actionRegistry = new TemplateActionRegistry();
|
||||
let fakeActionHandler: jest.Mock;
|
||||
|
||||
const integrations = ScmIntegrations.fromConfig(
|
||||
new ConfigReader({
|
||||
@@ -59,6 +60,11 @@ describe('LegacyWorkflowRunner', () => {
|
||||
ctx.output('badOutput', false);
|
||||
},
|
||||
});
|
||||
fakeActionHandler = jest.fn();
|
||||
actionRegistry.register({
|
||||
id: 'test-metadata-action',
|
||||
handler: fakeActionHandler,
|
||||
});
|
||||
|
||||
runner = new HandlebarsWorkflowRunner({
|
||||
actionRegistry,
|
||||
@@ -87,6 +93,30 @@ describe('LegacyWorkflowRunner', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('should pass metadata through', async () => {
|
||||
const templateName = 'template name';
|
||||
const task = createMockTaskWithSpec({
|
||||
apiVersion: 'backstage.io/v1beta2',
|
||||
steps: [
|
||||
{
|
||||
id: 'test',
|
||||
name: 'name',
|
||||
action: 'test-metadata-action',
|
||||
input: { foo: 1 },
|
||||
},
|
||||
],
|
||||
output: {},
|
||||
values: {},
|
||||
metadata: { name: templateName },
|
||||
});
|
||||
|
||||
await runner.execute(task);
|
||||
|
||||
expect(fakeActionHandler.mock.calls[0][0].metadata).toEqual({
|
||||
name: templateName,
|
||||
});
|
||||
});
|
||||
|
||||
describe('templating', () => {
|
||||
it('should template the output', async () => {
|
||||
const task = createMockTaskWithSpec({
|
||||
|
||||
@@ -228,6 +228,12 @@ export class NunjucksWorkflowRunner implements WorkflowRunner {
|
||||
const tmpDirs = new Array<string>();
|
||||
const stepOutput: { [outputName: string]: JsonValue } = {};
|
||||
|
||||
if (!task.spec.metadata) {
|
||||
console.warn(
|
||||
'DEPRECATION NOTICE: metadata is undefined. metadata will be required in the future.',
|
||||
);
|
||||
}
|
||||
|
||||
await action.handler({
|
||||
baseUrl: task.spec.baseUrl,
|
||||
input,
|
||||
@@ -244,6 +250,7 @@ export class NunjucksWorkflowRunner implements WorkflowRunner {
|
||||
output(name: string, value: JsonValue) {
|
||||
stepOutput[name] = value;
|
||||
},
|
||||
metadata: task.spec.metadata,
|
||||
});
|
||||
|
||||
// Remove all temporary directories that were created when executing the action
|
||||
|
||||
@@ -34,4 +34,5 @@ export type {
|
||||
TaskContext,
|
||||
TaskStore,
|
||||
DispatchResult,
|
||||
TemplateMetadata,
|
||||
} from './types';
|
||||
|
||||
@@ -69,6 +69,15 @@ export type SerializedTaskEvent = {
|
||||
createdAt: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* TemplateMetadata
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type TemplateMetadata = {
|
||||
name: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* TaskSpecV1beta2
|
||||
*
|
||||
@@ -86,6 +95,7 @@ export interface TaskSpecV1beta2 {
|
||||
if?: string | boolean;
|
||||
}>;
|
||||
output: { [name: string]: string };
|
||||
metadata?: TemplateMetadata;
|
||||
}
|
||||
|
||||
export interface TaskStep {
|
||||
@@ -107,6 +117,7 @@ export interface TaskSpecV1beta3 {
|
||||
parameters: JsonObject;
|
||||
steps: TaskStep[];
|
||||
output: { [name: string]: JsonValue };
|
||||
metadata?: TemplateMetadata;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -209,6 +209,7 @@ export async function createRouter(
|
||||
name: step.name ?? step.action,
|
||||
})),
|
||||
output: template.spec.output ?? {},
|
||||
metadata: { name: template.metadata?.name },
|
||||
}
|
||||
: {
|
||||
apiVersion: template.apiVersion,
|
||||
@@ -220,6 +221,7 @@ export async function createRouter(
|
||||
name: step.name ?? step.action,
|
||||
})),
|
||||
output: template.spec.output ?? {},
|
||||
metadata: { name: template.metadata?.name },
|
||||
};
|
||||
} else {
|
||||
throw new InputError(
|
||||
|
||||
Reference in New Issue
Block a user