@samitouri / QOS-React / commits / 0d022861d2

[ci] Refactor download-build-artifacts

Extracts out the code to download builds from GH into its own module so that it can be reused later. ghstack-source-id: 26687db971d06339d099d1d5075825efb82cf6b8 Pull Request resolved: https://github.com/facebook/react/pull/30484

Lauren Tan committed Jul 26, 2024 at 13:19 UTC 0d022861d23124b6f16a62764e11746e73cd6caf
2 files changed +140 -126
scripts/release/download-experimental-build-ghaction.js
+4 -126
@@ -3,12 +3,13 @@
3 'use strict';
4
5 const {join, relative} = require('path');
6 -const {logPromise, handleError} = require('./utils');
6 +const {handleError} = require('./utils');
7 const yargs = require('yargs');
8 const clear = require('clear');
9 const theme = require('./theme');
10 -const {exec} = require('child-process-promise');
11 -const {existsSync} = require('fs');
10 +const {
11 + downloadBuildArtifacts,
12 +} = require('./shared-commands/download-build-artifacts-ghaction');
13
14 const argv = yargs.wrap(yargs.terminalWidth()).options({
15 releaseChannel: {
@@ -46,122 +47,6 @@ function printSummary(commit) {
47 console.log(message.replace(/\n +/g, '\n').trim());
48 }
49
49 -const OWNER = 'facebook';
50 -const REPO = 'react';
51 -const WORKFLOW_ID = 'runtime_build_and_test.yml';
52 -const GITHUB_HEADERS = `
53 - -H "Accept: application/vnd.github+json" \
54 - -H "Authorization: Bearer ${process.env.GH_TOKEN}" \
55 - -H "X-GitHub-Api-Version: 2022-11-28"`.trim();
56 -
57 -function getWorkflowId() {
58 - if (existsSync(join(__dirname, `../../.github/workflows/${WORKFLOW_ID}`))) {
59 - return WORKFLOW_ID;
60 - } else {
61 - throw new Error(
62 - `Incorrect workflow ID: .github/workflows/${WORKFLOW_ID} does not exist. Please check the name of the workflow being downloaded from.`
63 - );
64 - }
65 -}
66 -
67 -async function getWorkflowRunId(commit) {
68 - const res = await exec(
69 - `curl -L ${GITHUB_HEADERS} https://api.github.com/repos/${OWNER}/${REPO}/actions/workflows/${getWorkflowId()}/runs?head_sha=${commit}&branch=main&exclude_pull_requests=true`
70 - );
71 -
72 - const json = JSON.parse(res.stdout);
73 - let workflowRun;
74 - if (json.total_count === 1) {
75 - workflowRun = json.workflow_runs[0];
76 - } else {
77 - workflowRun = json.workflow_runs.find(
78 - run => run.head_sha === commit && run.head_branch === 'main'
79 - );
80 - }
81 -
82 - if (workflowRun == null || workflowRun.id == null) {
83 - console.log(
84 - theme`{error The workflow run for the specified commit (${commit}) could not be found.}`
85 - );
86 - process.exit(1);
87 - }
88 -
89 - return workflowRun.id;
90 -}
91 -
92 -async function getArtifact(workflowRunId, artifactName) {
93 - const res = await exec(
94 - `curl -L ${GITHUB_HEADERS} https://api.github.com/repos/${OWNER}/${REPO}/actions/runs/${workflowRunId}/artifacts?per_page=100&name=${artifactName}`
95 - );
96 -
97 - const json = JSON.parse(res.stdout);
98 - let artifact;
99 - if (json.total_count === 1) {
100 - artifact = json.artifacts[0];
101 - } else {
102 - artifact = json.artifacts.find(
103 - _artifact => _artifact.name === artifactName
104 - );
105 - }
106 -
107 - if (artifact == null) {
108 - console.log(
109 - theme`{error The specified workflow run (${workflowRunId}) does not contain any build artifacts.}`
110 - );
111 - process.exit(1);
112 - }
113 -
114 - return artifact;
115 -}
116 -
117 -async function downloadArtifactsFromGitHub(commit, releaseChannel) {
118 - const workflowRunId = await getWorkflowRunId(commit);
119 - const artifact = await getArtifact(workflowRunId, 'artifacts_combined');
120 -
121 - // Download and extract artifact
122 - const cwd = join(__dirname, '..', '..');
123 - await exec(`rm -rf ./build`, {cwd});
124 - await exec(
125 - `curl -L ${GITHUB_HEADERS} ${artifact.archive_download_url} \
126 - > a.zip && unzip a.zip -d . && rm a.zip build2.tgz && tar -xvzf build.tgz && rm build.tgz`,
127 - {
128 - cwd,
129 - }
130 - );
131 -
132 - // Copy to staging directory
133 - // TODO: Consider staging the release in a different directory from the CI
134 - // build artifacts: `./build/node_modules` -> `./staged-releases`
135 - if (!existsSync(join(cwd, 'build'))) {
136 - await exec(`mkdir ./build`, {cwd});
137 - } else {
138 - await exec(`rm -rf ./build/node_modules`, {cwd});
139 - }
140 - let sourceDir;
141 - // TODO: Rename release channel to `next`
142 - if (releaseChannel === 'stable') {
143 - sourceDir = 'oss-stable';
144 - } else if (releaseChannel === 'experimental') {
145 - sourceDir = 'oss-experimental';
146 - } else if (releaseChannel === 'rc') {
147 - sourceDir = 'oss-stable-rc';
148 - } else if (releaseChannel === 'latest') {
149 - sourceDir = 'oss-stable-semver';
150 - } else {
151 - console.error('Internal error: Invalid release channel: ' + releaseChannel);
152 - process.exit(releaseChannel);
153 - }
154 - await exec(`cp -r ./build/${sourceDir} ./build/node_modules`, {cwd});
155 -}
156 -
157 -async function downloadBuildArtifacts(commit, releaseChannel) {
158 - const label = theme`commit {commit ${commit}})`;
159 - return logPromise(
160 - downloadArtifactsFromGitHub(commit, releaseChannel),
161 - theme`Downloading artifacts from GitHub for ${label}`
162 - );
163 -}
164 -
50 const main = async () => {
51 try {
52 await downloadBuildArtifacts(argv.commit, argv.releaseChannel);
@@ -171,11 +56,4 @@ const main = async () => {
56 }
57 };
58
174 -if (process.env.GH_TOKEN == null) {
175 - console.log(
176 - theme`{error Expected GH_TOKEN to be provided as an env variable}`
177 - );
178 - process.exit(1);
179 -}
180 -
59 main();
scripts/release/shared-commands/download-build-artifacts-ghaction.js new
+136
@@ -0,0 +1,136 @@
1 +'use strict';
2 +
3 +const {join} = require('path');
4 +const theme = require('../theme');
5 +const {exec} = require('child-process-promise');
6 +const {existsSync} = require('fs');
7 +const {logPromise} = require('../utils');
8 +
9 +if (process.env.GH_TOKEN == null) {
10 + console.log(
11 + theme`{error Expected GH_TOKEN to be provided as an env variable}`
12 + );
13 + process.exit(1);
14 +}
15 +
16 +const OWNER = 'facebook';
17 +const REPO = 'react';
18 +const WORKFLOW_ID = 'runtime_build_and_test.yml';
19 +const GITHUB_HEADERS = `
20 + -H "Accept: application/vnd.github+json" \
21 + -H "Authorization: Bearer ${process.env.GH_TOKEN}" \
22 + -H "X-GitHub-Api-Version: 2022-11-28"`.trim();
23 +
24 +function getWorkflowId() {
25 + if (
26 + existsSync(join(__dirname, `../../../.github/workflows/${WORKFLOW_ID}`))
27 + ) {
28 + return WORKFLOW_ID;
29 + } else {
30 + throw new Error(
31 + `Incorrect workflow ID: .github/workflows/${WORKFLOW_ID} does not exist. Please check the name of the workflow being downloaded from.`
32 + );
33 + }
34 +}
35 +
36 +async function getWorkflowRunId(commit) {
37 + const res = await exec(
38 + `curl -L ${GITHUB_HEADERS} https://api.github.com/repos/${OWNER}/${REPO}/actions/workflows/${getWorkflowId()}/runs?head_sha=${commit}&branch=main&exclude_pull_requests=true`
39 + );
40 +
41 + const json = JSON.parse(res.stdout);
42 + let workflowRun;
43 + if (json.total_count === 1) {
44 + workflowRun = json.workflow_runs[0];
45 + } else {
46 + workflowRun = json.workflow_runs.find(
47 + run => run.head_sha === commit && run.head_branch === 'main'
48 + );
49 + }
50 +
51 + if (workflowRun == null || workflowRun.id == null) {
52 + console.log(
53 + theme`{error The workflow run for the specified commit (${commit}) could not be found.}`
54 + );
55 + process.exit(1);
56 + }
57 +
58 + return workflowRun.id;
59 +}
60 +
61 +async function getArtifact(workflowRunId, artifactName) {
62 + const res = await exec(
63 + `curl -L ${GITHUB_HEADERS} https://api.github.com/repos/${OWNER}/${REPO}/actions/runs/${workflowRunId}/artifacts?per_page=100&name=${artifactName}`
64 + );
65 +
66 + const json = JSON.parse(res.stdout);
67 + let artifact;
68 + if (json.total_count === 1) {
69 + artifact = json.artifacts[0];
70 + } else {
71 + artifact = json.artifacts.find(
72 + _artifact => _artifact.name === artifactName
73 + );
74 + }
75 +
76 + if (artifact == null) {
77 + console.log(
78 + theme`{error The specified workflow run (${workflowRunId}) does not contain any build artifacts.}`
79 + );
80 + process.exit(1);
81 + }
82 +
83 + return artifact;
84 +}
85 +
86 +async function downloadArtifactsFromGitHub(commit, releaseChannel) {
87 + const workflowRunId = await getWorkflowRunId(commit);
88 + const artifact = await getArtifact(workflowRunId, 'artifacts_combined');
89 +
90 + // Download and extract artifact
91 + const cwd = join(__dirname, '..', '..', '..');
92 + await exec(`rm -rf ./build`, {cwd});
93 + await exec(
94 + `curl -L ${GITHUB_HEADERS} ${artifact.archive_download_url} \
95 + > a.zip && unzip a.zip -d . && rm a.zip build2.tgz && tar -xvzf build.tgz && rm build.tgz`,
96 + {
97 + cwd,
98 + }
99 + );
100 +
101 + // Copy to staging directory
102 + // TODO: Consider staging the release in a different directory from the CI
103 + // build artifacts: `./build/node_modules` -> `./staged-releases`
104 + if (!existsSync(join(cwd, 'build'))) {
105 + await exec(`mkdir ./build`, {cwd});
106 + } else {
107 + await exec(`rm -rf ./build/node_modules`, {cwd});
108 + }
109 + let sourceDir;
110 + // TODO: Rename release channel to `next`
111 + if (releaseChannel === 'stable') {
112 + sourceDir = 'oss-stable';
113 + } else if (releaseChannel === 'experimental') {
114 + sourceDir = 'oss-experimental';
115 + } else if (releaseChannel === 'rc') {
116 + sourceDir = 'oss-stable-rc';
117 + } else if (releaseChannel === 'latest') {
118 + sourceDir = 'oss-stable-semver';
119 + } else {
120 + console.error('Internal error: Invalid release channel: ' + releaseChannel);
121 + process.exit(releaseChannel);
122 + }
123 + await exec(`cp -r ./build/${sourceDir} ./build/node_modules`, {cwd});
124 +}
125 +
126 +async function downloadBuildArtifacts(commit, releaseChannel) {
127 + const label = theme`commit {commit ${commit}})`;
128 + return logPromise(
129 + downloadArtifactsFromGitHub(commit, releaseChannel),
130 + theme`Downloading artifacts from GitHub for ${label}`
131 + );
132 +}
133 +
134 +module.exports = {
135 + downloadBuildArtifacts,
136 +};