diff --git a/.changeset/breezy-gifts-wave.md b/.changeset/breezy-gifts-wave.md new file mode 100644 index 0000000000..2bea8e8cd1 --- /dev/null +++ b/.changeset/breezy-gifts-wave.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-backend': patch +--- + +Fix issue #7021 scaffolder action fetch:template preserves templates file permissions 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 752212606f..d676af56ca 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 @@ -146,6 +146,10 @@ describe('fetch:template', () => { mockFetchContents.mockImplementation(({ outputPath }) => { mockFs({ [outputPath]: { + 'an-executable.sh': mockFs.file({ + content: '#!/usr/bin/env bash', + mode: parseInt('100755', 8), + }), 'empty-dir-${{ values.count }}': {}, 'static.txt': 'static content', '${{ values.name }}.txt': 'static content', @@ -211,6 +215,13 @@ describe('fetch:template', () => { fs.readFile(`${workspacePath}/target/a-binary-file.png`), ).resolves.toEqual(aBinaryFile); }); + it('copies files and maintains the original file permissions', async () => { + await expect( + fs + .stat(`${workspacePath}/target/an-executable.sh`) + .then(fObj => fObj.mode), + ).resolves.toEqual(parseInt('100755', 8)); + }); }); 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 425623d154..bb568c25e6 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.ts @@ -262,8 +262,9 @@ export function createFetchTemplateAction(options: { ); await fs.copy(inputFilePath, outputPath); } else { + const statsObj = await fs.stat(inputFilePath); ctx.logger.info( - `Writing file ${location} to template output path.`, + `Writing file ${location} to template output path with mode ${statsObj.mode}.`, ); const inputFileContents = await fs.readFile(inputFilePath, 'utf-8'); await fs.outputFile( @@ -271,6 +272,7 @@ export function createFetchTemplateAction(options: { renderContents ? templater.renderString(inputFileContents, context) : inputFileContents, + { mode: statsObj.mode }, ); } }