Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/build/npm/postinstall.ts
5240 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import * as fs from 'fs';
7
import path from 'path';
8
import * as os from 'os';
9
import * as child_process from 'child_process';
10
import { dirs } from './dirs.ts';
11
12
const npm = process.platform === 'win32' ? 'npm.cmd' : 'npm';
13
const root = path.dirname(path.dirname(import.meta.dirname));
14
const rootNpmrcConfigKeys = getNpmrcConfigKeys(path.join(root, '.npmrc'));
15
16
function log(dir: string, message: string) {
17
if (process.stdout.isTTY) {
18
console.log(`\x1b[34m[${dir}]\x1b[0m`, message);
19
} else {
20
console.log(`[${dir}]`, message);
21
}
22
}
23
24
function run(command: string, args: string[], opts: child_process.SpawnSyncOptions) {
25
log(opts.cwd as string || '.', '$ ' + command + ' ' + args.join(' '));
26
27
const result = child_process.spawnSync(command, args, opts);
28
29
if (result.error) {
30
console.error(`ERR Failed to spawn process: ${result.error}`);
31
process.exit(1);
32
} else if (result.status !== 0) {
33
console.error(`ERR Process exited with code: ${result.status}`);
34
process.exit(result.status);
35
}
36
}
37
38
function npmInstall(dir: string, opts?: child_process.SpawnSyncOptions) {
39
opts = {
40
env: { ...process.env },
41
...(opts ?? {}),
42
cwd: dir,
43
stdio: 'inherit',
44
shell: true
45
};
46
47
const command = process.env['npm_command'] || 'install';
48
49
if (process.env['VSCODE_REMOTE_DEPENDENCIES_CONTAINER_NAME'] && /^(.build\/distro\/npm\/)?remote$/.test(dir)) {
50
const userinfo = os.userInfo();
51
log(dir, `Installing dependencies inside container ${process.env['VSCODE_REMOTE_DEPENDENCIES_CONTAINER_NAME']}...`);
52
53
opts.cwd = root;
54
if (process.env['npm_config_arch'] === 'arm64') {
55
run('sudo', ['docker', 'run', '--rm', '--privileged', 'multiarch/qemu-user-static', '--reset', '-p', 'yes'], opts);
56
}
57
run('sudo', [
58
'docker', 'run',
59
'-e', 'GITHUB_TOKEN',
60
'-v', `${process.env['VSCODE_HOST_MOUNT']}:/root/vscode`,
61
'-v', `${process.env['VSCODE_HOST_MOUNT']}/.build/.netrc:/root/.netrc`,
62
'-v', `${process.env['VSCODE_NPMRC_PATH']}:/root/.npmrc`,
63
'-w', path.resolve('/root/vscode', dir),
64
process.env['VSCODE_REMOTE_DEPENDENCIES_CONTAINER_NAME'],
65
'sh', '-c', `\"chown -R root:root ${path.resolve('/root/vscode', dir)} && export PATH="/root/vscode/.build/nodejs-musl/usr/local/bin:$PATH" && npm i -g node-gyp-build && npm ci\"`
66
], opts);
67
run('sudo', ['chown', '-R', `${userinfo.uid}:${userinfo.gid}`, `${path.resolve(root, dir)}`], opts);
68
} else {
69
log(dir, 'Installing dependencies...');
70
run(npm, command.split(' '), opts);
71
}
72
removeParcelWatcherPrebuild(dir);
73
}
74
75
function setNpmrcConfig(dir: string, env: NodeJS.ProcessEnv) {
76
const npmrcPath = path.join(root, dir, '.npmrc');
77
const lines = fs.readFileSync(npmrcPath, 'utf8').split('\n');
78
79
for (const line of lines) {
80
const trimmedLine = line.trim();
81
if (trimmedLine && !trimmedLine.startsWith('#')) {
82
const [key, value] = trimmedLine.split('=');
83
env[`npm_config_${key}`] = value.replace(/^"(.*)"$/, '$1');
84
}
85
}
86
87
// Use our bundled node-gyp version
88
env['npm_config_node_gyp'] =
89
process.platform === 'win32'
90
? path.join(import.meta.dirname, 'gyp', 'node_modules', '.bin', 'node-gyp.cmd')
91
: path.join(import.meta.dirname, 'gyp', 'node_modules', '.bin', 'node-gyp');
92
93
// Force node-gyp to use process.config on macOS
94
// which defines clang variable as expected. Otherwise we
95
// run into compilation errors due to incorrect compiler
96
// configuration.
97
// NOTE: This means the process.config should contain
98
// the correct clang variable. So keep the version check
99
// in preinstall sync with this logic.
100
// Change was first introduced in https://github.com/nodejs/node/commit/6e0a2bb54c5bbeff0e9e33e1a0c683ed980a8a0f
101
if ((dir === 'remote' || dir === 'build') && process.platform === 'darwin') {
102
env['npm_config_force_process_config'] = 'true';
103
} else {
104
delete env['npm_config_force_process_config'];
105
}
106
107
if (dir === 'build') {
108
env['npm_config_target'] = process.versions.node;
109
env['npm_config_arch'] = process.arch;
110
}
111
}
112
113
function removeParcelWatcherPrebuild(dir: string) {
114
const parcelModuleFolder = path.join(root, dir, 'node_modules', '@parcel');
115
if (!fs.existsSync(parcelModuleFolder)) {
116
return;
117
}
118
119
const parcelModules = fs.readdirSync(parcelModuleFolder);
120
for (const moduleName of parcelModules) {
121
if (moduleName.startsWith('watcher-')) {
122
const modulePath = path.join(parcelModuleFolder, moduleName);
123
fs.rmSync(modulePath, { recursive: true, force: true });
124
log(dir, `Removed @parcel/watcher prebuilt module ${modulePath}`);
125
}
126
}
127
}
128
129
function getNpmrcConfigKeys(npmrcPath: string): string[] {
130
if (!fs.existsSync(npmrcPath)) {
131
return [];
132
}
133
const lines = fs.readFileSync(npmrcPath, 'utf8').split('\n');
134
const keys: string[] = [];
135
for (const line of lines) {
136
const trimmedLine = line.trim();
137
if (trimmedLine && !trimmedLine.startsWith('#')) {
138
const eqIndex = trimmedLine.indexOf('=');
139
if (eqIndex > 0) {
140
keys.push(trimmedLine.substring(0, eqIndex).trim());
141
}
142
}
143
}
144
return keys;
145
}
146
147
function clearInheritedNpmrcConfig(dir: string, env: NodeJS.ProcessEnv): void {
148
const dirNpmrcPath = path.join(root, dir, '.npmrc');
149
if (fs.existsSync(dirNpmrcPath)) {
150
return;
151
}
152
153
for (const key of rootNpmrcConfigKeys) {
154
const envKey = `npm_config_${key.replace(/-/g, '_')}`;
155
delete env[envKey];
156
}
157
}
158
159
for (const dir of dirs) {
160
161
if (dir === '') {
162
removeParcelWatcherPrebuild(dir);
163
continue; // already executed in root
164
}
165
166
let opts: child_process.SpawnSyncOptions | undefined;
167
168
if (dir === 'build') {
169
opts = {
170
env: {
171
...process.env
172
},
173
};
174
if (process.env['CC']) { opts.env!['CC'] = 'gcc'; }
175
if (process.env['CXX']) { opts.env!['CXX'] = 'g++'; }
176
if (process.env['CXXFLAGS']) { opts.env!['CXXFLAGS'] = ''; }
177
if (process.env['LDFLAGS']) { opts.env!['LDFLAGS'] = ''; }
178
179
setNpmrcConfig('build', opts.env!);
180
npmInstall('build', opts);
181
continue;
182
}
183
184
if (/^(.build\/distro\/npm\/)?remote$/.test(dir)) {
185
// node modules used by vscode server
186
opts = {
187
env: {
188
...process.env
189
},
190
};
191
if (process.env['VSCODE_REMOTE_CC']) {
192
opts.env!['CC'] = process.env['VSCODE_REMOTE_CC'];
193
} else {
194
delete opts.env!['CC'];
195
}
196
if (process.env['VSCODE_REMOTE_CXX']) {
197
opts.env!['CXX'] = process.env['VSCODE_REMOTE_CXX'];
198
} else {
199
delete opts.env!['CXX'];
200
}
201
if (process.env['CXXFLAGS']) { delete opts.env!['CXXFLAGS']; }
202
if (process.env['CFLAGS']) { delete opts.env!['CFLAGS']; }
203
if (process.env['LDFLAGS']) { delete opts.env!['LDFLAGS']; }
204
if (process.env['VSCODE_REMOTE_CXXFLAGS']) { opts.env!['CXXFLAGS'] = process.env['VSCODE_REMOTE_CXXFLAGS']; }
205
if (process.env['VSCODE_REMOTE_LDFLAGS']) { opts.env!['LDFLAGS'] = process.env['VSCODE_REMOTE_LDFLAGS']; }
206
if (process.env['VSCODE_REMOTE_NODE_GYP']) { opts.env!['npm_config_node_gyp'] = process.env['VSCODE_REMOTE_NODE_GYP']; }
207
208
setNpmrcConfig('remote', opts.env!);
209
npmInstall(dir, opts);
210
continue;
211
}
212
213
// For directories that don't define their own .npmrc, clear inherited config
214
const env = { ...process.env };
215
clearInheritedNpmrcConfig(dir, env);
216
npmInstall(dir, { env });
217
}
218
219
child_process.execSync('git config pull.rebase merges');
220
child_process.execSync('git config blame.ignoreRevsFile .git-blame-ignore-revs');
221
222