X Tutup
Skip to content

Commit 14632cf

Browse files
authored
Merge pull request microsoft#666 from Microsoft/ianc/rush-install-script
[rush] Introduce script to install rush for CI.
2 parents a29b979 + 55b03eb commit 14632cf

File tree

8 files changed

+368
-127
lines changed

8 files changed

+368
-127
lines changed

.travis.yml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,19 @@ language: node_js
22
node_js:
33
- '8.9.4'
44
before_script:
5-
- node common/scripts/InstallRushOnlyIfNeeded.js
5+
- node common/scripts/ci-rush.js
66
script:
77
- set -e
88
- echo 'Checking change file...' && echo -en 'travis_fold:start:check\\r'
99
- git fetch origin master:refs/remotes/origin/master -a
10-
- node common/local-rush/node_modules/@microsoft/rush/lib/start.js change -v
10+
- node common/temp/ci-rush/node_modules/@microsoft/rush/lib/start.js change -v
1111
- echo -en 'travis_fold:end:check\\r'
1212
- echo 'Installing...' && echo -en 'travis_fold:start:install\\r'
13-
- node common/local-rush/node_modules/@microsoft/rush/lib/start.js install --bypass-policy
13+
- node common/temp/ci-rush/node_modules/@microsoft/rush/lib/start.js install --bypass-policy
1414
- echo -en 'travis_fold:end:install\\r'
1515
- echo 'Running rush check' && echo -en 'travis_fold:start:check\\r'
16-
- node common/local-rush/node_modules/@microsoft/rush/lib/start.js check
16+
- node common/temp/ci-rush/node_modules/@microsoft/rush/lib/start.js check
1717
- echo -en 'travis_fold:end:check\\r'
1818
- echo 'Building...' && echo -en 'travis_fold:start:build\\r'
19-
- node common/local-rush/node_modules/@microsoft/rush/lib/start.js rebuild --verbose --production
19+
- node common/temp/ci-rush/node_modules/@microsoft/rush/lib/start.js rebuild --verbose --production
2020
- echo -en 'travis_fold:end:build\\r'
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
// This script is invoked by the CI build, via a build definition step.
2+
//
3+
// 'npm install @microsoft/rush -g' will always delete and recreate the rush
4+
// global folder, even if it is already up to date. This causes a race condition
5+
// when multiple builds are running simultaneously on the same build machine.
6+
//
7+
// As a workaround, this script checks whether Rush is up to date before
8+
// running the command.
9+
10+
import * as childProcess from 'child_process';
11+
import * as fs from 'fs';
12+
import * as os from 'os';
13+
import * as path from 'path';
14+
import { IPackageJson } from '@microsoft/node-core-library';
15+
16+
const PACKAGE_NAME: string = '@microsoft/rush';
17+
const RUSH_JSON_FILENAME: string = 'rush.json';
18+
19+
let rushJsonDirectory: string = undefined!;
20+
let basePath: string = __dirname;
21+
let tempPath: string = __dirname;
22+
do {
23+
const testRushJsonPath: string = path.join(basePath, RUSH_JSON_FILENAME);
24+
if (fs.existsSync(testRushJsonPath)) {
25+
rushJsonDirectory = basePath;
26+
break;
27+
} else {
28+
basePath = tempPath;
29+
}
30+
} while (basePath !== (tempPath = path.resolve(basePath, '..'))); // Exit the loop when we hit the disk root
31+
32+
if (!rushJsonDirectory) {
33+
console.error('Unable to find rush.json.');
34+
process.exit(1);
35+
}
36+
37+
let expectedVersion: string = undefined!;
38+
const rushJsonPath: string = path.join(rushJsonDirectory, RUSH_JSON_FILENAME);
39+
try {
40+
const rushJsonContents: string = fs.readFileSync(rushJsonPath, 'UTF-8');
41+
// Use a regular expression to parse out the rushVersion value because rush.json supports comments,
42+
// but JSON.parse does not and we don't want to pull in more dependencies than we need to in this script.
43+
const rushJsonMatches: string[] = rushJsonContents.match(/\"rushVersion\"\s*\:\s*\"([0-9a-zA-Z.+\-]+)\"/)!;
44+
expectedVersion = rushJsonMatches[1];
45+
} catch (e) {
46+
console.error(
47+
`Unable to determine the required version of Rush from rush.json (${rushJsonDirectory}). ` +
48+
'The \'rushVersion\' field is either not assigned in rush.json or was specified ' +
49+
'using an unexpected syntax.'
50+
);
51+
process.exit(1);
52+
}
53+
54+
let npmPath: string = undefined!;
55+
try {
56+
if (os.platform() === 'win32') {
57+
// We're on Windows
58+
const whereOutput: string = childProcess.execSync('where npm', { stdio: [] }).toString();
59+
const lines: string[] = whereOutput.split(os.EOL).filter((line) => !!line);
60+
npmPath = lines[lines.length - 1];
61+
} else {
62+
// We aren't on Windows - assume we're on *NIX or Darwin
63+
npmPath = childProcess.execSync('which npm', { stdio: [] }).toString();
64+
}
65+
} catch (e) {
66+
console.error(`Unable to determine the path to the NPM tool: ${e}`);
67+
process.exit(1);
68+
}
69+
70+
npmPath = npmPath.trim();
71+
console.log(os.EOL + `NPM executable is '${npmPath}'`);
72+
73+
if (!fs.existsSync(npmPath)) {
74+
console.error('The NPM executable does not exist');
75+
process.exit(1);
76+
}
77+
78+
const rushPathParts: string[] = ['common', 'temp', 'ci-rush'];
79+
let rushPath: string = rushJsonDirectory;
80+
for (const rushPathPart of rushPathParts) {
81+
rushPath = path.join(rushPath, rushPathPart);
82+
try {
83+
if (!fs.existsSync(rushPath)) {
84+
fs.mkdirSync(rushPath);
85+
}
86+
} catch (e) {
87+
console.error(`Error building local rush installation directory: ${e}`);
88+
process.exit(1);
89+
}
90+
}
91+
92+
console.log(os.EOL + `Expected Rush version is ${expectedVersion}`);
93+
94+
// Check for the Rush version
95+
let installedVersion: string = undefined!;
96+
let installedVersionValid: boolean = false;
97+
try {
98+
const spawnResult: childProcess.SpawnSyncReturns<Buffer> = childProcess.spawnSync(
99+
npmPath, ['list', PACKAGE_NAME, 'version'],
100+
{ cwd: rushPath, stdio: ['pipe', 'pipe', 'pipe'] }
101+
);
102+
const output: string = spawnResult.output.toString();
103+
const matches: string[] | null = /@microsoft\/rush\@([0-9a-zA-Z.+\-]+)/.exec(output);
104+
// If NPM finds the wrong version in node_modules, that version will be in matches[1].
105+
// But if it's not installed at all, then NPM instead uselessly tells us all about
106+
// the version that we DON'T have ('missing:')
107+
if (matches && matches.length === 2 && !output.match(/missing\:/g)) {
108+
installedVersion = matches[1]!;
109+
110+
if (spawnResult.status === 0) {
111+
installedVersionValid = true;
112+
}
113+
}
114+
} catch (error) {
115+
// (this happens if we didn't find the installed package)
116+
}
117+
118+
if (installedVersion) {
119+
console.log(os.EOL + `Installed version is ${installedVersion}`);
120+
} else {
121+
console.log(os.EOL + 'Rush does not appear to be installed');
122+
}
123+
124+
if (!installedVersionValid || installedVersion !== expectedVersion) {
125+
const npmrcPath: string = path.join(rushJsonDirectory, 'common', 'config', 'rush', '.npmrc');
126+
const rushNpmrcPath: string = path.join(rushPath, '.npmrc');
127+
if (fs.existsSync(npmrcPath)) {
128+
try {
129+
let npmrcFileLines: string[] = fs.readFileSync(npmrcPath).toString().split('\n');
130+
npmrcFileLines = npmrcFileLines.map((line) => (line || '').trim());
131+
const resultLines: string[] = [];
132+
// Trim out lines that reference environment variables that aren't defined
133+
for (const line of npmrcFileLines) {
134+
const environmentVariables: string[] | null = line.match(/\$\{([^\}]+)\}/g);
135+
let lineShouldBeTrimmed: boolean = false;
136+
if (environmentVariables) {
137+
for (const environmentVariable of environmentVariables) {
138+
if (!process.env[environmentVariable]) {
139+
lineShouldBeTrimmed = true;
140+
break;
141+
}
142+
}
143+
}
144+
145+
if (!lineShouldBeTrimmed) {
146+
resultLines.push(line);
147+
}
148+
}
149+
150+
fs.writeFileSync(rushNpmrcPath, resultLines.join(os.EOL));
151+
} catch (e) {
152+
console.error(`Error reading or writing .npmrc file: ${e}`);
153+
process.exit(1);
154+
}
155+
}
156+
157+
const packageContents: IPackageJson = {
158+
'name': 'ci-rush',
159+
'version': '0.0.0',
160+
'dependencies': {
161+
[PACKAGE_NAME]: expectedVersion
162+
},
163+
'description': 'DON\'T WARN',
164+
'repository': 'DON\'T WARN',
165+
'license': 'MIT'
166+
};
167+
168+
const rushPackagePath: string = path.join(rushPath, 'package.json');
169+
fs.writeFileSync(rushPackagePath, JSON.stringify(packageContents, undefined, 2));
170+
171+
console.log(os.EOL + 'Installing Rush...');
172+
childProcess.execSync(`"${npmPath}" install ${PACKAGE_NAME}@${expectedVersion}`, { cwd: rushPath });
173+
console.log(os.EOL + `Successfully installed Rush ${expectedVersion}`);
174+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"changes": [
3+
{
4+
"packageName": "@microsoft/node-core-library",
5+
"comment": "Add missing \"repository\" property in IPackageJSON.",
6+
"type": "patch"
7+
}
8+
],
9+
"packageName": "@microsoft/node-core-library",
10+
"email": "iclanton@users.noreply.github.com"
11+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"changes": [
3+
{
4+
"comment": "Introduce a script to install rush for CI.",
5+
"packageName": "@microsoft/rush",
6+
"type": "none"
7+
}
8+
],
9+
"packageName": "@microsoft/rush",
10+
"email": "iclanton@users.noreply.github.com"
11+
}

common/reviews/api/node-core-library.api.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ interface IPackageJson {
5454
optionalDependencies?: IPackageJsonDependencyTable;
5555
peerDependencies?: IPackageJsonDependencyTable;
5656
private?: boolean;
57+
repository?: string;
5758
scripts?: IPackageJsonScriptTable;
5859
// @beta
5960
tsdoc?: IPackageJsonTsdocConfiguration;

common/scripts/InstallRushOnlyIfNeeded.js

Lines changed: 0 additions & 122 deletions
This file was deleted.

0 commit comments

Comments
 (0)
X Tutup