-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathprojectInstaller.ts
More file actions
108 lines (95 loc) · 3.62 KB
/
projectInstaller.ts
File metadata and controls
108 lines (95 loc) · 3.62 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
/** Full dependency installation utilities for retry scenarios. */
import { execFileSync } from 'child_process';
import { join } from 'path';
import type { FullDependencyInstallationResult } from './types';
import type { CdsProject } from '../cds/parser';
import { cdsExtractorLog } from '../logging';
/**
* Determines if a {@link CdsProject} requires "full" dependency installation.
*
* @param project The {@link CdsProject} to check
* @returns `true` if the project has at least one compilation task that is
* currently marked as `failed` AND has not yet been retried. Otherwise, `false`.
*/
export function needsFullDependencyInstallation(project: CdsProject): boolean {
// Check if already installed
if (project.retryStatus?.fullDependenciesInstalled) {
return false;
}
// Check if project has failed tasks that could benefit from full dependencies.
//
// Currently, we only allow for one retry, because the only significant change we
// can make (to justify a retry) is to install and use the full set of declared
// dependencies instead of the minimal set of cached (`@sap/cds` and `@sap/cds-dk`)
// dependencies.
const hasFailedTasks = project.compilationTasks.some(
task => task.status === 'failed' && !task.retryInfo?.hasBeenRetried,
);
return hasFailedTasks && project.packageJson !== undefined;
}
/**
* Installs full dependencies for a {@link CdsProject} in support of retry behavior
* for compilation tasks that fail unless the `cds` CLI/compiler has access to the
* full set of dependencies declared for the project.
*
* @param project The CDS project to install dependencies for
* @param sourceRoot Source root directory
* @returns Installation result with details
*/
export function projectInstallDependencies(
project: CdsProject,
sourceRoot: string,
): FullDependencyInstallationResult {
const startTime = Date.now();
const projectPath = join(sourceRoot, project.projectDir);
const result: FullDependencyInstallationResult = {
success: false,
projectDir: projectPath,
warnings: [],
durationMs: 0,
timedOut: false,
};
try {
// Check if project has package.json
if (!project.packageJson) {
result.error = 'No package.json found for project';
return result;
}
// Install dependencies using npm in the project's directory
cdsExtractorLog(
'info',
`Installing full dependencies for project ${project.projectDir} in project's node_modules`,
);
try {
execFileSync('npm', ['install', '--quiet', '--no-audit', '--no-fund'], {
cwd: projectPath,
stdio: 'inherit',
timeout: 120000, // 2-minute timeout
shell: true,
});
result.success = true;
cdsExtractorLog(
'info',
`Successfully installed full dependencies for project ${project.projectDir}`,
);
} catch (execError) {
if (execError instanceof Error && 'signal' in execError && execError.signal === 'SIGTERM') {
result.timedOut = true;
result.error = 'Dependency installation timed out';
} else {
result.error = `npm install failed: ${String(execError)}`;
}
// Still attempt retry compilation even if dependency installation fails (optimistic approach)
result.warnings.push(
`Dependency installation failed but will still attempt retry compilation: ${result.error}`,
);
cdsExtractorLog('warn', result.warnings[0]);
}
} catch (error) {
result.error = `Failed to install full dependencies: ${String(error)}`;
cdsExtractorLog('error', result.error);
} finally {
result.durationMs = Date.now() - startTime;
}
return result;
}