diff --git a/scripts/release-manager/cli.js b/scripts/release-manager/cli.js index 036ba4467a..9438822a4a 100755 --- a/scripts/release-manager/cli.js +++ b/scripts/release-manager/cli.js @@ -31,6 +31,8 @@ const COMMANDS = [ 'stable-prs', 'version', 'npm-publish', + 'npm-check-access', + 'npm-grant-access', ]; diff --git a/scripts/release-manager/commands/npm-check-access.js b/scripts/release-manager/commands/npm-check-access.js new file mode 100644 index 0000000000..ee0de8299c --- /dev/null +++ b/scripts/release-manager/commands/npm-check-access.js @@ -0,0 +1,40 @@ +'use strict'; + +const npmUtils = require('./utils/npm'); +const chalk = require('chalk'); +const opn = require('opn'); + +module.exports = function(vorpal, app) { + vorpal + .command('npm-check-access') + .description('Check to ensure you have correct access to npm packages') + .action(function(args) { + return new Promise((resolve, reject) => { + const username = npmUtils.whoami(app); + if (!username) { + return reject( + `${chalk.red('FAILED')} You aren't logged in to npm. Please run ` + + `${chalk.underline(`npm adduser`)} and try again.` + ); + } + + this.log(`${chalk.green('OK')} Logged in as ${chalk.bold(username)}`); + + const packagesNeedingAccess = npmUtils.packagesNeedingAccess(app, username); + + if (packagesNeedingAccess.length) { + this.log( + `${chalk.red('FAILED')} You don't have access to all of the packages ` + + `you need. We just opened a URL to file a new issue requesting access.` + ); + opn( + npmUtils.generateAccessNeededIssue(username, packagesNeedingAccess), + {wait: false} + ).then(resolve); + } else { + this.log(`${chalk.green('OK')} You can publish all React packages`); + resolve(); + } + }); + }); +}; diff --git a/scripts/release-manager/commands/npm-grant-access.js b/scripts/release-manager/commands/npm-grant-access.js new file mode 100644 index 0000000000..a8360c877a --- /dev/null +++ b/scripts/release-manager/commands/npm-grant-access.js @@ -0,0 +1,36 @@ +'use strict'; + +const npmUtils = require('./utils/npm'); +const chalk = require('chalk'); + +module.exports = function(vorpal, app) { + vorpal + .command('npm-grant-access') + .description('Grant access to somebody to publish React. Assumes you ran "npm-check-access" first.') + .action(function(args) { + return new Promise((resolve, reject) => { + this.prompt({ + type: 'input', + message: 'Who would you like to grant access to? ', + name: 'username', + }).then((answers) => { + if (!answers.username) { + return reject('ABORTING'); + } + + const packagesNeedingAccess = npmUtils.packagesNeedingAccess(app, answers.username); + + if (packagesNeedingAccess.length) { + this.log(`${chalk.yellow('PENDING')} Granting access to ${packagesNeedingAccess}`); + npmUtils.grantAccess(app, answers.username, packagesNeedingAccess); + this.log(`${chalk.green('OK')} Access has been granted to ${answers.username}.`); + resolve(); + } else { + this.log(`${chalk.green('OK')} ${answers.username} already has access.`); + resolve(); + } + + }); + }); + }); +}; diff --git a/scripts/release-manager/commands/utils/npm.js b/scripts/release-manager/commands/utils/npm.js new file mode 100644 index 0000000000..06ab2d035f --- /dev/null +++ b/scripts/release-manager/commands/utils/npm.js @@ -0,0 +1,48 @@ +'use strict'; + +const querystring = require('querystring'); + +const PACKAGES = [ + 'react-addons-create-fragment', + 'react-addons-css-transition-group', + 'react-addons-linked-state-mixin', + 'react-addons-perf', + 'react-addons-pure-render-mixin', + 'react-addons-shallow-compare', + 'react-addons-test-utils', + 'react-addons-transition-group', + 'react-addons-update', + 'react-dom', + 'react-native-renderer', + 'react', +]; + +function whoami(app) { + return app.execInRepo('npm whoami'); +} + +function packagesNeedingAccess(app, username) { + let packages = JSON.parse(app.execInRepo(`npm access ls-packages ${username}`)); + return PACKAGES.filter((pkg) => packages[pkg] !== 'read-write'); +} + +function generateAccessNeededIssue(username, packages) { + let data = { + title: `npm access request: ${username}`, + body: `In order to publish React to npm I need access to the following repositories: +${packages.map((pkg) => `- [${pkg}](https://npm.im/${pkg})`).join('\n')}`, + }; + return `https://github.com/facebook/react/issues/new?${querystring.stringify(data)}`; +} + +function grantAccess(app, username, packages) { + packages.forEach((pkg) => { + app.execInRepo(`npm owner add ${username} ${pkg}`); + }); +} + +module.exports.PACKAGES = PACKAGES; +module.exports.whoami = whoami; +module.exports.packagesNeedingAccess = packagesNeedingAccess; +module.exports.generateAccessNeededIssue = generateAccessNeededIssue; +module.exports.grantAccess = grantAccess; diff --git a/scripts/release-manager/package.json b/scripts/release-manager/package.json index 83050be41c..19a58d2182 100644 --- a/scripts/release-manager/package.json +++ b/scripts/release-manager/package.json @@ -9,6 +9,7 @@ "colors": "^1.1.2", "github-api": "^2.2.0", "glob": "^7.0.5", + "opn": "^4.0.2", "semver": "^5.3.0", "vorpal": "^1.10.10" },