Skip to content

Commit 10467ad

Browse files
feat: use SNYK_TMP_PATH env var for temp dir path (#267)
* feat: use SNYK_TMP_PATH env var for temp dir path * fix: resolve test failures on main * chore: provide more useful error messge when making and writing to temp dir fails * fix: resolve test failures
1 parent a61a22d commit 10467ad

File tree

6 files changed

+102
-7
lines changed

6 files changed

+102
-7
lines changed

lib/dependencies/inspect-implementation.ts

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { buildDepGraph, PartialDepTree } from './build-dep-graph';
88
import { FILENAMES } from '../types';
99
import {
1010
EmptyManifestError,
11+
FailedToWriteTempFiles,
1112
RequiredPackagesMissingError,
1213
UnparsableRequirementError,
1314
} from '../errors';
@@ -221,11 +222,23 @@ export async function inspectInstalledDeps(
221222
args?: string[],
222223
projectName?: string
223224
): Promise<DepGraph> {
224-
const tempDirObj = tmp.dirSync({
225-
unsafeCleanup: true,
226-
});
225+
const tmp_path = process.env.SNYK_TMP_PATH;
226+
let tempDirObj: tmp.DirResult;
227+
228+
try {
229+
tempDirObj = tmp.dirSync({
230+
unsafeCleanup: true,
231+
...(tmp_path ? { tmpdir: tmp_path } : {}),
232+
});
233+
dumpAllFilesInTempDir(tempDirObj.name);
234+
} catch (e) {
235+
throw new FailedToWriteTempFiles(
236+
`Failed to write temporary files:\n` +
237+
`${e}\n` +
238+
`Try running again with SNYK_TMP_PATH=<some directory>, where <some directory> is a valid directory that you have permissions to write to.`
239+
);
240+
}
227241

228-
dumpAllFilesInTempDir(tempDirObj.name);
229242
try {
230243
const pythonEnv = getPythonEnv(targetFile);
231244

lib/errors.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ export enum PythonPluginErrorNames {
22
EMPTY_MANIFEST_ERROR = 'EMPTY_MANIFEST_ERROR',
33
REQUIRED_PACKAGES_MISSING_ERROR = 'REQUIRED_PACKAGES_MISSING_ERROR',
44
UNPARSABLE_REQUIREMENT_ERROR = 'UNPARSABLE_REQUIREMENT_ERROR',
5+
FAILED_TO_WRITE_TEMP_FILES = 'FAILED_TO_WRITE_TEMP_FILES',
56
}
67

78
export class EmptyManifestError extends Error {
@@ -24,3 +25,10 @@ export class UnparsableRequirementError extends Error {
2425
this.name = PythonPluginErrorNames.UNPARSABLE_REQUIREMENT_ERROR;
2526
}
2627
}
28+
29+
export class FailedToWriteTempFiles extends Error {
30+
constructor(message: string) {
31+
super(message);
32+
this.name = PythonPluginErrorNames.FAILED_TO_WRITE_TEMP_FILES;
33+
}
34+
}

test/system/inspect.spec.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,6 @@ describe('inspect', () => {
250250
{
251251
pkg: {
252252
name: 'jsonschema',
253-
version: '4.23.0',
254253
},
255254
directDeps: ['openapi-spec-validator'],
256255
},
@@ -620,7 +619,6 @@ describe('inspect', () => {
620619
{
621620
pkg: {
622621
name: 'jsonschema',
623-
version: '4.23.0',
624622
},
625623
directDeps: ['openapi-spec-validator'],
626624
},

test/test-utils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ function updateSetuptools() {
176176

177177
function setupPyInstall() {
178178
updateSetuptools();
179-
const proc = subProcess.executeSync('python3', ['setup.py', 'install']);
179+
const proc = subProcess.executeSync('pip', ['install', '.']);
180180
if (proc.status !== 0) {
181181
console.log('' + proc.stderr);
182182
throw new Error(

test/unit/fixtures/requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# no deps
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import * as fs from 'fs';
2+
import * as tmp from 'tmp';
3+
4+
import { inspectInstalledDeps } from '../../lib/dependencies/inspect-implementation';
5+
6+
jest.mock('tmp');
7+
8+
describe('Test inspect-implementation.ts', () => {
9+
describe('inspectInstalledDeps', () => {
10+
const originalEnv = process.env;
11+
12+
const dirSyncMock = tmp.dirSync as jest.MockedFunction<typeof tmp.dirSync>;
13+
14+
beforeEach(() => {
15+
jest.resetModules();
16+
process.env = { ...originalEnv };
17+
18+
dirSyncMock.mockClear();
19+
dirSyncMock.mockReturnValue({
20+
name: 'tempDir',
21+
removeCallback: jest.fn(),
22+
});
23+
});
24+
25+
afterEach(() => {
26+
try {
27+
fs.rmSync('tempDir', { recursive: true, force: true });
28+
} catch (e) {
29+
// Ignore error
30+
}
31+
});
32+
33+
afterAll(() => {
34+
process.env = originalEnv;
35+
});
36+
37+
it('should call tmp.dirSync with tmpdir option when SNYK_TMP_PATH is set', async () => {
38+
const tmpDirPath = './test-temp-dir';
39+
process.env.SNYK_TMP_PATH = tmpDirPath;
40+
41+
await inspectInstalledDeps(
42+
'python3',
43+
[],
44+
'.',
45+
'test/unit/fixtures/requirements.txt',
46+
false,
47+
false,
48+
true
49+
);
50+
51+
expect(dirSyncMock).toHaveBeenCalledWith({
52+
tmpdir: tmpDirPath,
53+
unsafeCleanup: true,
54+
});
55+
expect(dirSyncMock).toHaveBeenCalledTimes(1);
56+
});
57+
58+
it('should call tmp.dirSync without tmpdir option when SNYK_TMP_PATH is not set', async () => {
59+
process.env.SNYK_TMP_PATH = undefined;
60+
61+
await inspectInstalledDeps(
62+
'python3',
63+
[],
64+
'.',
65+
'test/unit/fixtures/requirements.txt',
66+
false,
67+
false,
68+
true
69+
);
70+
71+
expect(dirSyncMock).toHaveBeenCalledWith({ unsafeCleanup: true });
72+
expect(dirSyncMock).toHaveBeenCalledTimes(1);
73+
});
74+
});
75+
});

0 commit comments

Comments
 (0)