From cc8cf9e89c2e1cf4216cfd57d262d22122e8d574 Mon Sep 17 00:00:00 2001 From: Mike Lewis Date: Fri, 2 Jul 2021 18:17:43 +0100 Subject: [PATCH] scaffolder: add support for copyWithoutRender to fetch:template Signed-off-by: Mike Lewis --- .../actions/builtin/fetch/template.ts | 61 +++++++++++++++---- 1 file changed, 49 insertions(+), 12 deletions(-) diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.ts index ee33465d5b..4f6f20cedb 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.ts @@ -34,6 +34,7 @@ export function createFetchTemplateAction(options: { url: string; targetPath?: string; values: any; + copyWithoutRender?: string[]; }>({ id: 'fetch:template', description: @@ -60,8 +61,15 @@ export function createFetchTemplateAction(options: { description: 'Values to pass on to the templating engine', type: 'object', }, - // TODO(mtlewis/orkohunter): add copyWithoutRender support - // + copyWithoutRender: { + title: 'Copy Without Render', + description: + 'Avoid rendering directories and files in the template', + type: 'array', + items: { + type: 'string', + }, + }, // TODO(mtlewis/orkohunter): do we need to replicate the template extensions support // from fetch:cookiecutter? }, @@ -69,18 +77,28 @@ export function createFetchTemplateAction(options: { }, async handler(ctx) { ctx.logger.info('Fetching template content from remote URL'); + const workDir = await ctx.createTemporaryDirectory(); const templateDir = resolvePath(workDir, 'template'); - // Finally move the template result into the task workspace const targetPath = ctx.input.targetPath ?? './'; const outputPath = path.resolve(ctx.workspacePath, targetPath); + if (!outputPath.startsWith(ctx.workspacePath)) { throw new InputError( `Fetch action targetPath may not specify a path outside the working directory`, ); } + if ( + ctx.input.copyWithoutRender && + !Array.isArray(ctx.input.copyWithoutRender) + ) { + throw new InputError( + 'Fetch action input copyWithoutRender must be an Array', + ); + } + await fetchContents({ reader, integrations, @@ -103,6 +121,15 @@ export function createFetchTemplateAction(options: { cwd: templateDir, dot: true, }); + const nonTemplatedFiles = new Set( + ( + await Promise.all( + (ctx.input.copyWithoutRender || []).map(pattern => + globby(pattern, { cwd: templateDir, dot: true }), + ), + ) + ).flat(), + ); // Nice for Cookiecutter compat // @@ -116,7 +143,9 @@ export function createFetchTemplateAction(options: { variableStart: '${{', variableEnd: '}}', }, - // We don't want this builtin auto-escaping since it is escaping as HTML which will often be incorrect e.g. adds things like " + // We don't want this builtin auto-escaping, since uses HTML escape sequences + // like `"` - the correct way to escape strings in our case depends on + // the file type. autoescape: false, }); @@ -127,16 +156,24 @@ export function createFetchTemplateAction(options: { templater.addFilter('jsonify', s => JSON.stringify(s)); for (const location of allFilesInTemplates) { - // handle variables in filenames - const filepath = templater.renderString(location, ctx.input.values); + const isTemplated = !nonTemplatedFiles.has(location); + const outputFilePath = resolvePath( + outputPath, + isTemplated + ? templater.renderString(location, ctx.input.values) + : location, + ); + + const inputFileContents = await fs.readFile( + resolvePath(templateDir, location), + 'utf-8', + ); - // write file await fs.outputFile( - resolvePath(outputPath, filepath), - templater.renderString( - await fs.readFile(resolvePath(templateDir, location), 'utf-8'), - ctx.input.values, - ), + outputFilePath, + isTemplated + ? templater.renderString(inputFileContents, ctx.input.values) + : inputFileContents, ); } },