Merge pull request #27104 from backstage/rugvip/scaffolder-module

cli: update scaffolder-module template
This commit is contained in:
Patrik Oldsberg
2024-10-14 15:51:54 +02:00
committed by GitHub
15 changed files with 91 additions and 61 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/cli': patch
---
The `scaffolder-module` template has been updated to use a more modern layout and new testing utilities for scaffolder actions.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/backend-test-utils': patch
---
The `createMockDirectory` cleanup strategy has been changed, no longer requiring it to be called outside individual tests.
@@ -349,8 +349,11 @@ class MockDirectoryImpl {
*/
export interface CreateMockDirectoryOptions {
/**
* In addition to creating a temporary directory, also mock `os.tmpdir()` to return the
* mock directory path until the end of the test suite.
* In addition to creating a temporary directory, also mock `os.tmpdir()` to
* return the mock directory path until the end of the test suite.
*
* When this option is provided the `createMockDirectory` call must happen in
* a scope where calling `afterAll` from Jest is allowed
*
* @returns
*/
@@ -362,6 +365,34 @@ export interface CreateMockDirectoryOptions {
content?: MockDirectoryContent;
}
const cleanupCallbacks = new Array<() => void>();
let registered = false;
function registerTestHooks() {
if (typeof afterAll !== 'function') {
return;
}
if (registered) {
return;
}
registered = true;
afterAll(async () => {
for (const callback of cleanupCallbacks) {
try {
callback();
} catch (error) {
console.error(
`Failed to clean up mock directory after tests, ${error}`,
);
}
}
cleanupCallbacks.length = 0;
});
}
registerTestHooks();
/**
* Creates a new temporary mock directory that will be removed after the tests have completed.
*
@@ -410,17 +441,14 @@ export function createMockDirectory(
process.on('beforeExit', mocker.remove);
}
try {
if (needsCleanup) {
cleanupCallbacks.push(() => mocker.remove());
}
if (origTmpdir) {
afterAll(() => {
if (origTmpdir) {
os.tmpdir = origTmpdir;
}
if (needsCleanup) {
mocker.remove();
}
os.tmpdir = origTmpdir;
});
} catch {
/* ignore */
}
if (options?.content) {
+1
View File
@@ -174,6 +174,7 @@
"@backstage/plugin-auth-backend-module-guest-provider": "workspace:^",
"@backstage/plugin-catalog-node": "workspace:^",
"@backstage/plugin-scaffolder-node": "workspace:^",
"@backstage/plugin-scaffolder-node-test-utils": "workspace:^",
"@backstage/test-utils": "workspace:^",
"@backstage/theme": "workspace:^",
"@rspack/core": "^1.0.10",
@@ -93,10 +93,8 @@ describe('scaffolderModule factory', () => {
'templating README.md.hbs',
'templating package.json.hbs',
'templating index.ts.hbs',
'copying index.ts',
'copying example.test.ts',
'copying example.ts',
'copying index.ts',
'copying module.ts',
'Installing:',
`moving plugins${sep}scaffolder-backend-module-test`,
+2
View File
@@ -44,6 +44,7 @@ import { version as devUtils } from '../../../../packages/dev-utils/package.json
import { version as errors } from '../../../../packages/errors/package.json';
import { version as testUtils } from '../../../../packages/test-utils/package.json';
import { version as scaffolderNode } from '../../../../plugins/scaffolder-node/package.json';
import { version as scaffolderNodeTestUtils } from '../../../../plugins/scaffolder-node-test-utils/package.json';
import { version as authBackend } from '../../../../plugins/auth-backend/package.json';
import { version as authBackendModuleGuestProvider } from '../../../../plugins/auth-backend-module-guest-provider/package.json';
import { version as catalogNode } from '../../../../plugins/catalog-node/package.json';
@@ -65,6 +66,7 @@ export const packageVersions: Record<string, string> = {
'@backstage/test-utils': testUtils,
'@backstage/theme': theme,
'@backstage/plugin-scaffolder-node': scaffolderNode,
'@backstage/plugin-scaffolder-node-test-utils': scaffolderNodeTestUtils,
'@backstage/plugin-auth-backend': authBackend,
'@backstage/plugin-auth-backend-module-guest-provider':
authBackendModuleGuestProvider,
@@ -29,10 +29,12 @@
"postpack": "backstage-cli package postpack"
},
"dependencies": {
"@backstage/backend-plugin-api": "{{versionQuery '@backstage/backend-plugin-api'}}",
"@backstage/plugin-scaffolder-node": "{{versionQuery '@backstage/plugin-scaffolder-node'}}"
},
"devDependencies": {
"@backstage/cli": "{{versionQuery '@backstage/cli'}}"
"@backstage/cli": "{{versionQuery '@backstage/cli'}}",
"@backstage/plugin-scaffolder-node-test-utils": "{{versionQuery '@backstage/plugin-scaffolder-node-test-utils'}}"
},
"files": [
"dist"
@@ -0,0 +1,24 @@
import { createExampleAction } from './example';
import {createMockActionContext} from '@backstage/plugin-scaffolder-node-test-utils'
describe('createExampleAction', () => {
it('should call action', async () => {
const action = createExampleAction();
await expect(action.handler(createMockActionContext({
input: {
myParameter: 'test',
},
}))).resolves.toBeUndefined()
});
it('should fail when passing foo', async () => {
const action = createExampleAction();
await expect(action.handler(createMockActionContext({
input: {
myParameter: 'foo',
},
}))).rejects.toThrow("myParameter cannot be 'foo'")
});
});
@@ -9,14 +9,14 @@ import { createTemplateAction } from '@backstage/plugin-scaffolder-node';
*
* @public
*/
export function createAcmeExampleAction() {
export function createExampleAction() {
// For more information on how to define custom actions, see
// https://backstage.io/docs/features/software-templates/writing-custom-actions
return createTemplateAction<{
myParameter: string;
}>({
id: 'acme:example',
description: 'Runs Yeoman on an installed Yeoman generator',
description: 'Runs an example action',
schema: {
input: {
type: 'object',
@@ -24,7 +24,7 @@ export function createAcmeExampleAction() {
properties: {
myParameter: {
title: 'An example parameter',
description: 'This is the schema for our example parameter',
description: "This is an example parameter, don't set it to foo",
type: 'string',
},
},
@@ -35,6 +35,10 @@ export function createAcmeExampleAction() {
`Running example template with parameters: ${ctx.input.myParameter}`,
);
if (ctx.input.myParameter === 'foo') {
throw new Error(`myParameter cannot be 'foo'`);
}
await new Promise(resolve => setTimeout(resolve, 1000));
},
});
@@ -1,32 +0,0 @@
import { PassThrough } from 'stream';
import { createAcmeExampleAction } from './example';
describe('acme:example', () => {
afterEach(() => {
jest.resetAllMocks();
});
it('should call action', async () => {
const action = createAcmeExampleAction();
const logger = { info: jest.fn() };
await action.handler({
input: {
myParameter: 'test',
},
workspacePath: '/tmp',
logger: logger as any,
logStream: new PassThrough(),
output: jest.fn(),
createTemporaryDirectory() {
// Usage of createMockDirectory is recommended for testing of filesystem operations
throw new Error('Not implemented');
},
});
expect(logger.info).toHaveBeenCalledWith(
'Running example template with parameters: test',
);
});
});
@@ -1,7 +0,0 @@
import { scaffolderModule } from './module';
/*
@deprecated - this way of importing modules will soon be unsupported, and you should use `backend.add(import(...))` instead.
*/
export { createAcmeExampleAction } from './example';
export default scaffolderModule;
@@ -1 +0,0 @@
export * from './example';
@@ -5,4 +5,4 @@
* @packageDocumentation
*/
export * from './actions';
export { scaffolderModule as default } from './module';
@@ -1,12 +1,12 @@
import { createBackendModule } from "@backstage/backend-plugin-api";
import { scaffolderActionsExtensionPoint } from '@backstage/plugin-scaffolder-node/alpha';
import { createAcmeExampleAction } from "./example";
import { createExampleAction } from "./actions/example";
/**
* A backend module that registers the action into the scaffolder
*/
export const scaffolderModule = createBackendModule({
moduleId: 'acme:example',
moduleId: 'example-action',
pluginId: 'scaffolder',
register({ registerInit }) {
registerInit({
@@ -14,7 +14,7 @@ export const scaffolderModule = createBackendModule({
scaffolderActions: scaffolderActionsExtensionPoint
},
async init({ scaffolderActions}) {
scaffolderActions.addActions(createAcmeExampleAction());
scaffolderActions.addActions(createExampleAction());
}
});
},
+1
View File
@@ -3930,6 +3930,7 @@ __metadata:
"@backstage/plugin-auth-backend-module-guest-provider": "workspace:^"
"@backstage/plugin-catalog-node": "workspace:^"
"@backstage/plugin-scaffolder-node": "workspace:^"
"@backstage/plugin-scaffolder-node-test-utils": "workspace:^"
"@backstage/release-manifests": "workspace:^"
"@backstage/test-utils": "workspace:^"
"@backstage/theme": "workspace:^"