cli/new: update template loading to match new design

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2025-02-11 00:07:45 +01:00
parent 7c430f3567
commit 7fb1408bb2
4 changed files with 92 additions and 55 deletions
@@ -18,26 +18,56 @@ import { createMockDirectory } from '@backstage/backend-test-utils';
import { loadPortableTemplate } from './loadPortableTemplate';
describe('loadTemplate', () => {
const mockDir = createMockDirectory();
afterEach(() => {
mockDir.clear();
});
it('should load a valid template', async () => {
const mockDir = createMockDirectory({
content: {
'path/to/template1.yaml': `
template: template1
mockDir.setContent({
'path/to/template.yaml': `
name: template1
role: frontend-plugin
parameters:
foo: bar
`,
'path/to/template1/hello.txt': 'hello world',
},
'path/to/hello.txt': 'hello world',
});
const result = await loadPortableTemplate({
id: 'template1',
target: mockDir.resolve('path/to/template1.yaml'),
await expect(
loadPortableTemplate({
name: 'template1',
target: mockDir.resolve('path/to/template.yaml'),
}),
).resolves.toEqual({
name: 'template1',
role: 'frontend-plugin',
files: [{ path: 'hello.txt', content: 'hello world' }],
parameters: { foo: 'bar' },
templateValues: {},
});
});
it('should load a valid template with files in a separate dir', async () => {
mockDir.setContent({
'path/to/template.yaml': `
name: template1
role: frontend-plugin
files: content
parameters:
foo: bar
`,
'path/to/content/hello.txt': 'hello world',
});
expect(result).toEqual({
id: 'template1',
await expect(
loadPortableTemplate({
name: 'template1',
target: mockDir.resolve('path/to/template.yaml'),
}),
).resolves.toEqual({
name: 'template1',
role: 'frontend-plugin',
files: [{ path: 'hello.txt', content: 'hello world' }],
parameters: { foo: 'bar' },
@@ -46,11 +76,11 @@ describe('loadTemplate', () => {
});
it('should throw an error if template file does not exist', async () => {
const mockDir = createMockDirectory();
mockDir.setContent({});
await expect(
loadPortableTemplate({
id: 'template1',
name: 'template1',
target: mockDir.resolve('path/to/template1.yaml'),
}),
).rejects.toThrow(
@@ -59,15 +89,13 @@ describe('loadTemplate', () => {
});
it('should throw an error if template definition is invalid', async () => {
const mockDir = createMockDirectory({
content: {
'path/to/template1.yaml': `invalid: definition`,
},
mockDir.setContent({
'path/to/template1.yaml': `invalid: definition`,
});
await expect(
loadPortableTemplate({
id: 'template1',
name: 'template1',
target: mockDir.resolve('path/to/template1.yaml'),
}),
).rejects.toThrow(
@@ -78,34 +106,24 @@ describe('loadTemplate', () => {
it('should throw an error if target is a remote URL', async () => {
await expect(
loadPortableTemplate({
id: 'template1',
target: 'http://example.com',
}),
).rejects.toThrow('Remote templates are not supported yet');
});
it('should throw an error if target directory does not exist', async () => {
await expect(
loadPortableTemplate({
id: 'template1',
name: 'template1',
target: 'http://example.com',
}),
).rejects.toThrow('Remote templates are not supported yet');
});
it('should throw an error if the package role is invalid', async () => {
const mockDir = createMockDirectory({
content: {
'path/to/template1.yaml': `
template: template1
role: invalid-role
`,
},
mockDir.setContent({
'path/to/template1.yaml': `
name: x
role: invalid-role
files: template1
`,
});
await expect(
loadPortableTemplate({
id: 'template1',
name: 'template1',
target: mockDir.resolve('path/to/template1.yaml'),
}),
).rejects.toThrow(
@@ -116,18 +134,17 @@ describe('loadTemplate', () => {
});
it('should throw an error if template directory does not exist', async () => {
const mockDir = createMockDirectory({
content: {
'path/to/template1.yaml': `
template: template1
role: frontend-plugin
`,
},
mockDir.setContent({
'path/to/template1.yaml': `
name: x
role: frontend-plugin
files: template1
`,
});
await expect(
loadPortableTemplate({
id: 'template1',
name: 'template1',
target: mockDir.resolve('path/to/template1.yaml'),
}),
).rejects.toThrow(
@@ -32,8 +32,10 @@ import { fromZodError } from 'zod-validation-error';
const templateDefinitionSchema = z
.object({
name: z.string(),
role: z.enum(TEMPLATE_ROLES),
template: z.string(),
description: z.string().optional(),
files: z.string().optional(),
parameters: z.record(z.string()).optional(),
templateValues: z.record(z.string()).optional(),
})
@@ -63,9 +65,14 @@ export async function loadPortableTemplate(
);
}
const { role, template, parameters = {}, templateValues = {} } = parsed.data;
const {
role,
files = '.',
parameters = {},
templateValues = {},
} = parsed.data;
const templatePath = resolvePath(dirname(pointer.target), template);
const templatePath = resolvePath(dirname(pointer.target), files);
const filePaths = await recursiveReaddir(templatePath).catch(error => {
throw new ForwardedError(
`Failed to load template contents from '${templatePath}'`,
@@ -73,10 +80,13 @@ export async function loadPortableTemplate(
);
});
const files = new Array<PortableTemplateFile>();
const loadedFiles = new Array<PortableTemplateFile>();
for (const filePath of filePaths) {
const path = relativePath(templatePath, filePath);
if (filePath === pointer.target) {
continue;
}
const content = await fs.readFile(filePath, 'utf-8').catch(error => {
throw new ForwardedError(
@@ -86,11 +96,21 @@ export async function loadPortableTemplate(
});
if (path.endsWith('.hbs')) {
files.push({ path: path.slice(0, -4), content, syntax: 'handlebars' });
loadedFiles.push({
path: path.slice(0, -4),
content,
syntax: 'handlebars',
});
} else {
files.push({ path, content });
loadedFiles.push({ path, content });
}
}
return { id: pointer.id, role, files, parameters, templateValues };
return {
name: pointer.name,
role,
files: loadedFiles,
parameters,
templateValues,
};
}
@@ -38,15 +38,15 @@ export async function selectTemplateInteractively(
message: 'What do you want to create?',
choices: config.templatePointers.map(t =>
t.description
? { name: `${t.id} - ${t.description}`, value: t.id }
: t.id,
? { name: `${t.name} - ${t.description}`, value: t.name }
: t.name,
),
},
]);
selectedId = answers.id;
}
const template = config.templatePointers.find(t => t.id === selectedId);
const template = config.templatePointers.find(t => t.name === selectedId);
if (!template) {
throw new Error(`Template '${selectedId}' not found`);
}
+1 -1
View File
@@ -66,7 +66,7 @@ export type PortableTemplateFile = {
};
export type PortableTemplate = {
id: string;
name: string;
role: PortableTemplateRole;
files: PortableTemplateFile[];
parameters: Record<string, string>;