Migrate prepare-release-from-ci to new workflow (#20581)

* Migrate prepare-release-from-ci to new workflow

I added a `--releaseChannel (-r)` argument to script. You must choose
either "stable" or "experimental", because every build job now includes
both channels.

The prepare-release-from-npm script is unchanged since those releases
are downloaded from npm, nt CI.

(As a side note, I think we should start preparing semver releases using
the prepare-release-from-ci script, too, and get rid of
prepare-release-from-npm. I think that was a neat idea originally but
because we already run `npm pack` before storing the artifacts in CI,
there's really not much additional safety; the only safeguard it adds is
the requirement that a "next" release must have already been published.)

* Move validation to parse-params module
This commit is contained in:
Andrew Clark 2021-01-14 11:20:20 -06:00 committed by GitHub
parent 42e04b46d1
commit 98313aaa7e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 47 additions and 56 deletions

View File

@ -3,8 +3,7 @@
'use strict';
const {join} = require('path');
const {readJsonSync} = require('fs-extra');
const {getPublicPackages, handleError} = require('./utils');
const {handleError} = require('./utils');
const checkEnvironmentVariables = require('./shared-commands/check-environment-variables');
const downloadBuildArtifacts = require('./shared-commands/download-build-artifacts');
@ -26,11 +25,6 @@ const run = async () => {
await checkEnvironmentVariables(params);
await downloadBuildArtifacts(params);
const version = readJsonSync('./build/node_modules/react/package.json')
.version;
const isExperimental = version.includes('experimental');
params.packages = await getPublicPackages(isExperimental);
if (!params.skipTests) {
await testPackagingFixture(params);
await testTracingFixture(params);

View File

@ -3,65 +3,56 @@
'use strict';
const {exec} = require('child-process-promise');
const {existsSync, readdirSync} = require('fs');
const {readJsonSync} = require('fs-extra');
const {existsSync} = require('fs');
const {join} = require('path');
const {getArtifactsList, logPromise} = require('../utils');
const theme = require('../theme');
const run = async ({build, cwd}) => {
const run = async ({build, cwd, releaseChannel}) => {
const artifacts = await getArtifactsList(build);
const nodeModulesArtifact = artifacts.find(entry =>
entry.path.endsWith('node_modules.tgz')
const buildArtifacts = artifacts.find(entry =>
entry.path.endsWith('build2.tgz')
);
if (!nodeModulesArtifact) {
if (!buildArtifacts) {
console.log(
theme`{error The specified build (${build}) does not contain any build artifacts.}`
);
process.exit(1);
}
const nodeModulesURL = nodeModulesArtifact.url;
// Download and extract artifact
await exec(`rm -rf ./build2`, {cwd});
await exec(
`curl -L $(fwdproxy-config curl) ${buildArtifacts.url} | tar -xvz`,
{
cwd,
}
);
// Copy to staging directory
// TODO: Consider staging the release in a different directory from the CI
// build artifacts: `./build/node_modules` -> `./staged-releases`
if (!existsSync(join(cwd, 'build'))) {
await exec(`mkdir ./build`, {cwd});
} else {
await exec(`rm -rf ./build/node_modules`, {cwd});
}
// Download and extract artifact
await exec(`rm -rf ./build/node_modules*`, {cwd});
await exec(`curl -L ${nodeModulesURL} --output ./build/node_modules.tgz`, {
cwd,
});
await exec(`mkdir ./build/node_modules`, {cwd});
await exec(`tar zxvf ./build/node_modules.tgz -C ./build/node_modules/`, {
cwd,
});
// Unpack packages and prepare to publish
const compressedPackages = readdirSync(join(cwd, 'build/node_modules/'));
for (let i = 0; i < compressedPackages.length; i++) {
await exec(
`tar zxvf ./build/node_modules/${compressedPackages[i]} -C ./build/node_modules/`,
{cwd}
);
const packageJSON = readJsonSync(
join(cwd, `/build/node_modules/package/package.json`)
);
await exec(
`mv ./build/node_modules/package ./build/node_modules/${packageJSON.name}`,
{cwd}
);
let sourceDir;
if (releaseChannel === 'stable') {
sourceDir = 'oss-stable';
} else if (releaseChannel === 'experimental') {
sourceDir = 'oss-experimental';
} else {
console.error('Internal error: Invalid release channel: ' + releaseChannel);
process.exit(releaseChannel);
}
// Cleanup
await exec(`rm ./build/node_modules.tgz`, {cwd});
await exec(`rm ./build/node_modules/*.tgz`, {cwd});
await exec(`cp -r ./build2/${sourceDir} ./build/node_modules`, {cwd});
};
module.exports = async ({build, cwd}) => {
module.exports = async ({build, cwd, releaseChannel}) => {
return logPromise(
run({build, cwd}),
run({build, cwd, releaseChannel}),
theme`Downloading artifacts from Circle CI for build {build ${build}}`
);
};

View File

@ -5,11 +5,7 @@
const http = require('request-promise-json');
const {logPromise} = require('../utils');
const run = async useExperimentalBuild => {
const targetJobName = useExperimentalBuild
? 'process_artifacts_experimental'
: 'process_artifacts';
const run = async () => {
// https://circleci.com/docs/api/#recent-builds-for-a-project-branch
const metadataURL = `https://circleci.com/api/v1.1/project/github/facebook/react/tree/master`;
const metadata = await http.get(metadataURL, true);
@ -17,7 +13,7 @@ const run = async useExperimentalBuild => {
entry =>
entry.branch === 'master' &&
entry.status === 'success' &&
entry.workflows.job_name === targetJobName
entry.workflows.job_name === 'process_artifacts_combined'
).build_num;
return build;

View File

@ -17,10 +17,24 @@ const paramDefinitions = [
description: 'Skip automated fixture tests.',
defaultValue: false,
},
{
name: 'releaseChannel',
alias: 'r',
type: String,
description: 'Release channel (stable or experimental)',
},
];
module.exports = () => {
const params = commandLineArgs(paramDefinitions);
const channel = params.releaseChannel;
if (channel !== 'experimental' && channel !== 'stable') {
console.error(
`Invalid release channel (-r) "${channel}". Must be "stable" or "experimental".`
);
process.exit(1);
}
return params;
};

View File

@ -41,11 +41,7 @@ const getArtifactsList = async buildID => {
);
process.exit(1);
}
const artifactsJobName = buildMetadata.workflows.job_name.endsWith(
'_experimental'
)
? 'process_artifacts_experimental'
: 'process_artifacts';
const artifactsJobName = 'process_artifacts_combined';
const workflowID = buildMetadata.workflows.workflow_id;
const workflowMetadataURL = `https://circleci.com/api/v2/workflow/${workflowID}/job?circle-token=${process.env.CIRCLE_CI_API_TOKEN}`;
const workflowMetadata = await http.get(workflowMetadataURL, true);