From 3d6ef79fb654f961a6ac7bce28cbafbcb0379083 Mon Sep 17 00:00:00 2001 From: Gavin Clarke <431018+gavinclarkeuk@users.noreply.github.com> Date: Sun, 9 Feb 2025 16:45:44 +0000 Subject: [PATCH 1/2] Add support for file globbing to fs:delete Signed-off-by: Gavin Clarke <431018+gavinclarkeuk@users.noreply.github.com> --- .changeset/proud-dolls-behave.md | 5 ++++ plugins/scaffolder-backend/package.json | 1 + .../builtin/filesystem/delete.examples.ts | 15 ++++++++++ .../actions/builtin/filesystem/delete.test.ts | 30 +++++++++++++++++++ .../actions/builtin/filesystem/delete.ts | 21 ++++++++----- yarn.lock | 1 + 6 files changed, 66 insertions(+), 7 deletions(-) create mode 100644 .changeset/proud-dolls-behave.md 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 From f0f7e4a0a673a00559033d209ae75ec35ae6b6e3 Mon Sep 17 00:00:00 2001 From: Gavin Clarke <431018+gavinclarkeuk@users.noreply.github.com> Date: Tue, 11 Feb 2025 10:42:10 +0000 Subject: [PATCH 2/2] Switch to using globby library Signed-off-by: Gavin Clarke <431018+gavinclarkeuk@users.noreply.github.com> --- .github/vale/config/vocabularies/Backstage/accept.txt | 1 + plugins/scaffolder-backend/package.json | 1 - .../src/scaffolder/actions/builtin/filesystem/delete.ts | 4 ++-- yarn.lock | 1 - 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/vale/config/vocabularies/Backstage/accept.txt b/.github/vale/config/vocabularies/Backstage/accept.txt index 936b0ec8b6..253a8feb0a 100644 --- a/.github/vale/config/vocabularies/Backstage/accept.txt +++ b/.github/vale/config/vocabularies/Backstage/accept.txt @@ -157,6 +157,7 @@ Gitiles gitlab GitLab globals +globbing Gource Grafana graphql diff --git a/plugins/scaffolder-backend/package.json b/plugins/scaffolder-backend/package.json index 219a16b592..9e45865447 100644 --- a/plugins/scaffolder-backend/package.json +++ b/plugins/scaffolder-backend/package.json @@ -93,7 +93,6 @@ "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.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/filesystem/delete.ts index f785431e17..5a06c7d2aa 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/filesystem/delete.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/filesystem/delete.ts @@ -18,7 +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 globby from 'globby'; import { examples } from './delete.examples'; /** @@ -54,7 +54,7 @@ export const createFilesystemDeleteAction = () => { for (const file of ctx.input.files) { const safeFilepath = resolveSafeChildPath(ctx.workspacePath, file); - const resolvedPaths = await fg(safeFilepath, { + const resolvedPaths = await globby(safeFilepath, { cwd: ctx.workspacePath, absolute: true, }); diff --git a/yarn.lock b/yarn.lock index 2e76627f5b..94746fde49 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7753,7 +7753,6 @@ __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