@samitouri / QOS-React-2 / commits / f6bc05a22a

[ci] Add script to download artifact from github

Updates the forked script to download build artifacts from GitHub. Note that this PR just adds the script, it does not add it to GH actions yet. ghstack-source-id: 08b0d2f93a88d55386e43a7d1bf88a67a117e899 Pull Request resolved: https://github.com/facebook/react/pull/30376

Lauren Tan committed Jul 18, 2024 at 16:19 UTC f6bc05a22a7b3ff609d441053b73a4f7e65a38e0
1 file changed +129 -18
scripts/release/download-experimental-build-ghaction.js
+129 -18
@@ -3,10 +3,12 @@
3 'use strict';
4
5 const {join, relative} = require('path');
6 -const {getPublicPackages, handleError} = require('./utils');
6 +const {logPromise, 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');
12
13 const argv = yargs.wrap(yargs.terminalWidth()).options({
14 releaseChannel: {
@@ -21,21 +23,11 @@ const argv = yargs.wrap(yargs.terminalWidth()).options({
23 alias: 'c',
24 describe: 'Commit hash to download.',
25 requiresArg: true,
26 + demandOption: true,
27 type: 'string',
28 },
26 - skipTests: {
27 - requiresArg: false,
28 - type: 'boolean',
29 - default: false,
30 - },
31 - allowBrokenCI: {
32 - requiresArg: false,
33 - type: 'boolean',
34 - default: false,
35 - },
29 }).argv;
30
38 -// Inlined from scripts/release/download-experimental-build-commands/print-summary.js
31 function printSummary(commit) {
32 const commandPath = relative(
33 process.env.PWD,
@@ -54,17 +46,136 @@ function printSummary(commit) {
46 console.log(message.replace(/\n +/g, '\n').trim());
47 }
48
57 -const run = async () => {
58 - try {
59 - argv.cwd = join(__dirname, '..', '..');
60 - argv.packages = await getPublicPackages(true);
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
62 - console.log(argv);
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 +
165 +const main = async () => {
166 + try {
167 + await downloadBuildArtifacts(argv.commit, argv.releaseChannel);
168 printSummary(argv.commit);
169 } catch (error) {
170 handleError(error);
171 }
172 };
173
70 -run();
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 +
181 +main();