diff --git a/package.json b/package.json index 24569856..41a6c4a0 100644 --- a/package.json +++ b/package.json @@ -52,6 +52,7 @@ "needle": "2.9.1", "p-map": "4.0.0", "parse-link-header": "2.0.0", + "rimraf": "3.0.2", "simple-git": "3.16.0", "sleep-promise": "8.0.1", "snyk-request-manager": "1.8.0", @@ -70,6 +71,7 @@ "@types/needle": "2.0.4", "@types/node": "14.14.45", "@types/parse-link-header": "1.0.0", + "@types/rimraf": "3.0.2", "@types/split": "1.0.0", "@typescript-eslint/eslint-plugin": "4.28.1", "@typescript-eslint/parser": "4.28.1", diff --git a/src/lib/delete-directory.ts b/src/lib/delete-directory.ts new file mode 100644 index 00000000..18d2def6 --- /dev/null +++ b/src/lib/delete-directory.ts @@ -0,0 +1,12 @@ +import * as rmrf from 'rimraf'; +import * as fs from 'fs'; + +export async function deleteDirectory(dir: string): Promise { + try { + fs.rmdirSync(dir, { recursive: true, maxRetries: 3 }); + } catch (e) { + await new Promise((resolve, reject) => + rmrf(dir, (err) => (err ? reject(err) : resolve())), + ); + } +} diff --git a/src/lib/git-clone.ts b/src/lib/git-clone.ts index b690ad11..2f2bef7d 100644 --- a/src/lib/git-clone.ts +++ b/src/lib/git-clone.ts @@ -8,6 +8,7 @@ import { simpleGit } from 'simple-git'; import * as github from '../lib/source-handlers/github'; import type { RepoMetaData } from './types'; import { SupportedIntegrationTypesUpdateProject } from './types'; +import { deleteDirectory } from './delete-directory'; const debug = debugLib('snyk:git-clone'); @@ -53,7 +54,7 @@ export async function gitClone( } catch (err: any) { debug(`Could not shallow clone the repo:\n ${err}`); if (fs.existsSync(repoClonePath)) { - fs.rmdirSync(repoClonePath, { recursive: true, maxRetries: 3 }); + await deleteDirectory(repoClonePath); } return { success: false, diff --git a/src/scripts/sync/clone-and-analyze.ts b/src/scripts/sync/clone-and-analyze.ts index 9cb0ffd2..264838d3 100644 --- a/src/scripts/sync/clone-and-analyze.ts +++ b/src/scripts/sync/clone-and-analyze.ts @@ -1,5 +1,4 @@ import * as debugLib from 'debug'; -import * as fs from 'fs'; import * as path from 'path'; import { defaultExclusionGlobs } from '../../common'; @@ -12,6 +11,7 @@ import type { SyncTargetsConfig, } from '../../lib/types'; import { generateProjectDiffActions } from './generate-projects-diff-actions'; +import { deleteDirectory } from '../../lib/delete-directory'; const debug = debugLib('snyk:clone-and-analyze'); @@ -63,7 +63,7 @@ export async function cloneAndAnalyze( ); try { - fs.rmdirSync(repoPath, { recursive: true, maxRetries: 3 }); + await deleteDirectory(repoPath); } catch (error) { debug(`Failed to delete ${repoPath}. Error was ${error}.`); } diff --git a/test/lib/delete-directory.test.ts b/test/lib/delete-directory.test.ts new file mode 100644 index 00000000..bfb4e3fe --- /dev/null +++ b/test/lib/delete-directory.test.ts @@ -0,0 +1,15 @@ +import { promises as fs } from 'fs'; +import * as os from 'os'; +import * as path from 'path'; +import { deleteDirectory } from '../../src/lib/delete-directory'; + +test('directory is deleted with contents', async () => { + const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'deletion-test-')); + await fs.writeFile(path.join(dir, 'root.txt'), ''); + + await deleteDirectory(dir); + + console.log(dir); + + await expect(fs.stat(dir)).rejects.toThrowError('no such file or directory'); +});