Skip to content

Commit d1b4bcd

Browse files
Saadnajmiclaude
andcommitted
ci: add SPM build CI jobs and visionOS platform support
Add a new reusable workflow (microsoft-build-spm.yml) that tests SPM builds for ios, macos, and visionos on every PR. Wire it into the PR gate job in microsoft-pr.yml. Also add visionos/visionos-simulator as supported platforms in the ios-prebuild CLI so CI and local builds can target visionOS. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 410c01e commit d1b4bcd

File tree

6 files changed

+182
-15
lines changed

6 files changed

+182
-15
lines changed
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
name: Build SPM
2+
3+
on:
4+
workflow_call:
5+
6+
jobs:
7+
build-hermes:
8+
name: "Build Hermes"
9+
runs-on: macos-15
10+
timeout-minutes: 90
11+
steps:
12+
- uses: actions/checkout@v4
13+
with:
14+
filter: blob:none
15+
fetch-depth: 0
16+
17+
- name: Setup toolchain
18+
uses: ./.github/actions/microsoft-setup-toolchain
19+
with:
20+
node-version: '22'
21+
platform: macos
22+
23+
- name: Install npm dependencies
24+
run: yarn install
25+
26+
- name: Build Hermes from source
27+
working-directory: packages/react-native
28+
run: node scripts/ios-prebuild.js -s -f Debug
29+
30+
- name: Upload Hermes artifacts
31+
uses: actions/upload-artifact@v4
32+
with:
33+
name: hermes-artifacts
34+
path: packages/react-native/.build/artifacts/hermes
35+
retention-days: 1
36+
37+
build-spm:
38+
name: "${{ matrix.platform }}"
39+
needs: build-hermes
40+
runs-on: macos-26
41+
timeout-minutes: 60
42+
strategy:
43+
fail-fast: false
44+
matrix:
45+
platform: [ios, macos, visionos]
46+
steps:
47+
- uses: actions/checkout@v4
48+
with:
49+
filter: blob:none
50+
fetch-depth: 0
51+
52+
- name: Setup toolchain
53+
uses: ./.github/actions/microsoft-setup-toolchain
54+
with:
55+
node-version: '22'
56+
platform: ${{ matrix.platform }}
57+
58+
- name: Install npm dependencies
59+
run: yarn install
60+
61+
- name: Download Hermes artifacts
62+
uses: actions/download-artifact@v4
63+
with:
64+
name: hermes-artifacts
65+
path: packages/react-native/.build/artifacts/hermes
66+
67+
- name: Setup SPM workspace (using prebuilt Hermes)
68+
working-directory: packages/react-native
69+
run: node scripts/ios-prebuild.js -s -f Debug
70+
71+
- name: Build SPM (${{ matrix.platform }})
72+
working-directory: packages/react-native
73+
run: node scripts/ios-prebuild.js -b -f Debug -p ${{ matrix.platform }}

.github/workflows/microsoft-pr.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,11 @@ jobs:
132132
permissions: {}
133133
uses: ./.github/workflows/microsoft-build-rntester.yml
134134

135+
build-spm:
136+
name: "Build SPM"
137+
permissions: {}
138+
uses: ./.github/workflows/microsoft-build-spm.yml
139+
135140
test-react-native-macos-init:
136141
name: "Test react-native-macos init"
137142
permissions: {}
@@ -156,6 +161,7 @@ jobs:
156161
- yarn-constraints
157162
- javascript-tests
158163
- build-rntester
164+
- build-spm
159165
- test-react-native-macos-init
160166
# - react-native-test-app-integration
161167
steps:

packages/react-native/scripts/ios-prebuild/cli.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,19 @@ const platforms /*: $ReadOnlyArray<Platform> */ = [
1919
'ios-simulator',
2020
'macos', // [macOS]
2121
'mac-catalyst',
22+
'visionos', // [macOS]
23+
'visionos-simulator', // [macOS]
2224
];
2325

