Skip to content

Commit 870a4e2

Browse files
authored
feat: improve upgrade when patches fail to apply (#461)
1 parent 8cb7f88 commit 870a4e2

File tree

2 files changed

+61
-27
lines changed

2 files changed

+61
-27
lines changed

packages/cli/src/commands/upgrade/__tests__/upgrade.test.js

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ jest.mock('@react-native-community/cli-tools', () => ({
5353
error: jest.fn((...args) => mockPushLog('error', args)),
5454
warn: jest.fn((...args) => mockPushLog('warn', args)),
5555
success: jest.fn((...args) => mockPushLog('success', args)),
56+
debug: jest.fn((...args) => mockPushLog('debug', args)),
5657
log: jest.fn((...args) => mockPushLog(args)),
5758
},
5859
}));
@@ -252,7 +253,8 @@ test('cleans up if patching fails,', async () => {
252253
if (command === 'git' && args[0] === 'apply') {
253254
return Promise.reject({
254255
code: 1,
255-
stderr: 'error: .flowconfig: does not exist in index\n',
256+
stderr:
257+
'error: .flowconfig: does not exist in index\nerror: ios/MyApp.xcodeproj/project.pbxproj: patch does not apply',
256258
});
257259
}
258260
if (command === 'git' && args[0] === 'rev-parse') {
@@ -272,16 +274,24 @@ test('cleans up if patching fails,', async () => {
272274
[fs] write tmp-upgrade-rn.patch
273275
$ execa git rev-parse --show-prefix
274276
$ execa git apply --binary --check tmp-upgrade-rn.patch --exclude=package.json -p2 --3way --directory=
275-
info Applying diff (excluding: package.json, .flowconfig)...
276-
$ execa git apply tmp-upgrade-rn.patch --exclude=package.json --exclude=.flowconfig -p2 --3way --directory=
277+
info Applying diff...
278+
warn Excluding files that exist in the template, but not in your project:
279+
- .flowconfig
280+
error Excluding files that failed to apply the diff:
281+
- ios/MyApp.xcodeproj/project.pbxproj
282+
Please make sure to check the actual changes after the upgrade command is finished.
283+
You can find them in our Upgrade Helper web app: https://react-native-community.github.io/upgrade-helper/?from=0.57.8&to=0.58.4
284+
$ execa git apply tmp-upgrade-rn.patch --exclude=package.json --exclude=.flowconfig --exclude=ios/MyApp.xcodeproj/project.pbxproj -p2 --3way --directory=
285+
debug \\"git apply\\" failed. Error output:
277286
error: .flowconfig: does not exist in index
278-
error Automatically applying diff failed
287+
error: ios/MyApp.xcodeproj/project.pbxproj: patch does not apply
288+
error Automatically applying diff failed. We did our best to automatically upgrade as many files as possible
279289
[fs] unlink tmp-upgrade-rn.patch
280290
$ execa git status -s
281291
error Patch failed to apply for unknown reason. Please fall back to manual way of upgrading
282292
info You may find these resources helpful:
283293
• Release notes: https://github.com/facebook/react-native/releases/tag/v0.58.4
284-
Comparison between versions: https://github.com/react-native-community/rn-diff-purge/compare/release/0.57.8..release/0.58.4
294+
Manual Upgrade Helper: https://react-native-community.github.io/upgrade-helper/?from=0.57.8&to=0.58.4
285295
• Git diff: https://raw.githubusercontent.com/react-native-community/rn-diff-purge/diffs/diffs/0.57.8..0.58.4.diff"
286296
`);
287297
}, 60000);

packages/cli/src/commands/upgrade/upgrade.js

Lines changed: 46 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ type FlagsT = {
1414
legacy: boolean | void,
1515
};
1616

17-
const rnDiffPurgeUrl =
18-
'https://github.com/react-native-community/rn-diff-purge';
19-
const rnDiffPurgeRawDiffsUrl =
17+
// https://react-native-community.github.io/upgrade-helper/?from=0.59.10&to=0.60.0-rc.3
18+
const webDiffUrl = 'https://react-native-community.github.io/upgrade-helper';
19+
const rawDiffUrl =
2020
'https://raw.githubusercontent.com/react-native-community/rn-diff-purge/diffs/diffs';
2121

2222
const getLatestRNVersion = async (): Promise<string> => {
@@ -46,9 +46,7 @@ const getPatch = async (currentVersion, newVersion, config) => {
4646
logger.info(`Fetching diff between v${currentVersion} and v${newVersion}...`);
4747

4848
try {
49-
patch = await fetch(
50-
`${rnDiffPurgeRawDiffsUrl}/${currentVersion}..${newVersion}.diff`,
51-
);
49+
patch = await fetch(`${rawDiffUrl}/${currentVersion}..${newVersion}.diff`);
5250
} catch (error) {
5351
logger.error(
5452
`Failed to fetch diff for react-native@${newVersion}. Maybe it's not released yet?`,
@@ -166,15 +164,17 @@ const applyPatch = async (
166164
newVersion: string,
167165
tmpPatchFile: string,
168166
) => {
169-
let filesToExclude = ['package.json'];
167+
const defaultExcludes = ['package.json'];
168+
let filesThatDontExist = [];
169+
let filesThatFailedToApply = [];
170170
// $FlowFixMe ThenableChildProcess is incompatible with Promise
171171
const {stdout: relativePathFromRoot} = await execa('git', [
172172
'rev-parse',
173173
'--show-prefix',
174174
]);
175175
try {
176176
try {
177-
const excludes = filesToExclude.map(
177+
const excludes = defaultExcludes.map(
178178
e => `--exclude=${path.join(relativePathFromRoot, e)}`,
179179
);
180180
await execa('git', [
@@ -192,19 +192,41 @@ const applyPatch = async (
192192
]);
193193
logger.info('Applying diff...');
194194
} catch (error) {
195-
filesToExclude = [
196-
...filesToExclude,
197-
...error.stderr
198-
.split('\n')
195+
const errorLines = error.stderr.split('\n');
196+
filesThatDontExist = [
197+
...errorLines
199198
.filter(x => x.includes('does not exist in index'))
200199
.map(x => x.replace(/^error: (.*): does not exist in index$/, '$1')),
201200
].filter(Boolean);
202201

203-
logger.info(`Applying diff (excluding: ${filesToExclude.join(', ')})...`);
204-
} finally {
205-
const excludes = filesToExclude.map(
206-
e => `--exclude=${path.join(relativePathFromRoot, e)}`,
202+
filesThatFailedToApply = errorLines
203+
.filter(x => x.includes('patch does not apply'))
204+
.map(x => x.replace(/^error: (.*): patch does not apply$/, '$1'))
205+
.filter(Boolean);
206+
207+
logger.info('Applying diff...');
208+
logger.warn(
209+
`Excluding files that exist in the template, but not in your project:\n${filesThatDontExist
210+
.map(file => ` - ${chalk.bold(file)}`)
211+
.join('\n')}`,
207212
);
213+
if (filesThatFailedToApply.length) {
214+
logger.error(
215+
`Excluding files that failed to apply the diff:\n${filesThatFailedToApply
216+
.map(file => ` - ${chalk.bold(file)}`)
217+
.join(
218+
'\n',
219+
)}\nPlease make sure to check the actual changes after the upgrade command is finished.\nYou can find them in our Upgrade Helper web app: ${chalk.underline.dim(
220+
`${webDiffUrl}/?from=${currentVersion}&to=${newVersion}`,
221+
)}`,
222+
);
223+
}
224+
} finally {
225+
const excludes = [
226+
...defaultExcludes,
227+
...filesThatDontExist,
228+
...filesThatFailedToApply,
229+
].map(e => `--exclude=${path.join(relativePathFromRoot, e)}`);
208230
await execa('git', [
209231
'apply',
210232
tmpPatchFile,
@@ -216,9 +238,11 @@ const applyPatch = async (
216238
}
217239
} catch (error) {
218240
if (error.stderr) {
219-
logger.log(`${chalk.dim(error.stderr.trim())}`);
241+
logger.debug(`"git apply" failed. Error output:\n${error.stderr}`);
220242
}
221-
logger.error('Automatically applying diff failed');
243+
logger.error(
244+
'Automatically applying diff failed. We did our best to automatically upgrade as many files as possible',
245+
);
222246
return false;
223247
}
224248
return true;
@@ -279,7 +303,7 @@ async function upgrade(argv: Array<string>, ctx: ConfigT, args: FlagsT) {
279303
if (!patchSuccess) {
280304
if (stdout) {
281305
logger.warn(
282-
'Continuing after failure. Most of the files are upgraded but you will need to deal with some conflicts manually',
306+
'Continuing after failure. Some of the files are upgraded but you will need to deal with conflicts manually',
283307
);
284308
await installDeps(newVersion, projectDir);
285309
logger.info('Running "git status" to check what changed...');
@@ -304,11 +328,11 @@ async function upgrade(argv: Array<string>, ctx: ConfigT, args: FlagsT) {
304328
• Release notes: ${chalk.underline.dim(
305329
`https://github.com/facebook/react-native/releases/tag/v${newVersion}`,
306330
)}
307-
Comparison between versions: ${chalk.underline.dim(
308-
`${rnDiffPurgeUrl}/compare/release/${currentVersion}..release/${newVersion}`,
331+
Manual Upgrade Helper: ${chalk.underline.dim(
332+
`${webDiffUrl}/?from=${currentVersion}&to=${newVersion}`,
309333
)}
310334
• Git diff: ${chalk.underline.dim(
311-
`${rnDiffPurgeRawDiffsUrl}/${currentVersion}..${newVersion}.diff`,
335+
`${rawDiffUrl}/${currentVersion}..${newVersion}.diff`,
312336
)}`);
313337

314338
throw new CLIError(

0 commit comments

Comments
 (0)