diff --git a/scripts/release-manager/Readme.md b/scripts/release-manager/Readme.md index 2062b9f491..aa972deedf 100644 --- a/scripts/release-manager/Readme.md +++ b/scripts/release-manager/Readme.md @@ -3,7 +3,7 @@ This is a WIP tool that is being used to manage React releases. General prereqs: -- A separate clone of React, checked out to this branch (currently `release-manager`). This clone should be a sibling to your primary working copy of React. I have these 2 as siblings: `react/` & `react@release-manager`. +- A separate clone of React, checked out to this branch (currently `release-manager`). This clone can be anywhere, so long as it is a separate working copy. Ideally this is where you are most comfortable working on React. - Your `react` clone should have the latest commits fetched before running commands. You may need to rebase or reset & re-run commands if not. diff --git a/scripts/release-manager/cli.js b/scripts/release-manager/cli.js index 9438822a4a..3c0c3a0216 100755 --- a/scripts/release-manager/cli.js +++ b/scripts/release-manager/cli.js @@ -5,9 +5,11 @@ const chalk = require('chalk'); const Vorpal = require('vorpal'); const GitHubAPI = require('github-api'); +const untildify = require('untildify'); const fs = require('fs'); const path = require('path'); +const os = require('os'); const child_process = require('child_process'); const execSync = child_process.execSync; @@ -17,7 +19,14 @@ const vorpal = new Vorpal(); // Expects to be in a checkout of react that is a sibling of the react checkout you want to operate on // eg ~/code/react@release-manager/scripts/release-manager & ~/code/react // TODO: Make this an argument to the script -const PATH_TO_REPO = path.resolve('../../../react'); +let PATH_TO_REPO = null; + +const PATH_TO_CONFIG = path.resolve(os.homedir(), '.react-release-manager.json'); + +const DEFAULT_CONFIG = { + githubToken: null, + reactPath: path.resolve('../../../react'), +}; // Quick dry run opt-in. This allows quick debugging of execInRepo without // actually running the command, ensuring no accidental publishing. @@ -92,24 +101,30 @@ const app = { // TODO: write this. This should make it possible to start without a config // and go through the init process to create one and then re-init the github // setup. + this.config = this.loadConfig(); + }, + + loadConfig() { + try { + // TODO: validate config + let config = JSON.parse(fs.readFileSync(PATH_TO_CONFIG, 'utf8')); + config.reactPath = path.normalize(untildify(config.reactPath)); + PATH_TO_REPO = config.reactPath; + return config; + } catch (e) { + console.error('Attempt to load config file failed. Please run `init` command for initial setup or make sure ~/.react-release-manager.json is valid JSON. Using a default config which may not work properly.'); + return DEFAULT_CONFIG; + } }, init() { - // Config - try { - this.config = JSON.parse(fs.readFileSync('./.config.json', 'utf8')); - } catch (e) { - this.config = { - token: null, - }; - console.error('Could not read .config.json. Rate limits are much stricter as a result. Run init to setup.'); - } + this.config = this.loadConfig(); - this.PATH_TO_REPO = PATH_TO_REPO; + this.PATH_TO_CONFIG = PATH_TO_CONFIG; // GITHUB this.github = new GitHubAPI({ - token: this.config.token, + token: this.config.githubToken, }); this.ghrepo = this.github.getRepo('facebook', 'react'); this.ghissues = this.github.getIssues('facebook', 'react'); diff --git a/scripts/release-manager/commands/init.js b/scripts/release-manager/commands/init.js index 0adcfbda77..033bbbf9b8 100644 --- a/scripts/release-manager/commands/init.js +++ b/scripts/release-manager/commands/init.js @@ -7,15 +7,15 @@ const chalk = require('chalk'); const fs = require('fs'); +const path = require('path'); +const untildify = require('untildify'); -const FILENAME = '.config.json'; - -module.exports = function(vorpal, options) { +module.exports = function(vorpal, app) { vorpal .command('init') .description('Initializes a .config.json file for use') .action(function(args, cb) { - fs.stat(FILENAME, (err, stats) => { + fs.stat(app.PATH_TO_CONFIG, (err, stats) => { if (stats) { this.log('Config file exists, nothing to do.'); cb(); @@ -23,14 +23,40 @@ module.exports = function(vorpal, options) { this.prompt([ { - name: 'token', + name: 'githubToken', type: 'input', message: `${chalk.bold('GitHub token?')} ${chalk.grey('(needs "repo" privs)')} `, }, + { + name: 'reactPath', + type: 'input', + message: `${chalk.bold('Location of local React checkout?')} `, + validate: (input) => { + let npath = path.normalize(untildify(input)); + + if (npath === '.') { + return 'Cannot be `.`'; + } + + let stats; + try { + stats = fs.statSync(npath); + } catch (e) { + return `Error: ${e}`; + } + + if (!stats.isDirectory()) { + return `${npath} is not a directory.`; + } + + // TODO: Look for markers indicating this is a React checkout. + return true; + }, + }, ]).then((answers) => { - fs.writeFile(FILENAME, JSON.stringify(answers, null, 2), (err) => { + fs.writeFile(app.PATH_TO_CONFIG, JSON.stringify(answers, null, 2), (err) => { if (err) { - this.log('ERROR WRITING .config.json', err); + this.log('Error writing config file.', err); } cb(); }); diff --git a/scripts/release-manager/commands/npm-publish.js b/scripts/release-manager/commands/npm-publish.js index 245ff332b5..122854127d 100644 --- a/scripts/release-manager/commands/npm-publish.js +++ b/scripts/release-manager/commands/npm-publish.js @@ -44,7 +44,7 @@ module.exports = function(vorpal, app) { // just npm publish pkg1.tgz && npm publish pkg2.tgz. This // avoided the need to cd and publish. const tgz = glob.sync('build/packages/*.tgz', { - cwd: app.PATH_TO_REPO, + cwd: app.config.reactPath, }); // Just in case they didn't actually prep this. diff --git a/scripts/release-manager/commands/version.js b/scripts/release-manager/commands/version.js index 3d69595b8f..1e1f2f867c 100644 --- a/scripts/release-manager/commands/version.js +++ b/scripts/release-manager/commands/version.js @@ -117,13 +117,13 @@ module.exports = function(vorpal, app) { fields: ['version', 'peerDependencies.react'], }, ].forEach((opts) => { - updateJSON.apply(this, [path.join(app.PATH_TO_REPO, opts.file), opts.fields, newVersion]); + updateJSON.apply(this, [path.join(app.config.reactPath, opts.file), opts.fields, newVersion]); }); // We also need to update src/ReactVersion.js which has the version in // string form in JS code. We'll just do a string replace. - const PATH_TO_REACTVERSION = path.join(app.PATH_TO_REPO, 'src/ReactVersion.js'); + const PATH_TO_REACTVERSION = path.join(app.config.reactPath, 'src/ReactVersion.js'); let reactVersionContents = fs.readFileSync(PATH_TO_REACTVERSION, 'utf8'); diff --git a/scripts/release-manager/package.json b/scripts/release-manager/package.json index 19a58d2182..208ebffd8f 100644 --- a/scripts/release-manager/package.json +++ b/scripts/release-manager/package.json @@ -11,6 +11,7 @@ "glob": "^7.0.5", "opn": "^4.0.2", "semver": "^5.3.0", + "untildify": "^3.0.2", "vorpal": "^1.10.10" }, "devDependencies": {},