-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Expand file tree
/
Copy pathintegrateRN.ts
More file actions
220 lines (193 loc) · 5.4 KB
/
integrateRN.ts
File metadata and controls
220 lines (193 loc) · 5.4 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
/**
* Copyright (c) Microsoft Corporation.
* Licensed under the MIT License.
*
* @format
*/
import fs from '@react-native-windows/fs';
import path from 'path';
import semver from 'semver';
import yargs from 'yargs';
import {
findPackage,
enumerateRepoPackages,
NpmPackage,
WritableNpmPackage,
} from '@react-native-windows/package-utils';
import {
getAbbreviatedRef,
upgradeOverrides,
validateManifest,
ValidationError,
} from 'react-native-platform-override';
import runCommand from './runCommand';
import upgradeDependencies from './upgradeDependencies';
import {Logger, CompositeLogger, ConsoleLogger, MarkdownLogger} from './logger';
let logger: Logger;
(async () => {
const {argv} = yargs
.options({
reportPath: {
type: 'string',
describe: 'Optional path to generate a markdown output of results',
demandOption: false,
},
})
.check(args => {
if (args._.length === 1 && semver.valid(<string>args._[0])) {
return true;
} else {
throw new Error('Usage: integrate-rn <version>');
}
})
.showHelpOnFail(false);
const version = <string>argv._[0];
logger = new CompositeLogger([
new ConsoleLogger(process.stdout),
...(argv.reportPath
? [new MarkdownLogger(fs.createWriteStream(argv.reportPath))]
: []),
]);
try {
await performSteps(version);
} finally {
logger.close();
}
})();
async function performSteps(newVersion: string) {
logger.info(`Commits: ${await generateCommitsUrl(newVersion)}`);
await funcStep(
`Updating packages and dependents to react-native@${newVersion}`,
async () => {
await upgradeDependencies(newVersion);
return {status: 'success'};
},
);
await funcStep('Upgrading out-of-date overrides', () =>
upgradePlatformOverrides(newVersion),
);
await funcStep(
'Performing additional override validation',
validatePlatformOverrides,
);
await failableCommandStep('yarn lint:fix');
await commandStep('yarn build');
await failableCommandStep('yarn lint');
}
/**
* Enumerate packages subject to override validation
*/
async function enumerateOverridePackages(): Promise<WritableNpmPackage[]> {
return await enumerateRepoPackages(isOverridePackage);
}
/**
* Whether the NPM package is subject to override validation
*/
async function isOverridePackage(pkg: NpmPackage): Promise<boolean> {
return await fs.exists(path.join(pkg.path, 'overrides.json'));
}
/**
* Upgrade platform overrides in the repo to the current version of react
* native, disallowing files with conflicts to be written
*/
async function upgradePlatformOverrides(
newVersion: string,
): Promise<StepResult> {
const overridesWithConflicts: string[] = [];
for (const pkg of await enumerateOverridePackages()) {
const results = await upgradeOverrides(
path.join(pkg.path, 'overrides.json'),
{reactNativeVersion: newVersion, allowConflicts: false},
);
overridesWithConflicts.push(
...results
.filter(r => r.hasConflicts)
.map(r => r.overrideName)
.map(o => path.join(pkg.path, o)),
);
}
if (overridesWithConflicts.length === 0) {
return {status: 'success'};
} else {
return {
status: 'warn',
body: `Conflicts detected in the following files. Use 'react-native-platform-override upgrade' to resolve: \n- ${overridesWithConflicts.join(
'\n- ',
)}`,
};
}
}
/**
* Perform additional validation on platform overrides apart from the previous
* up-to-date check
*/
async function validatePlatformOverrides(): Promise<StepResult> {
const errors: ValidationError[] = [];
for (const pkg of await enumerateOverridePackages()) {
errors.push(
...(await validateManifest(path.join(pkg.path, 'overrides.json'))),
);
}
if (errors.filter(e => e.type !== 'outOfDate').length !== 0) {
return {
status: 'warn',
body: 'Override validation failed. Run `yarn validate-overrides` for more information',
};
} else {
return {status: 'success'};
}
}
/**
* Creates a URL to show commits that have ocurred between the current RN
* version and the new version
*/
async function generateCommitsUrl(newRnVersion: string): Promise<string> {
const rnwPackage = (await findPackage('react-native-windows'))!;
const rnPackage = (await findPackage('react-native', {
searchPath: rnwPackage.path,
}))!;
return `https://github.com/facebook/react-native/compare/${getAbbreviatedRef(
rnPackage.json.version,
)}...${getAbbreviatedRef(newRnVersion)}`;
}
type StepResult = {
status: 'success' | 'warn' | 'error';
body?: string;
};
/**
* Run the function while showing a spinner
*/
async function funcStep(
name: string,
func: () => Promise<StepResult>,
): Promise<void> {
logger.newTask(name);
try {
const result = await func();
logger[result.status](name, result.body);
} catch (ex) {
logger.error(name, (ex as Error).stack);
throw ex;
}
}
/**
* Run the command while showing a spinner
*/
async function commandStep(cmd: string) {
return funcStep(cmd, async () => {
await runCommand(cmd);
return {status: 'success'};
});
}
/**
* Run the command while showing a spinner. Continue if the command returns a non-zero exit code.
*/
async function failableCommandStep(cmd: string) {
logger.newTask(cmd);
try {
await runCommand(cmd);
logger.success(cmd);
} catch (ex) {
logger.error(cmd, (ex as Error).stack);
}
}