diff --git a/scripts/release/build-commands/add-git-tag.js b/scripts/release/build-commands/add-git-tag.js new file mode 100644 index 000000000000..d8e3bf7bc2b4 --- /dev/null +++ b/scripts/release/build-commands/add-git-tag.js @@ -0,0 +1,20 @@ +#!/usr/bin/env node + +'use strict'; + +const chalk = require('chalk'); +const {execUnlessDry, logPromise} = require('../utils'); + +const run = async ({cwd, dry, version}) => { + await execUnlessDry(`git tag -a ${version} -m "Tagging ${version} release"`, { + cwd, + dry, + }); +}; + +module.exports = async ({cwd, dry, version}) => { + return logPromise( + run({cwd, dry, version}), + `Creating git tag ${chalk.yellow.bold(version)}` + ); +}; diff --git a/scripts/release/build-commands/print-post-build-summary.js b/scripts/release/build-commands/print-post-build-summary.js index 92cceff5140f..a9c660d0c58b 100644 --- a/scripts/release/build-commands/print-post-build-summary.js +++ b/scripts/release/build-commands/print-post-build-summary.js @@ -3,16 +3,27 @@ 'use strict'; const chalk = require('chalk'); +const {join, relative} = require('path'); const {getUnexecutedCommands} = require('../utils'); const CHANGELOG_PATH = 'https://github.com/facebook/react/edit/master/CHANGELOG.md'; -module.exports = params => { +module.exports = ({cwd, dry, path, version}) => { + const publishPath = relative( + process.env.PWD, + join(__dirname, '../publish.js') + ); const command = - `./publish.js -v ${params.version}` + - (params.path ? ` -p ${params.path}` : '') + - (params.dry ? ' --dry' : ''); + `${publishPath} -v ${version}` + + (path ? ` -p ${path}` : '') + + (dry ? ' --dry' : ''); + + const packagingFixturesPath = join(cwd, 'fixtures/packaging'); + const standaloneFixturePath = join( + cwd, + 'fixtures/packaging/babel-standalone/dev.html' + ); console.log( chalk` @@ -29,9 +40,9 @@ module.exports = params => { {bold.underline Step 2: Smoke test the packages} - 1. Open {yellow.bold fixtures/packaging/babel-standalone/dev.html} in the browser. + 1. Open {yellow.bold ${standaloneFixturePath}} in the browser. 2. It should say {italic "Hello world!"} - 3. Next go to {yellow.bold fixtures/packaging} and run {bold node build-all.js} + 3. Next go to {yellow.bold ${packagingFixturesPath}} and run {bold node build-all.js} 4. Install the "serve" module ({bold npm install -g serve}) 5. Go to the repo root and {bold serve -s .} 6. Open {blue.bold http://localhost:5000/fixtures/packaging} diff --git a/scripts/release/build-commands/update-package-versions.js b/scripts/release/build-commands/update-package-versions.js index 84b0212e8270..be297ac6fbf1 100644 --- a/scripts/release/build-commands/update-package-versions.js +++ b/scripts/release/build-commands/update-package-versions.js @@ -32,11 +32,18 @@ const update = async ({cwd, dry, version}) => { const path = join(cwd, 'packages', project, 'package.json'); const json = await readJson(path); - // Unstable packages (eg version < 1.0) are treated differently. - // In order to simplify DX for the release engineer, - // These packages are auto-incremented by a minor version number. + // Unstable packages (eg version < 1.0) are treated specially: + // Rather than use the release version (eg 16.1.0)- + // We just auto-increment the minor version (eg 0.1.0 -> 0.2.0). + // If we're doing a prerelease, we also append the suffix (eg 0.2.0-beta). if (semver.lt(json.version, '1.0.0')) { - json.version = `0.${semver.minor(json.version) + 1}.0`; + const prerelease = semver.prerelease(version); + let suffix = ''; + if (prerelease) { + suffix = `-${prerelease.join('.')}`; + } + + json.version = `0.${semver.minor(json.version) + 1}.0${suffix}`; } else { json.version = version; } diff --git a/scripts/release/build.js b/scripts/release/build.js index 7295f16ebb92..13d7cfce782b 100755 --- a/scripts/release/build.js +++ b/scripts/release/build.js @@ -9,6 +9,7 @@ const run = async () => { const chalk = require('chalk'); const logUpdate = require('log-update'); + const addGitTag = require('./build-commands/add-git-tag'); const buildArtifacts = require('./build-commands/build-artifacts'); const checkCircleCiStatus = require('./build-commands/check-circle-ci-status'); const checkEnvironmentVariables = require('./build-commands/check-environment-variables'); @@ -39,6 +40,7 @@ const run = async () => { await runAutomatedTests(params); await updatePackageVersions(params); await buildArtifacts(params); + await addGitTag(params); await printPostBuildSummary(params); } catch (error) { logUpdate.clear(); diff --git a/scripts/release/publish-commands/publish-to-npm.js b/scripts/release/publish-commands/publish-to-npm.js index a4d11f54a7ef..34237765f7cd 100644 --- a/scripts/release/publish-commands/publish-to-npm.js +++ b/scripts/release/publish-commands/publish-to-npm.js @@ -3,6 +3,7 @@ 'use strict'; const chalk = require('chalk'); +const {readJson} = require('fs-extra'); const {join} = require('path'); const semver = require('semver'); const {execRead, execUnlessDry, logPromise} = require('../utils'); @@ -10,25 +11,46 @@ const {projects} = require('../config'); const push = async ({cwd, dry, version}) => { const errors = []; - const tag = semver.prerelease(version) ? 'next' : 'latest'; + const isPrerelease = semver.prerelease(version); + const tag = isPrerelease ? 'next' : 'latest'; const publishProject = async project => { try { const path = join(cwd, 'build', 'packages', project); await execUnlessDry(`npm publish --tag ${tag}`, {cwd: path, dry}); + const packagePath = join( + cwd, + 'build', + 'packages', + project, + 'package.json' + ); + const packageJSON = await readJson(packagePath); + const packageVersion = packageJSON.version; + if (!dry) { const status = JSON.parse( await execRead(`npm info ${project} dist-tags --json`) ); const remoteVersion = status[tag]; - if (remoteVersion !== version) { + // Compare remote version to package.json version, + // To better handle the case of pre-release versions. + if (remoteVersion !== packageVersion) { throw Error( - chalk`Publised version {yellow.bold ${version}} for ` + + chalk`Publised version {yellow.bold ${packageVersion}} for ` + `{bold ${project}} but NPM shows {yellow.bold ${remoteVersion}}` ); } + + // If we've just published a stable release, + // Update the @next tag to also point to it (so @next doens't lag behind). + if (!isPrerelease) { + await execUnlessDry( + `npm dist-tag add ${project}@${packageVersion} next` + ); + } } } catch (error) { errors.push(error.message); @@ -48,5 +70,5 @@ const push = async ({cwd, dry, version}) => { }; module.exports = async params => { - return logPromise(push(params), 'Pushing to git remote'); + return logPromise(push(params), 'Publishing packages to NPM'); };