Skip to content

Commit 49c3e7e

Browse files
chrfalchclaude
andcommitted
feat(releases): publish ReactNativeHeaders + ReactNativeDependenciesHeaders to Maven
Publish the two headers-only xcframeworks as standalone artifacts on the react-native-artifacts coordinate so SwiftPM consumers can wire them as separate binaryTargets (they also keep shipping inside the ReactCore / ReactNativeDependencies tarballs for CocoaPods): - external-artifacts/build.gradle.kts: four new PublishArtifacts with classifiers reactnative-headers-{debug,release} and reactnative-dependencies-headers-{debug,release}. - prebuild-ios-core.yml: upload the standalone ReactNativeHeaders<flavor>.xcframework.tar.gz the compose step already produces (previously only bundled inside the ReactCore tarball); include it in the workflow cache. - build-npm-package action: download ReactNativeHeaders* artifacts (ReactNativeDependenciesHeaders* already rides the existing ReactNativeDependencies* pattern). - verifyArtifactsAreOnMaven.js: HEAD-check every classifier tarball, not just the POM - a release with a missing classifier artifact now fails verification instead of passing silently. Validated locally end-to-end with publishReleasePublicationToMavenLocal: all 12 files + POM land on com.facebook.react:react-native-artifacts with the expected classifier names. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 2e489f4 commit 49c3e7e

4 files changed

Lines changed: 107 additions & 9 deletions

File tree

.github/actions/build-npm-package/action.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,15 @@ runs:
3131
pattern: ReactCore*
3232
path: ./packages/react-native/ReactAndroid/external-artifacts/artifacts
3333
merge-multiple: true
34+
# ReactNativeDependenciesHeaders* is already covered by the
35+
# ReactNativeDependencies* pattern above; ReactNativeHeaders* needs its own.
36+
- name: Download ReactNativeHeaders artifacts
37+
if: ${{ inputs.skip-apple-prebuilts != 'true' }}
38+
uses: actions/download-artifact@v7
39+
with:
40+
pattern: ReactNativeHeaders*
41+
path: ./packages/react-native/ReactAndroid/external-artifacts/artifacts
42+
merge-multiple: true
3443
- name: Print Artifacts Directory
3544
if: ${{ inputs.skip-apple-prebuilts != 'true' }}
3645
shell: bash

.github/workflow-scripts/verifyArtifactsAreOnMaven.js

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
* @format
88
*/
99

10+
// @flow
1011
const {log, sleep} = require('./utils');
1112

1213
const SLEEP_S = 60; // 1 minute
@@ -15,23 +16,49 @@ const ARTIFACT_URL =
1516
'https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/';
1617
const ARTIFACT_NAME = 'react-native-artifacts-';
1718

