Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Misc small adjustments to build scripts #20723

Merged
merged 1 commit into from
Feb 3, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'Once 4783999 lands you can alternatively read from build2/COMMIT_SHA

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh nice. Fair.

'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