2426
// CI can't use commas in cache keys, so 'macOS,variant=Mac Catalyst' was creating troubles
2527
// This map that converts from platforms to valid Xcodebuild destinations.
2628
const platformToDestination /*: $ReadOnly<{|[Platform]: Destination|}> */ = {
2729
ios: 'iOS',
2830
'ios-simulator': 'iOS Simulator',
29-
'macos': 'macOS', // [macOS]
31+
macos: 'macOS', // [macOS]
3032
'mac-catalyst': 'macOS,variant=Mac Catalyst',
33+
visionos: 'xrOS', // [macOS]
34+
'visionos-simulator': 'xrOS Simulator', // [macOS]
3135
};
3236

3337
const cli = yargs

packages/react-native/scripts/ios-prebuild/hermes.js

Lines changed: 91 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -180,10 +180,17 @@ async function prepareHermesArtifactsAsync(
180180
// enables the fallback to hermesCommitAtMergeBase() when no prebuilt artifacts exist.
181181
let allowBuildFromSource = false;
182182
if (!process.env.HERMES_VERSION) {
183-
const packageJsonPath = path.resolve(__dirname, '..', '..', 'package.json');
183+
const packageJsonPath = path.resolve(
184+
__dirname,
185+
'..',
186+
'..',
187+
'package.json',
188+
);
184189
const mappedVersion = findMatchingHermesVersion(packageJsonPath);
185190
if (mappedVersion != null) {
186-
hermesLog(`Using mapped upstream version for Hermes lookup: ${mappedVersion}`);
191+
hermesLog(
192+
`Using mapped upstream version for Hermes lookup: ${mappedVersion}`,
193+
);
187194
resolvedVersion = mappedVersion;
188195
} else {
189196
allowBuildFromSource = true;
@@ -209,7 +216,11 @@ async function prepareHermesArtifactsAsync(
209216
return artifactsPath;
210217
}
211218

212-
const sourceType = await hermesSourceType(resolvedVersion, buildType, allowBuildFromSource);
219+
const sourceType = await hermesSourceType(
220+
resolvedVersion,
221+
buildType,
222+
allowBuildFromSource,
223+
);
213224
localPath = await resolveSourceFromSourceType(
214225
sourceType,
215226
resolvedVersion,
@@ -536,15 +547,84 @@ async function buildFromHermesCommit(
536547
artifactsPath /*: string */,
537548
) /*: Promise<string> */ {
538549
const {commit, timestamp} = hermesCommitAtMergeBase();
539-
abort(
540-
`[Hermes] No prebuilt Hermes artifacts available for version "${version}".\n` +
541-
`Hermes commit at merge base with facebook/react-native: ${commit} (timestamp: ${timestamp})\n` +
542-
`To resolve, either:\n` +
543-
` 1. Set HERMES_ENGINE_TARBALL_PATH to a local Hermes tarball path\n` +
544-
` 2. Set HERMES_VERSION to an upstream RN version with published artifacts (e.g., HERMES_VERSION=nightly)\n` +
545-
` 3. Build Hermes from commit ${commit} and provide the tarball path via HERMES_ENGINE_TARBALL_PATH`,
550+
hermesLog(
551+
`Building Hermes from source at commit ${commit} (merge base timestamp: ${timestamp})`,
546552
);
547-
return ''; // unreachable
553+
554+
const HERMES_GITHUB_URL = 'https://github.com/facebook/hermes.git';
555+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'hermes-build-'));
556+
const hermesDir = path.join(tmpDir, 'hermes');
557+
558+
try {
559+
// Clone Hermes at the identified commit
560+
hermesLog(`Cloning Hermes at commit ${commit}...`);
561+
execSync(`git clone --depth 1 ${HERMES_GITHUB_URL} "${hermesDir}"`, {
562+
stdio: 'inherit',
563+
timeout: 300000,
564+
});
565+
execSync(`git -C "${hermesDir}" fetch --depth 1 origin ${commit}`, {
566+
stdio: 'inherit',
567+
timeout: 120000,
568+
});
569+
execSync(`git -C "${hermesDir}" checkout ${commit}`, {
570+
stdio: 'inherit',
571+
});
572+
573+
// The build-ios-framework.sh script runs from the hermes directory.
574+
// It sources build-apple-framework.sh which sets HERMES_PATH relative to itself,
575+
// but we override it to point to the cloned Hermes repo.
576+
const reactNativeRoot = path.resolve(__dirname, '..', '..');
577+
const buildScript = path.join(
578+
reactNativeRoot,
579+
'sdks',
580+
'hermes-engine',
581+
'utils',
582+
'build-ios-framework.sh',
583+
);
584+
585+
hermesLog(`Building Hermes frameworks (${buildType})...`);
586+
execSync(`bash "${buildScript}"`, {
587+
cwd: hermesDir,
588+
stdio: 'inherit',
589+
timeout: 3600000, // 60 minutes
590+
env: {
591+
...process.env,
592+
BUILD_TYPE: buildType,
593+
HERMES_PATH: hermesDir,
594+
JSI_PATH: path.join(hermesDir, 'API', 'jsi'),
595+
REACT_NATIVE_PATH: reactNativeRoot,
596+
// Deployment targets matching react-native-macos minimums
597+
IOS_DEPLOYMENT_TARGET: '15.1',
598+
MAC_DEPLOYMENT_TARGET: '14.0',
599+
XROS_DEPLOYMENT_TARGET: '1.0',
600+
RELEASE_VERSION: version,
601+
},
602+
});
603+
604+
// Create tarball from the destroot (same structure as Maven artifacts)
605+
const tarballName = `hermes-ios-${buildType.toLowerCase()}.tar.gz`;
606+
const tarballPath = path.join(artifactsPath, tarballName);
607+
hermesLog('Creating Hermes tarball from build output...');
608+
execSync(`tar -czf "${tarballPath}" -C "${hermesDir}" destroot`, {
609+
stdio: 'inherit',
610+
});
611+
612+
hermesLog(`Hermes built from source and packaged at ${tarballPath}`);
613+
return tarballPath;
614+
} catch (e) {
615+
abort(
616+
`[Hermes] Failed to build Hermes from source at commit ${commit}.\n` +
617+
`Error: ${e.message}\n` +
618+
`To resolve, either:\n` +
619+
` 1. Set HERMES_ENGINE_TARBALL_PATH to a local Hermes tarball path\n` +
620+
` 2. Set HERMES_VERSION to an upstream RN version with published artifacts\n` +
621+
` 3. Build Hermes manually from commit ${commit} and provide the tarball path via HERMES_ENGINE_TARBALL_PATH`,
622+
);
623+
return ''; // unreachable
624+
} finally {
625+
// Clean up
626+
fs.rmSync(tmpDir, {recursive: true, force: true});
627+
}
548628
}
549629
// macOS]
550630

packages/react-native/scripts/ios-prebuild/types.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,17 @@ export type Platform =
1313
'ios' |
1414
'ios-simulator' |
1515
'macos' |
16-
'mac-catalyst';
16+
'mac-catalyst' |
17+
'visionos' |
18+
'visionos-simulator';
1719
1820
export type Destination =
1921
'iOS' |
2022
'iOS Simulator' |
2123
'macOS' |
22-
'macOS,variant=Mac Catalyst';
24+
'macOS,variant=Mac Catalyst' |
25+
'xrOS' |
26+
'xrOS Simulator';
2327
2428
export type BuildFlavor = 'Debug' | 'Release';
2529
*/

packages/react-native/sdks/hermes-engine/utils/build-apple-framework.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ CURR_SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
1212
IMPORT_HERMESC_PATH=${HERMES_OVERRIDE_HERMESC_PATH:-$PWD/build_host_hermesc/ImportHermesc.cmake}
1313
BUILD_TYPE=${BUILD_TYPE:-Debug}
1414

15-
HERMES_PATH="$CURR_SCRIPT_DIR/.."
15+
HERMES_PATH=${HERMES_PATH:-"$CURR_SCRIPT_DIR/.."}
1616
REACT_NATIVE_PATH=${REACT_NATIVE_PATH:-$CURR_SCRIPT_DIR/../../..}
1717

1818
NUM_CORES=$(sysctl -n hw.ncpu)

0 commit comments

Comments
 (0)