@samitouri / QOS-React-2 / commits / 98313aaa7e

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

Andrew Clark committed Jan 14, 2021 at 11:20 UTC 98313aaa7ea58d49eb30ecffc0f9eb6fc1ef467a
5 files changed +47 -56
scripts/release/prepare-release-from-ci.js
+1 -7
@@ -3,8 +3,7 @@
3 'use strict';
4
5 const {join} = require('path');
6 -const {readJsonSync} = require('fs-extra');
7 -const {getPublicPackages, handleError} = require('./utils');
6 +const {handleError} = require('./utils');
7
8 const checkEnvironmentVariables = require('./shared-commands/check-environment-variables');
9 const downloadBuildArtifacts = require('./shared-commands/download-build-artifacts');
@@ -26,11 +25,6 @@ const run = async () => {
25 await checkEnvironmentVariables(params);
26 await downloadBuildArtifacts(params);
27
29 - const version = readJsonSync('./build/node_modules/react/package.json')
30 - .version;
31 - const isExperimental = version.includes('experimental');
32 - params.packages = await getPublicPackages(isExperimental);
33 -
28 if (!params.skipTests) {
29 await testPackagingFixture(params);
30 await testTracingFixture(params);
scripts/release/shared-commands/download-build-artifacts.js
+29 -38
@@ -3,65 +3,56 @@
3 'use strict';
4
5 const {exec} = require('child-process-promise');
6 -const {existsSync, readdirSync} = require('fs');
7 -const {readJsonSync} = require('fs-extra');
6 +const {existsSync} = require('fs');
7 const {join} = require('path');
8 const {getArtifactsList, logPromise} = require('../utils');
9 const theme = require('../theme');
10
12 -const run = async ({build, cwd}) => {
11 +const run = async ({build, cwd, releaseChannel}) => {
12 const artifacts = await getArtifactsList(build);
14 - const nodeModulesArtifact = artifacts.find(entry =>
15 - entry.path.endsWith('node_modules.tgz')
13 + const buildArtifacts = artifacts.find(entry =>
14 + entry.path.endsWith('build2.tgz')
15 );
16
18 - if (!nodeModulesArtifact) {
17 + if (!buildArtifacts) {
18 console.log(
19 theme`{error The specified build (${build}) does not contain any build artifacts.}`
20 );
21 process.exit(1);
22 }
23
25 - const nodeModulesURL = nodeModulesArtifact.url;
24 + // Download and extract artifact
25 + await exec(`rm -rf ./build2`, {cwd});
26 + await exec(
27 + `curl -L $(fwdproxy-config curl) ${buildArtifacts.url} | tar -xvz`,
28 + {
29 + cwd,
30 + }
31 + );
32
33 + // Copy to staging directory
34 + // TODO: Consider staging the release in a different directory from the CI
35 + // build artifacts: `./build/node_modules` -> `./staged-releases`
36 if (!existsSync(join(cwd, 'build'))) {
37 await exec(`mkdir ./build`, {cwd});
38 + } else {
39 + await exec(`rm -rf ./build/node_modules`, {cwd});
40 }
30 -
31 - // Download and extract artifact
32 - await exec(`rm -rf ./build/node_modules*`, {cwd});
33 - await exec(`curl -L ${nodeModulesURL} --output ./build/node_modules.tgz`, {
34 - cwd,
35 - });
36 - await exec(`mkdir ./build/node_modules`, {cwd});
37 - await exec(`tar zxvf ./build/node_modules.tgz -C ./build/node_modules/`, {
38 - cwd,
39 - });
40 -
41 - // Unpack packages and prepare to publish
42 - const compressedPackages = readdirSync(join(cwd, 'build/node_modules/'));
43 - for (let i = 0; i < compressedPackages.length; i++) {
44 - await exec(
45 - `tar zxvf ./build/node_modules/${compressedPackages[i]} -C ./build/node_modules/`,
46 - {cwd}
47 - );
48 - const packageJSON = readJsonSync(
49 - join(cwd, `/build/node_modules/package/package.json`)
50 - );
51 - await exec(
52 - `mv ./build/node_modules/package ./build/node_modules/${packageJSON.name}`,
53 - {cwd}
54 - );
41 + let sourceDir;
42 + if (releaseChannel === 'stable') {
43 + sourceDir = 'oss-stable';
44 + } else if (releaseChannel === 'experimental') {
45 + sourceDir = 'oss-experimental';
46 + } else {
47 + console.error('Internal error: Invalid release channel: ' + releaseChannel);
48 + process.exit(releaseChannel);
49 }
56 -
57 - // Cleanup
58 - await exec(`rm ./build/node_modules.tgz`, {cwd});
59 - await exec(`rm ./build/node_modules/*.tgz`, {cwd});
50 + await exec(`cp -r ./build2/${sourceDir} ./build/node_modules`, {cwd});
51 };
52
62 -module.exports = async ({build, cwd}) => {
53 +module.exports = async ({build, cwd, releaseChannel}) => {
54 return logPromise(
64 - run({build, cwd}),
55 + run({build, cwd, releaseChannel}),
56 theme`Downloading artifacts from Circle CI for build {build ${build}}`
57 );
58 };
scripts/release/shared-commands/get-latest-master-build-number.js
+2 -6
@@ -5,11 +5,7 @@
5 const http = require('request-promise-json');
6 const {logPromise} = require('../utils');
7
8 -const run = async useExperimentalBuild => {
9 - const targetJobName = useExperimentalBuild
10 - ? 'process_artifacts_experimental'
11 - : 'process_artifacts';
12 -
8 +const run = async () => {
9 // https://circleci.com/docs/api/#recent-builds-for-a-project-branch
10 const metadataURL = `https://circleci.com/api/v1.1/project/github/facebook/react/tree/master`;
11 const metadata = await http.get(metadataURL, true);
@@ -17,7 +13,7 @@ const run = async useExperimentalBuild => {
13 entry =>
14 entry.branch === 'master' &&
15 entry.status === 'success' &&
20 - entry.workflows.job_name === targetJobName
16 + entry.workflows.job_name === 'process_artifacts_combined'
17 ).build_num;
18
19 return build;
scripts/release/shared-commands/parse-params.js
+14
@@ -17,10 +17,24 @@ const paramDefinitions = [
17 description: 'Skip automated fixture tests.',
18 defaultValue: false,
19 },
20 + {
21 + name: 'releaseChannel',
22 + alias: 'r',
23 + type: String,
24 + description: 'Release channel (stable or experimental)',
25 + },
26 ];
27
28 module.exports = () => {
29 const params = commandLineArgs(paramDefinitions);
30
31 + const channel = params.releaseChannel;
32 + if (channel !== 'experimental' && channel !== 'stable') {
33 + console.error(
34 + `Invalid release channel (-r) "${channel}". Must be "stable" or "experimental".`
35 + );
36 + process.exit(1);
37 + }
38 +
39 return params;
40 };
scripts/release/utils.js
+1 -5
@@ -41,11 +41,7 @@ const getArtifactsList = async buildID => {
41 );
42 process.exit(1);
43 }
44 - const artifactsJobName = buildMetadata.workflows.job_name.endsWith(
45 - '_experimental'
46 - )
47 - ? 'process_artifacts_experimental'
48 - : 'process_artifacts';
44 + const artifactsJobName = 'process_artifacts_combined';
45 const workflowID = buildMetadata.workflows.workflow_id;
46 const workflowMetadataURL = `https://circleci.com/api/v2/workflow/${workflowID}/job?circle-token=${process.env.CIRCLE_CI_API_TOKEN}`;
47 const workflowMetadata = await http.get(workflowMetadataURL, true);