Skip to content

Commit 09d0422

Browse files
author
Riccardo Cipolleschi
committed
Move utilities to testing-utils and circleci to a separate file
1 parent f9a0a5c commit 09d0422

File tree

3 files changed

+203
-197
lines changed

3 files changed

+203
-197
lines changed

scripts/circle-ci-artifacts-utils.js

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
#!/usr/bin/env node
2+
/**
3+
* Copyright (c) Meta Platforms, Inc. and affiliates.
4+
*
5+
* This source code is licensed under the MIT license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*
8+
* @format
9+
*/
10+
11+
'use strict';
12+
13+
const {exec} = require('shelljs');
14+
15+
const util = require('util');
16+
const asyncRequest = require('request');
17+
const request = util.promisify(asyncRequest);
18+
19+
let circleCIHeaders;
20+
let jobs;
21+
let baseTemporaryPath;
22+
23+
async function initialize(circleCIToken, baseTempPath, branchName) {
24+
console.info('Getting CircleCI infoes');
25+
circleCIHeaders = {'Circle-Token': circleCIToken};
26+
baseTemporaryPath = baseTempPath;
27+
exec(`mkdir -p ${baseTemporaryPath}`);
28+
const pipeline = await _getLastCircleCIPipelineID(branchName);
29+
const packageAndReleaseWorkflow = await _getPackageAndReleaseWorkflow(
30+
pipeline.id,
31+
);
32+
_throwIfPendingOrUnsuccessfulWorkflow(packageAndReleaseWorkflow);
33+
const testsWorkflow = await _getTestsWorkflow(pipeline.id);
34+
_throwIfPendingOrUnsuccessfulWorkflow(testsWorkflow);
35+
const jobsPromises = [
36+
_getCircleCIJobs(packageAndReleaseWorkflow.id),
37+
_getCircleCIJobs(testsWorkflow.id),
38+
];
39+
40+
const jobsResults = await Promise.all(jobsPromises);
41+
42+
jobs = jobsResults.flatMap(jobs => jobs);
43+
}
44+
45+
function baseTmpPath() {
46+
return baseTemporaryPath;
47+
}
48+
49+
async function _throwIfPendingOrUnsuccessfulWorkflow(workflow) {
50+
if (workflow.status !== 'success') {
51+
throw new Error(
52+
`The ${workflow.name} workflow status is ${workflow.status}. Please, wait for it to be finished before start testing or fix it`,
53+
);
54+
}
55+
}
56+
57+
async function _getLastCircleCIPipelineID(branchName) {
58+
const options = {
59+
method: 'GET',
60+
url: 'https://circleci.com/api/v2/project/gh/facebook/react-native/pipeline',
61+
qs: {
62+
branch: branchName,
63+
},
64+
headers: circleCIHeaders,
65+
};
66+
67+
const response = await request(options);
68+
if (response.error) {
69+
throw new Error(error);
70+
}
71+
72+
const lastPipeline = JSON.parse(response.body).items[0];
73+
return {id: lastPipeline.id, number: lastPipeline.number};
74+
}
75+
76+
async function _getSpecificWorkflow(pipelineId, workflowName) {
77+
const options = {
78+
method: 'GET',
79+
url: `https://circleci.com/api/v2/pipeline/${pipelineId}/workflow`,
80+
headers: circleCIHeaders,
81+
};
82+
const response = await request(options);
83+
if (response.error) {
84+
throw new Error(error);
85+
}
86+
87+
const body = JSON.parse(response.body);
88+
return body.items.find(workflow => workflow.name === workflowName);
89+
}
90+
91+
async function _getPackageAndReleaseWorkflow(pipelineId) {
92+
return _getSpecificWorkflow(
93+
pipelineId,
94+
'package_and_publish_release_dryrun',
95+
);
96+
}
97+
98+
async function _getTestsWorkflow(pipelineId) {
99+
return _getSpecificWorkflow(pipelineId, 'tests');
100+
}
101+
102+
async function _getCircleCIJobs(workflowId) {
103+
const options = {
104+
method: 'GET',
105+
url: `https://circleci.com/api/v2/workflow/${workflowId}/job`,
106+
headers: circleCIHeaders,
107+
};
108+
const response = await request(options);
109+
if (response.error) {
110+
throw new Error(error);
111+
}
112+
113+
const body = JSON.parse(response.body);
114+
return body.items;
115+
}
116+
117+
async function _getJobsArtifacts(jobNumber) {
118+
const options = {
119+
method: 'GET',
120+
url: `https://circleci.com/api/v2/project/gh/facebook/react-native/${jobNumber}/artifacts`,
121+
headers: circleCIHeaders,
122+
};
123+
const response = await request(options);
124+
if (response.error) {
125+
throw new Error(error);
126+
}
127+
128+
const body = JSON.parse(response.body);
129+
return body.items;
130+
}
131+
132+
async function _findUrlForJob(jobName, artifactPath) {
133+
const job = jobs.find(j => j.name === jobName);
134+
const artifacts = await _getJobsArtifacts(job.job_number);
135+
return artifacts.find(artifact => artifact.path.indexOf(artifactPath) > -1)
136+
.url;
137+
}
138+
139+
async function artifactURLHermesDebug() {
140+
return _findUrlForJob(
141+
'build_hermes_macos-Debug',
142+
'hermes-ios-debug.tar.gz',
143+
);
144+
}
145+
146+
async function artifactURLForMavenLocal() {
147+
return _findUrlForJob(
148+
'build_and_publish_npm_package-2',
149+
'maven-local.zip',
150+
);
151+
}
152+
153+
async function artifactURLForHermesRNTesterAPK() {
154+
const emulatorArch = exec('adb shell getprop ro.product.cpu.abi').trim();
155+
return _findUrlForJob(
156+
'test_android',
157+
`rntester-apk/hermes/release/app-hermes-${emulatorArch}-release.apk`,
158+
);
159+
}
160+
161+
async function artifactURLForJSCRNTesterAPK() {
162+
return _findUrlForJob(
163+
'test_android',
164+
'rntester-apk/jsc/release/app-jsc-arm64-v8a-release.apk',
165+
);
166+
}
167+
168+
function downloadArtifact(artifactURL, destination) {
169+
exec(`rm -rf ${destination}`);
170+
exec(`curl ${artifactURL} -Lo ${destination}`);
171+
}
172+
173+
174+
module.exports = {
175+
initialize,
176+
downloadArtifact,
177+
artifactURLForJSCRNTesterAPK,
178+
artifactURLForHermesRNTesterAPK,
179+
artifactURLForMavenLocal,
180+
artifactURLHermesDebug,
181+
baseTmpPath,
182+
};