19+
// The classifier-suffixed tarballs attached to the react-native-artifacts
20+
// publication (external-artifacts/build.gradle.kts). The POM check alone
21+
// would pass even when a classifier artifact never made it to Maven.
22+
const ARTIFACT_CLASSIFIERS = [
23+
'reactnative-core-debug',
24+
'reactnative-core-release',
25+
'reactnative-dependencies-debug',
26+
'reactnative-dependencies-release',
27+
'reactnative-headers-debug',
28+
'reactnative-headers-release',
29+
'reactnative-dependencies-headers-debug',
30+
'reactnative-dependencies-headers-release',
31+
];
32+
1833
async function verifyArtifactsAreOnMaven(version, retries = MAX_RETRIES) {
1934
if (version.startsWith('v')) {
2035
version = version.substring(1);
2136
}
2237

23-
const artifactUrl = `${ARTIFACT_URL}${version}/${ARTIFACT_NAME}${version}.pom`;
38+
const urls = [
39+
`${ARTIFACT_URL}${version}/${ARTIFACT_NAME}${version}.pom`,
40+
...ARTIFACT_CLASSIFIERS.map(
41+
classifier =>
42+
`${ARTIFACT_URL}${version}/${ARTIFACT_NAME}${version}-${classifier}.tar.gz`,
43+
),
44+
];
2445
for (let currentAttempt = 1; currentAttempt <= retries; currentAttempt++) {
25-
const response = await fetch(artifactUrl);
26-
27-
if (response.status !== 200) {
28-
log(
29-
`${currentAttempt}) Artifact's for version ${version} are not on maven yet.\nURL: ${artifactUrl}\nLet's wait a minute and try again.\n`,
30-
);
31-
await sleep(SLEEP_S);
32-
} else {
46+
let missingUrl = null;
47+
for (const url of urls) {
48+
const response = await fetch(url, {method: 'HEAD'});
49+
if (response.status !== 200) {
50+
missingUrl = url;
51+
break;
52+
}
53+
}
54+
55+
if (missingUrl == null) {
3356
return;
3457
}
58+
log(
59+
`${currentAttempt}) Artifact's for version ${version} are not on maven yet.\nURL: ${missingUrl}\nLet's wait a minute and try again.\n`,
60+
);
61+
await sleep(SLEEP_S);
3562
}
3663

3764
log(

.github/workflows/prebuild-ios-core.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,15 @@ jobs:
214214
run: |
215215
cd packages/react-native/.build/output/xcframeworks/${{matrix.flavor}}/Symbols
216216
tar -cz -f ../../ReactCore${{ matrix.flavor }}.framework.dSYM.tar.gz .
217+
- name: Rename ReactNativeHeaders XCFramework tarball
218+
if: steps.restore-ios-xcframework.outputs.cache-hit != 'true'
219+
run: |
220+
# The compose step already tars ReactNativeHeaders.xcframework standalone;
221+
# published as its own Maven artifact (classifier reactnative-headers-*)
222+
# so SwiftPM consumers can wire it as a separate binaryTarget. It also
223+
# ships inside the ReactCore tarball for the CocoaPods pod.
224+
cp packages/react-native/.build/output/xcframeworks/${{matrix.flavor}}/ReactNativeHeaders.xcframework.tar.gz \
225+
packages/react-native/.build/output/xcframeworks/ReactNativeHeaders${{matrix.flavor}}.xcframework.tar.gz
217226
- name: Upload XCFramework Artifact
218227
uses: actions/upload-artifact@v6
219228
with:
@@ -224,11 +233,17 @@ jobs:
224233
with:
225234
name: ReactCore${{ matrix.flavor }}.framework.dSYM.tar.gz
226235
path: packages/react-native/.build/output/xcframeworks/ReactCore${{matrix.flavor}}.framework.dSYM.tar.gz
236+
- name: Upload ReactNativeHeaders XCFramework Artifact
237+
uses: actions/upload-artifact@v6
238+
with:
239+
name: ReactNativeHeaders${{ matrix.flavor }}.xcframework.tar.gz
240+
path: packages/react-native/.build/output/xcframeworks/ReactNativeHeaders${{matrix.flavor}}.xcframework.tar.gz
227241
- name: Save cache if present
228242
if: ${{ github.ref == 'refs/heads/main' }} # To avoid that the cache explode
229243
uses: actions/cache/save@v5
230244
with:
231245
path: |
232246
packages/react-native/.build/output/xcframeworks/ReactCore${{matrix.flavor}}.xcframework.tar.gz
233247
packages/react-native/.build/output/xcframeworks/ReactCore${{matrix.flavor}}.framework.dSYM.tar.gz
248+
packages/react-native/.build/output/xcframeworks/ReactNativeHeaders${{matrix.flavor}}.xcframework.tar.gz
234249
key: v2-ios-core-xcframework-${{ matrix.flavor }}-${{ hashFiles('packages/react-native/Package.swift', 'packages/react-native/scripts/ios-prebuild/*.js', 'packages/react-native/scripts/ios-prebuild.js', 'packages/react-native/React/**/*', 'packages/react-native/ReactCommon/**/*', 'packages/react-native/Libraries/**/*') }}

packages/react-native/ReactAndroid/external-artifacts/build.gradle.kts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,28 @@ val reactNativeDependenciesReleaseDSYMArtifact: PublishArtifact =
5353
classifier = "reactnative-dependencies-dSYM-release"
5454
}
5555

56+
// [iOS] React Native Dependencies Headers — the headers-only LIBRARY-type
57+
// sidecar (per-slice Headers/ + HeadersPath) that SwiftPM auto-serves; the
58+
// binary xcframework above is framework-type and cannot expose headers to
59+
// SwiftPM binaryTargets. Also shipped INSIDE the deps tarball for CocoaPods.
60+
val reactNativeDependenciesHeadersDebugArtifactFile: RegularFile =
61+
layout.projectDirectory.file("artifacts/ReactNativeDependenciesHeadersDebug.xcframework.tar.gz")
62+
val reactNativeDependenciesHeadersDebugArtifact: PublishArtifact =
63+
artifacts.add("externalArtifacts", reactNativeDependenciesHeadersDebugArtifactFile) {
64+
type = "tgz"
65+
extension = "tar.gz"
66+
classifier = "reactnative-dependencies-headers-debug"
67+
}
68+
69+
val reactNativeDependenciesHeadersReleaseArtifactFile: RegularFile =
70+
layout.projectDirectory.file("artifacts/ReactNativeDependenciesHeadersRelease.xcframework.tar.gz")
71+
val reactNativeDependenciesHeadersReleaseArtifact: PublishArtifact =
72+
artifacts.add("externalArtifacts", reactNativeDependenciesHeadersReleaseArtifactFile) {
73+
type = "tgz"
74+
extension = "tar.gz"
75+
classifier = "reactnative-dependencies-headers-release"
76+
}
77+
5678
// [iOS] React Native Core
5779
val reactCoreDebugArtifactFile: RegularFile =
5880
layout.projectDirectory.file("artifacts/ReactCoreDebug.xcframework.tar.gz")
@@ -89,6 +111,27 @@ val reactCoreReleaseDSYMArtifact: PublishArtifact =
89111
classifier = "reactnative-core-dSYM-release"
90112
}
91113

114+
// [iOS] React Native Headers — the pure-RN headers-only xcframework, published
115+
// standalone (it also ships inside the ReactCore tarball for CocoaPods) so
116+
// SwiftPM consumers can wire it as its own binaryTarget.
117+
val reactNativeHeadersDebugArtifactFile: RegularFile =
118+
layout.projectDirectory.file("artifacts/ReactNativeHeadersDebug.xcframework.tar.gz")
119+
val reactNativeHeadersDebugArtifact: PublishArtifact =
120+
artifacts.add("externalArtifacts", reactNativeHeadersDebugArtifactFile) {
121+
type = "tgz"
122+
extension = "tar.gz"
123+
classifier = "reactnative-headers-debug"
124+
}
125+
126+
val reactNativeHeadersReleaseArtifactFile: RegularFile =
127+
layout.projectDirectory.file("artifacts/ReactNativeHeadersRelease.xcframework.tar.gz")
128+
val reactNativeHeadersReleaseArtifact: PublishArtifact =
129+
artifacts.add("externalArtifacts", reactNativeHeadersReleaseArtifactFile) {
130+
type = "tgz"
131+
extension = "tar.gz"
132+
classifier = "reactnative-headers-release"
133+
}
134+
92135
apply(from = "../publish.gradle")
93136

94137
publishing {
@@ -99,10 +142,14 @@ publishing {
99142
artifact(reactNativeDependenciesReleaseArtifact)
100143
artifact(reactNativeDependenciesDebugDSYMArtifact)
101144
artifact(reactNativeDependenciesReleaseDSYMArtifact)
145+
artifact(reactNativeDependenciesHeadersDebugArtifact)
146+
artifact(reactNativeDependenciesHeadersReleaseArtifact)
102147
artifact(reactCoreDebugArtifact)
103148
artifact(reactCoreReleaseArtifact)
104149
artifact(reactCoreDebugDSYMArtifact)
105150
artifact(reactCoreReleaseDSYMArtifact)
151+
artifact(reactNativeHeadersDebugArtifact)
152+
artifact(reactNativeHeadersReleaseArtifact)
106153
}
107154
}
108155
}

0 commit comments

Comments
 (0)