Merge pull request #9573 from backstage/blam/deprecations/metadata

deprecations: Some deprecations for the `specs` in `scaffolder-common`
This commit is contained in:
Ben Lambert
2022-02-18 11:36:38 +01:00
committed by GitHub
19 changed files with 132 additions and 52 deletions
+10
View File
@@ -0,0 +1,10 @@
---
'@backstage/plugin-scaffolder-common': patch
---
**DEPRECATED** - The `TaskSpec.metadata` and `TaskSpec.baseUrl` has been deprecated in favour of the new `TaskSpec.templateInfo`.
The `baseUrl` is now found on the `templateInfo` object, and the name can be inferred from the `templateInfo.entityRef` property.
Usages of `TaskSpec.metadata.name` or `ctx.metadata.name` in Actions should migrate to using `parseEntityRef(taskSpec.templateInfo.entityRef)` to get the parsed entity triplet.
Usages of `ctx.baseUrl` in Actions should migrate to using `ctx.templateInfo.baseUrl` instead.
+8
View File
@@ -0,0 +1,8 @@
---
'@backstage/plugin-scaffolder': patch
'@backstage/plugin-scaffolder-backend': patch
'@backstage/plugin-scaffolder-backend-module-cookiecutter': patch
'@backstage/plugin-scaffolder-backend-module-rails': patch
---
Migrated over from the deprecated `spec.metadata` to `spec.templateInfo` for the `name` and the `baseUrl` of the template.
@@ -89,7 +89,10 @@ describe('fetch:cookiecutter', () => {
help: 'me',
},
},
baseUrl: 'somebase',
templateInfo: {
entityRef: 'template:default/cookiecutter',
baseUrl: 'somebase',
},
workspacePath: mockTmpDir,
logger: getVoidLogger(),
logStream: new PassThrough(),
@@ -149,7 +152,7 @@ describe('fetch:cookiecutter', () => {
expect.objectContaining({
reader: mockReader,
integrations,
baseUrl: mockContext.baseUrl,
baseUrl: mockContext.templateInfo?.baseUrl,
fetchUrl: mockContext.input.url,
outputPath: join(
mockTmpDir,
@@ -222,7 +222,7 @@ export function createFetchCookiecutterAction(options: {
await fetchContents({
reader,
integrations,
baseUrl: ctx.baseUrl,
baseUrl: ctx.templateInfo?.baseUrl,
fetchUrl: ctx.input.url,
outputPath: templateContentsDir,
});
@@ -62,7 +62,10 @@ describe('fetch:rails', () => {
help: 'me',
},
},
baseUrl: 'somebase',
templateInfo: {
baseUrl: 'somebase',
entityRef: 'template:default/myTemplate',
},
workspacePath: mockTmpDir,
logger: getVoidLogger(),
logStream: new PassThrough(),
@@ -100,7 +103,7 @@ describe('fetch:rails', () => {
expect(fetchContents).toHaveBeenCalledWith({
reader: mockReader,
integrations,
baseUrl: mockContext.baseUrl,
baseUrl: mockContext.templateInfo.baseUrl,
fetchUrl: mockContext.input.url,
outputPath: resolvePath(mockContext.workspacePath),
});
@@ -170,7 +170,7 @@ export function createFetchRailsAction(options: {
await fetchContents({
reader,
integrations,
baseUrl: ctx.baseUrl,
baseUrl: ctx.templateInfo?.baseUrl,
fetchUrl: ctx.input.url,
outputPath: workDir,
});
+2
View File
@@ -29,6 +29,7 @@ import { SpawnOptionsWithoutStdio } from 'child_process';
import { TaskSpec } from '@backstage/plugin-scaffolder-common';
import { TaskSpecV1beta2 } from '@backstage/plugin-scaffolder-common';
import { TaskSpecV1beta3 } from '@backstage/plugin-scaffolder-common';
import { TemplateInfo } from '@backstage/plugin-scaffolder-common';
import { TemplateMetadata } from '@backstage/plugin-scaffolder-common';
import { UrlReader } from '@backstage/backend-common';
import { Writable } from 'stream';
@@ -46,6 +47,7 @@ export type ActionContext<Input extends JsonObject> = {
output(name: string, value: JsonValue): void;
createTemporaryDirectory(): Promise<string>;
metadata?: TemplateMetadata;
templateInfo?: TemplateInfo;
};
// @public
@@ -59,7 +59,7 @@ export function createFetchPlainAction(options: {
await fetchContents({
reader,
integrations,
baseUrl: ctx.baseUrl,
baseUrl: ctx.templateInfo?.baseUrl,
fetchUrl: ctx.input.url,
outputPath,
});
@@ -75,7 +75,10 @@ describe('fetch:template', () => {
const logger = getVoidLogger();
const mockContext = (inputPatch: Partial<FetchTemplateInput> = {}) => ({
baseUrl: 'base-url',
templateInfo: {
baseUrl: 'base-url',
entityRef: 'template:default/test-template',
},
input: {
url: './skeleton',
targetPath: './target',
@@ -199,7 +202,7 @@ describe('fetch:template', () => {
it('uses fetchContents to retrieve the template content', () => {
expect(mockFetchContents).toHaveBeenCalledWith(
expect.objectContaining({
baseUrl: context.baseUrl,
baseUrl: context.templateInfo?.baseUrl,
fetchUrl: context.input.url,
}),
);
@@ -135,7 +135,7 @@ export function createFetchTemplateAction(options: {
await fetchContents({
reader,
integrations,
baseUrl: ctx.baseUrl,
baseUrl: ctx.templateInfo?.baseUrl,
fetchUrl: ctx.input.url,
outputPath: templateDir,
});
@@ -19,10 +19,12 @@ import { Writable } from 'stream';
import { JsonValue, JsonObject } from '@backstage/types';
import { Schema } from 'jsonschema';
import { TaskSecrets, TemplateMetadata } from '../tasks/types';
import { TemplateInfo } from '@backstage/plugin-scaffolder-common';
export type ActionContext<Input extends JsonObject> = {
/**
* Base URL for the location of the task spec, typically the url of the source entity file.
* @deprecated please use templateInfo.baseUrl instead
*/
baseUrl?: string;
@@ -38,7 +40,11 @@ export type ActionContext<Input extends JsonObject> = {
*/
createTemporaryDirectory(): Promise<string>;
/**
* @deprecated please use templateInfo instead
*/
metadata?: TemplateMetadata;
templateInfo?: TemplateInfo;
};
export type TemplateAction<Input extends JsonObject> = {
@@ -94,7 +94,7 @@ describe('LegacyWorkflowRunner', () => {
});
it('should pass metadata through', async () => {
const templateName = 'template name';
const entityRef = `template:default/templateName`;
const task = createMockTaskWithSpec({
apiVersion: 'backstage.io/v1beta2',
steps: [
@@ -107,13 +107,13 @@ describe('LegacyWorkflowRunner', () => {
],
output: {},
values: {},
metadata: { name: templateName },
templateInfo: { entityRef },
});
await runner.execute(task);
expect(fakeActionHandler.mock.calls[0][0].metadata).toEqual({
name: templateName,
expect(fakeActionHandler.mock.calls[0][0].templateInfo).toEqual({
entityRef,
});
});
@@ -225,13 +225,8 @@ 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({
// deprecated in favor of templateInfo.baseUrl
baseUrl: task.spec.baseUrl,
logger: taskLogger,
logStream: stream,
@@ -248,7 +243,9 @@ export class HandlebarsWorkflowRunner implements WorkflowRunner {
output(name: string, value: JsonValue) {
stepOutputs[name] = value;
},
// deprecated in favor of templateInfo
metadata: task.spec.metadata,
templateInfo: task.spec.templateInfo,
});
// Remove all temporary directories that were created when executing the action
@@ -163,7 +163,7 @@ describe('DefaultWorkflowRunner', () => {
});
it('should pass metadata through', async () => {
const templateName = 'template name';
const entityRef = `template:default/templateName`;
const task = createMockTaskWithSpec({
apiVersion: 'scaffolder.backstage.io/v1beta3',
parameters: {},
@@ -176,13 +176,13 @@ describe('DefaultWorkflowRunner', () => {
input: { foo: 1 },
},
],
metadata: { name: templateName },
templateInfo: { entityRef },
});
await runner.execute(task);
expect(fakeActionHandler.mock.calls[0][0].metadata).toEqual({
name: templateName,
expect(fakeActionHandler.mock.calls[0][0].templateInfo).toEqual({
entityRef,
});
});
@@ -259,6 +259,7 @@ export class NunjucksWorkflowRunner implements WorkflowRunner {
const stepOutput: { [outputName: string]: JsonValue } = {};
await action.handler({
// deprecated in favourof templateInfo.baseUrl
baseUrl: task.spec.baseUrl,
input,
secrets: task.secrets ?? {},
@@ -275,7 +276,9 @@ export class NunjucksWorkflowRunner implements WorkflowRunner {
output(name: string, value: JsonValue) {
stepOutput[name] = value;
},
// deprecated in favour of templateInfo
metadata: task.spec.metadata,
templateInfo: task.spec.templateInfo,
});
// Remove all temporary directories that were created when executing the action
@@ -28,6 +28,8 @@ import { ScmIntegrations } from '@backstage/integration';
import {
TemplateEntityV1beta2,
TemplateEntityV1beta3,
TaskSpecV1beta3,
TaskSpecV1beta2,
} from '@backstage/plugin-scaffolder-common';
import express from 'express';
import Router from 'express-promise-router';
@@ -186,12 +188,15 @@ export async function createRouter(
})
.post('/v2/tasks', async (req, res) => {
const templateName: string = req.body.templateName;
const { kind, namespace } = { kind: 'template', namespace: 'default' };
const values = req.body.values;
const token = getBearerToken(req.headers.authorization);
const template = await findTemplate({
catalogApi: catalogClient,
entityRef: {
name: templateName,
kind,
namespace,
},
token: getBearerToken(req.headers.authorization),
});
@@ -219,32 +224,40 @@ export async function createRouter(
const baseUrl = getEntityBaseUrl(template);
const baseTaskSpec = {
baseUrl,
steps: template.spec.steps.map((step, index) => ({
...step,
id: step.id ?? `step-${index + 1}`,
name: step.name ?? step.action,
})),
output: template.spec.output ?? {},
// deprecated in favour of templateInfo
metadata: { name: template.metadata?.name },
templateInfo: {
entityRef: stringifyEntityRef({
kind,
namespace,
name: template.metadata?.name,
}),
baseUrl,
},
};
taskSpec =
template.apiVersion === 'backstage.io/v1beta2'
? {
? ({
...baseTaskSpec,
apiVersion: template.apiVersion,
baseUrl,
values,
steps: template.spec.steps.map((step, index) => ({
...step,
id: step.id ?? `step-${index + 1}`,
name: step.name ?? step.action,
})),
output: template.spec.output ?? {},
metadata: { name: template.metadata?.name },
}
: {
} as TaskSpecV1beta2)
: ({
...baseTaskSpec,
apiVersion: template.apiVersion,
baseUrl,
parameters: values,
steps: template.spec.steps.map((step, index) => ({
...step,
id: step.id ?? `step-${index + 1}`,
name: step.name ?? step.action,
})),
output: template.spec.output ?? {},
metadata: { name: template.metadata?.name },
};
} as TaskSpecV1beta3);
} else {
throw new InputError(
`Unsupported apiVersion field in schema entity, ${
@@ -256,8 +269,6 @@ export async function createRouter(
const result = await taskBroker.dispatch(taskSpec, {
...req.body.secrets,
backstageToken: token,
// This is deprecated, but we need to support it for now if people are running their own task broker.
token: token,
});
res.status(201).json({ id: result.taskId });
+14 -4
View File
@@ -15,9 +15,9 @@ export type TaskSpec = TaskSpecV1beta2 | TaskSpecV1beta3;
export interface TaskSpecV1beta2 {
// (undocumented)
apiVersion: 'backstage.io/v1beta2';
// (undocumented)
// @deprecated (undocumented)
baseUrl?: string;
// (undocumented)
// @deprecated (undocumented)
metadata?: TemplateMetadata;
// (undocumented)
output: {
@@ -26,6 +26,8 @@ export interface TaskSpecV1beta2 {
// (undocumented)
steps: TaskStep[];
// (undocumented)
templateInfo?: TemplateInfo;
// (undocumented)
values: JsonObject;
}
@@ -33,9 +35,9 @@ export interface TaskSpecV1beta2 {
export interface TaskSpecV1beta3 {
// (undocumented)
apiVersion: 'scaffolder.backstage.io/v1beta3';
// (undocumented)
// @deprecated (undocumented)
baseUrl?: string;
// (undocumented)
// @deprecated (undocumented)
metadata?: TemplateMetadata;
// (undocumented)
output: {
@@ -45,6 +47,8 @@ export interface TaskSpecV1beta3 {
parameters: JsonObject;
// (undocumented)
steps: TaskStep[];
// (undocumented)
templateInfo?: TemplateInfo;
}
// @public
@@ -116,6 +120,12 @@ export interface TemplateEntityV1beta3 extends Entity {
export const templateEntityV1beta3Validator: KindValidator;
// @public
export type TemplateInfo = {
entityRef: string;
baseUrl?: string;
};
// @public @deprecated
export type TemplateMetadata = {
name: string;
};
+18
View File
@@ -21,11 +21,23 @@ import { JsonValue, JsonObject } from '@backstage/types';
* stored in the database.
*
* @public
* @deprecated use templateInfo on the spec instead
*/
export type TemplateMetadata = {
name: string;
};
/**
* Information about a template that is stored on a task specification.
* Includes a stringified entityRef, and the baseUrl which is usually the relative path of the template definition
*
* @public
*/
export type TemplateInfo = {
entityRef: string;
baseUrl?: string;
};
/**
* An individual step of a scaffolder task, as stored in the database.
*
@@ -49,11 +61,14 @@ export interface TaskStep {
*/
export interface TaskSpecV1beta2 {
apiVersion: 'backstage.io/v1beta2';
/** @deprecated use templateInfo.baseUrl instead */
baseUrl?: string;
values: JsonObject;
steps: TaskStep[];
output: { [name: string]: string };
/** @deprecated use templateInfo instead */
metadata?: TemplateMetadata;
templateInfo?: TemplateInfo;
}
/**
@@ -64,11 +79,14 @@ export interface TaskSpecV1beta2 {
*/
export interface TaskSpecV1beta3 {
apiVersion: 'scaffolder.backstage.io/v1beta3';
/** @deprecated use templateInfo.baseUrl instead */
baseUrl?: string;
parameters: JsonObject;
steps: TaskStep[];
output: { [name: string]: JsonValue };
/** @deprecated use templateInfo instead */
metadata?: TemplateMetadata;
templateInfo?: TemplateInfo;
}
/**
@@ -14,6 +14,7 @@
* limitations under the License.
*/
import { parseEntityRef } from '@backstage/catalog-model';
import {
Content,
ErrorPage,
@@ -291,8 +292,9 @@ export const TaskPage = ({ loadingText }: TaskPageProps) => {
const { output } = taskStream;
const handleStartOver = () => {
if (!taskStream.task || !taskStream.task?.spec.metadata?.name) {
if (!taskStream.task || !taskStream.task?.spec.templateInfo?.entityRef) {
navigate(generatePath(rootLink()));
return;
}
const formData =
@@ -300,13 +302,17 @@ export const TaskPage = ({ loadingText }: TaskPageProps) => {
? taskStream.task!.spec.values
: taskStream.task!.spec.parameters;
const { name } = parseEntityRef(
taskStream.task!.spec.templateInfo?.entityRef,
);
navigate(
generatePath(
`${rootLink()}/templates/:templateName?${qs.stringify({
formData: JSON.stringify(formData),
})}`,
{
templateName: taskStream.task!.spec.metadata!.name,
templateName: name,
},
),
);