Skip to content

Commit

Permalink
Remove --build flag from release scripts (facebook#20723)
Browse files Browse the repository at this point in the history
Also update instructions to match recent script changes.

Also add reproducible commit SHA to post download instructions to support publishing the Firefox DevTools extension.
  • Loading branch information
Brian Vaughn authored and koto committed Jun 15, 2021
1 parent 27b13e4 commit a3b1afd
Show file tree
Hide file tree
Showing 9 changed files with 77 additions and 84 deletions.
30 changes: 8 additions & 22 deletions scripts/release/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,35 +30,27 @@ The sections below include meaningful `--tags` in the instructions. However, kee

To prepare a build for a particular commit:
1. Choose a commit from [the commit log](https://github.com/facebook/react/commits/master).
2. Click the "“✓" icon and click the Circle CI "Details" link.
3. Select the `process_artifacts ` job (**not** the `process_artifacts_experimental`job; see the next section)
* If it's still pending, you'll need to wait for it to finish. <sup>1</sup>
4. Copy the build ID from the URL
* e.g. the build ID for commit [e5d06e34b](https://github.com/facebook/react/commit/e5d06e34b) is [**124756**](https://circleci.com/gh/facebook/react/124756)
5. Run the [`prepare-release-from-ci`](#prepare-release-from-ci) script with the build ID <sup>2</sup> you found:
2. Copy the SHA (by clicking the 📋 button)
5. Run the [`prepare-release-from-ci`](#prepare-release-from-ci) script with the SHA <sup>1</sup> you found:
```sh
scripts/release/prepare-release-from-ci.js --build=124756
scripts/release/prepare-release-from-ci.js -r stable --commit=0e526bc
```

Once the build has been checked out and tested locally, you're ready to publish it:
```sh
scripts/release/publish.js --tags next
```

If the OTP code expires while publishing, re-run this command and answer "y" to the questions about whether it was expected for already published packages.

<sup>1: This is the most awkward part of cutting a release right now. We have plans to improve it.</sup><br/>
<sup>2: You can omit the `build` param if you just want to release the latest commit as to "next".</sup>
<sup>1: You can omit the `commit` param if you just want to release the latest commit as to "next".</sup>

## Publishing an Experimental Release

Experimental releases are special because they have additional features turned on.

The steps for publishing an experimental release are almost the same as for publishing a "next" release, except in step 3 you should choose the `process_artifacts_experimental ` job (instead of `process_artifacts`) <sup>1</sup>
The steps for publishing an experimental release are almost the same as for publishing a "next" release except for the release channel (`-r`) flag.

For example, the experimental build ID for commit [e5d06e34b](https://github.com/facebook/react/commit/e5d06e34b) is [**124763**](https://circleci.com/gh/facebook/react/124763):
```sh
scripts/release/prepare-release-from-ci.js --build=124763
scripts/release/prepare-release-from-ci.js -r experimental --commit=0e526bc
```

Once the build has been checked out and tested locally, you're ready to publish it. When publishing an experimental release, use the `experimental` tag:
Expand All @@ -67,10 +59,6 @@ Once the build has been checked out and tested locally, you're ready to publish
scripts/release/publish.js --tags experimental
```

If the OTP code expires while publishing, re-run this command and answer "y" to the questions about whether it was expected for already published packages.

<sup>1: We have plans to make this less awkward. Ideally these releases will be published by a cron job.</sup>

## Publishing a Stable Release

Stable releases should always be created from the "next" channel. This encourages better testing of the actual release artifacts and reduces the chance of unintended changes accidentally being included in a stable release.
Expand All @@ -92,8 +80,6 @@ scripts/release/publish.js --tags latest
scripts/release/publish.js --tags latest next
```

If the OTP code expires while publishing, re-run this command and answer "y" to the questions about whether it was expected for already published packages.

After successfully publishing the release, follow the on-screen instructions to ensure that all of the appropriate post-release steps are executed.

<sup>1: You can omit the `version` param if you just want to promote the latest "next" candidate to stable.</sup>
Expand Down Expand Up @@ -145,9 +131,9 @@ Downloads build artifacts from Circle CI in preparation to be published to NPM a
All artifacts built by Circle CI have already been unit-tested (both source and bundles) but these candidates should **always be manually tested** before being published. Upon completion, this script prints manual testing instructions.

#### Example usage
To prepare the artifacts created by [Circle CI build 124756](https://circleci.com/gh/facebook/react/124756#artifacts/containers/0) you would run:
To prepare the artifacts created by Circle CI for commit [0e526bc](https://github.com/facebook/react/commit/0e526bc) you would run:
```sh
scripts/release/prepare-release-from-ci.js --build=124756
scripts/release/prepare-release-from-ci.js --commit=0e526bc -r stable
```

## `prepare-release-from-npm`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,23 @@
const clear = require('clear');
const {join, relative} = require('path');
const theme = require('../theme');
const {getCommitFromCurrentBuild} = require('../utils');

module.exports = ({build}) => {
module.exports = async () => {
const commandPath = relative(
process.env.PWD,
join(__dirname, '../download-experimental-build.js')
);

clear();

const commit = await getCommitFromCurrentBuild();

const message = theme`
{caution An experimental build has been downloaded!}
You can download this build again by running:
{path ${commandPath}} --build={build ${build}}
{path ${commandPath}} --commit={commit ${commit}}
`;

console.log(message.replace(/\n +/g, '\n').trim());
Expand Down
3 changes: 3 additions & 0 deletions scripts/release/download-experimental-build.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ const printSummary = require('./download-experimental-build-commands/print-summa
const run = async () => {
try {
addDefaultParamValue('-r', '--releaseChannel', 'experimental');

// Default to the latest commit in master.
// If this is a reproducible build (e.g. Firefox tester) a --commit will be specified.
addDefaultParamValue(null, '--commit', 'master');

const params = await parseParams();
Expand Down
9 changes: 3 additions & 6 deletions scripts/release/prepare-release-from-ci.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,22 @@
'use strict';

const {join} = require('path');
const {handleError} = require('./utils');
const {addDefaultParamValue, handleError} = require('./utils');

const checkEnvironmentVariables = require('./shared-commands/check-environment-variables');
const downloadBuildArtifacts = require('./shared-commands/download-build-artifacts');
const getLatestMasterBuildNumber = require('./shared-commands/get-latest-master-build-number');
const parseParams = require('./shared-commands/parse-params');
const printPrereleaseSummary = require('./shared-commands/print-prerelease-summary');
const testPackagingFixture = require('./shared-commands/test-packaging-fixture');
const testTracingFixture = require('./shared-commands/test-tracing-fixture');

const run = async () => {
try {
addDefaultParamValue(null, '--commit', 'master');

const params = await parseParams();
params.cwd = join(__dirname, '..', '..');

if (!params.build) {
params.build = await getLatestMasterBuildNumber(false);
}

await checkEnvironmentVariables(params);
await downloadBuildArtifacts(params);

Expand Down
4 changes: 2 additions & 2 deletions scripts/release/shared-commands/download-build-artifacts.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,9 @@ const run = async ({build, cwd, releaseChannel}) => {
await exec(`cp -r ./build2/${sourceDir} ./build/node_modules`, {cwd});
};

module.exports = async ({build, cwd, releaseChannel}) => {
module.exports = async ({build, commit, cwd, releaseChannel}) => {
return logPromise(
run({build, cwd, releaseChannel}),
theme`Downloading artifacts from Circle CI for build {build ${build}}`
theme`Downloading artifacts from Circle CI for commit {commit ${commit}} (build {build ${build}})`
);
};
27 changes: 0 additions & 27 deletions scripts/release/shared-commands/get-latest-master-build-number.js

This file was deleted.

38 changes: 14 additions & 24 deletions scripts/release/shared-commands/parse-params.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,9 @@

const commandLineArgs = require('command-line-args');
const getBuildIdForCommit = require('../get-build-id-for-commit');
const theme = require('../theme');

const paramDefinitions = [
{
name: 'build',
type: Number,
description:
'Circle CI build identifier (e.g. https://circleci.com/gh/facebook/react/<build>)',
defaultValue: null,
},
{
name: 'commit',
type: String,
Expand All @@ -37,29 +31,25 @@ const paramDefinitions = [
module.exports = async () => {
const params = commandLineArgs(paramDefinitions);

if (params.build !== null) {
// TODO: Should we just remove the `build` param? Seems like `commit` is a
// sufficient replacement.
} else {
if (params.commit === null) {
console.error('Must provide either `build` or `commit`.');
process.exit(1);
}
try {
params.build = await getBuildIdForCommit(params.commit);
} catch (error) {
console.error(error.message);
process.exit(1);
}
}

const channel = params.releaseChannel;
if (channel !== 'experimental' && channel !== 'stable') {
console.error(
`Invalid release channel (-r) "${channel}". Must be "stable" or "experimental".`
theme.error`Invalid release channel (-r) "${channel}". Must be "stable" or "experimental".`
);
process.exit(1);
}

if (params.commit === null) {
console.error(theme.error`No --commit param specified.`);
process.exit(1);
}

try {
params.build = await getBuildIdForCommit(params.commit);
} catch (error) {
console.error(theme.error(error));
process.exit(1);
}

return params;
};
1 change: 1 addition & 0 deletions scripts/release/theme.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ theme.package = theme.hex(colors.green);
theme.version = theme.hex(colors.yellow);
theme.tag = theme.hex(colors.yellow);
theme.build = theme.hex(colors.yellow);
theme.commit = theme.hex(colors.yellow);
theme.error = theme.hex(colors.red).bold;
theme.dimmed = theme.hex(colors.gray);
theme.caution = theme.hex(colors.red).bold;
Expand Down
42 changes: 41 additions & 1 deletion scripts/release/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
const {exec} = require('child-process-promise');
const {createPatch} = require('diff');
const {hashElement} = require('folder-hash');
const {readFileSync, writeFileSync} = require('fs');
const {existsSync, readFileSync, writeFileSync} = require('fs');
const {readJson, writeJson} = require('fs-extra');
const http = require('request-promise-json');
const logUpdate = require('log-update');
Expand Down Expand Up @@ -47,6 +47,16 @@ const execRead = async (command, options) => {
return stdout.trim();
};

const extractCommitFromVersionNumber = version => {
// Support stable version format e.g. "0.0.0-0e526bcec"
// and experimental version format e.g. "0.0.0-experimental-0e526bcec"
const match = version.match(/0\.0\.0\-([a-z]+\-){0,1}(.+)/);
if (match === null) {
throw Error(`Could not extra commit from version "${version}"`);
}
return match[2];
};

const getArtifactsList = async buildID => {
const buildMetadataURL = `https://circleci.com/api/v1.1/project/github/facebook/react/${buildID}?circle-token=${process.env.CIRCLE_CI_API_TOKEN}`;
const buildMetadata = await http.get(buildMetadataURL, true);
Expand Down Expand Up @@ -115,6 +125,35 @@ const getChecksumForCurrentRevision = async cwd => {
return hashedPackages.hash.slice(0, 7);
};

const getCommitFromCurrentBuild = async () => {
const cwd = join(__dirname, '..', '..');

// If this build includes a build-info.json file, extract the commit from it.
// Otherwise fall back to parsing from the package version number.
// This is important to make the build reproducible (e.g. by Mozilla reviewers).
const buildInfoJSON = join(
cwd,
'build2',
'oss-experimental',
'react',
'build-info.json'
);
if (existsSync(buildInfoJSON)) {
const buildInfo = await readJson(buildInfoJSON);
return buildInfo.commit;
} else {
const packageJSON = join(
cwd,
'build2',
'oss-experimental',
'react',
'package.json'
);
const {version} = await readJson(packageJSON);
return extractCommitFromVersionNumber(version);
}
};

const getPublicPackages = isExperimental => {
if (isExperimental) {
return [
Expand Down Expand Up @@ -270,6 +309,7 @@ module.exports = {
getArtifactsList,
getBuildInfo,
getChecksumForCurrentRevision,
getCommitFromCurrentBuild,
getPublicPackages,
handleError,
logPromise,
Expand Down

0 comments on commit a3b1afd

Please sign in to comment.