[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
+};