Skip to content

Commit

Permalink
Next/experimental release versions include commit date (#21700)
Browse files Browse the repository at this point in the history
Change format of @next and @experimental release versions from <number>-<sha> to <number>-<sha>-<date> to make them more human readable. This format still preserves the ability for us to easily map a version number to the changes it contains, while also being able to more easily know at a glance how recent a release is.
  • Loading branch information
Brian Vaughn authored Jun 23, 2021
1 parent d7dce57 commit 355591a
Show file tree
Hide file tree
Showing 7 changed files with 62 additions and 27 deletions.
4 changes: 2 additions & 2 deletions ReactVersions.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@
//
// 18.0.0-alpha-a1c2d3e4
//
// The @experimental channel doesn't include a version, only a sha, e.g.:
// The @experimental channel doesn't include a version, only a date and a sha, e.g.:
//
// 0.0.0-experimental-a1c2d3e4
// 0.0.0-experimental-241c4467e-20200129

const ReactVersion = '18.0.0';

Expand Down
6 changes: 3 additions & 3 deletions scripts/release/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ Stable releases should always be created from the "next" channel. This encourage
To prepare a stable release, choose a "next" version and run the [`prepare-release-from-npm`](#prepare-release-from-npm) script <sup>1</sup>:

```sh
scripts/release/prepare-release-from-npm.js --version=0.0.0-241c4467e
scripts/release/prepare-release-from-npm.js --version=0.0.0-241c4467e-20200129
```

This script will prompt you to select stable version numbers for each of the packages. It will update the package JSON versions (and dependencies) based on the numbers you select.
Expand Down Expand Up @@ -165,9 +165,9 @@ This script prompts for new (stable) release versions for each public package an
"Next" releases have already been tested but it is still a good idea to **manually test and verify a release** before publishing to ensure that e.g. version numbers are correct. Upon completion, this script prints manual testing instructions.

#### Example usage
To promote the "next" release `0.0.0-241c4467e` (aka commit [241c4467e](https://github.com/facebook/react/commit/241c4467e)) to stable:
To promote the "next" release `0.0.0-241c4467e-20200129` (aka commit [241c4467e](https://github.com/facebook/react/commit/241c4467e)) to stable:
```sh
scripts/release/prepare-release-from-npm.js --version=0.0.0-241c4467e
scripts/release/prepare-release-from-npm.js --version=0.0.0-241c4467e-20200129
```

## `publish`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ const paramDefinitions = [
{
name: 'version',
type: String,
description: 'Version of published "next" release (e.g. 0.0.0-ddaf2b07c)',
description:
'Version of published "next" release (e.g. 0.0.0-0e526bcec-20210202)',
},
];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ const run = async ({cwd, packages, version}, versionsMap) => {
// (e.g. scheduler@^0.11.0 becomes scheduler@^0.12.0 when we release scheduler 0.12.0).
// Otherwise we leave the constraint alone (e.g. react@^16.0.0 doesn't change between releases).
// Note that in both cases, we must update the target package JSON,
// since "next" releases are all locked to the version (e.g. 0.0.0-ddaf2b07c).
// since "next" releases are all locked to the version (e.g. 0.0.0-0e526bcec-20210202).
if (
sourceDependencyVersion ===
sourceDependencyConstraint.replace(/^[\^\~]/, '')
Expand All @@ -69,7 +69,7 @@ const run = async ({cwd, packages, version}, versionsMap) => {
// Update all package JSON versions and their dependencies/peerDependencies.
// This must be done in a way that respects semver constraints (e.g. 16.7.0, ^16.7.0, ^16.0.0).
// To do this, we use the dependencies defined in the source package JSONs,
// because the "next" dependencies have already been flattened to an exact match (e.g. 0.0.0-ddaf2b07c).
// because the "next" dependencies have already been flattened to an exact match (e.g. 0.0.0-0e526bcec-20210202).
for (let i = 0; i < packages.length; i++) {
const packageName = packages[i];
const packageJSONPath = join(nodeModulesPath, packageName, 'package.json');
Expand Down
6 changes: 4 additions & 2 deletions scripts/release/snapshot-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const {exec, spawn} = require('child-process-promise');
const {join} = require('path');
const {readFileSync} = require('fs');
const theme = require('./theme');
const {logPromise, printDiff} = require('./utils');
const {getDateStringForCommit, logPromise, printDiff} = require('./utils');

const cwd = join(__dirname, '..', '..');

Expand Down Expand Up @@ -37,14 +37,16 @@ const run = async () => {
);
await promise;

const dateString = await getDateStringForCommit(COMMIT);

// Upgrade the above build top a known React version.
// Note that using the --local flag skips NPM checkout.
// This isn't totally necessary but is useful if we want to test an unpublished "next" build.
promise = spawn(
'node',
[
'./scripts/release/prepare-release-from-npm.js',
`--version=0.0.0-${COMMIT}`,
`--version=0.0.0-${COMMIT}-${dateString}`,
'--local',
],
defaultOptions
Expand Down
33 changes: 24 additions & 9 deletions scripts/release/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,9 @@ const execRead = async (command, options) => {
};

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}(.+)/);
// Support stable version format e.g. "0.0.0-0e526bcec-20210202"
// and experimental version format e.g. "0.0.0-experimental-0e526bcec-20210202"
const match = version.match(/0\.0\.0\-([a-z]+\-){0,1}([^-]+).+/);
if (match === null) {
throw Error(`Could not extra commit from version "${version}"`);
}
Expand All @@ -74,9 +74,10 @@ const getBuildInfo = async () => {
});
const commit = await execRead('git show -s --format=%h', {cwd});
const checksum = await getChecksumForCurrentRevision(cwd);
const dateString = await getDateStringForCommit(commit);
const version = isExperimental
? `0.0.0-experimental-${commit}`
: `0.0.0-${commit}`;
? `0.0.0-experimental-${commit}-${dateString}`
: `0.0.0-${commit}-${dateString}`;

// Only available for Circle CI builds.
// https://circleci.com/docs/2.0/env-vars/
Expand All @@ -88,8 +89,8 @@ const getBuildInfo = async () => {
join(cwd, 'packages', 'react', 'package.json')
);
const reactVersion = isExperimental
? `${packageJSON.version}-experimental-${commit}`
: `${packageJSON.version}-${commit}`;
? `${packageJSON.version}-experimental-${commit}-${dateString}`
: `${packageJSON.version}-${commit}-${dateString}`;

return {branch, buildNumber, checksum, commit, reactVersion, version};
};
Expand All @@ -103,6 +104,19 @@ const getChecksumForCurrentRevision = async cwd => {
return hashedPackages.hash.slice(0, 7);
};

const getDateStringForCommit = async commit => {
let dateString = await execRead(
`git show -s --format=%cd --date=format:%Y%m%d ${commit}`
);

// On CI environment, this string is wrapped with quotes '...'s
if (dateString.startsWith("'")) {
dateString = dateString.substr(1, 8);
}

return dateString;
};

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

Expand Down Expand Up @@ -194,10 +208,10 @@ const splitCommaParams = array => {
// This method is used by both local Node release scripts and Circle CI bash scripts.
// It updates version numbers in package JSONs (both the version field and dependencies),
// As well as the embedded renderer version in "packages/shared/ReactVersion".
// Canaries version numbers use the format of 0.0.0-<sha> to be easily recognized (e.g. 0.0.0-01974a867).
// Canaries version numbers use the format of 0.0.0-<sha>-<date> to be easily recognized (e.g. 0.0.0-01974a867-20200129).
// A separate "React version" is used for the embedded renderer version to support DevTools,
// since it needs to distinguish between different version ranges of React.
// It is based on the version of React in the local package.json (e.g. 16.12.0-01974a867).
// It is based on the version of React in the local package.json (e.g. 16.12.0-01974a867-20200129).
// Both numbers will be replaced if the "next" release is promoted to a stable release.
const updateVersionsForNext = async (cwd, reactVersion, version) => {
const isExperimental = reactVersion.includes('experimental');
Expand Down Expand Up @@ -258,6 +272,7 @@ module.exports = {
getBuildInfo,
getChecksumForCurrentRevision,
getCommitFromCurrentBuild,
getDateStringForCommit,
getPublicPackages,
handleError,
logPromise,
Expand Down
33 changes: 25 additions & 8 deletions scripts/rollup/build-all-release-channels.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,16 @@ const sha = (
spawnSync('git', ['show', '-s', '--format=%h']).stdout + ''
).trim();

let dateString = (
spawnSync('git', ['show', '-s', '--format=%cd', '--date=format:%Y%m%d', sha])
.stdout + ''
).trim();

// On CI environment, this string is wrapped with quotes '...'s
if (dateString.startsWith("'")) {
dateString = dateString.substr(1, 8);
}

if (process.env.CIRCLE_NODE_TOTAL) {
// In CI, we use multiple concurrent processes. Allocate half the processes to
// build the stable channel, and the other half for experimental. Override
Expand All @@ -32,14 +42,16 @@ if (process.env.CIRCLE_NODE_TOTAL) {
if (index < halfTotal) {
const nodeTotal = halfTotal;
const nodeIndex = index;
updateTheReactVersionThatDevToolsReads(ReactVersion + '-' + sha);
updateTheReactVersionThatDevToolsReads(
ReactVersion + '-' + sha + '-' + dateString
);
buildForChannel('stable', nodeTotal, nodeIndex);
processStable('./build');
} else {
const nodeTotal = total - halfTotal;
const nodeIndex = index - halfTotal;
updateTheReactVersionThatDevToolsReads(
ReactVersion + '-experimental-' + sha
ReactVersion + '-experimental-' + sha + '-' + dateString
);
buildForChannel('experimental', nodeTotal, nodeIndex);
processExperimental('./build');
Expand All @@ -51,12 +63,16 @@ if (process.env.CIRCLE_NODE_TOTAL) {
} else {
// Running locally, no concurrency. Move each channel's build artifacts into
// a temporary directory so that they don't conflict.
updateTheReactVersionThatDevToolsReads(ReactVersion + '-' + sha);
updateTheReactVersionThatDevToolsReads(
ReactVersion + '-' + sha + '-' + dateString
);
buildForChannel('stable', '', '');
const stableDir = tmp.dirSync().name;
crossDeviceRenameSync('./build', stableDir);
processStable(stableDir);
updateTheReactVersionThatDevToolsReads(ReactVersion + '-experimental-' + sha);
updateTheReactVersionThatDevToolsReads(
ReactVersion + '-experimental-' + sha + '-' + dateString
);
buildForChannel('experimental', '', '');
const experimentalDir = tmp.dirSync().name;
crossDeviceRenameSync('./build', experimentalDir);
Expand Down Expand Up @@ -88,13 +104,13 @@ function buildForChannel(channel, nodeTotal, nodeIndex) {

function processStable(buildDir) {
if (fs.existsSync(buildDir + '/node_modules')) {
const defaultVersionIfNotFound = '0.0.0' + '-' + sha;
const defaultVersionIfNotFound = '0.0.0' + '-' + sha + '-' + dateString;
const versionsMap = new Map();
for (const moduleName in stablePackages) {
const version = stablePackages[moduleName];
versionsMap.set(
moduleName,
version + '-' + nextChannelLabel + '-' + sha,
version + '-' + nextChannelLabel + '-' + sha + '-' + dateString,
defaultVersionIfNotFound
);
}
Expand Down Expand Up @@ -143,7 +159,8 @@ function processStable(buildDir) {

function processExperimental(buildDir, version) {
if (fs.existsSync(buildDir + '/node_modules')) {
const defaultVersionIfNotFound = '0.0.0' + '-' + 'experimental' + '-' + sha;
const defaultVersionIfNotFound =
'0.0.0' + '-' + 'experimental' + '-' + sha + '-' + dateString;
const versionsMap = new Map();
for (const moduleName in stablePackages) {
versionsMap.set(moduleName, defaultVersionIfNotFound);
Expand Down Expand Up @@ -195,7 +212,7 @@ function crossDeviceRenameSync(source, destination) {

/*
* Grabs the built packages in ${tmp_build_dir}/node_modules and updates the
* `version` key in their package.json to 0.0.0-${commitHash} for the commit
* `version` key in their package.json to 0.0.0-${date}-${commitHash} for the commit
* you're building. Also updates the dependencies and peerDependencies
* to match this version for all of the 'React' packages
* (packages available in this repo).
Expand Down

0 comments on commit 355591a

Please sign in to comment.