Merge pull request #7780 from backstage/joonp/provide-scaffolder-template-metadata

Expose template metadata to custom action handler
This commit is contained in:
Ben Lambert
2021-11-17 20:59:11 +01:00
committed by GitHub
11 changed files with 103 additions and 0 deletions
+6
View File
@@ -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
+10
View File
@@ -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(