diff --git a/.changeset/proud-dolls-behave.md b/.changeset/proud-dolls-behave.md new file mode 100644 index 0000000000..8a93bdb4bf --- /dev/null +++ b/.changeset/proud-dolls-behave.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-backend': minor +--- + +Support file globbing in fs:delete diff --git a/plugins/scaffolder-backend/package.json b/plugins/scaffolder-backend/package.json index 9e45865447..219a16b592 100644 --- a/plugins/scaffolder-backend/package.json +++ b/plugins/scaffolder-backend/package.json @@ -93,6 +93,7 @@ "concat-stream": "^2.0.0", "express": "^4.17.1", "express-promise-router": "^4.1.0", + "fast-glob": "^3.3.3", "fs-extra": "^11.2.0", "globby": "^11.0.0", "isbinaryfile": "^5.0.0", diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/filesystem/delete.examples.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/filesystem/delete.examples.ts index 8aea076b9d..a32249f210 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/filesystem/delete.examples.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/filesystem/delete.examples.ts @@ -33,4 +33,19 @@ export const examples: TemplateExample[] = [ ], }), }, + { + description: 'Delete files with wildcard', + example: yaml.stringify({ + steps: [ + { + action: 'fs:delete', + id: 'deleteFiles', + name: 'Delete files', + input: { + files: ['*.txt'], + }, + }, + ], + }), + }, ]; diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/filesystem/delete.test.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/filesystem/delete.test.ts index 8226133376..f037323c04 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/filesystem/delete.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/filesystem/delete.test.ts @@ -95,6 +95,15 @@ describe('fs:delete', () => { ).rejects.toThrow( /Relative path is not allowed to refer to a directory outside its parent/, ); + + await expect( + action.handler({ + ...mockContext, + input: { files: ['../../../**/index.js'] }, + }), + ).rejects.toThrow( + /Relative path is not allowed to refer to a directory outside its parent/, + ); }); it('should call fs.rm with the correct values', async () => { @@ -114,4 +123,25 @@ describe('fs:delete', () => { expect(fileExists).toBe(false); }); }); + + it('should handle wildcards', async () => { + const files = ['unit-test-a.js', 'unit-test-b.js']; + + files.forEach(file => { + const filePath = resolvePath(workspacePath, file); + const fileExists = fs.existsSync(filePath); + expect(fileExists).toBe(true); + }); + + await action.handler({ + ...mockContext, + input: { files: ['unit-*.js'] }, + }); + + files.forEach(file => { + const filePath = resolvePath(workspacePath, file); + const fileExists = fs.existsSync(filePath); + expect(fileExists).toBe(false); + }); + }); }); diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/filesystem/delete.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/filesystem/delete.ts index 13dfc69525..f785431e17 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/filesystem/delete.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/filesystem/delete.ts @@ -18,6 +18,7 @@ import { createTemplateAction } from '@backstage/plugin-scaffolder-node'; import { InputError } from '@backstage/errors'; import { resolveSafeChildPath } from '@backstage/backend-plugin-api'; import fs from 'fs-extra'; +import fg from 'fast-glob'; import { examples } from './delete.examples'; /** @@ -52,14 +53,20 @@ export const createFilesystemDeleteAction = () => { } for (const file of ctx.input.files) { - const filepath = resolveSafeChildPath(ctx.workspacePath, file); + const safeFilepath = resolveSafeChildPath(ctx.workspacePath, file); + const resolvedPaths = await fg(safeFilepath, { + cwd: ctx.workspacePath, + absolute: true, + }); - try { - await fs.remove(filepath); - ctx.logger.info(`File ${filepath} deleted successfully`); - } catch (err) { - ctx.logger.error(`Failed to delete file ${filepath}:`, err); - throw err; + for (const filepath of resolvedPaths) { + try { + await fs.remove(filepath); + ctx.logger.info(`File ${filepath} deleted successfully`); + } catch (err) { + ctx.logger.error(`Failed to delete file ${filepath}:`, err); + throw err; + } } } }, diff --git a/yarn.lock b/yarn.lock index 94746fde49..2e76627f5b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7753,6 +7753,7 @@ __metadata: esbuild: ^0.24.0 express: ^4.17.1 express-promise-router: ^4.1.0 + fast-glob: ^3.3.3 fs-extra: ^11.2.0 globby: ^11.0.0 isbinaryfile: ^5.0.0