-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathenvironment.ts
More file actions
356 lines (323 loc) · 12.7 KB
/
environment.ts
File metadata and controls
356 lines (323 loc) · 12.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
import { execFileSync } from 'child_process';
import { existsSync } from 'fs';
import { arch, platform } from 'os';
import { join, resolve } from 'path';
import { cdsExtractorMarkerFileName } from './constants';
import { dirExists } from './filesystem';
import { cdsExtractorLog } from './logging';
import { getPathsIgnorePatterns } from './paths-ignore';
/**
* Interface for platform information
*/
export interface PlatformInfo {
platform: string;
arch: string;
isWindows: boolean;
exeExtension: string;
}
/**
* Interface for environment validation results
*/
export interface EnvironmentSetupResult {
success: boolean;
errorMessages: string[];
codeqlExePath: string;
jsExtractorRoot: string;
autobuildScriptPath: string;
platformInfo: PlatformInfo;
}
/**
* Get platform information
* @returns Platform information including OS platform, architecture, and whether it's Windows
*/
export function getPlatformInfo(): PlatformInfo {
const osPlatform: string = platform();
const osPlatformArch: string = arch();
const isWindows = osPlatform === 'win32';
const exeExtension = isWindows ? '.exe' : '';
return {
platform: osPlatform,
arch: osPlatformArch,
isWindows,
exeExtension,
};
}
/**
* Get the path to the CodeQL executable.
* Prioritizes CODEQL_DIST if set and valid. Otherwise, tries to find CodeQL via system PATH.
* @returns The resolved path to the CodeQL executable, or an empty string if not found.
*/
export function getCodeQLExePath(): string {
const platformInfo = getPlatformInfo();
const codeqlExeName: string = platformInfo.isWindows ? 'codeql.exe' : 'codeql';
// First, check if CODEQL_DIST is set and valid
const codeqlDist = process.env.CODEQL_DIST;
if (codeqlDist) {
const codeqlPathFromDist = resolve(join(codeqlDist, codeqlExeName));
if (existsSync(codeqlPathFromDist)) {
cdsExtractorLog('info', `Using CodeQL executable from CODEQL_DIST: ${codeqlPathFromDist}`);
return codeqlPathFromDist;
} else {
cdsExtractorLog(
'error',
`CODEQL_DIST is set to '${codeqlDist}', but CodeQL executable was not found at '${codeqlPathFromDist}'. Please ensure this path is correct. Falling back to PATH-based discovery.`,
);
// Fall through to PATH-based discovery
}
}
// CODEQL_DIST is not set or was invalid, attempt to find CodeQL via system PATH using 'codeql version --format=json'
cdsExtractorLog(
'info',
'CODEQL_DIST environment variable not set or invalid. Attempting to find CodeQL executable via system PATH using "codeql version --format=json".',
);
try {
const versionOutput = execFileSync(codeqlExeName, ['version', '--format=json'], {
encoding: 'utf8',
timeout: 5000, // 5 seconds timeout
stdio: 'pipe', // Suppress output to console
});
interface CodeQLVersionInfo {
unpackedLocation?: string;
cliVersion?: string; // For potential future use or richer logging
}
try {
const versionInfo = JSON.parse(versionOutput) as CodeQLVersionInfo;
if (
versionInfo &&
typeof versionInfo.unpackedLocation === 'string' &&
versionInfo.unpackedLocation
) {
const resolvedPathFromVersion = resolve(join(versionInfo.unpackedLocation, codeqlExeName));
if (existsSync(resolvedPathFromVersion)) {
cdsExtractorLog(
'info',
`CodeQL executable found via 'codeql version --format=json' at: ${resolvedPathFromVersion}`,
);
return resolvedPathFromVersion;
}
cdsExtractorLog(
'warn',
`'codeql version --format=json' provided unpackedLocation '${versionInfo.unpackedLocation}', but executable not found at '${resolvedPathFromVersion}'.`,
);
} else {
cdsExtractorLog(
'warn',
"Could not determine CodeQL executable path from 'codeql version --format=json' output. 'unpackedLocation' field missing, empty, or invalid.",
);
}
} catch (parseError) {
cdsExtractorLog(
'warn',
`Failed to parse 'codeql version --format=json' output: ${String(parseError)}. Output was: ${versionOutput}`,
);
}
} catch (error) {
let errorMessage = `INFO: Failed to find CodeQL executable via 'codeql version --format=json'. Error: ${String(error)}`;
if (error && typeof error === 'object' && 'code' in error && error.code === 'ENOENT') {
errorMessage += `\nINFO: The command '${codeqlExeName}' was not found in your system PATH.`;
}
cdsExtractorLog('info', errorMessage);
}
cdsExtractorLog(
'error',
'Failed to determine CodeQL executable path. Please ensure the CODEQL_DIST environment variable is set and points to a valid CodeQL distribution, or that the CodeQL CLI (codeql) is available in your system PATH and "codeql version --format=json" can provide its location.',
);
return ''; // Return empty string if all attempts fail
}
/**
* Get the JavaScript extractor root path.
* @param codeqlExePath The path to the CodeQL executable. If empty, resolution will be skipped.
* @returns The JavaScript extractor root path, or an empty string if not found or if codeqlExePath is empty.
*/
export function getJavaScriptExtractorRoot(codeqlExePath: string): string {
let jsExtractorRoot = process.env.CODEQL_EXTRACTOR_JAVASCRIPT_ROOT ?? '';
if (jsExtractorRoot) {
cdsExtractorLog(
'info',
`Using JavaScript extractor root from environment variable CODEQL_EXTRACTOR_JAVASCRIPT_ROOT: ${jsExtractorRoot}`,
);
return jsExtractorRoot;
}
if (!codeqlExePath) {
cdsExtractorLog(
'warn',
'Cannot resolve JavaScript extractor root because the CodeQL executable path was not provided or found.',
);
return '';
}
try {
jsExtractorRoot = execFileSync(
codeqlExePath,
['resolve', 'extractor', '--language=javascript'],
{ stdio: 'pipe' }, // Suppress output from the command itself
)
.toString()
.trim();
if (jsExtractorRoot) {
cdsExtractorLog('info', `JavaScript extractor root resolved to: ${jsExtractorRoot}`);
} else {
cdsExtractorLog(
'warn',
`'codeql resolve extractor --language=javascript' using '${codeqlExePath}' returned an empty path.`,
);
}
} catch (error) {
cdsExtractorLog(
'error',
`Error resolving JavaScript extractor root using '${codeqlExePath}': ${String(error)}`,
);
jsExtractorRoot = ''; // Ensure it's empty on error
}
return jsExtractorRoot;
}
/**
* Set JavaScript extractor environment variables using CDS extractor variables
*/
export function setupJavaScriptExtractorEnv(): void {
process.env.CODEQL_EXTRACTOR_JAVASCRIPT_WIP_DATABASE =
process.env.CODEQL_EXTRACTOR_CDS_WIP_DATABASE;
process.env.CODEQL_EXTRACTOR_JAVASCRIPT_DIAGNOSTIC_DIR =
process.env.CODEQL_EXTRACTOR_CDS_DIAGNOSTIC_DIR;
process.env.CODEQL_EXTRACTOR_JAVASCRIPT_LOG_DIR = process.env.CODEQL_EXTRACTOR_CDS_LOG_DIR;
process.env.CODEQL_EXTRACTOR_JAVASCRIPT_SCRATCH_DIR =
process.env.CODEQL_EXTRACTOR_CDS_SCRATCH_DIR;
process.env.CODEQL_EXTRACTOR_JAVASCRIPT_TRAP_DIR = process.env.CODEQL_EXTRACTOR_CDS_TRAP_DIR;
process.env.CODEQL_EXTRACTOR_JAVASCRIPT_SOURCE_ARCHIVE_DIR =
process.env.CODEQL_EXTRACTOR_CDS_SOURCE_ARCHIVE_DIR;
}
/**
* Get the path to the autobuild script
* @param jsExtractorRoot The JavaScript extractor root path
* @returns The path to the autobuild script, or an empty string if jsExtractorRoot is empty.
*/
export function getAutobuildScriptPath(jsExtractorRoot: string): string {
if (!jsExtractorRoot) return '';
const platformInfo = getPlatformInfo();
const autobuildScriptName: string = platformInfo.isWindows ? 'autobuild.cmd' : 'autobuild.sh';
return resolve(join(jsExtractorRoot, 'tools', autobuildScriptName));
}
/**
* Configure LGTM index filters for CDS files
*/
export function configureLgtmIndexFilters(): void {
let excludeFilters = '';
if (process.env.LGTM_INDEX_FILTERS) {
cdsExtractorLog(
'info',
`Found $LGTM_INDEX_FILTERS already set to:
${process.env.LGTM_INDEX_FILTERS}`,
);
// Use forward slashes explicitly — join() uses backslashes on Windows
// which causes 'Illegal use of **' errors in the JS extractor.
const allowedExcludePatterns = ['exclude:**/*', 'exclude:**/*.*'];
excludeFilters =
'\n' +
process.env.LGTM_INDEX_FILTERS.split('\n')
.filter(
line =>
line.startsWith('exclude') &&
!allowedExcludePatterns.some(pattern => line.includes(pattern)),
)
.join('\n');
}
// Enable extraction of the .cds.json files only.
//
// The cdsExtractorMarkerFileName file is auto-created by the CDS extractor in order
// to force the underlying JS extractor to see at least one .js file, which became a
// requirement starting with v2.23.5 of the CodeQL CLI.
// Use forward slashes explicitly — join() uses backslashes on Windows
// which causes 'Illegal use of **' errors in the JS extractor.
const lgtmIndexFiltersPatterns = [
'exclude:**/*.*',
'include:**/*.cds.json',
'include:**/*.cds',
`include:**/${cdsExtractorMarkerFileName}`,
'exclude:**/node_modules/**/*.*',
].join('\n');
process.env.LGTM_INDEX_FILTERS = lgtmIndexFiltersPatterns + excludeFilters;
process.env.LGTM_INDEX_TYPESCRIPT = 'NONE';
// Configure to copy over the .cds files as well, by pretending they are JSON.
process.env.LGTM_INDEX_FILETYPES = '.cds:JSON';
}
/**
* Applies paths-ignore patterns from the CodeQL configuration to the
* LGTM_INDEX_FILTERS environment variable. This ensures the JavaScript
* extractor also respects the user's paths-ignore configuration for
* compiled .cds.json output files.
*
* @param sourceRoot - The source root directory used to locate the config file
*/
export function applyPathsIgnoreToLgtmFilters(sourceRoot: string): void {
const patterns = getPathsIgnorePatterns(sourceRoot);
if (patterns.length === 0) {
return;
}
const excludeLines = patterns.map(p => `exclude:${p}`).join('\n');
const current = process.env.LGTM_INDEX_FILTERS ?? '';
process.env.LGTM_INDEX_FILTERS = current + '\n' + excludeLines;
cdsExtractorLog(
'info',
`Applied ${patterns.length} paths-ignore pattern(s) to LGTM_INDEX_FILTERS`,
);
}
/**
* Sets up the environment and validates key components for running the CDS extractor.
* This includes checking for the CodeQL executable, validating the source root directory,
* and setting up environment variables for the JavaScript extractor.
*
* @param sourceRoot The source root directory.
*
* @returns The {@link EnvironmentSetupResult} containing success status, error messages,
* CodeQL executable path, JavaScript extractor root, autobuild script path,
* and platform information.
*
* @throws Will throw an error if the environment setup fails.
*/
export function setupAndValidateEnvironment(sourceRoot: string): EnvironmentSetupResult {
const errorMessages: string[] = [];
const platformInfo = getPlatformInfo();
// Get the CodeQL executable path
const codeqlExePath = getCodeQLExePath();
if (!codeqlExePath) {
errorMessages.push(
'Failed to find CodeQL executable. Ensure CODEQL_DIST is set and valid, or CodeQL CLI is in PATH.',
);
}
// Validate that the required source root directory exists
if (!dirExists(sourceRoot)) {
errorMessages.push(`Project root directory '${sourceRoot}' does not exist.`);
}
// Get JavaScript extractor root
const jsExtractorRoot = getJavaScriptExtractorRoot(codeqlExePath);
if (!jsExtractorRoot) {
if (codeqlExePath) {
// Only add this error if codeqlExePath was found but JS extractor root wasn't
errorMessages.push(
'Failed to determine JavaScript extractor root using the found CodeQL executable.',
);
} else {
// If codeqlExePath is empty, the error from getCodeQLExePath is usually sufficient.
// However, we can add a more specific one if needed.
errorMessages.push(
'Cannot determine JavaScript extractor root because CodeQL executable was not found.',
);
}
}
// Set environment variables for JavaScript extractor only if jsExtractorRoot is valid
if (jsExtractorRoot) {
process.env.CODEQL_EXTRACTOR_JAVASCRIPT_ROOT = jsExtractorRoot;
setupJavaScriptExtractorEnv();
}
// Get autobuild script path
const autobuildScriptPath = jsExtractorRoot ? getAutobuildScriptPath(jsExtractorRoot) : '';
// Not having an autobuild script path might be an error depending on the run mode,
// but for now, the function just returns what it found.
return {
success: errorMessages.length === 0,
errorMessages,
codeqlExePath, // Will be '' if not found
jsExtractorRoot, // Will be '' if not found
autobuildScriptPath,
platformInfo,
};
}