2016-11-28 16:17:51 +00:00
|
|
|
|
/**
|
2018-09-11 22:27:47 +00:00
|
|
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
2016-11-28 16:17:51 +00:00
|
|
|
|
*
|
2018-02-17 02:24:55 +00:00
|
|
|
|
* This source code is licensed under the MIT license found in the
|
|
|
|
|
* LICENSE file in the root directory of this source tree.
|
2018-05-11 20:32:37 +00:00
|
|
|
|
*
|
|
|
|
|
* @format
|
2016-11-28 16:17:51 +00:00
|
|
|
|
*/
|
2018-05-11 20:32:37 +00:00
|
|
|
|
|
2016-11-28 16:17:51 +00:00
|
|
|
|
'use strict';
|
|
|
|
|
|
|
|
|
|
const fs = require('fs');
|
|
|
|
|
const os = require('os');
|
2016-12-02 16:20:39 +00:00
|
|
|
|
const assert = require('assert');
|
2016-11-28 16:17:51 +00:00
|
|
|
|
const path = require('path');
|
|
|
|
|
const shell = require('shelljs');
|
|
|
|
|
const Promise = require('promise');
|
|
|
|
|
const yeoman = require('yeoman-environment');
|
|
|
|
|
const TerminalAdapter = require('yeoman-environment/lib/adapter');
|
|
|
|
|
const log = require('npmlog');
|
|
|
|
|
const rimraf = require('rimraf');
|
|
|
|
|
const semver = require('semver');
|
2016-12-02 08:04:37 +00:00
|
|
|
|
const yarn = require('./yarn');
|
2016-11-28 16:17:51 +00:00
|
|
|
|
|
|
|
|
|
const {
|
|
|
|
|
checkDeclaredVersion,
|
|
|
|
|
checkMatchingVersions,
|
|
|
|
|
checkReactPeerDependency,
|
|
|
|
|
checkGitAvailable,
|
|
|
|
|
} = require('./checks');
|
|
|
|
|
|
|
|
|
|
log.heading = 'git-upgrade';
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Promisify the callback-based shelljs function exec
|
2016-12-02 08:04:37 +00:00
|
|
|
|
* @param logOutput If true, log the stdout of the command.
|
2018-02-13 16:51:12 +00:00
|
|
|
|
* @param logger Custom logger to modify the output, invoked with the data and the stream.
|
2016-11-28 16:17:51 +00:00
|
|
|
|
* @returns {Promise}
|
|
|
|
|
*/
|
2018-02-13 16:51:12 +00:00
|
|
|
|
function exec(command, logOutput, logger = null) {
|
2016-11-28 16:17:51 +00:00
|
|
|
|
return new Promise((resolve, reject) => {
|
2018-05-11 20:32:37 +00:00
|
|
|
|
let stderr,
|
|
|
|
|
stdout = '';
|
2016-11-28 16:17:51 +00:00
|
|
|
|
const child = shell.exec(command, {async: true, silent: true});
|
|
|
|
|
|
|
|
|
|
child.stdout.on('data', data => {
|
|
|
|
|
stdout += data;
|
|
|
|
|
if (logOutput) {
|
2018-02-13 16:51:12 +00:00
|
|
|
|
if (logger) {
|
|
|
|
|
logger(data, process.stdout);
|
|
|
|
|
} else {
|
|
|
|
|
process.stdout.write(data);
|
|
|
|
|
}
|
2016-11-28 16:17:51 +00:00
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
child.stderr.on('data', data => {
|
|
|
|
|
stderr += data;
|
2018-02-13 16:51:12 +00:00
|
|
|
|
if (logger) {
|
|
|
|
|
logger(data, process.stderr);
|
|
|
|
|
} else {
|
|
|
|
|
process.stderr.write(data);
|
|
|
|
|
}
|
2016-11-28 16:17:51 +00:00
|
|
|
|
});
|
|
|
|
|
|
react-native-git-upgrade signal termination
Summary:
When performing an upgrade using react-native-git-upgrade git can fail with a signal, however, only exit codes are accounted for.
Related issue: #12336
In my case, Git fails with a `SIGPIPE` signal - have not figured out why yet, but that's a separate issue. Before, since exit code was not zero, the console would default to the error case, but since no exit code was supplied, the error message was meaningless - `exited with code null`. Now, it errors out with `terminated with signal 'SIGPIPE'`, while still taking account of exit codes. Quoting [nodejs docs](https://nodejs.org/api/child_process.html#child_process_event_exit):
> The 'exit' event is emitted after the child process ends. If the process exited, code is the final exit code of the process, otherwise null. If the process terminated due to receipt of a signal, signal is the string name of the signal, otherwise null. One of the two will always be non-null.
I would be happy to write a test case for this but I haven't seen any react-native-git-upgrade ones?
[CLI] [ENHANCEMENT] [react-native/react-native-git-upgrade] - Git terminated by signal error message
Closes https://github.com/facebook/react-native/pull/16822
Differential Revision: D6387451
Pulled By: shergin
fbshipit-source-id: 25bf9dabdb5fda70d220bcd5f14c3c92bba8c3d4
2017-11-21 19:50:36 +00:00
|
|
|
|
child.on('exit', (code, signal) => {
|
|
|
|
|
if (code === 0) {
|
|
|
|
|
resolve(stdout);
|
|
|
|
|
} else if (code) {
|
2018-05-11 20:32:37 +00:00
|
|
|
|
reject(
|
|
|
|
|
new Error(`Command '${command}' exited with code ${code}:
|
2016-11-28 16:17:51 +00:00
|
|
|
|
stderr: ${stderr}
|
2018-05-11 20:32:37 +00:00
|
|
|
|
stdout: ${stdout}`),
|
|
|
|
|
);
|
react-native-git-upgrade signal termination
Summary:
When performing an upgrade using react-native-git-upgrade git can fail with a signal, however, only exit codes are accounted for.
Related issue: #12336
In my case, Git fails with a `SIGPIPE` signal - have not figured out why yet, but that's a separate issue. Before, since exit code was not zero, the console would default to the error case, but since no exit code was supplied, the error message was meaningless - `exited with code null`. Now, it errors out with `terminated with signal 'SIGPIPE'`, while still taking account of exit codes. Quoting [nodejs docs](https://nodejs.org/api/child_process.html#child_process_event_exit):
> The 'exit' event is emitted after the child process ends. If the process exited, code is the final exit code of the process, otherwise null. If the process terminated due to receipt of a signal, signal is the string name of the signal, otherwise null. One of the two will always be non-null.
I would be happy to write a test case for this but I haven't seen any react-native-git-upgrade ones?
[CLI] [ENHANCEMENT] [react-native/react-native-git-upgrade] - Git terminated by signal error message
Closes https://github.com/facebook/react-native/pull/16822
Differential Revision: D6387451
Pulled By: shergin
fbshipit-source-id: 25bf9dabdb5fda70d220bcd5f14c3c92bba8c3d4
2017-11-21 19:50:36 +00:00
|
|
|
|
} else {
|
2018-05-11 20:32:37 +00:00
|
|
|
|
reject(
|
|
|
|
|
new Error(`Command '${command}' terminated with signal '${signal}':
|
react-native-git-upgrade signal termination
Summary:
When performing an upgrade using react-native-git-upgrade git can fail with a signal, however, only exit codes are accounted for.
Related issue: #12336
In my case, Git fails with a `SIGPIPE` signal - have not figured out why yet, but that's a separate issue. Before, since exit code was not zero, the console would default to the error case, but since no exit code was supplied, the error message was meaningless - `exited with code null`. Now, it errors out with `terminated with signal 'SIGPIPE'`, while still taking account of exit codes. Quoting [nodejs docs](https://nodejs.org/api/child_process.html#child_process_event_exit):
> The 'exit' event is emitted after the child process ends. If the process exited, code is the final exit code of the process, otherwise null. If the process terminated due to receipt of a signal, signal is the string name of the signal, otherwise null. One of the two will always be non-null.
I would be happy to write a test case for this but I haven't seen any react-native-git-upgrade ones?
[CLI] [ENHANCEMENT] [react-native/react-native-git-upgrade] - Git terminated by signal error message
Closes https://github.com/facebook/react-native/pull/16822
Differential Revision: D6387451
Pulled By: shergin
fbshipit-source-id: 25bf9dabdb5fda70d220bcd5f14c3c92bba8c3d4
2017-11-21 19:50:36 +00:00
|
|
|
|
stderr: ${stderr}
|
2018-05-11 20:32:37 +00:00
|
|
|
|
stdout: ${stdout}`),
|
|
|
|
|
);
|
react-native-git-upgrade signal termination
Summary:
When performing an upgrade using react-native-git-upgrade git can fail with a signal, however, only exit codes are accounted for.
Related issue: #12336
In my case, Git fails with a `SIGPIPE` signal - have not figured out why yet, but that's a separate issue. Before, since exit code was not zero, the console would default to the error case, but since no exit code was supplied, the error message was meaningless - `exited with code null`. Now, it errors out with `terminated with signal 'SIGPIPE'`, while still taking account of exit codes. Quoting [nodejs docs](https://nodejs.org/api/child_process.html#child_process_event_exit):
> The 'exit' event is emitted after the child process ends. If the process exited, code is the final exit code of the process, otherwise null. If the process terminated due to receipt of a signal, signal is the string name of the signal, otherwise null. One of the two will always be non-null.
I would be happy to write a test case for this but I haven't seen any react-native-git-upgrade ones?
[CLI] [ENHANCEMENT] [react-native/react-native-git-upgrade] - Git terminated by signal error message
Closes https://github.com/facebook/react-native/pull/16822
Differential Revision: D6387451
Pulled By: shergin
fbshipit-source-id: 25bf9dabdb5fda70d220bcd5f14c3c92bba8c3d4
2017-11-21 19:50:36 +00:00
|
|
|
|
}
|
2016-11-28 16:17:51 +00:00
|
|
|
|
});
|
2017-10-10 00:37:08 +00:00
|
|
|
|
});
|
2016-11-28 16:17:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-12-02 08:04:37 +00:00
|
|
|
|
function parseJsonFile(path, useYarn) {
|
2018-05-11 20:32:37 +00:00
|
|
|
|
const installHint = useYarn
|
|
|
|
|
? 'Make sure you ran "yarn" and that you are inside a React Native project.'
|
|
|
|
|
: 'Make sure you ran "npm install" and that you are inside a React Native project.';
|
2016-12-02 08:04:37 +00:00
|
|
|
|
let fileContents;
|
|
|
|
|
try {
|
|
|
|
|
fileContents = fs.readFileSync(path, 'utf8');
|
|
|
|
|
} catch (err) {
|
|
|
|
|
throw new Error('Cannot find "' + path + '". ' + installHint);
|
|
|
|
|
}
|
|
|
|
|
try {
|
|
|
|
|
return JSON.parse(fileContents);
|
|
|
|
|
} catch (err) {
|
|
|
|
|
throw new Error('Cannot parse "' + path + '": ' + err.message);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function readPackageFiles(useYarn) {
|
2016-12-01 11:35:45 +00:00
|
|
|
|
const reactNativeNodeModulesPakPath = path.resolve(
|
2018-05-11 20:32:37 +00:00
|
|
|
|
process.cwd(),
|
|
|
|
|
'node_modules',
|
|
|
|
|
'react-native',
|
|
|
|
|
'package.json',
|
2016-11-28 16:17:51 +00:00
|
|
|
|
);
|
2016-12-01 11:35:45 +00:00
|
|
|
|
const reactNodeModulesPakPath = path.resolve(
|
2018-05-11 20:32:37 +00:00
|
|
|
|
process.cwd(),
|
|
|
|
|
'node_modules',
|
|
|
|
|
'react',
|
|
|
|
|
'package.json',
|
2016-11-28 16:17:51 +00:00
|
|
|
|
);
|
2018-05-11 20:32:37 +00:00
|
|
|
|
const pakPath = path.resolve(process.cwd(), 'package.json');
|
2016-12-02 08:04:37 +00:00
|
|
|
|
return {
|
|
|
|
|
reactNativeNodeModulesPak: parseJsonFile(reactNativeNodeModulesPakPath),
|
|
|
|
|
reactNodeModulesPak: parseJsonFile(reactNodeModulesPakPath),
|
2018-05-11 20:32:37 +00:00
|
|
|
|
pak: parseJsonFile(pakPath),
|
2017-10-10 00:37:08 +00:00
|
|
|
|
};
|
2016-11-28 16:17:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-12-02 16:20:39 +00:00
|
|
|
|
function parseInformationJsonOutput(jsonOutput, requestedVersion) {
|
|
|
|
|
try {
|
|
|
|
|
const output = JSON.parse(jsonOutput);
|
|
|
|
|
const newVersion = output.version;
|
2016-12-09 15:55:18 +00:00
|
|
|
|
const peerDependencies = output.peerDependencies;
|
|
|
|
|
const newReactVersionRange = peerDependencies.react;
|
2016-12-02 16:20:39 +00:00
|
|
|
|
|
|
|
|
|
assert(semver.valid(newVersion));
|
|
|
|
|
|
2017-10-10 00:37:08 +00:00
|
|
|
|
return {newVersion, newReactVersionRange};
|
2016-12-02 16:20:39 +00:00
|
|
|
|
} catch (err) {
|
|
|
|
|
throw new Error(
|
2018-05-11 20:32:37 +00:00
|
|
|
|
'The specified version of React Native ' +
|
|
|
|
|
requestedVersion +
|
|
|
|
|
" doesn't exist.\n" +
|
|
|
|
|
'Re-run the react-native-git-upgrade command with an existing version,\n' +
|
|
|
|
|
'for example: "react-native-git-upgrade 0.38.0",\n' +
|
|
|
|
|
'or without arguments to upgrade to the latest: "react-native-git-upgrade".',
|
2016-12-02 16:20:39 +00:00
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-28 16:17:51 +00:00
|
|
|
|
function setupWorkingDir(tmpDir) {
|
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
|
rimraf(tmpDir, err => {
|
|
|
|
|
if (err) {
|
|
|
|
|
reject(err);
|
|
|
|
|
} else {
|
|
|
|
|
fs.mkdirSync(tmpDir);
|
|
|
|
|
resolve();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function configureGitEnv(tmpDir) {
|
|
|
|
|
/*
|
|
|
|
|
* The workflow inits a temporary Git repository. We don't want to interfere
|
|
|
|
|
* with an existing user's Git repository.
|
2016-11-30 16:17:55 +00:00
|
|
|
|
* Thanks to Git env vars, we can make Git use a different directory for its ".git" folder.
|
|
|
|
|
* See https://git-scm.com/book/tr/v2/Git-Internals-Environment-Variables
|
2016-11-28 16:17:51 +00:00
|
|
|
|
*/
|
|
|
|
|
process.env.GIT_DIR = path.resolve(tmpDir, '.gitrn');
|
|
|
|
|
process.env.GIT_WORK_TREE = '.';
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-01 21:59:57 +00:00
|
|
|
|
function copyCurrentGitIgnoreFile(tmpDir) {
|
|
|
|
|
/*
|
|
|
|
|
* The user may have added new files or directories in the .gitignore file.
|
|
|
|
|
* We need to keep those files ignored during the process, otherwise they
|
|
|
|
|
* will be deleted.
|
|
|
|
|
* See https://github.com/facebook/react-native/issues/12237
|
|
|
|
|
*/
|
|
|
|
|
try {
|
|
|
|
|
const gitignorePath = path.resolve(process.cwd(), '.gitignore');
|
2018-05-11 20:32:37 +00:00
|
|
|
|
const repoExcludePath = path.resolve(
|
|
|
|
|
tmpDir,
|
|
|
|
|
process.env.GIT_DIR,
|
|
|
|
|
'info/exclude',
|
|
|
|
|
);
|
2018-02-01 21:59:57 +00:00
|
|
|
|
const content = fs.readFileSync(gitignorePath, 'utf8');
|
|
|
|
|
fs.appendFileSync(repoExcludePath, content);
|
|
|
|
|
} catch (err) {
|
|
|
|
|
if (err.code === 'ENOENT') {
|
|
|
|
|
log.info('No .gitignore file found, this step is a no-op');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
throw err;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-28 16:17:51 +00:00
|
|
|
|
function generateTemplates(generatorDir, appName, verbose) {
|
|
|
|
|
try {
|
|
|
|
|
const yeomanGeneratorEntryPoint = path.resolve(generatorDir, 'index.js');
|
|
|
|
|
// Try requiring the index.js (entry-point of Yeoman generators)
|
|
|
|
|
fs.accessSync(yeomanGeneratorEntryPoint);
|
|
|
|
|
return runYeomanGenerators(generatorDir, appName, verbose);
|
2017-10-10 00:37:08 +00:00
|
|
|
|
} catch (err) {
|
2016-11-28 16:17:51 +00:00
|
|
|
|
return runCopyAndReplace(generatorDir, appName);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function runCopyAndReplace(generatorDir, appName) {
|
2018-05-11 20:32:37 +00:00
|
|
|
|
const copyProjectTemplateAndReplacePath = path.resolve(
|
|
|
|
|
generatorDir,
|
|
|
|
|
'copyProjectTemplateAndReplace',
|
|
|
|
|
);
|
2016-11-28 16:17:51 +00:00
|
|
|
|
/*
|
|
|
|
|
* This module is required twice during the process: for both old and new version
|
|
|
|
|
* of React Native.
|
|
|
|
|
* This file could have changed between these 2 versions. When generating the new template,
|
|
|
|
|
* we don't want to load the old version of the generator from the cache
|
|
|
|
|
*/
|
2017-02-19 00:41:30 +00:00
|
|
|
|
delete require.cache[require.resolve(copyProjectTemplateAndReplacePath)];
|
2016-11-28 16:17:51 +00:00
|
|
|
|
const copyProjectTemplateAndReplace = require(copyProjectTemplateAndReplacePath);
|
|
|
|
|
copyProjectTemplateAndReplace(
|
|
|
|
|
path.resolve(generatorDir, '..', 'templates', 'HelloWorld'),
|
|
|
|
|
process.cwd(),
|
|
|
|
|
appName,
|
2018-05-11 20:32:37 +00:00
|
|
|
|
{upgrade: true, force: true},
|
2016-11-28 16:17:51 +00:00
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function runYeomanGenerators(generatorDir, appName, verbose) {
|
|
|
|
|
if (!verbose) {
|
|
|
|
|
// Yeoman output needs monkey-patching (no silent option)
|
|
|
|
|
TerminalAdapter.prototype.log = () => {};
|
|
|
|
|
TerminalAdapter.prototype.log.force = () => {};
|
|
|
|
|
TerminalAdapter.prototype.log.create = () => {};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const env = yeoman.createEnv();
|
|
|
|
|
env.register(generatorDir, 'react:app');
|
|
|
|
|
const generatorArgs = ['react:app', appName];
|
2018-05-11 20:32:37 +00:00
|
|
|
|
return new Promise(resolve =>
|
|
|
|
|
env.run(generatorArgs, {upgrade: true, force: true}, resolve),
|
|
|
|
|
);
|
2016-11-28 16:17:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-11-29 14:10:24 +00:00
|
|
|
|
/**
|
|
|
|
|
* If there's a newer version of react-native-git-upgrade in npm, suggest to the user to upgrade.
|
|
|
|
|
*/
|
|
|
|
|
async function checkForUpdates() {
|
2016-11-28 16:17:51 +00:00
|
|
|
|
try {
|
2016-11-30 16:17:55 +00:00
|
|
|
|
log.info('Check for updates');
|
2018-05-11 20:32:37 +00:00
|
|
|
|
const lastGitUpgradeVersion = await exec(
|
|
|
|
|
'npm view react-native-git-upgrade@latest version',
|
|
|
|
|
);
|
2016-11-28 16:17:51 +00:00
|
|
|
|
const current = require('./package').version;
|
|
|
|
|
const latest = semver.clean(lastGitUpgradeVersion);
|
2016-11-29 14:10:24 +00:00
|
|
|
|
if (semver.gt(latest, current)) {
|
2016-11-28 16:17:51 +00:00
|
|
|
|
log.warn(
|
|
|
|
|
'A more recent version of "react-native-git-upgrade" has been found.\n' +
|
2018-05-11 20:32:37 +00:00
|
|
|
|
`Current: ${current}\n` +
|
|
|
|
|
`Latest: ${latest}\n` +
|
|
|
|
|
'Please run "npm install -g react-native-git-upgrade"',
|
2016-11-28 16:17:51 +00:00
|
|
|
|
);
|
|
|
|
|
}
|
2016-11-29 14:10:24 +00:00
|
|
|
|
} catch (err) {
|
|
|
|
|
log.warn('Check for latest version failed', err.message);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-12-02 08:04:37 +00:00
|
|
|
|
/**
|
|
|
|
|
* If true, use yarn instead of the npm client to upgrade the project.
|
|
|
|
|
*/
|
|
|
|
|
function shouldUseYarn(cliArgs, projectDir) {
|
|
|
|
|
if (cliArgs && cliArgs.npm) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
const yarnVersion = yarn.getYarnVersionIfAvailable();
|
|
|
|
|
if (yarnVersion && yarn.isProjectUsingYarn(projectDir)) {
|
|
|
|
|
log.info('Using yarn ' + yarnVersion);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-30 16:17:55 +00:00
|
|
|
|
/**
|
|
|
|
|
* @param requestedVersion The version argument, e.g. 'react-native-git-upgrade 0.38'.
|
|
|
|
|
* `undefined` if no argument passed.
|
|
|
|
|
* @param cliArgs Additional arguments parsed using minimist.
|
|
|
|
|
*/
|
|
|
|
|
async function run(requestedVersion, cliArgs) {
|
|
|
|
|
const tmpDir = path.resolve(os.tmpdir(), 'react-native-git-upgrade');
|
2018-05-11 20:32:37 +00:00
|
|
|
|
const generatorDir = path.resolve(
|
|
|
|
|
process.cwd(),
|
|
|
|
|
'node_modules',
|
|
|
|
|
'react-native',
|
|
|
|
|
'local-cli',
|
|
|
|
|
'generator',
|
|
|
|
|
);
|
2016-12-01 11:35:45 +00:00
|
|
|
|
let projectBackupCreated = false;
|
2016-11-29 14:10:24 +00:00
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
await checkForUpdates();
|
2016-11-28 16:17:51 +00:00
|
|
|
|
|
2016-12-02 08:04:37 +00:00
|
|
|
|
const useYarn = shouldUseYarn(cliArgs, path.resolve(process.cwd()));
|
|
|
|
|
|
2016-11-28 16:17:51 +00:00
|
|
|
|
log.info('Read package.json files');
|
2018-05-11 20:32:37 +00:00
|
|
|
|
const {
|
|
|
|
|
reactNativeNodeModulesPak,
|
|
|
|
|
reactNodeModulesPak,
|
|
|
|
|
pak,
|
|
|
|
|
} = readPackageFiles(useYarn);
|
2016-11-30 16:17:55 +00:00
|
|
|
|
const appName = pak.name;
|
2016-12-01 11:35:45 +00:00
|
|
|
|
const currentVersion = reactNativeNodeModulesPak.version;
|
|
|
|
|
const currentReactVersion = reactNodeModulesPak.version;
|
2016-11-30 16:17:55 +00:00
|
|
|
|
const declaredVersion = pak.dependencies['react-native'];
|
|
|
|
|
const declaredReactVersion = pak.dependencies.react;
|
2016-11-28 16:17:51 +00:00
|
|
|
|
|
2016-11-30 16:17:55 +00:00
|
|
|
|
const verbose = cliArgs.verbose;
|
2016-11-28 16:17:51 +00:00
|
|
|
|
|
|
|
|
|
log.info('Check declared version');
|
2016-11-30 16:17:55 +00:00
|
|
|
|
checkDeclaredVersion(declaredVersion);
|
2016-11-28 16:17:51 +00:00
|
|
|
|
|
|
|
|
|
log.info('Check matching versions');
|
2016-12-02 08:04:37 +00:00
|
|
|
|
checkMatchingVersions(currentVersion, declaredVersion, useYarn);
|
2016-11-28 16:17:51 +00:00
|
|
|
|
|
|
|
|
|
log.info('Check React peer dependency');
|
2016-11-30 16:17:55 +00:00
|
|
|
|
checkReactPeerDependency(currentVersion, declaredReactVersion);
|
2016-11-28 16:17:51 +00:00
|
|
|
|
|
2016-11-30 16:17:55 +00:00
|
|
|
|
log.info('Check that Git is installed');
|
2016-11-28 16:17:51 +00:00
|
|
|
|
checkGitAvailable();
|
|
|
|
|
|
2016-12-01 11:35:45 +00:00
|
|
|
|
log.info('Get information from NPM registry');
|
2018-05-11 20:32:37 +00:00
|
|
|
|
const viewCommand =
|
|
|
|
|
'npm view react-native@' + (requestedVersion || 'latest') + ' --json';
|
2016-12-02 16:20:39 +00:00
|
|
|
|
const jsonOutput = await exec(viewCommand, verbose);
|
2018-05-11 20:32:37 +00:00
|
|
|
|
const {newVersion, newReactVersionRange} = parseInformationJsonOutput(
|
|
|
|
|
jsonOutput,
|
|
|
|
|
requestedVersion,
|
|
|
|
|
);
|
2016-12-02 08:04:37 +00:00
|
|
|
|
// Print which versions we're upgrading to
|
2018-05-11 20:32:37 +00:00
|
|
|
|
log.info(
|
|
|
|
|
'Upgrading to React Native ' +
|
|
|
|
|
newVersion +
|
|
|
|
|
(newReactVersionRange ? ', React ' + newReactVersionRange : ''),
|
|
|
|
|
);
|
2016-11-28 16:17:51 +00:00
|
|
|
|
|
|
|
|
|
log.info('Setup temporary working directory');
|
2016-11-30 16:17:55 +00:00
|
|
|
|
await setupWorkingDir(tmpDir);
|
2016-11-28 16:17:51 +00:00
|
|
|
|
|
|
|
|
|
log.info('Configure Git environment');
|
2016-11-30 16:17:55 +00:00
|
|
|
|
configureGitEnv(tmpDir);
|
2016-11-28 16:17:51 +00:00
|
|
|
|
|
2018-02-01 21:59:57 +00:00
|
|
|
|
log.info('Init temporary Git repository');
|
2016-11-28 16:17:51 +00:00
|
|
|
|
await exec('git init', verbose);
|
|
|
|
|
|
2018-02-01 21:59:57 +00:00
|
|
|
|
log.info('Save current .gitignore file');
|
|
|
|
|
copyCurrentGitIgnoreFile(tmpDir);
|
|
|
|
|
|
2016-11-28 16:17:51 +00:00
|
|
|
|
log.info('Add all files to commit');
|
|
|
|
|
await exec('git add .', verbose);
|
|
|
|
|
|
2016-11-30 16:17:55 +00:00
|
|
|
|
log.info('Commit current project sources');
|
2017-11-17 00:16:30 +00:00
|
|
|
|
await exec('git commit -m "Project snapshot" --no-verify', verbose);
|
2016-11-28 16:17:51 +00:00
|
|
|
|
|
2017-10-10 00:37:08 +00:00
|
|
|
|
log.info('Create a tag before updating sources');
|
2016-11-28 16:17:51 +00:00
|
|
|
|
await exec('git tag project-snapshot', verbose);
|
2016-11-30 16:17:55 +00:00
|
|
|
|
projectBackupCreated = true;
|
2016-11-28 16:17:51 +00:00
|
|
|
|
|
|
|
|
|
log.info('Generate old version template');
|
2016-11-30 16:17:55 +00:00
|
|
|
|
await generateTemplates(generatorDir, appName, verbose);
|
2016-11-28 16:17:51 +00:00
|
|
|
|
|
|
|
|
|
log.info('Add updated files to commit');
|
|
|
|
|
await exec('git add .', verbose);
|
|
|
|
|
|
|
|
|
|
log.info('Commit old version template');
|
2018-05-11 20:32:37 +00:00
|
|
|
|
await exec(
|
|
|
|
|
'git commit -m "Old version" --allow-empty --no-verify',
|
|
|
|
|
verbose,
|
|
|
|
|
);
|
2016-11-28 16:17:51 +00:00
|
|
|
|
|
|
|
|
|
log.info('Install the new version');
|
2016-12-02 08:04:37 +00:00
|
|
|
|
let installCommand;
|
|
|
|
|
if (useYarn) {
|
|
|
|
|
installCommand = 'yarn add';
|
|
|
|
|
} else {
|
|
|
|
|
installCommand = 'npm install --save --color=always';
|
|
|
|
|
}
|
2016-12-01 11:35:45 +00:00
|
|
|
|
installCommand += ' react-native@' + newVersion;
|
2018-05-11 20:32:37 +00:00
|
|
|
|
if (
|
|
|
|
|
newReactVersionRange &&
|
|
|
|
|
!semver.satisfies(currentReactVersion, newReactVersionRange)
|
|
|
|
|
) {
|
2016-12-01 11:35:45 +00:00
|
|
|
|
// Install React as well to avoid unmet peer dependency
|
|
|
|
|
installCommand += ' react@' + newReactVersionRange;
|
|
|
|
|
}
|
|
|
|
|
await exec(installCommand, verbose);
|
2016-11-28 16:17:51 +00:00
|
|
|
|
|
|
|
|
|
log.info('Generate new version template');
|
2016-11-30 16:17:55 +00:00
|
|
|
|
await generateTemplates(generatorDir, appName, verbose);
|
2016-11-28 16:17:51 +00:00
|
|
|
|
|
|
|
|
|
log.info('Add updated files to commit');
|
|
|
|
|
await exec('git add .', verbose);
|
|
|
|
|
|
|
|
|
|
log.info('Commit new version template');
|
2018-05-11 20:32:37 +00:00
|
|
|
|
await exec(
|
|
|
|
|
'git commit -m "New version" --allow-empty --no-verify',
|
|
|
|
|
verbose,
|
|
|
|
|
);
|
2016-11-28 16:17:51 +00:00
|
|
|
|
|
|
|
|
|
log.info('Generate the patch between the 2 versions');
|
2018-05-11 20:32:37 +00:00
|
|
|
|
const diffOutput = await exec(
|
|
|
|
|
'git diff --binary --no-color HEAD~1 HEAD',
|
|
|
|
|
verbose,
|
|
|
|
|
);
|
2016-11-28 16:17:51 +00:00
|
|
|
|
|
|
|
|
|
log.info('Save the patch in temp directory');
|
2018-05-11 20:32:37 +00:00
|
|
|
|
const patchPath = path.resolve(
|
|
|
|
|
tmpDir,
|
|
|
|
|
`upgrade_${currentVersion}_${newVersion}.patch`,
|
|
|
|
|
);
|
2016-11-30 16:17:55 +00:00
|
|
|
|
fs.writeFileSync(patchPath, diffOutput);
|
2016-11-28 16:17:51 +00:00
|
|
|
|
|
|
|
|
|
log.info('Reset the 2 temporary commits');
|
|
|
|
|
await exec('git reset HEAD~2 --hard', verbose);
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
log.info('Apply the patch');
|
2018-02-13 16:51:12 +00:00
|
|
|
|
await exec(`git apply --3way ${patchPath}`, true, (data, stream) => {
|
|
|
|
|
if (data.indexOf('conflicts') >= 0 || data.startsWith('U ')) {
|
|
|
|
|
stream.write(`\x1b[31m${data}\x1b[0m`);
|
|
|
|
|
} else {
|
|
|
|
|
stream.write(data);
|
|
|
|
|
}
|
|
|
|
|
});
|
2016-11-28 16:17:51 +00:00
|
|
|
|
} catch (err) {
|
2016-11-30 16:17:55 +00:00
|
|
|
|
log.warn(
|
2016-12-02 08:04:37 +00:00
|
|
|
|
'The upgrade process succeeded but there might be conflicts to be resolved. ' +
|
2018-05-11 20:32:37 +00:00
|
|
|
|
'See above for the list of files that have merge conflicts. ' +
|
|
|
|
|
'If you don’t see the expected changes, try running:\n' +
|
|
|
|
|
`git apply --reject ${patchPath}`,
|
|
|
|
|
);
|
2016-11-28 16:17:51 +00:00
|
|
|
|
} finally {
|
|
|
|
|
log.info('Upgrade done');
|
2016-11-30 16:17:55 +00:00
|
|
|
|
if (cliArgs.verbose) {
|
|
|
|
|
log.info(`Temporary working directory: ${tmpDir}`);
|
2016-11-28 16:17:51 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} catch (err) {
|
|
|
|
|
log.error('An error occurred during upgrade:');
|
|
|
|
|
log.error(err.stack);
|
2016-11-30 16:17:55 +00:00
|
|
|
|
if (projectBackupCreated) {
|
2016-11-28 16:17:51 +00:00
|
|
|
|
log.error('Restore initial sources');
|
2017-11-17 00:16:30 +00:00
|
|
|
|
await exec('git checkout project-snapshot --no-verify', true);
|
2016-11-28 16:17:51 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
module.exports = {
|
|
|
|
|
run: run,
|
|
|
|
|
};
|