From 7d5a921114f2a0285ef3a856f5d2e7312b78b1b3 Mon Sep 17 00:00:00 2001 From: Darrgh Bailey Date: Mon, 6 Nov 2023 11:48:43 +0000 Subject: [PATCH] Allow globby negative matches for copyWithoutTemplating The option copyWithoutTemplating (and depracted copyWithoutRender) would require listing all files in a path to include even if there was only one or two files that should still be templated while skipping all others. This works fine when just one or two files or paths that need to be copied without being templated, however when there are many files and only one or two should be templated, it becomes more error prone to try and maintain a complete list and ensure others will update it correctly. While it's possible to use separate paths to avoid this issue, it results in subsequently needing to move files around, or use separate template calls, this makes it more difficult to maintain a template logically. The existing template code already makes use of 'globby', which supports negative matching to explicitly exclude individual paths from other wildcard matches. The existing code would iterate over each entry calling globby separately for each pattern passed in. This would skip globby's ability to have negative matches remove previously matched paths. This change switches to pass the entire list of patterns to globby in a single call, leaving it up to globby to parse and return the entire set in a single operation. Signed-off-by: Darragh Bailey --- .changeset/strong-sloths-push.md | 5 ++ .../actions/builtin/fetch/template.test.ts | 51 +++++++++++++++++++ .../actions/builtin/fetch/template.ts | 20 +++----- 3 files changed, 63 insertions(+), 13 deletions(-) create mode 100644 .changeset/strong-sloths-push.md diff --git a/.changeset/strong-sloths-push.md b/.changeset/strong-sloths-push.md new file mode 100644 index 0000000000..5e449b21ef --- /dev/null +++ b/.changeset/strong-sloths-push.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-backend': minor +--- + +Allow using globby's negative matching with copyWithoutTemplating/copyWithoutRender. Allows including an entire subdirectory while excluding a single file so that it will still be templated instead of needing to list every other file and ensure the list is updated when new files are added. diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.test.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.test.ts index d396a00c26..119cd78eb7 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.test.ts @@ -470,6 +470,57 @@ describe('fetch:template', () => { ), ).resolves.toEqual('1234'); }); + + describe('with exclusion filter', () => { + beforeEach(async () => { + context = mockContext({ + values: { + name: 'test-project', + count: 1234, + }, + copyWithoutTemplating: [ + '.unprocessed', + '!*/templated-process-content-${{ values.name }}.txt', + ], + }); + + mockFetchContents.mockImplementation(({ outputPath }) => { + mockDir.setContent({ + [outputPath]: { + processed: { + 'templated-content-${{ values.name }}.txt': + '${{ values.count }}', + }, + '.unprocessed': { + 'templated-content-${{ values.name }}.txt': + '${{ values.count }}', + 'templated-process-content-${{ values.name }}.txt': + '${{ values.count }}', + }, + }, + }); + + return Promise.resolve(); + }); + + await action.handler(context); + }); + + it('renders path template including excluded matches in copyWithoutTemplating', async () => { + await expect( + fs.readFile( + `${workspacePath}/target/.unprocessed/templated-process-content-test-project.txt`, + 'utf-8', + ), + ).resolves.toEqual('1234'); + await expect( + fs.readFile( + `${workspacePath}/target/.unprocessed/templated-content-test-project.txt`, + 'utf-8', + ), + ).resolves.toEqual('${{ values.count }}'); + }); + }); }); describe('cookiecutter compatibility mode', () => { 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 1116d4dd5d..26b249b76d 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.ts @@ -207,19 +207,13 @@ export function createFetchTemplateAction(options: { }); const nonTemplatedEntries = new Set( - ( - await Promise.all( - (copyOnlyPatterns || []).map(pattern => - globby(pattern, { - cwd: templateDir, - dot: true, - onlyFiles: false, - markDirectories: true, - followSymbolicLinks: false, - }), - ), - ) - ).flat(), + await globby(copyOnlyPatterns || [], { + cwd: templateDir, + dot: true, + onlyFiles: false, + markDirectories: true, + followSymbolicLinks: false, + }), ); // Cookiecutter prefixes all parameters in templates with