feat: allow listing file contents with debug:log

This further improves the debug functionality to be able to actually see
what is the content of the files generated by the template

Signed-off-by: Heikki Hellgren <heikki.hellgren@op.fi>
This commit is contained in:
Heikki Hellgren
2024-08-29 11:20:02 +03:00
parent 0a2ccf8458
commit f0c6b252cc
5 changed files with 77 additions and 23 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-scaffolder-backend': patch
---
Allow listing file contents with `debug:log` scaffolder action
+1 -1
View File
@@ -130,7 +130,7 @@ export function createCatalogWriteAction(): TemplateAction_2<
export function createDebugLogAction(): TemplateAction_2<
{
message?: string | undefined;
listWorkspace?: boolean | undefined;
listWorkspace?: boolean | 'with-contents' | 'with-filenames' | undefined;
},
JsonObject
>;
@@ -48,4 +48,19 @@ export const examples: TemplateExample[] = [
],
}),
},
{
description: 'List the workspace directory with file contents',
example: yaml.stringify({
steps: [
{
action: 'debug:log',
id: 'write-workspace-directory',
name: 'List the workspace directory with file contents',
input: {
listWorkspace: 'with-contents',
},
},
],
}),
},
];
@@ -35,8 +35,9 @@ describe('debug:log', () => {
beforeEach(() => {
mockDir.setContent({
[`${mockContext.workspacePath}/README.md`]: '',
[`${mockContext.workspacePath}/a-directory/index.md`]: '',
[`${mockContext.workspacePath}/README.md`]: 'This is a README file',
[`${mockContext.workspacePath}/a-directory/index.md`]:
'This is a markdown file',
});
jest.resetAllMocks();
});
@@ -94,6 +95,34 @@ describe('debug:log', () => {
);
});
it('should log the workspace content with file contents from an example, if active', async () => {
const example = action.examples?.find(
sample =>
sample.description ===
'List the workspace directory with file contents',
)?.example as string;
expect(typeof example).toEqual('string');
const context = {
...mockContext,
...yaml.parse(example).steps[0],
};
await action.handler(context);
expect(logger.info).toHaveBeenCalledWith(
expect.stringContaining('README.md'),
);
expect(logger.info).toHaveBeenCalledWith(
expect.stringContaining(join('a-directory', 'index.md')),
);
expect(logger.info).toHaveBeenCalledWith(
expect.stringContaining('This is a README file'),
);
expect(logger.info).toHaveBeenCalledWith(
expect.stringContaining('This is a markdown file'),
);
});
it('should log message from an example', async () => {
const example = action.examples?.find(
sample => sample.description === 'Write a debug message',
@@ -15,9 +15,11 @@
*/
import { readdir, stat } from 'fs-extra';
import { relative, join } from 'path';
import { join, relative } from 'path';
import { createTemplateAction } from '@backstage/plugin-scaffolder-node';
import { examples } from './log.examples';
import fs from 'fs';
import { z } from 'zod';
const id = 'debug:log';
@@ -32,28 +34,24 @@ const id = 'debug:log';
* @public
*/
export function createDebugLogAction() {
return createTemplateAction<{ message?: string; listWorkspace?: boolean }>({
return createTemplateAction<{
message?: string;
listWorkspace?: boolean | 'with-filenames' | 'with-contents';
}>({
id,
description:
'Writes a message into the log or lists all files in the workspace.',
'Writes a message into the log and/or lists all files in the workspace.',
examples,
schema: {
input: {
type: 'object',
properties: {
message: {
title: 'Message to output.',
type: 'string',
},
listWorkspace: {
title: 'List all files in the workspace, if true.',
type: 'boolean',
},
extra: {
title: 'Extra info',
},
},
},
input: z.object({
message: z.string({ description: 'Message to output.' }).optional(),
listWorkspace: z
.union([z.boolean(), z.enum(['with-filenames', 'with-contents'])], {
description:
'List all files in the workspace. If used with "with-contents", also the file contents are listed.',
})
.optional(),
}),
},
supportsDryRun: true,
async handler(ctx) {
@@ -67,7 +65,14 @@ export function createDebugLogAction() {
const files = await recursiveReadDir(ctx.workspacePath);
ctx.logger.info(
`Workspace:\n${files
.map(f => ` - ${relative(ctx.workspacePath, f)}`)
.map(f => {
const relativePath = relative(ctx.workspacePath, f);
if (ctx.input?.listWorkspace === 'with-contents') {
const content = fs.readFileSync(f, 'utf-8');
return ` - ${relativePath}:\n\n ${content}`;
}
return ` - ${relativePath}`;
})
.join('\n')}`,
);
}