From 5185c58bf5461933f4ab1ef628e4637c99f18370 Mon Sep 17 00:00:00 2001 From: blam Date: Thu, 7 Jul 2022 19:20:52 +0200 Subject: [PATCH 1/3] chore: copy symlinks as is, rather than copying the realpath into the place Signed-off-by: blam --- .../actions/builtin/fetch/template.test.ts | 15 +++++++++++++++ .../scaffolder/actions/builtin/fetch/template.ts | 5 +++-- 2 files changed, 18 insertions(+), 2 deletions(-) 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 8f641fe2e2..06def076ea 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 @@ -186,6 +186,10 @@ describe('fetch:template', () => { }, '.${{ values.name }}': '${{ values.itemList | dump }}', 'a-binary-file.png': aBinaryFile, + symlink: mockFs.symlink({ + path: 'a-binary-file.png', + }), + '{% if values.showDummyFile %}dummy-file.txt{% else %}{% endif %}': 'dummy file', '${{ "dummy-file2.txt" if values.showDummyFile else "" }}': @@ -265,6 +269,17 @@ describe('fetch:template', () => { .then(fObj => fObj.mode), ).resolves.toEqual(parseInt('100755', 8)); }); + it('copies file symlinks as-is without processing them', async () => { + await expect( + fs + .lstat(`${workspacePath}/target/symlink`) + .then(i => i.isSymbolicLink()), + ).resolves.toBe(true); + + await expect( + fs.realpath(`${workspacePath}/target/symlink`), + ).resolves.toBe(`${workspacePath}/target/a-binary-file.png`); + }); }); describe('copyWithoutRender', () => { 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 c6d6b86e7b..a1afabc39e 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.ts @@ -229,10 +229,11 @@ export function createFetchTemplateAction(options: { await fs.ensureDir(outputPath); } else { const inputFilePath = resolveSafeChildPath(templateDir, location); + const stats = await fs.promises.lstat(inputFilePath); - if (await isBinaryFile(inputFilePath)) { + if (stats.isSymbolicLink() || (await isBinaryFile(inputFilePath))) { ctx.logger.info( - `Copying binary file ${location} to template output path.`, + `Copying file binary or symbolic link at ${location}, to template output path.`, ); await fs.copy(inputFilePath, outputPath); } else { From ea6dcb84a47544d931954158d1ce2cd402cafa05 Mon Sep 17 00:00:00 2001 From: blam Date: Thu, 7 Jul 2022 19:24:47 +0200 Subject: [PATCH 2/3] chore: added changeset Signed-off-by: blam --- .changeset/perfect-donuts-applaud.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/perfect-donuts-applaud.md diff --git a/.changeset/perfect-donuts-applaud.md b/.changeset/perfect-donuts-applaud.md new file mode 100644 index 0000000000..3b663fa2c2 --- /dev/null +++ b/.changeset/perfect-donuts-applaud.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-backend': patch +--- + +Don't resolve symlinks, treat them as binary files and copy them as-is From c9bf1c98b5c5c04a39eaf2b4564a55496320f63b Mon Sep 17 00:00:00 2001 From: blam Date: Fri, 8 Jul 2022 11:04:40 +0200 Subject: [PATCH 3/3] chore: added some tests to ensure that we skip symlinked folders and files Signed-off-by: blam --- .../files/serializeDirectoryContents.test.ts | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/plugins/scaffolder-backend/src/lib/files/serializeDirectoryContents.test.ts b/plugins/scaffolder-backend/src/lib/files/serializeDirectoryContents.test.ts index e1ea676ec5..b24a867504 100644 --- a/plugins/scaffolder-backend/src/lib/files/serializeDirectoryContents.test.ts +++ b/plugins/scaffolder-backend/src/lib/files/serializeDirectoryContents.test.ts @@ -97,6 +97,52 @@ describe('serializeDirectoryContents', () => { ]); }); + it('should ignore symlinked files', async () => { + mockFs({ + root: { + 'a.txt': 'some text', + sym: mockFs.symlink({ + path: './a.txt', + }), + }, + }); + + await expect(serializeDirectoryContents('root')).resolves.toEqual([ + { + path: 'a.txt', + executable: false, + content: Buffer.from('some text', 'utf8'), + }, + ]); + }); + + it('should ignore symlinked folder files', async () => { + mockFs({ + root: { + 'a.txt': 'some text', + linkme: { + 'b.txt': 'lols', + }, + sym: mockFs.symlink({ + path: './linkme', + }), + }, + }); + + await expect(serializeDirectoryContents('root')).resolves.toEqual([ + { + path: 'a.txt', + executable: false, + content: Buffer.from('some text', 'utf8'), + }, + { + path: 'linkme/b.txt', + executable: false, + content: Buffer.from('lols', 'utf8'), + }, + ]); + }); + it('should ignore gitignored files', async () => { mockFs({ root: {