Skip to content

Commit 99144b0

Browse files
committed
feat: improve upgrade when patches fail to apply (#461)
1 parent baed104 commit 99144b0

File tree

2 files changed

+62
-27
lines changed

2 files changed

+62
-27
lines changed

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

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ jest.mock('../../../tools/logger', () => ({
4949
error: jest.fn((...args) => mockPushLog('error', args)),
5050
warn: jest.fn((...args) => mockPushLog('warn', args)),
5151
success: jest.fn((...args) => mockPushLog('success', args)),
52+
debug: jest.fn((...args) => mockPushLog('debug', args)),
5253
log: jest.fn((...args) => mockPushLog(args)),
5354
}));
5455

@@ -239,7 +240,8 @@ test('cleans up if patching fails,', async () => {
239240
if (command === 'git' && args[0] === 'apply') {
240241
return Promise.reject({
241242
code: 1,
242-
stderr: 'error: .flowconfig: does not exist in index\n',
243+
stderr:
244+
'error: .flowconfig: does not exist in index\nerror: ios/MyApp.xcodeproj/project.pbxproj: patch does not apply',
243245
});
244246
}
245247
if (command === 'git' && args[0] === 'rev-parse') {
@@ -259,16 +261,24 @@ test('cleans up if patching fails,', async () => {
259261
[fs] write tmp-upgrade-rn.patch
260262
$ execa git rev-parse --show-prefix
261263
$ execa git apply --binary --check tmp-upgrade-rn.patch --exclude=package.json -p2 --3way --directory=
262-
info Applying diff (excluding: package.json, .flowconfig)...
263-
$ execa git apply tmp-upgrade-rn.patch --exclude=package.json --exclude=.flowconfig -p2 --3way --directory=
264+
info Applying diff...
265+
warn Excluding files that exist in the template, but not in your project:
266+
- .flowconfig
267+
error Excluding files that failed to apply the diff:
268+
- ios/MyApp.xcodeproj/project.pbxproj
269+
Please make sure to check the actual changes after the upgrade command is finished.
270+
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
271+
$ execa git apply tmp-upgrade-rn.patch --exclude=package.json --exclude=.flowconfig --exclude=ios/MyApp.xcodeproj/project.pbxproj -p2 --3way --directory=
272+
debug \\"git apply\\" failed. Error output:
264273
error: .flowconfig: does not exist in index
265-
error Automatically applying diff failed
274+
error: ios/MyApp.xcodeproj/project.pbxproj: patch does not apply
275+
error Automatically applying diff failed. We did our best to automatically upgrade as many files as possible
266276
[fs] unlink tmp-upgrade-rn.patch
267277
$ execa git status -s
268278
error Patch failed to apply for unknown reason. Please fall back to manual way of upgrading
269279
info You may find these resources helpful:
270280
• Release notes: https://github.com/facebook/react-native/releases/tag/v0.58.4
271-
Comparison between versions: https://github.com/react-native-community/rn-diff-purge/compare/version/0.57.8..version/0.58.4
272-
• Git diff: https://github.com/react-native-community/rn-diff-purge/compare/version/0.57.8..version/0.58.4.diff"
281+
Manual Upgrade Helper: https://react-native-community.github.io/upgrade-helper/?from=0.57.8&to=0.58.4
282+
• Git diff: https://raw.githubusercontent.com/react-native-community/rn-diff-purge/diffs/diffs/0.57.8..0.58.4.diff"
273283
`);
274284
});

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

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

17-
const rnDiffPurgeUrl =
18-
'https://github.com/react-native-community/rn-diff-purge';
17+
const webDiffUrl = 'https://react-native-community.github.io/upgrade-helper';
18+
const rawDiffUrl =
19+
'https://raw.githubusercontent.com/react-native-community/rn-diff-purge/diffs/diffs';
1920

2021
const getLatestRNVersion = async (): Promise<string> => {
2122
logger.info('No version passed. Fetching latest...');
@@ -45,9 +46,7 @@ const getPatch = async (currentVersion, newVersion, projectDir) => {
4546
logger.info(`Fetching diff between v${currentVersion} and v${newVersion}...`);
4647

4748
try {
48-
patch = await fetch(
49-
`${rnDiffPurgeUrl}/compare/version/${currentVersion}...version/${newVersion}.diff`,
50-
);
49+
patch = await fetch(`${rawDiffUrl}/${currentVersion}..${newVersion}.diff`);
5150
} catch (error) {
5251
logger.error(
5352
`Failed to fetch diff for react-native@${newVersion}. Maybe it's not released yet?`,
@@ -134,15 +133,17 @@ const applyPatch = async (
134133
newVersion: string,
135134
tmpPatchFile: string,
136135
) => {
137-
let filesToExclude = ['package.json'];
136+
const defaultExcludes = ['package.json'];
137+
let filesThatDontExist = [];
138+
let filesThatFailedToApply = [];
138139
// $FlowFixMe ThenableChildProcess is incompatible with Promise
139140
const {stdout: relativePathFromRoot} = await execa('git', [
140141
'rev-parse',
141142
'--show-prefix',
142143
]);
143144
try {
144145
try {
145-
const excludes = filesToExclude.map(
146+
const excludes = defaultExcludes.map(
146147
e => `--exclude=${path.join(relativePathFromRoot, e)}`,
147148
);
148149
await execa('git', [
@@ -160,19 +161,41 @@ const applyPatch = async (
160161
]);
161162
logger.info('Applying diff...');
162163
} catch (error) {
163-
filesToExclude = [
164-
...filesToExclude,
165-
...error.stderr
166-
.split('\n')
164+
const errorLines = error.stderr.split('\n');
165+
filesThatDontExist = [
166+
...errorLines
167167
.filter(x => x.includes('does not exist in index'))
168168
.map(x => x.replace(/^error: (.*): does not exist in index$/, '$1')),
169169
].filter(Boolean);
170170

171-
logger.info(`Applying diff (excluding: ${filesToExclude.join(', ')})...`);
172-
} finally {
173-
const excludes = filesToExclude.map(
174-
e => `--exclude=${path.join(relativePathFromRoot, e)}`,
171+
filesThatFailedToApply = errorLines
172+
.filter(x => x.includes('patch does not apply'))
173+
.map(x => x.replace(/^error: (.*): patch does not apply$/, '$1'))
174+
.filter(Boolean);
175+
176+
logger.info('Applying diff...');
177+
logger.warn(
178+
`Excluding files that exist in the template, but not in your project:\n${filesThatDontExist
179+
.map(file => ` - ${chalk.bold(file)}`)
180+
.join('\n')}`,
175181
);
182+
if (filesThatFailedToApply.length) {
183+
logger.error(
184+
`Excluding files that failed to apply the diff:\n${filesThatFailedToApply
185+
.map(file => ` - ${chalk.bold(file)}`)
186+
.join(
187+
'\n',
188+
)}\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(
189+
`${webDiffUrl}/?from=${currentVersion}&to=${newVersion}`,
190+
)}`,
191+
);
192+
}
193+
} finally {
194+
const excludes = [
195+
...defaultExcludes,
196+
...filesThatDontExist,
197+
...filesThatFailedToApply,
198+
].map(e => `--exclude=${path.join(relativePathFromRoot, e)}`);
176199
await execa('git', [
177200
'apply',
178201
tmpPatchFile,
@@ -184,9 +207,11 @@ const applyPatch = async (
184207
}
185208
} catch (error) {
186209
if (error.stderr) {
187-
logger.log(`${chalk.dim(error.stderr.trim())}`);
210+
logger.debug(`"git apply" failed. Error output:\n${error.stderr}`);
188211
}
189-
logger.error('Automatically applying diff failed');
212+
logger.error(
213+
'Automatically applying diff failed. We did our best to automatically upgrade as many files as possible',
214+
);
190215
return false;
191216
}
192217
return true;
@@ -247,7 +272,7 @@ async function upgrade(argv: Array<string>, ctx: ContextT, args: FlagsT) {
247272
if (!patchSuccess) {
248273
if (stdout) {
249274
logger.warn(
250-
'Continuing after failure. Most of the files are upgraded but you will need to deal with some conflicts manually',
275+
'Continuing after failure. Some of the files are upgraded but you will need to deal with conflicts manually',
251276
);
252277
await installDeps(newVersion, projectDir);
253278
logger.info('Running "git status" to check what changed...');
@@ -272,11 +297,11 @@ async function upgrade(argv: Array<string>, ctx: ContextT, args: FlagsT) {
272297
• Release notes: ${chalk.underline.dim(
273298
`https://github.com/facebook/react-native/releases/tag/v${newVersion}`,
274299
)}
275-
Comparison between versions: ${chalk.underline.dim(
276-
`${rnDiffPurgeUrl}/compare/version/${currentVersion}..version/${newVersion}`,
300+
Manual Upgrade Helper: ${chalk.underline.dim(
301+
`${webDiffUrl}/?from=${currentVersion}&to=${newVersion}`,
277302
)}
278303
• Git diff: ${chalk.underline.dim(
279-
`${rnDiffPurgeUrl}/compare/version/${currentVersion}..version/${newVersion}.diff`,
304+
`${rawDiffUrl}/${currentVersion}..${newVersion}.diff`,
280305
)}`);
281306

282307
throw new Error(

0 commit comments

Comments
 (0)