diff --git a/packages/cli/src/commands/new/TemporaryDirectoryManager.ts b/packages/cli/src/commands/new/TemporaryDirectoryManager.ts new file mode 100644 index 0000000000..491c90db4b --- /dev/null +++ b/packages/cli/src/commands/new/TemporaryDirectoryManager.ts @@ -0,0 +1,42 @@ +/* + * Copyright 2025 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import os from 'os'; +import fs from 'fs-extra'; +import { join as joinPath } from 'path'; + +export class TemporaryDirectoryManager { + #dirs = new Array(); + + static create() { + const manager = new TemporaryDirectoryManager(); + process.on('beforeExit', manager.cleanup); + return manager; + } + + private constructor() {} + + createDir = async (name: string): Promise => { + const dir = await fs.mkdtemp(joinPath(os.tmpdir(), name)); + this.#dirs.push(dir); + return dir; + }; + + cleanup = () => { + for (const dir of this.#dirs) { + fs.rmSync(dir, { recursive: true, force: true, maxRetries: 10 }); + } + }; +} diff --git a/packages/cli/src/commands/new/new.ts b/packages/cli/src/commands/new/new.ts index 6581fb4cb9..6a8477f487 100644 --- a/packages/cli/src/commands/new/new.ts +++ b/packages/cli/src/commands/new/new.ts @@ -14,9 +14,7 @@ * limitations under the License. */ -import os from 'os'; import fs from 'fs-extra'; -import { join as joinPath } from 'path'; import camelCase from 'lodash/camelCase'; import upperFirst from 'lodash/upperFirst'; import { isMonoRepo } from '@backstage/cli-node'; @@ -42,6 +40,7 @@ import { } from '../../lib/new/utils'; import { runAdditionalActions } from '../../lib/new/additionalActions'; import { executePluginPackageTemplate } from '../../lib/new/executeTemplate'; +import { TemporaryDirectoryManager } from './TemporaryDirectoryManager'; export default async () => { const pkgJson = await fs.readJson(paths.resolveTargetRoot('package.json')); @@ -59,12 +58,7 @@ export default async () => { }); const options = populateOptions(prompts, template); - const tempDirs = new Array(); - async function createTemporaryDirectory(name: string): Promise { - const dir = await fs.mkdtemp(joinPath(os.tmpdir(), name)); - tempDirs.push(dir); - return dir; - } + const tmpDirManager = TemporaryDirectoryManager.create(); const dirName = createDirName(template, options); const targetDir = paths.resolveTargetRoot(options.targetPath, dirName); @@ -88,7 +82,7 @@ export default async () => { await executePluginPackageTemplate( { isMonoRepo: await isMonoRepo(), - createTemporaryDirectory, + createTemporaryDirectory: tmpDirManager.createDir, markAsModified() { modified = true; }, @@ -150,14 +144,6 @@ export default async () => { Task.error(`🔥 Failed to create ${template.id}!`); } } finally { - for (const dir of tempDirs) { - try { - await fs.remove(dir); - } catch (error) { - console.error( - `Failed to remove temporary directory '${dir}', ${error}`, - ); - } - } + tmpDirManager.cleanup(); } };