Skip to content

Commit e85eb1a

Browse files
Merge remote-tracking branch 'upstream/main' into assets-catalog
# Conflicts: # packages/react-native/package.json
2 parents adf495d + eb4d389 commit e85eb1a

848 files changed

Lines changed: 42398 additions & 9506 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.eslintrc.js

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ module.exports = {
1515

1616
extends: ['@react-native'],
1717

18-
plugins: ['@react-native/monorepo', '@react-native/specs'],
18+
plugins: ['@react-native/monorepo', '@react-native/specs', 'import'],
1919

2020
overrides: [
2121
// overriding the JS config from @react-native/eslint-config to ensure
@@ -44,6 +44,7 @@ module.exports = {
4444
files: ['*.js', '*.jsx', '*.ts', '*.tsx'],
4545
rules: {
4646
'@react-native/no-deep-imports': 'off',
47+
'import/enforce-node-protocol-usage': ['warn', 'always'],
4748
},
4849
},
4950
{
@@ -61,17 +62,14 @@ module.exports = {
6162
files: ['package.json'],
6263
parser: 'jsonc-eslint-parser',
6364
},
64-
{
65-
files: ['package.json'],
66-
rules: {
67-
'@react-native/monorepo/react-native-manifest': 'error',
68-
},
69-
},
7065
{
7166
files: ['flow-typed/**/*.js', 'packages/react-native/flow/**/*'],
7267
rules: {
7368
'@react-native/monorepo/valid-flow-typed-signature': 'error',
7469
'ft-flow/require-valid-file-annotation': 'off',
70+
// These libdefs are kept byte-identical across projects (see
71+
// flow-typed-sync-test), so they must not be migrated independently.
72+
'import/enforce-node-protocol-usage': 'off',
7573
'no-shadow': 'off',
7674
'no-unused-vars': 'off',
7775
quotes: 'off',

.flowconfig

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ experimental.multi_platform.extensions=.android
5656
munge_underscores=true
5757

5858
module.name_mapper='^react-native$' -> '<PROJECT_ROOT>/packages/react-native/index.js'
59+
module.name_mapper='^react-native/react-private-interface$' -> '<PROJECT_ROOT>/packages/react-native/src/react-private-interface.js'
60+
module.name_mapper='^react-native/setup-env$' -> '<PROJECT_ROOT>/packages/react-native/src/setup-env.js'
5961
module.name_mapper='^react-native/\(.*\)$' -> '<PROJECT_ROOT>/packages/react-native/\1'
6062
module.name_mapper='^@react-native/dev-middleware$' -> '<PROJECT_ROOT>/packages/dev-middleware'
6163
module.name_mapper='^@?[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\|xml\|ktx\|heic\|heif\)$' -> '<PROJECT_ROOT>/packages/react-native/Libraries/Image/RelativeImageStub'
@@ -87,4 +89,4 @@ untyped-import
8789
untyped-type-import
8890

8991
[version]
90-
^0.319.0
92+
^0.322.0

.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/actions/test-ios-rntester/action.yml

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,18 @@ inputs:
1515
required: false
1616
default: false
1717
use-frameworks:
18-
description: Whether we have to build with Dynamic Frameworks. If this is set to true, it builds from source
18+
description: Whether we have to build with Dynamic Frameworks.
1919
required: false
2020
default: false
21+
use-prebuilds:
22+
description: >-
23+
Whether to consume the prebuilt ReactCore/ReactNativeDependencies
24+
artifacts. 'auto' (default) keeps the historical coupling: prebuilds for
25+
static, source for dynamic frameworks. Pass 'true' with
26+
use-frameworks:true for the prebuilt + dynamic-frameworks lane (the
27+
config of the 2026-07-03 SocketRocket dual-copy regression).
28+
required: false
29+
default: auto
2130

2231
runs:
2332
using: composite
@@ -41,33 +50,53 @@ runs:
4150
- name: Prepare IOS Tests
4251
if: ${{ inputs.run-unit-tests == 'true' }}
4352
uses: ./.github/actions/prepare-ios-tests
53+
- name: Resolve prebuilds mode
54+
id: prebuilds
55+
shell: bash
56+
run: |
57+
if [[ "${{ inputs.use-prebuilds }}" == "auto" ]]; then
58+
# Historical coupling: prebuilds for static, source for dynamic frameworks.
59+
if [[ "${{ inputs.use-frameworks }}" == "true" ]]; then
60+
echo "enabled=false" >> "$GITHUB_OUTPUT"
61+
else
62+
echo "enabled=true" >> "$GITHUB_OUTPUT"
63+
fi
64+
elif [[ "${{ inputs.use-prebuilds }}" == "true" ]]; then
65+
echo "enabled=true" >> "$GITHUB_OUTPUT"
66+
elif [[ "${{ inputs.use-prebuilds }}" == "false" ]]; then
67+
echo "enabled=false" >> "$GITHUB_OUTPUT"
68+
else
69+
# Don't silently treat a typo as 'disabled' — surface it.
70+
echo "::warning::Unexpected use-prebuilds value '${{ inputs.use-prebuilds }}' (expected auto/true/false); treating as disabled."
71+
echo "enabled=false" >> "$GITHUB_OUTPUT"
72+
fi
4473
- name: Download ReactNativeDependencies
45-
if: ${{ inputs.use-frameworks == 'false' }}
74+
if: ${{ steps.prebuilds.outputs.enabled == 'true' }}
4675
uses: actions/download-artifact@v7
4776
with:
4877
name: ReactNativeDependencies${{ inputs.flavor }}.xcframework.tar.gz
4978
path: /tmp/third-party/
5079
- name: Print third-party folder
51-
if: ${{ inputs.use-frameworks == 'false' }}
80+
if: ${{ steps.prebuilds.outputs.enabled == 'true' }}
5281
shell: bash
5382
run: ls -lR /tmp/third-party
5483
- name: Download React Native Prebuilds
55-
if: ${{ inputs.use-frameworks == 'false' }}
84+
if: ${{ steps.prebuilds.outputs.enabled == 'true' }}
5685
uses: actions/download-artifact@v7
5786
with:
5887
name: ReactCore${{ inputs.flavor }}.xcframework.tar.gz
5988
path: /tmp/ReactCore
6089
- name: Print ReactCore folder
61-
if: ${{ inputs.use-frameworks == 'false' }}
90+
if: ${{ steps.prebuilds.outputs.enabled == 'true' }}
6291
shell: bash
6392
run: ls -lR /tmp/ReactCore
6493
- name: Install CocoaPods dependencies
6594
shell: bash
6695
run: |
6796
if [[ ${{ inputs.use-frameworks }} == "true" ]]; then
6897
export USE_FRAMEWORKS=dynamic
69-
else
70-
# If use-frameworks is false, let's use prebuilds
98+
fi
99+
if [[ "${{ steps.prebuilds.outputs.enabled }}" == "true" ]]; then
71100
export RCT_USE_LOCAL_RN_DEP="/tmp/third-party/ReactNativeDependencies${{ inputs.flavor }}.xcframework.tar.gz"
72101
export RCT_TESTONLY_RNCORE_TARBALL_PATH="/tmp/ReactCore/ReactCore${{ inputs.flavor }}.xcframework.tar.gz"
73102
fi

.github/workflow-scripts/__tests__/verifyArtifactsAreOnMaven-test.js

Lines changed: 55 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,24 +22,47 @@ jest.mock('../utils.js', () => ({
2222
process.exit = mockExit;
2323
global.fetch = mockFetch;
2424

25+
const BASE_URL =
26+
'https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts';
27+
28+
// The verifier HEAD-checks the POM plus every classifier tarball attached to
29+
// the react-native-artifacts publication (external-artifacts/build.gradle.kts).
30+
const expectedUrls = version => [
31+
`${BASE_URL}/${version}/react-native-artifacts-${version}.pom`,
32+
...[
33+
'reactnative-core-debug',
34+
'reactnative-core-release',
35+
'reactnative-dependencies-debug',
36+
'reactnative-dependencies-release',
37+
'reactnative-headers-debug',
38+
'reactnative-headers-release',
39+
'reactnative-dependencies-headers-debug',
40+
'reactnative-dependencies-headers-release',
41+
].map(
42+
classifier =>
43+
`${BASE_URL}/${version}/react-native-artifacts-${version}-${classifier}.tar.gz`,
44+
),
45+
];
46+
2547
describe('#verifyArtifactsAreOnMaven', () => {
2648
beforeEach(jest.clearAllMocks);
2749

2850
it('waits for the packages to be published on maven when version has no v', async () => {
2951
mockSleep.mockReturnValueOnce(Promise.resolve()).mockImplementation(() => {
3052
throw new Error('Should not be called again!');
3153
});
54+
// First attempt: the POM is not there yet. Second attempt: every URL is.
3255
mockFetch
3356
.mockReturnValueOnce(Promise.resolve({status: 404}))
34-
.mockReturnValueOnce(Promise.resolve({status: 200}));
57+
.mockReturnValue(Promise.resolve({status: 200}));
3558

3659
const version = '0.78.1';
3760
await verifyArtifactsAreOnMaven(version);
3861

3962
expect(mockSleep).toHaveBeenCalledTimes(1);
40-
expect(mockFetch).toHaveBeenCalledWith(
41-
'https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/0.78.1/react-native-artifacts-0.78.1.pom',
42-
);
63+
for (const url of expectedUrls('0.78.1')) {
64+
expect(mockFetch).toHaveBeenCalledWith(url, {method: 'HEAD'});
65+
}
4366
});
4467

4568
it('waits for the packages to be published on maven, when version starts with v', async () => {
@@ -48,27 +71,46 @@ describe('#verifyArtifactsAreOnMaven', () => {
4871
});
4972
mockFetch
5073
.mockReturnValueOnce(Promise.resolve({status: 404}))
51-
.mockReturnValueOnce(Promise.resolve({status: 200}));
74+
.mockReturnValue(Promise.resolve({status: 200}));
5275

5376
const version = 'v0.78.1';
5477
await verifyArtifactsAreOnMaven(version);
5578

5679
expect(mockSleep).toHaveBeenCalledTimes(1);
57-
expect(mockFetch).toHaveBeenCalledWith(
58-
'https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/0.78.1/react-native-artifacts-0.78.1.pom',
59-
);
80+
for (const url of expectedUrls('0.78.1')) {
81+
expect(mockFetch).toHaveBeenCalledWith(url, {method: 'HEAD'});
82+
}
6083
});
6184

6285
it('passes immediately if packages are already on Maven', async () => {
63-
mockFetch.mockReturnValueOnce(Promise.resolve({status: 200}));
86+
mockFetch.mockReturnValue(Promise.resolve({status: 200}));
6487

6588
const version = '0.78.1';
6689
await verifyArtifactsAreOnMaven(version);
6790

6891
expect(mockSleep).toHaveBeenCalledTimes(0);
69-
expect(mockFetch).toHaveBeenCalledWith(
70-
'https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/0.78.1/react-native-artifacts-0.78.1.pom',
71-
);
92+
// All nine URLs (POM + 8 classifier tarballs) are verified in one pass.
93+
expect(mockFetch).toHaveBeenCalledTimes(9);
94+
for (const url of expectedUrls('0.78.1')) {
95+
expect(mockFetch).toHaveBeenCalledWith(url, {method: 'HEAD'});
96+
}
97+
});
98+
99+
it('waits when a classifier artifact is missing even though the POM exists', async () => {
100+
mockSleep.mockReturnValueOnce(Promise.resolve()).mockImplementation(() => {
101+
throw new Error('Should not be called again!');
102+
});
103+
// First attempt: POM ok, first classifier missing. Second attempt: all ok.
104+
mockFetch
105+
.mockReturnValueOnce(Promise.resolve({status: 200}))
106+
.mockReturnValueOnce(Promise.resolve({status: 404}))
107+
.mockReturnValue(Promise.resolve({status: 200}));
108+
109+
const version = '0.78.1';
110+
await verifyArtifactsAreOnMaven(version);
111+
112+
expect(mockSleep).toHaveBeenCalledTimes(1);
113+
expect(mockExit).not.toHaveBeenCalled();
72114
});
73115

74116
it('tries 90 times and then exits', async () => {
@@ -82,6 +124,7 @@ describe('#verifyArtifactsAreOnMaven', () => {
82124
expect(mockExit).toHaveBeenCalledWith(1);
83125
expect(mockFetch).toHaveBeenCalledWith(
84126
'https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/0.78.1/react-native-artifacts-0.78.1.pom',
127+
{method: 'HEAD'},
85128
);
86129
});
87130
});

.github/workflow-scripts/lint_files.sh

Lines changed: 0 additions & 12 deletions
This file was deleted.

.github/workflow-scripts/verifyArtifactsAreOnMaven.js

Lines changed: 52 additions & 10 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,64 @@ const ARTIFACT_URL =
1516
'https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/';
1617
const ARTIFACT_NAME = 'react-native-artifacts-';
1718

18-
async function verifyArtifactsAreOnMaven(version, retries = MAX_RETRIES) {
19+
// The primary xcframework classifier tarballs attached to the
20+
// react-native-artifacts publication (external-artifacts/build.gradle.kts).
21+
// The 4 dSYM classifiers (core/deps dSYM debug+release) are intentionally
22+
// excluded — they are debug-symbol sidecars, not consumed at install time.
23+
// The POM check alone would pass even when a classifier artifact never made
24+
// it to Maven.
25+
const ARTIFACT_CLASSIFIERS = [
26+
'reactnative-core-debug',
27+
'reactnative-core-release',
28+
'reactnative-dependencies-debug',
29+
'reactnative-dependencies-release',
30+
'reactnative-headers-debug',
31+
'reactnative-headers-release',
32+
'reactnative-dependencies-headers-debug',
33+
'reactnative-dependencies-headers-release',
34+
];
35+
36+
async function verifyArtifactsAreOnMaven(
37+
version /*: string */,
38+
retries /*: number */ = MAX_RETRIES,
39+
) /*: Promise<void> */ {
1940
if (version.startsWith('v')) {
2041
version = version.substring(1);
2142
}
2243

23-
const artifactUrl = `${ARTIFACT_URL}${version}/${ARTIFACT_NAME}${version}.pom`;
44+
const urls = [
45+
`${ARTIFACT_URL}${version}/${ARTIFACT_NAME}${version}.pom`,
46+
...ARTIFACT_CLASSIFIERS.map(
47+
classifier =>
48+
`${ARTIFACT_URL}${version}/${ARTIFACT_NAME}${version}-${classifier}.tar.gz`,
49+
),
50+
];
2451
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 {
52+
let missingUrl = null;
53+
for (const url of urls) {
54+
try {
55+
const response = await fetch(url, {method: 'HEAD'});
56+
if (response.status === 200) {
57+
continue;
58+
}
59+
log(`Got status ${response.status} while checking ${url}`);
60+
} catch (error) {
61+
const message = error instanceof Error ? error.message : String(error);
62+
log(`Network error while checking ${url}: ${message}`);
63+
missingUrl = url;
64+
break;
65+
}
66+
missingUrl = url;
67+
break;
68+
}
69+
70+
if (missingUrl == null) {
3371
return;
3472
}
73+
log(
74+
`${currentAttempt}) Artifact's for version ${version} are not on maven yet.\nURL: ${missingUrl}\nLet's wait a minute and try again.\n`,
75+
);
76+
await sleep(SLEEP_S);
3577
}
3678

3779
log(

.github/workflows/bump-podfile-lock.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ on:
55

66
jobs:
77
bump-podfile-lock:
8-
runs-on: macos-latest
8+
runs-on: macos-15
99
steps:
1010
- name: Checkout
1111
uses: actions/checkout@v6

0 commit comments

Comments
 (0)