Next/experimental release versions include commit date (#21700)

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.
This commit is contained in:
Brian Vaughn 2021-06-23 13:50:09 -04:00 committed by GitHub
parent d7dce572c7
commit 355591add4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 62 additions and 27 deletions

View File

@ -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';

View File

@ -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.
@ -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`

View File

@ -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)',
},
];

View File

@ -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(/^[\^\~]/, '')
@ -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');

View File

@ -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, '..', '..');
@ -37,6 +37,8 @@ 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.
@ -44,7 +46,7 @@ const run = async () => {
'node',
[
'./scripts/release/prepare-release-from-npm.js',
`--version=0.0.0-${COMMIT}`,
`--version=0.0.0-${COMMIT}-${dateString}`,
'--local',
],
defaultOptions

View File

@ -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}"`);
}
@ -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/
@ -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};
};
@ -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, '..', '..');
@ -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');
@ -258,6 +272,7 @@ module.exports = {
getBuildInfo,
getChecksumForCurrentRevision,
getCommitFromCurrentBuild,
getDateStringForCommit,
getPublicPackages,
handleError,
logPromise,

View File

@ -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
@ -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');
@ -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);
@ -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
);
}
@ -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);
@ -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).