Merge pull request #9573 from backstage/blam/deprecations/metadata
deprecations: Some deprecations for the `specs` in `scaffolder-common`
This commit is contained in:
@@ -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.
|
||||
@@ -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.
|
||||
+5
-2
@@ -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,
|
||||
});
|
||||
|
||||
@@ -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 });
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user