Merge pull request #26684 from MarkForesta/include_dry_run_template_metadata

Include templateMetadata in Dry Run
This commit is contained in:
Ben Lambert
2024-11-06 10:09:24 +01:00
committed by GitHub
4 changed files with 73 additions and 18 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-scaffolder-backend': patch
---
Modified `createDryRunner` and corresponding route to include `templateMetaData` inside the `templateInfo`. This allows custom action writers to access things like `templateInfo.entity.metadata.name` via the action context while executing templates using the dry run framework.
@@ -15,10 +15,9 @@
*/
import { ScmIntegrations } from '@backstage/integration';
import { TaskSpec } from '@backstage/plugin-scaffolder-common';
import { TaskSpec, TemplateInfo } from '@backstage/plugin-scaffolder-common';
import { JsonObject } from '@backstage/types';
import { v4 as uuid } from 'uuid';
import { pathToFileURL } from 'url';
import { fileURLToPath } from 'url';
import { Logger } from 'winston';
import {
createTemplateAction,
@@ -29,19 +28,19 @@ import {
SerializedFile,
serializeDirectoryContents,
} from '@backstage/plugin-scaffolder-node';
import path from 'path';
import { TemplateActionRegistry } from '../actions';
import { NunjucksWorkflowRunner } from '../tasks/NunjucksWorkflowRunner';
import { DecoratedActionsRegistry } from './DecoratedActionsRegistry';
import fs from 'fs-extra';
import { PermissionEvaluator } from '@backstage/plugin-permission-common';
import {
BackstageCredentials,
resolveSafeChildPath,
} from '@backstage/backend-plugin-api';
import { BackstageCredentials } from '@backstage/backend-plugin-api';
import type { UserEntity } from '@backstage/catalog-model';
import { v4 as uuid } from 'uuid';
interface DryRunInput {
spec: TaskSpec;
templateInfo: TemplateInfo;
secrets?: TaskSecrets;
directoryContents: SerializedFile[];
credentials: BackstageCredentials;
@@ -94,18 +93,21 @@ export function createDryRunner(options: TemplateTesterCreateOptions) {
]),
});
// Extracting contentsPath and dryRunId from the baseUrl
const baseUrl = input.templateInfo.baseUrl;
if (!baseUrl) {
throw new Error('baseUrl is required');
}
const basePath = fileURLToPath(new URL(baseUrl));
const contentsPath = path.dirname(basePath);
const dryRunId = uuid();
const log = new Array<{ body: JsonObject }>();
const contentsPath = resolveSafeChildPath(
options.workingDirectory,
`dry-run-content-${dryRunId}`,
);
try {
await deserializeDirectoryContents(contentsPath, input.directoryContents);
const abortSignal = new AbortController().signal;
const result = await workflowRunner.execute({
spec: {
...input.spec,
@@ -117,12 +119,7 @@ export function createDryRunner(options: TemplateTesterCreateOptions) {
action: 'dry-run:extract',
},
],
templateInfo: {
entityRef: 'template:default/dry-run',
baseUrl: pathToFileURL(
resolveSafeChildPath(contentsPath, 'template.yaml'),
).toString(),
},
templateInfo: input.templateInfo,
},
secrets: input.secrets,
getInitiatorCredentials: () => Promise.resolve(input.credentials),
@@ -1331,6 +1331,39 @@ describe('NunjucksWorkflowRunner', () => {
expect(fakeActionHandler.mock.calls[0][0].isDryRun).toEqual(true);
});
it('should have metadata in action context during dry run', async () => {
const task = createMockTaskWithSpec(
{
apiVersion: 'scaffolder.backstage.io/v1beta3',
templateInfo: {
entityRef: 'dryRun-Entity',
entity: { metadata: { name: 'test-template' } },
},
parameters: {},
output: {},
steps: [
{
id: 'test',
name: 'name',
action: 'jest-validated-action',
input: { foo: 1 },
},
],
},
{
backstageToken: token,
},
true,
);
await runner.execute(task);
expect(fakeActionHandler.mock.calls[0][0].isDryRun).toEqual(true);
expect(
fakeActionHandler.mock.calls[0][0].templateInfo.entity.metadata.name,
).toEqual('test-template');
});
});
describe('permissions', () => {
@@ -94,6 +94,7 @@ import {
PermissionsService,
SchedulerService,
UrlReaderService,
resolveSafeChildPath,
} from '@backstage/backend-plugin-api';
import {
IdentityApi,
@@ -105,6 +106,8 @@ import {
AutocompleteHandler,
WorkspaceProvider,
} from '@backstage/plugin-scaffolder-node/alpha';
import { pathToFileURL } from 'url';
import { v4 as uuid } from 'uuid';
/**
*
@@ -814,6 +817,22 @@ export async function createRouter(
name: step.name ?? step.action,
}));
const dryRunId = uuid();
const contentsPath = resolveSafeChildPath(
workingDirectory,
`dry-run-content-${dryRunId}`,
);
const templateInfo = {
entityRef: stringifyEntityRef(template),
entity: {
metadata: template.metadata,
},
baseUrl: pathToFileURL(
resolveSafeChildPath(contentsPath, 'template.yaml'),
).toString(),
};
const result = await dryRunner({
spec: {
apiVersion: template.apiVersion,
@@ -825,6 +844,7 @@ export async function createRouter(
ref: userEntityRef,
},
},
templateInfo: templateInfo,
directoryContents: (body.directoryContents ?? []).map(file => ({
path: file.path,
content: Buffer.from(file.base64Content, 'base64'),