scripts/test-e2e-local.js

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
*/
1818

1919
const {exec, pushd, popd, pwd, cd, cp} = require('shelljs');
20-
const updateTemplatePackage = require('../scripts/update-template-package');
20+
const updateTemplatePackage = require('./update-template-package');
2121
const yargs = require('yargs');
2222
const path = require('path');
2323
const fs = require('fs');
@@ -84,7 +84,7 @@ async function testRNTesterIOS(circleCIArtifacts, onReleaseBranch) {
8484
if (argv.hermes && circleCIArtifacts != null) {
8585
const hermesURL = await circleCIArtifacts.artifactURLHermesDebug();
8686
const hermesPath = path.join(
87-
circleCIArtifacts.baseTmpPath,
87+
circleCIArtifacts.baseTmpPath(),
8888
'hermes-ios-debug.tar.gz',
8989
);
9090
// download hermes source code from manifold
@@ -129,7 +129,7 @@ async function testRNTesterAndroid(circleCIArtifacts) {
129129

130130
if (circleCIArtifacts != null) {
131131
const downloadPath = path.join(
132-
circleCIArtifacts.baseTmpPath,
132+
circleCIArtifacts.baseTmpPath(),
133133
'rntester.apk',
134134
);
135135

@@ -208,11 +208,10 @@ async function testRNTestProject(circleCIArtifacts) {
208208
const repoRoot = pwd();
209209
const reactNativePackagePath = `${repoRoot}/packages/react-native`;
210210
const localNodeTGZPath = `${reactNativePackagePath}/react-native-${releaseVersion}.tgz`;
211-
console.log(`The final path for react native archive is: ${localNodeTGZPath}`);
212211

213212
const mavenLocalPath =
214213
circleCIArtifacts != null
215-
? path.join(circleCIArtifacts.baseTmpPath, 'maven-local.zip')
214+
? path.join(circleCIArtifacts.baseTmpPath(), 'maven-local.zip')
216215
: '/private/tmp/maven-local';
217216
const hermesPath = await prepareArtifacts(
218217
circleCIArtifacts,
@@ -228,7 +227,18 @@ async function testRNTestProject(circleCIArtifacts) {
228227
});
229228

230229
// create locally the node module
231-
exec('npm pack', {cwd: reactNativePackagePath});
230+
exec('npm pack --pack-destination ', {cwd: reactNativePackagePath});
231+
232+
// node pack does not creates a version of React Native with the right name on main.
233+
// Let's add some defensive programming checks:
234+
if (!fs.existsSync(localNodeTGZPath)) {
235+
const tarfile = fs.readdirSync(reactNativePackagePath)
236+
.find(name => name.startsWith('react-native-') && name.endsWith('.tgz'));
237+
if (!tarfile) {
238+
throw new Error("Couldn't find a zipped version of react-native");
239+
}
240+
exec(`cp ${path.join(reactNativePackagePath, tarfile)} ${localNodeTGZPath}`);
241+
}
232242

233243
pushd('/tmp/');
234244
// // need to avoid the pod install step - we'll do it later

0 commit comments

Comments
 (0)