Compare commits

..
Author SHA1 Message Date
Rob Hogan c970986b5b [LOCAL] Update Podfile.lock
Changelog: [Internal]
2025-07-21 19:35:14 +01:00
React Native Bot 0d3a7ca52f Release 0.77.3
#publish-packages-to-npm&0.77-stable
2025-07-21 14:59:32 +00:00
Nick Gerleman 8f18015a49 Fix build_android GHA Job (#52694)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/52694

build_android job is always failing, with this assertion. Seems like find/replace in D78484060 gone wrong?

Changelog: [Internal]

Reviewed By: sbuggay

Differential Revision: D78534334

fbshipit-source-id: 291bdd01b41fa6efea00ed63a0dee8bdb14cbc3a
2025-07-21 13:56:47 +01:00
Rob Hogan 2412da9b7b Revert "Fix Windows path expectation in OsTest.kt"
This reverts commit f2b846fcc4.
2025-07-21 13:56:41 +01:00
Rob Hogan f2b846fcc4 Fix Windows path expectation in OsTest.kt 2025-07-21 13:52:56 +01:00
Riccardo Cipolleschi 906468faeb [RN][CI]Fix Windows CI 2025-07-21 11:54:09 +01:00
Nicola Corti 710d499f13 Fix Modal first frame being rendered on top-left corner (#51048)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/51048

Fixes https://github.com/facebook/react-native/issues/50442
Closes https://github.com/facebook/react-native/pull/50704

Users reported that Modals on Android are first renderer anchored in 0,0.
That results in them being on the top left corner of the screen for some seconds.

This is happening because the native state of the Modal on Android as width/height set at 0,0 - which we then update in a subsequent callback.

I'm fixing this by making sure we render the Modal the first time with the right screen size - the status bar size

Changelog:
[Android] [Fixed] - Fix Modal first frame being rendered on top-left corner

Reviewed By: javache

Differential Revision: D73948178

fbshipit-source-id: 055c12aa62d70acc1e4c5a2a5c4ea0c5608e22c7
2025-07-17 16:58:20 +01:00
Rob Hogan c43952ac22 [LOCAL] Backout #46896 - Attempt to fix #50274 (#52515) 2025-07-09 17:57:14 +01:00
Rob Hogan dfa81638dd [LOCAL] Bump Metro minimum ^0.81.3 -> ^0.81.5 (#52504) 2025-07-09 12:04:47 +01:00
Nicola Corti 89eff977c7 RNGP - Add support for exclusiveEnterpriseRepository (#52378)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/52378

This adds a Gradle property called `exclusiveEnterpriseRepository`
that users can set in their `android/gradle.properties` as such:

```diff
hermesEnabled=true

+exclusiveEnterpriseRepository=https://my.internal.proxy.net/
```

This will remove all the existing Maven repositories and only use the internal mirror they have.

Changelog:
[Android] [Added] - RNGP - Add support for `exclusiveEnterpriseRepository` to specify an internal Maven mirror.

Reviewed By: mdvacca

Differential Revision: D77667573

fbshipit-source-id: 835004d2ae7aa4e250b6f7a88a41918b573f5bd5
2025-07-03 13:32:01 +01:00
Ritesh Shukla 04fffa556d Fixed borderBottomEndRadius on RTL (#51229)
Summary:
Fixes https://github.com/facebook/react-native/issues/51193
[ANDROID][FIXED] Wrong borderBottomEndRadius on RTL

Pull Request resolved: https://github.com/facebook/react-native/pull/51229

Test Plan:
Tested with Arabic (RTL) and English(LTR)

English
<img src="https://github.com/user-attachments/assets/5bd1eca9-194c-4d83-b75f-689bc13d827b" width=200>

Arabic
<img src="https://github.com/user-attachments/assets/459320a0-6bb2-4585-97bb-bd4ded6ec399" width=200>

Reviewed By: cortinico

Differential Revision: D74709219

Pulled By: NickGerleman

fbshipit-source-id: 885bb84d97d73324983d044fd2874a457b40f646
2025-07-01 15:04:42 +01:00
Rubén Norte b47a1b0f4e Avoid errors when dispatching mount operations within mount hooks (#50091)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/50091

Changelog: [internal]

If a library uses mount hooks to perform mount operations, it's possible to get concurrent modifications of the list of pending surface IDs to report.

This fixes that potential error by making a copy of the list before dispatching the mount notifications.

Fixes https://github.com/facebook/react-native/issues/49783.

Reviewed By: javache

Differential Revision: D71387739

fbshipit-source-id: 96c723ef2d6bcc659c4452434b7a4d5af26117ef
2025-07-01 15:00:09 +01:00
zhongwuzw 3e09e59912 Fabric: Fixes Modal snapshot when closing it (#48252)
Summary:
Fixes https://github.com/facebook/react-native/issues/48245 . The reason is we reuse the modal component and also reuse the snapshot of previous modal.

## Changelog:

[IOS] [FIXED] - Fabric: Fixes Modal snapshot when closing it

Pull Request resolved: https://github.com/facebook/react-native/pull/48252

Test Plan: Repro repo please see Fixes https://github.com/facebook/react-native/issues/48245

Reviewed By: rshest

Differential Revision: D70629570

Pulled By: cipolleschi

fbshipit-source-id: fe97184d2f27283a9c7e6df09e9df35630429ff1
2025-07-01 13:52:46 +00:00
zhongwuzw 4ec5e99a72 iOS: Fixes Switch component incorrectly renders as toggled on even though value prop is hardcoded to false (#50049)
Summary:
Fixes https://github.com/facebook/react-native/issues/50026

## Changelog:

[IOS] [FIXED] - [Fabric] iOS: Fixes Switch component incorrectly renders as toggled on even though value prop is hardcoded to false

Pull Request resolved: https://github.com/facebook/react-native/pull/50049

Test Plan: Repro please see https://github.com/facebook/react-native/issues/50026.

Reviewed By: javache

Differential Revision: D71307864

Pulled By: cipolleschi

fbshipit-source-id: 57238296842229cf2f23a3f44134f43e27b54609
2025-07-01 13:51:44 +00:00
Rubén Norte 2c2c0729a8 Correctly batch reportMount calls (#50090)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/50090

Changelog: [internal]

I refactored `FabricUIManager` in D54547194 / https://github.com/facebook/react-native/pull/43337 and accidentally removed setting this flag to avoid scheduling redundant tasks in the UI thread to report mount. This fixes it.

Reviewed By: javache

Differential Revision: D71387374

fbshipit-source-id: cad8a3ead2434738325560902cbab817e5d5dde7
2025-07-01 13:50:30 +00:00
Christian Falch ab385592c0 fix adding children (#51213)
Summary:
See https://github.com/facebook/react-native/issues/51212 - children aren't updated correctly in an old arch native view using the interop layer under Fabric.

This is caused by the mountChildComponentView method not updating the view, only adding the new view to a list that will be used on the next update to mount the child.

This commit fixes this by adding the same pattern as in unmountChildComponentView where children are inserted directly if the underlying paperview is available in the adapter - otherwise it uses the mounting list as before.

#Closes 51212

bypass-github-export-checks

## Changelog:

[IOS] [FIXED] - fixed adding child views to a native view using the interop layer

Pull Request resolved: https://github.com/facebook/react-native/pull/51213

Test Plan:
**Previous output**:

<image src="https://github.com/user-attachments/assets/472b95e7-0921-46c9-be6a-f31759c0cd26" width="200px" />

**After fix**:

<image src="https://github.com/user-attachments/assets/554387cd-c264-483e-9c52-d9cd40b42601" width="200px" />

Reviewed By: sammy-SC

Differential Revision: D74471278

Pulled By: cipolleschi

fbshipit-source-id: 798f9e7be389359bd6e3aa1b6a6e9fb799fcb369
2025-07-01 13:48:26 +00:00
Phil Pluckthun 26b70c2e40 fix: Align TimerManager sequential ids and function error handling with web standard (#51500)
Summary:
Calls to create timers should return sequential ids (integers greater than zero in the spec's words). This regressed in the `TimerManager` implementation, which instead starts at zero inclusively.

This has two side-effects for code assuming a spec-compliant implementation of `setTimeout` and `setInterval`:
- Calls to `clearTimeout(0)` or `clearInterval(0)` will potentially cancel scheduled timers, although it's supposed to be a noop
- Predicates like `if (timeoutId)` will fail since they assume non-negative ids

The change in this PR is to align with WHATWG HTML 8.6.2 (Timers): https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#timers

> otherwise, let id be an [implementation-defined](https://infra.spec.whatwg.org/#implementation-defined) integer that is **greater than zero** and does not already [exist](https://infra.spec.whatwg.org/#map-exists) in global's [map of setTimeout and setInterval IDs](https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#map-of-settimeout-and-setinterval-ids).

Specifically,
- we should return `0` to indicate that no timer was scheduled
- we should start generating timer IDs at `1` instead of `0`

This was previously raised in review comments here: https://github.com/facebook/react-native/pull/45092/files#r1650790008

The spec-incompliant behaviour was raised in an issue here: https://github.com/apollographql/apollo-client/issues/12632#issue-3075269978

This PR does not,
- add bounds checking on `timerIndex_` and add a search of an available id that isn't in the unordered map
- exclude `0` from being an accepted `TimerHandle` in `TimerManager::createTimer` or `TimerManager::deleteTimer` since the above bounds checking hasn't been added either

## Changelog:

[GENERAL] [FIXED] - Align timer IDs and timer function argument error handling with web standards.

Pull Request resolved: https://github.com/facebook/react-native/pull/51500

Test Plan:
- Run `setTimeout` / `setInterval`; before applied changes the timeout for the first timer will be `0`
- Run `setTimeout(null)`; before applied changes the timer ID will be non-zero
- Run `setInterval(null)`; before applied changes an error will be thrown rather than `0` being returned

Reviewed By: cipolleschi

Differential Revision: D75145909

Pulled By: rshest

fbshipit-source-id: 6646439abd29cf3cfa9e5cf0a57448e3b7cd1b48
2025-07-01 13:46:44 +00:00
Rob Hogan db4a5cc860 Fix "paused on debugger" overlay icon (#48736)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/48736

Pull Request resolved: https://github.com/facebook/react-native/pull/48735

This icon was broken by D65457771, identified in [OSS testing for the 0.77 release](https://github.com/reactwg/react-native-releases/issues/724).

By explicitly setting the image for the `Disabled` state to the same as `Normal`, we get the same behaviour as the deprecated [`adjustsImageWhenDisabled = NO`](https://developer.apple.com/documentation/uikit/uibutton/adjustsimagewhendisabled?language=objc) without the need for `configurationUpdateHandler`.

Changelog: [iOS][Fixed] Restore "Paused in debugger" overlay icon

Reviewed By: cipolleschi

Differential Revision: D68274336

fbshipit-source-id: 3f4b84eb7cfb518ca953c721da9885df8f98b437
2025-07-01 13:38:49 +00:00
Mateo Guzmán 0750c6a91b Modal: FLAG_SECURE not respected in modal dialog (#48317)
Summary:
Fixes https://github.com/facebook/react-native/issues/38537

Setting `WindowManager.LayoutParams.FLAG_SECURE` in the window flags is not respected in the Android Modal component, causing security issues with screenshots or screen recordings as the content in the modal is visible. The flag works correctly in the rest of the components, see the videos in the linked issue.

This PR addresses that by checking whether this flag is set in the current activity and then setting it in the dialog when creating a new one in the `ReactModalHostView`.

## Changelog:

[ANDROID][FIXED] - `FLAG_SECURE` not respected in Modal dialog

Pull Request resolved: https://github.com/facebook/react-native/pull/48317

Test Plan:
To test this, you need a physical device as with the emulator the flags don't seem to be respected either.

The easiest way to test this in code is by setting the flags in the main activity. You can do so by adding this code snippet:

<details>
<summary>onCreate in RNTesterApplication.kt</summary>

```kt
override fun onCreate() {
    ReactFontManager.getInstance().addCustomFont(this, "Rubik", R.font.rubik)
    super.onCreate()

    ...

    registerActivityLifecycleCallbacks(
            object : ActivityLifecycleCallbacks {
              override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
                activity.window.setFlags(
                        WindowManager.LayoutParams.FLAG_SECURE,
                        WindowManager.LayoutParams.FLAG_SECURE
                )
              }

              override fun onActivityStarted(activity: Activity) {}
              override fun onActivityResumed(activity: Activity) {}
              override fun onActivityPaused(activity: Activity) {}
              override fun onActivityStopped(activity: Activity) {}
              override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {}
              override fun onActivityDestroyed(activity: Activity) {}
            }
    )
  }
```
</details>

Then, you can render a simple modal component:

<details>
<summary>RNTesterPlayground.js</summary>

```tsx
function Playground() {
  const [modalVisible, setModalVisible] = React.useState(false);

  return (
    <>
      <Modal
        visible={modalVisible}
        testID="playground-modal">
        <Text testID="inner-text-test-id">Hello World!</Text>
        <Button title="Close Modal" onPress={() => setModalVisible(false)} />
      </Modal>

      <Button
        title="Open Modal"
        onPress={() => {
          setModalVisible(true);
        }}
      />
    </>
  );
}
```
</details>

You can then try to record the screen or take screenshots. You will notice that before opening the modal, you won't be able to see anything in the recording, but when opening the modal, the content is visible.

I've tried my best to record the before and after the fix, but as the screen recordings will mostly show a black screen, you have to forward a bit in both videos to see the difference.

<details>
<summary>Before the fix (notice the blank screen and then content visible)</summary>

https://github.com/user-attachments/assets/fc5bbe26-d238-425b-90d3-0e43c89ccaac

</details>

<details>
<summary>After the fix (notice all the screen recording is a black screen)</summary>

https://github.com/user-attachments/assets/0d6991a0-974b-45c5-8f4a-bf4718c284e6

</details>

Reviewed By: cipolleschi

Differential Revision: D67368741

Pulled By: alanleedev

fbshipit-source-id: 9f31063a9208a6df257da424bf3096bf15a5ddcb
2025-07-01 13:38:11 +00:00
chrsmys bf17c3be3a Fix force RTL support on new architecture. (#49455)
Summary:
This fixes an issue in Fabric where changing the layout direction and then reloading the JS bundle did not honor the layout direction until the app was restarted on iOS. This now calls  `_updateLayoutContext` whenever RCTSurfaceView is recreated which happens on bundle reload. This is not an issue on the old architecture because the layout direction is determined within the [SurfaceViews](https://github.com/facebook/react-native/blob/acdddef48eb60b002c954d7d2447cb9c2883c8b3/packages/react-native/React/Views/RCTRootShadowView.m#L18) which were recreated on bundle reload.

## Related Issues:
- https://github.com/react-native-community/discussions-and-proposals/issues/847
- https://github.com/facebook/react-native/issues/49451
- https://github.com/facebook/react-native/issues/48311
- https://github.com/facebook/react-native/issues/45661

## How can we take this further?
If we want to make it so that it doesn't require an entire bundle reload for RTL to take effect I believe these are the steps that would need to be taken:
- Make it so [RCTI18nManager](https://github.com/facebook/react-native/blob/acdddef48eb60b002c954d7d2447cb9c2883c8b3/packages/react-native/React/CoreModules/RCTI18nManager.mm#L52) exports isRTL as a method instead of consts
- Send Notification Center notif when RTL is forced on or off
- Listen for that notification RCTSurfaceView and call _updateLayoutContext similar to UIContentSizeCategoryDidChangeNotification.

## Changelog:

[iOS] [FIXED] - Layout direction changes are now honored on bundle reload.

<!-- Help reviewers and the release process by writing your own changelog entry.

Pick one each for the category and type tags:

For more details, see:
https://reactnative.dev/contributing/changelogs-in-pull-requests

Pull Request resolved: https://github.com/facebook/react-native/pull/49455

Test Plan:
On the new architecture change force the layout direction and reload the bundle:
```
import React, { useCallback } from "react";
import { Button, I18nManager, StyleSheet, Text, View } from "react-native";
import RNRestart from "react-native-restart";

export default function Explore() {
    const onApplyRTL = useCallback(() => {
        if (!I18nManager.isRTL) {
            I18nManager.forceRTL(true);
            RNRestart.restart();
        }
    }, []);

    const onApplyLTR = useCallback(() => {
        if (I18nManager.isRTL) {
            I18nManager.forceRTL(false);
            RNRestart.restart();
        }
    }, []);

    return (
        <View style={styles.area}>
            <Text>Test Block</Text>
            <View style={styles.testBlock}>
                <Text>Leading</Text>
                <Text>Trailing</Text>
            </View>
            <Button title={"Apply RTL"} onPress={onApplyRTL} />
            <Button title={"Apply LTR"} onPress={onApplyLTR} />
        </View>
    );
}

const styles = StyleSheet.create({
    area: {
        marginVertical: 50,
        paddingHorizontal: 24,
    },
    testBlock: {
        paddingVertical: 10,
        flexDirection: "row",
        justifyContent: "space-between",
    },
});

```

https://github.com/user-attachments/assets/0eab0d79-de3f-4eeb-abd0-439ba4fe25c0

Reviewed By: cortinico, cipolleschi

Differential Revision: D69797645

Pulled By: NickGerleman

fbshipit-source-id: 97499621f3dd735d466f5119e0f2a0eccf1c3c05
2025-07-01 13:29:50 +00:00
zhongwuzw acb4564304 Fabric: Added ScrollEndDragEvent for scrollEndDrag event (#48319)
Summary:
Fixes https://github.com/facebook/react-native/issues/42533 .

## Changelog:

[IOS] [FIXED] -  Fabric: Added ScrollEndDragEvent for scrollEndDrag event

Pull Request resolved: https://github.com/facebook/react-native/pull/48319

Test Plan: Repro please see https://github.com/facebook/react-native/issues/42533 .

Reviewed By: javache

Differential Revision: D67517912

Pulled By: cipolleschi

fbshipit-source-id: aa1caebfb690d09a207b3ebce382eceb520009e5
2025-07-01 13:29:31 +00:00
Zhi Zhou 9469ec7466 fix: iOS app crash caused by the request operation canceling (#48350)
Summary:
Currently we observed many iOS app crashes caused by the `[RCTFileRequestHanlder invalidate]` method, just as the below screenshot.
<img width="1008" alt="image" src="https://github.com/user-attachments/assets/d2d6714f-63d9-40ae-8de5-742cfe718a36" />

## Changelog:

<!-- Help reviewers and the release process by writing your own changelog entry.

Pick one each for the category and type tags:

[IOS] [FIXED] - app crash caused by the `[RCTFileRequestHanlder invalidate]` method

For more details, see:
https://reactnative.dev/contributing/changelogs-in-pull-requests
-->
[IOS] [FIXED] - app crash caused by the `[RCTFileRequestHanlder invalidate]` method

Pull Request resolved: https://github.com/facebook/react-native/pull/48350

Test Plan: I am not able to reproduce this issue locally either, so the changes in this PR are totally from my inference, I am not sure if it really makes sense, so please help take a deeper look, thanks.

Reviewed By: javache

Differential Revision: D69751695

Pulled By: cipolleschi

fbshipit-source-id: aa4654a30f5dfac99b72ed1bda0dae1e0dc881c9
2025-07-01 13:28:59 +00:00
Riccardo Cipolleschi cdbd01100b Pin cmake version to 3.31.6 (#50464)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/50464

Runners in GHA has been updated by github and they now ship with CMake 4.0. (actions/runner-images#11926)

This version is not compatible with React Native, so we are pinning cmake to 3.36.1

## Changelog:
[Internal] - Pin cmake to 3.36.1

Reviewed By: cortinico

Differential Revision: D72379834

fbshipit-source-id: ab09009102118e6590f02cf57fa6f9149482f62b
2025-07-01 13:27:50 +00:00
Riccardo Cipolleschi 5fd25e3ebb Bump windows to latests as GH will remove windows-2019 EOM (#51798)
Summary:
As per title, GH is [removing windows-2019](https://github.com/facebook/react-native/actions/runs/15421451006/job/43403215354) at the end of the month. We need to migrate away from them.

## Changelog:
[Internal] - Bump windows runners

Pull Request resolved: https://github.com/facebook/react-native/pull/51798

Test Plan: GHA

Reviewed By: cortinico

Differential Revision: D75946994

Pulled By: cipolleschi

fbshipit-source-id: 972b60300c918c0eae0403ed4149347a4ffa8bd0
2025-07-01 14:25:51 +01:00
Nicola Corti e30475db51 Publish, Close and Release the Maven repository during build_npm_package
Summary:
Due to us moving to central.sonatype.com for publishing, we cannot publish and release the Maven repository in 2 distinct invocations.
This consolidates all the publishing job to happen during build_npm_package

Changelog:
[Internal] [Changed] -

Reviewed By: fabriziocucci

Differential Revision: D76888543

fbshipit-source-id: 3cb0db6176ed2221a12b4f3f1f575232aa006a6c
2025-06-18 14:01:49 +01:00
Nicola Corti e7e8222ff4 Update Nightly URL for newly published versions on central.sonatype.com (#52004)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/52004

This is necessary because the snapshots are now going to be published on a different repository:
central.sonatype.com.

Changelog:
[Internal] [Changed] -

Reviewed By: cipolleschi

Differential Revision: D76596802

fbshipit-source-id: 424fb1134e41502d53b76209fba325c895c79ba8
2025-06-16 10:44:37 +01:00
Nicola Corti 9399becf4e Move React Native publishing URLs to Central Portal (#51693)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/51693

This moves React Native to use the Central Portal URLs rather than the legacy OSSRH ones.
See https://github.com/gradle-nexus/publish-plugin for more context.

Changelog:
[Internal] [Changed] -

Reviewed By: cipolleschi

Differential Revision: D75673984

fbshipit-source-id: 1de6746809eed72f232eac0c3fb4d809c2046620
2025-06-16 10:44:37 +01:00
Rob Hogan d7b2d973d1 Update Podfile.lock
Changelog: [Internal]
2025-03-25 22:13:38 +00:00
React Native Bot 7ea8af836d Release 0.77.2
#publish-packages-to-npm&0.77-stable
2025-03-25 12:46:58 +00:00
Riccardo Cipolleschi 1e9f118589 Convert to JSException only NSException from sync methods (#50193)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/50193

This fix makes sure that we convert to JSException only NSException thrwn by sync methods.
Currently, nothing in the stack will be capable of understanding that js error if it is triggered by an exception raised by an asyc method.

See https://github.com/reactwg/react-native-new-architecture/discussions/276 for further details

We need to cherry pick this in 0.78 and 0.79

## Changelog:
[iOS][Fixed] - Make sure the TM infra does not crash on NSException when triggered by async method

Reviewed By: fabriziocucci

Differential Revision: D71619229

fbshipit-source-id: b87aef5dd2720a2641c8da0904da651866370dc6
2025-03-24 12:47:44 +00:00
Rob Hogan b335436113 Bump minimum Metro to 0.81.3, remove import/require from always-asserted resolver conditions (#50127) 2025-03-19 13:05:06 +00:00
Riccardo Cipolleschi a1339673ad Handle null params in the Interop TM layer (#49873)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/49873

In the old architecture, when we were passing a `null` value as a parameter in a function that accepted nullable parameter, the null value was mapped to `nil` on iOS.

After my changes in [d4236791e2](https://github.com/facebook/react-native/commit/d4236791e238a614d2fadf5c5659874d983ab029), in the New Architecture, through the interop layer, legacy modules were receiving an `NSNull` object instead of nil.

This was breaking those modules which started crashing or observing undesired behavior.

This change fixes the issue by making sure that, in those cases, a `nil` value is passed.

Note that nested objects in the old architecture were correctly receiving NSNull, so nested objects were behaving correctly already.

## Changelog:
[iOS][Fixed] - Properly pass `nil` for nullable parameters instead of `NSNull` for legacy modules

Reviewed By: javache

Differential Revision: D70723460

fbshipit-source-id: 384f48b6dbb3f54c369b31b6d2ee06069fa3591c
2025-03-19 11:10:29 +00:00
Riccardo Cipolleschi 4e5da2ea1d Add extra parameter to define whether codegen is invoked by lib or app (#48995)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/48995

This change adds an extra parameter to the codegen script that allow our users to trigger codegen for Apps or for Libraries.

When running codegen for Apps, we have to generate some extra files that are not needed by the Libraries. This is causing issues to our library maintainers and this change will provide more flexibility in the DevX of libraries.

The default value is App, so if the new parameter is not passed, nothing will change in the current behavior.
[iOS][Added] - Add the `source` parameter to generate-codegen-artifacts to avoid generating files not needed by libraries.

Reviewed By: cortinico

Differential Revision: D68765478

fbshipit-source-id: 8030b4472ad4f5058e58b1c91089de5122a4f60a
2025-03-18 15:30:31 +00:00
Rob Hogan 1f002f9999 community-cli-plugin: resolve cli-server-api via peer dependency on cli (#50096) 2025-03-18 14:30:44 +00:00
CHOIMINSEOK f7720b35a4 Avoid NPE when touch event is triggered before SurfaceManager is initiated (#48007)
Summary:
A NPE can occur when a user touches the screen before the `SurfaceMountingManager` is initialized. Below is an example of the error log from our production service. This issue can also be reproduced using RNTester. To prevent invalid touch events during init time of rn app from causing an NPE, add a null check for SurfaceMountingManager before calling mark/sweepActiveTouchForTag.

```
Fatal Exception: java.lang.NullPointerException: Attempt to invoke virtual method 'void com.facebook.react.fabric.mounting.SurfaceMountingManager.markActiveTouchForTag(int)' on a null object reference
       at com.facebook.react.fabric.FabricUIManager.markActiveTouchForTag(FabricUIManager.java)
       at com.facebook.react.uimanager.JSTouchDispatcher.markActiveTouchForTag(JSTouchDispatcher.java)
       at com.facebook.react.uimanager.JSTouchDispatcher.handleTouchEvent(JSTouchDispatcher.java)
       at com.facebook.react.runtime.ReactSurfaceView.dispatchJSTouchEvent(ReactSurfaceView.java)
       at com.facebook.react.ReactRootView.onInterceptTouchEvent(ReactRootView.java)
       at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2870)
       at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3352)
       at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2963)
       at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3352)
       at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2963)
       at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3352)
       at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2963)
       at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3352)
       at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2963)
       at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3352)
       at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2963)
       at com.android.internal.policy.DecorView.superDispatchTouchEvent(DecorView.java:794)
       at com.android.internal.policy.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1967)
       at android.app.Activity.dispatchTouchEvent(Activity.java:4571)
       at com.rainist.banksalad2.feature.common.BaseActivity.dispatchTouchEvent(BaseActivity.java)
       at androidx.appcompat.view.WindowCallbackWrapper.dispatchTouchEvent(WindowCallbackWrapper.java:70)
       at com.android.internal.policy.DecorView.dispatchTouchEvent(DecorView.java:752)
       at android.view.View.dispatchPointerEvent(View.java:16498)
       at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:8676)
       at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:8423)
       at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:7752)
       at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:7809)
       at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:7775)
       at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:7978)
       at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:7783)
       at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:8035)
       at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:7756)
       at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:7809)
       at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:7775)
       at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:7783)
       at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:7756)
       at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:11343)
       at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:11212)
       at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:11168)
       at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:11477)
       at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:337)
       at android.os.MessageQueue.nativePollOnce(MessageQueue.java)
       at android.os.MessageQueue.next(MessageQueue.java:335)
       at android.os.Looper.loopOnce(Looper.java:187)
       at android.os.Looper.loop(Looper.java:319)
       at android.app.ActivityThread.main(ActivityThread.java:9063)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:588)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1103)
```
https://github.com/user-attachments/assets/e9c6ff84-c94d-4392-9042-8e635197202e

## Changelog:

[Android] [Fixed] - Avoid NPE when touch event is triggered before SurfaceManager is initiated

Pull Request resolved: https://github.com/facebook/react-native/pull/48007

Test Plan:
I checked the crashed being fixed on RNTester.

https://github.com/user-attachments/assets/71f7e359-707a-494c-ae34-fef8d432e612

Reviewed By: cortinico

Differential Revision: D66594576

Pulled By: javache

fbshipit-source-id: b1559d94866bdb021e0374f1953684849603033c
2025-03-17 11:12:35 +00:00
zhongwuzw adc5c3afa2 Fixes TextInput crashes when any text is entered while running as iOS app on apple silicon mac (#49320)
Summary:
Fixes https://github.com/facebook/react-native/issues/48544. We can make `RCTWeakEventEmitterWrapper` to subclass `NSDictionary` that Textinput supports encode it.

System allowed classes are:
```
Allowed classes are:
 {(
    "'NSMorphology' (0x20088b6d8) [/System/Library/Frameworks/Foundation.framework]",
    "'NSString' (0x2003f4738) [/System/Library/Frameworks/Foundation.framework]",
    "'NSInflectionRule' (0x20088d348) [/System/Library/Frameworks/Foundation.framework]",
    "'UIColor' (0x2006c0520) [/System/iOSSupport/System/Library/PrivateFrameworks/UIKitCore.framework]",
    "'NSTextAttachment' (0x20042af98) [/System/Library/PrivateFrameworks/UIFoundation.framework]",
    "'NSShadow' (0x20042ae30) [/System/Library/PrivateFrameworks/UIFoundation.framework]",
    "'NSTextEncapsulation' (0x200897e50) [/System/Library/Frameworks/CoreText.framework]",
    "'NSTextAlternatives' (0x20042af70) [/System/Library/PrivateFrameworks/UIFoundation.framework]",
    "'NSFont' (0x20042a9f8) [/System/Library/PrivateFrameworks/UIFoundation.framework]",
    "'NSAttributedString' (0x2003f3838) [/System/Library/Frameworks/Foundation.framework]",
    "'NSData' (0x2003ed528) [/System/Library/Frameworks/CoreFoundation.framework]",
    "'NSURL' (0x2003ed938) [/System/Library/Frameworks/CoreFoundation.framework]",
    "'NSAdaptiveImageGlyph' (0x2008ae538) [/System/Library/PrivateFrameworks/UIFoundation.framework]",
    "'NSNumber' (0x2003f4238) [/System/Library/Frameworks/Foundation.framework]",
    "'NSParagraphStyle' (0x20042ad40) [/System/Library/PrivateFrameworks/UIFoundation.framework]",
    "'NSDictionary' (0x2003ed5a0) [/System/Library/Frameworks/CoreFoundation.framework]",
    "'UIFont' (0x20042c668) [/System/Library/PrivateFrameworks/UIFoundation.framework]",
    "'NSColor' (0x200412350) [/System/Library/Frameworks/AppKit.framework]",
    "'NSGlyphInfo' (0x20042aa98) [/System/Library/PrivateFrameworks/UIFoundation.framework]",
    "'NSArray' (0x2003ed460) [/System/Library/Frameworks/CoreFoundation.framework]",
    "'NSPresentationIntent' (0x20088da28) [/System/Library/Frameworks/Foundation.framework]"
)}
```

## Changelog:

[IOS] [FIXED] -  Fixes TextInput crashes when any text is entered while running as iOS app on apple silicon mac

Pull Request resolved: https://github.com/facebook/react-native/pull/49320

Test Plan:
Run RNTester on apple silicon mac, and entered some text in textinput, no crash occured. Also, verified that the caret was not jumping around, thus preserving the original fix.

https://github.com/user-attachments/assets/6304f6e7-c663-4351-ace8-ab1842ee545f

Reviewed By: javache

Differential Revision: D69981500

Pulled By: cipolleschi

fbshipit-source-id: 2af9b280e42f621446efda9b101af50525e8fef7
2025-03-11 14:23:53 +00:00
Vitali Zaidman 4c95fcd53b increase ping-pong timeout before killing WS connection to DevTools (#49358)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/49358

When the network is under strain, the code responsible for detecting if the inspector proxy's connection to the client has been lost may incorrectly assume the connection is dead. This false positive occurs because the system assumes that if a pong is not received within 5 seconds of a ping, the other side has disconnected. However, I was able to consistently reproduce scenarios where a delay of more than 5 seconds (even more than 20 seconds) was followed by a return to normal ping-pong communication without any issues.

Since I can't think of any issues with increasing this number, I'm increasing it to 60s.

Changelog:
[General][Fixed] - Disconnections of DevTools when the network is under significant strain.

Reviewed By: robhogan, huntie

Differential Revision: D69523906

fbshipit-source-id: 50db1e7bbe690b42421bc226aa30fd6571ba2257
2025-03-11 11:47:46 +00:00
Fabio PelizzolaandMateo Guzmán 7c50b5eca5 Fix Android Image defaultSource runtime error (#49097) (#49890)
Summary:
Fixes https://github.com/facebook/react-native/issues/49075

The Image `defaultSource` prop is causing a runtime error from 0.77 just by using it in the Image component (see error in the linked issue). This might be a regression from some changes in the prop processing logic from either https://github.com/facebook/react-native/issues/47710, https://github.com/facebook/react-native/issues/47713 or https://github.com/facebook/react-native/issues/47754.

## Changelog:

[ANDROID] [FIXED] - Fix Image defaultSource runtime error

Pull Request resolved: https://github.com/facebook/react-native/pull/49097

Test Plan:
- Verify it doesn't throw any error on runtime anymore for Android.
- iOS behaviour should not be impacted as changes are Android specific

In the RNTester, you can use the following component:

```tsx
import * as React from 'react';
import {Image, View} from 'react-native';

function Playground() {
  return (
    <View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
      <Image
        defaultSource={require('../../assets/bandaged.png')}
        source={{
          uri: 'https://i.natgeofe.com/n/548467d8-c5f1-4551-9f58-6817a8d2c45e/NationalGeographic_2572187_4x3.jpg',
        }}
        style={{width: 200, height: 200}}
      />
    </View>
  );
}
```

Also, this `defaultSource` prop is ignored in debug builds for Android ([as per the docs](https://reactnative.dev/docs/image#defaultsource)) – but I've verified we get the defaultSource as a string, which is what we expect on the native side:

<details>
<summary>Screenshot of the Android logs</summary>

![image](https://github.com/user-attachments/assets/e62ae2c3-6a93-4e44-a2d7-c913f5db2173)

</details>

Reviewed By: javache

Differential Revision: D69052723

Pulled By: cortinico

fbshipit-source-id: 2860dd4c18cefcfcbc4e39f94dfa6305f45773a3

# Conflicts:
#	packages/react-native/Libraries/Image/ImageViewNativeComponent.js

Co-authored-by: Mateo Guzmán <info@mateoguzman.net>
2025-03-10 11:25:12 +00:00
Rob Hogan 61314c73f0 Update Podfile.lock
Changelog: [Internal]
2025-02-19 12:39:38 +00:00
React Native Bot 110450105e Release 0.77.1
#publish-packages-to-npm&latest
2025-02-13 18:07:43 +00:00
Alex Hunt d682655ec1 Fix rn-tester convenience scripts (#49401)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/49401

These scripts erroneously invoked `npm`, requiring `--` to forward args.

Discovered while testing https://github.com/facebook/react-native/pull/49353.

Changelog: [Internal]

Reviewed By: cortinico

Differential Revision: D69594024

fbshipit-source-id: a6e8cccaf994f1472b51155d36a49ed17cd291dc
2025-02-13 16:49:38 +00:00
Alex Hunt 969eb3f007 Restore Metro log streaming via CLI flag (#49356)
Summary:
This change adds an opt-in to restore JavaScript log streaming via the Metro dev server, [removed from React Native core in 0.77](https://reactnative.dev/blog/2025/01/21/version-0.77#removal-of-consolelog-streaming-in-metro).

Users can opt into this legacy behaviour by adding the `--client-logs` flag to `npx react-native-community/cli start`.

- The default experience remains without streamed JS logs.
- The existing "JavaScript logs have moved! ..." notice is printed in all cases, and we do not advertise the new flag for new users.
- Under non-Community CLI dev servers (i.e. Expo), log streaming is restored implicitly.

We will clean up this functionality again when we eventually remove JS log streaming over `HMRClient`, tasked in T214991636.

**Implementation notes**

- Logs are always sent over `HMRClient` (previous status quo), even with log streaming off in the dev server. This is a necessary evil to be able to flag this functionality in a user-accessible place, and to move fast for 0.78.
- Necessarily, emitting `fusebox_console_notice` moves to the dev server itself, on first device (Fusebox) connection.

Changelog:
[General][Added] - Add opt in for legacy Metro log streaming via `--client-logs` flag
2025-02-12 15:17:13 +00:00
Riccardo Cipolleschi 475f797a51 Properly handle null values coming from JS (#49250)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/49250

The TurboModule System decided to ignore the Null values when they are coming to JS. However, in iOS, null value can be mapped to `[NSNull null];` and this value is a valid value that can be used on the native side.

In the old architecture, when the user were sending a null value from JS to a native module, the Native side was receiving the value.

In the New Architecture, the value was stripped away.

This change allow us to handle the `null` value properly in the interop layer, to restore the usage of legacy modules in the New Arch.

I also tried with a more radical approach, but several tests were crashing because some modules do not know how to handle `NSNull`.

See discussion happening here: https://github.com/invertase/react-native-firebase/issues/8144#issuecomment-2548067344

## Changelog:
[iOS][Changed] - Properly handle `null` values coming from NativeModules.

Reviewed By: sammy-SC

Differential Revision: D69301396

fbshipit-source-id: be275185e2643092f6c3dc2481fe9381bbcf69e9
2025-02-11 15:03:00 +00:00
David Vacca d8af0ae3f2 Fix execution of early InteropEvents (#48823)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/48823

This diff is fixing the execution of Events that are sent early in the rendering of surfaces.

This diff fixes a bug in the queueing of events that are built with not surfaceId (-1), the fixes is to call getSurfaceManagerForView() to retrieve the proper surfaceId (as we do in the execution of events)

calling getSurfaceManagerForView() has a perf hit, we believe this won't be a problem because this method will only be called in edge cases (no surfaceId and early execution of events)

changelog: [Android][Fixed] Fix execution of early InteropEvents

Reviewed By: shwanton, lenaic

Differential Revision: D68454811

fbshipit-source-id: a79be0b392004e645c48d1683bba774b6b597ca0
2025-02-11 14:59:48 +00:00
Riccardo Cipolleschi 83b986d370 Be less strict with method parsing of TurboModule Interop Layer (#49072)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/49072

We have instance of apps crashing when enabling the New Architecture because of the TurboModule interop layer.

What's happening is that when the module is loaded, the TM Interop Layer tries to parse the method definition to expose them in JS. However, for some libraries in the Legacy Architecture, it is possible to define a method in Objective-C and to define a different signature in Swift.

For example, the [`RNBluetoothClassic` library](https://github.com/kenjdavidson/react-native-bluetooth-classic) defines a selector in objective-c which [has the signature](https://github.com/kenjdavidson/react-native-bluetooth-classic/blob/main/ios/RNBluetoothClassic.m#L134-L136)

```
RCT_EXTERN_METHOD(available: (NSString *)deviceId
                  resolver: (RCTPromiseResolveBlock)resolve
                  rejecter: (RCTPromiseRejectBlock)reject)
```

And the method is inmplemented in Swift with [the signature](https://github.com/kenjdavidson/react-native-bluetooth-classic/blob/main/ios/RNBluetoothClassic.swift#L502-L505):

```
func availableFromDevice(
        _ deviceId: String,
        resolver resolve: RCTPromiseResolveBlock,
        rejecter reject: RCTPromiseRejectBlock
    )
```

When the TurboModule interop layer tries to parse the method, it receives the `accept:resolver:rejecter:` signature, but that signature is not actually defined in as a method in the module instance, and it crashes.

This crash was not happening in the Old Architecture, which was handling this case gracefully. Notice that the specific method from the example is not working in the Old Architecture either. However, the app is not crashing in the old architecture.

This change adds the same graceful behaviors plus it adds a warning in development to notify the developer about which methods couldn't be found in the interface.

Fixes:
- https://github.com/facebook/react-native/issues/47587
- https://github.com/facebook/react-native/issues/48065

## Changelog:
[iOS][Fixed] - Avoid crashing the app when the InteropLayer can't find some methods in the native implementation.

Reviewed By: javache

Differential Revision: D68901734

fbshipit-source-id: 844d1bf29423d5c601b583540e86d57dfffd1428
2025-02-11 14:58:41 +00:00
Rickard Zrinski 8baa858412 Fix maxFontSizeMultiplier prop on Text and TextInput components in new architecture (#47614)
Summary:
The `maxFontSizeMultiplier` prop for `Text` and `TextInput` was not handled in Fabric / New Architecture as documented in https://github.com/facebook/react-native/issues/47499.

bypass-github-export-checks

[GENERAL] [FIXED] - Fix `maxFontSizeMultiplier` prop on `Text` and `TextInput` components in Fabric / New Architecture

Pull Request resolved: https://github.com/facebook/react-native/pull/47614

Test Plan:
I have not added any automated tests for this change but try to do so if requested. I have however added examples to RN Tester for both the Text and TextInput components, as well as compared the behaviour with Paper / Old Architecture. Both on version 0.76.

Noticed now I didn't do exactly the same steps in both videos, oops! Be aware that reapplying changes made in the Settings are currently half-broken on the new architecture, thus I'm restarting the app on Android and iOS. But this issue is unrelated to my changes. I've tested on main branch and it has the same issue.

Here are comparison videos between Paper and Fabric on iOS *after* I've made my fix.

| Paper  | Fabric |
| ------------- | ------------- |
| <video src="https://github.com/user-attachments/assets/f4fd009f-aa6d-41ab-92fa-8dcf1e351ba1" /> | <video src="https://github.com/user-attachments/assets/fda42cc6-34c2-42a7-a6e2-028e7c866075" /> |

| Paper  | Fabric |
| ------------- | ------------- |
| <video src="https://github.com/user-attachments/assets/59b59f7b-25d2-4b5b-a8e2-d2054cc6390b" /> | <video src="https://github.com/user-attachments/assets/72068566-8f2a-4463-874c-45a6f5b63b0d" /> |

Reviewed By: Abbondanzo

Differential Revision: D65953019

Pulled By: cipolleschi

fbshipit-source-id: 90c3c7e236229e9ad9bd346941fafe4af8a9d9fc
2025-02-11 14:55:34 +00:00
Tommy Nguyen 877e82c1a9 fix(dev-middleware): add missing invariant dependency (#49047)
Summary:
`dev-middleware` uses `invariant` but does not declare it as a dependency. Under certain hoisting scenarios, or when using pnpm, this will cause `dev-middleware` to fail while being loaded.

## Changelog:

[GENERAL] [FIXED] - add missing `invariant` dependency

Pull Request resolved: https://github.com/facebook/react-native/pull/49047

Test Plan: n/a

Reviewed By: cortinico

Differential Revision: D68835789

Pulled By: huntie

fbshipit-source-id: 13718f4970ed55e6e062b7c2bd719be977abdd0c
2025-02-11 14:36:09 +00:00
Jorge Cabiedes Acosta 55d5c44976 Fix elevation with border-radius set (#48982)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/48982

We were missing a conversion to px on the `getOutline()` function of `CompositeBackgroundDrawable` which led to incorrect elevation prop rendering

Changelog: [Android][Fixed] - Elevation prop on android has incorrect border-radius

Reviewed By: NickGerleman

Differential Revision: D68724947

fbshipit-source-id: b3a7a4919bfd7c60fac7c3d6e3ba760e3f74d190
2025-02-11 14:35:21 +00:00
Riccardo Cipolleschi bc35afefd5 Fix images not displayed when extension is implicit (#48888)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/48888

We have a report from OSS where Images are not displayed properly in case they are saved on disk with no extension.

We previously had a fix attempt iwith [this pr](https://github.com/facebook/react-native/pull/46971), but this was breaking some internal apps.

This second attempt should work for both cases.

## Changelog:
[iOS][Fixed] - Load images even when the extension is implicit

Reviewed By: cortinico

Differential Revision: D68555813

fbshipit-source-id: bc25970aafe3e6e5284163b663d36e00b3df3d82
2025-02-11 14:30:24 +00:00
Rob Hogan ad8a5de275 Update Podfile.lock
Changelog: [Internal]
2025-01-21 15:22:00 +00:00
Riccardo Cipolleschi aef6243edd Fix ruby (#48721)
Summary:
Following the suggestions [here](https://stackoverflow.com/questions/79360526/uninitialized-constant-activesupportloggerthreadsafelevellogger-nameerror), it seems that concurrent-ruby has been released tonight and it is bugged. Let's pin it to the right version.

## Changelog:
[iOS][Changed] - Pin 'concurrent-ruby' to a working version

Pull Request resolved: https://github.com/facebook/react-native/pull/48721

Test Plan: GHA

Reviewed By: robhogan

Differential Revision: D68262719

Pulled By: cipolleschi

fbshipit-source-id: fc6410e28cc96f9d3769d3082a77cac0a3efe6db
2025-01-21 15:16:30 +00:00
React Native Bot 4eef167252 Release 0.77.0
#publish-packages-to-npm&latest
2025-01-21 11:14:55 +00:00
Rob Hogan 46cf58770d Update Podfile.lock
Changelog: [Internal]
2025-01-16 17:57:53 +00:00
React Native Bot 06c8a745b0 Release 0.77.0-rc.7
#publish-packages-to-npm&next
2025-01-16 16:32:06 +00:00
Rob Hogan 50678997b9 fix hermes param handling in test-e2e-local.js (#48068)
Summary:
why: running `yarn test-e2e-local -t "RNTestProject" -p "Android" -h false -c $GITHUB_TOKEN` would actually build the app with Hermes even though it's specified as disabled.

This is because of the `if (argv.hermes == null)` condition whose body would not execute.
The condition was changed [recently](f322dc7#diff-56f57bf0eac99b0fda1b2938aceb8d9b663db82c07cb405bd53a01c8689710ffR258).

Reason for `await` being used:

```
Error ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ scripts/release-testing/test-e2e-local.js:303:32

Cannot get argv.hermes because property hermes is missing in Promise [1]. [prop-missing]

     scripts/release-testing/test-e2e-local.js
     300│     'reactNativeArchitectures=arm64-v8a',
     301│     'android/gradle.properties',
     302│   );
     303│   const hermesEnabled = (argv).hermes === true;
     304│
     305│   // Update gradle properties to set Hermes as false
     306│   if (!hermesEnabled) {

     flow-typed/npm/yargs_v17.x.x.js
 [1]  80│     argv: Argv | Promise<Argv>;
 ```

## Changelog:

[INTERNAL] [FIXED] - fix `hermes` param handling in `test-e2e-local.js`
2025-01-16 15:13:05 +00:00
Rob Hogan 0ed16b407f [Local] Update feature flags generated signature
Changelog: [Internal]
2025-01-15 10:44:25 +00:00
Rob Hogan 2561db80b1 [Local] Fix Flow error with unsupported feature flag metadata
Fixes a Flow error introduced when picking https://github.com/facebook/react-native/pull/48669

Changelog: [Internal]
2025-01-15 10:39:30 +00:00
Samuel Susla 2013bad254 Back out "RN: Enable useInsertionEffectsForAnimations" (#48669)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/48669

Original commit changeset: d09b2f1b7607

Original Phabricator Diff: D65906157

[General] [Fixed] - Disable useInsertionEffectsForAnimations, Fix buttons becoming unresponsive when transform is animated

Reviewed By: yungsters

Differential Revision: D68152746

fbshipit-source-id: aa0c0aa3243c67c95128a75b40dd6aa1251abbca
2025-01-15 10:20:31 +00:00
Alex Hunt 36a0cad953 Convert assets-registry to Flow comment syntax (#48458)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/48458

NOTE: This change is made once and is not guaranteed.

**Motivation**: Requiring Flow parsing for `react-native` and its dependencies in user space can involve friction. For the case of `react-native/assets-registry` → `react-native-web`, I believe we should do the pragmatic thing to relax this requirement.

- This is a convenience stopgap until https://github.com/facebook/react-native/pull/39542 can be stabilised.
- This package is tiny and infrequently modified — I believe it's pragmatic/safe to do a one-time conversion, with the above notice and no changelog (i.e. "experimental" for now).

Resolves https://github.com/facebook/react-native/issues/48349.

Changelog: [Internal]

Reviewed By: cortinico

Differential Revision: D67764460

fbshipit-source-id: 7687fd79c6dea73c234a46e475c1cc745225830b
2025-01-15 10:11:00 +00:00
Mateo Guzmán 4b3ef3b00c Paper: TextInput maxLength is not working in old arch (#48126)
Summary:
Fixes https://github.com/facebook/react-native/issues/47563

It seems like a regression from https://github.com/facebook/react-native/issues/45401, where it was aimed to fix `onChangeText` being called multiple times when changing the text programmatically in an input with the `multiline` prop set as true.

This PR reverts that partially, as the `maxLength` check is not being evaluated correctly before setting the text. Not reverting it completely as when removing the second part of the fix, the `onChangeText` gets called multiple times again.

## Changelog:

[IOS] [FIXED] - Fixing TextInput `maxLength` not working in old arch

Pull Request resolved: https://github.com/facebook/react-native/pull/48126

Test Plan:
The issue could be reproduced in the rn-tester. See my videos with the before and after the fix.

<details>
<summary>Before:</summary>

https://github.com/user-attachments/assets/86fd67eb-fc14-469a-a5f8-8e83b49f857c

</details>

<details>
<summary>After:</summary>

https://github.com/user-attachments/assets/368383b1-c1bd-4e0b-ac44-c78022462fa0

</details>

Reviewed By: cortinico

Differential Revision: D67025182

Pulled By: cipolleschi

fbshipit-source-id: 720c400eef362618106ae434aef421c7529214fe
2025-01-15 10:09:39 +00:00
Nick Gerleman e7db7a7266 Fix TextMeasureCacheKey Throwing Out Some LayoutConstraints (#48525)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/48525

Fixes https://github.com/facebook/react-native/issues/48249

`TextMeasureCacheKey` hash and equality functions only incorporates the maximum width constraint. I'm guessing this was an attempt at an optimization, but it can lead to incorrect results in pretty trivial cases. E.g. if Yoga knows a definite size of `Text` in one dimension,  and measures via `YGMeasureModeExactly`, we can have a minimum size corresponding specific to the style in which the text was laid out.

Changelog:
[General][Fixed] - Fix TextMeasureCacheKey Throwing Out Some LayoutConstraints

Reviewed By: christophpurrer

Differential Revision: D67922414

fbshipit-source-id: 0ee0220059fc4e4645b1684c42a0587fe728bedd
2025-01-15 10:08:23 +00:00
Rob Hogan bbbeef6fdb Update Podfile.lock
Changelog: [Internal]
2025-01-06 19:57:56 +00:00
React Native Bot 407c4fbffd Release 0.77.0-rc.6
#publish-packages-to-npm&next
2025-01-06 17:16:33 +00:00
timbocole 5a64bde701 fix: Prioritise local cpp (use default as fallback) (#48340)
Summary:
https://github.com/facebook/react-native/pull/47379 removed local cpp sources from the sources being built with the app. This resulted in a local `android/app/src/main/jni/OnLoad.cpp` file being ignored at build time. I have therefore added logic to the cmake file to prioritise local `cpp` files and fallback to `${REACT_ANDROID_DIR}/cmake-utils/default-app-setup/*.cpp` if none exist.

This resolves https://github.com/facebook/react-native/issues/48298

## Changelog:
[ANDROID] [FIXED] - Prioritise local OnLoad.cpp, falling back to default-app-setup

Pull Request resolved: https://github.com/facebook/react-native/pull/48340

Test Plan:
- Followed the https://reactnative.dev/docs/the-new-architecture/pure-cxx-modules guide (which was broken > 0.76.1)
- Applied the patch to the reproduction repository linked to https://github.com/facebook/react-native/issues/47352 to ensure no regression

Reviewed By: cipolleschi

Differential Revision: D67736012

Pulled By: cortinico

fbshipit-source-id: 87f6b8edf1613682585a94e1d1b3e6b4b792e4f5
2025-01-06 13:03:32 +00:00
Nicola Corti 17a5d2be5a [0.77] Fix RN-Tester JSC instacrashing (#48417) 2024-12-31 12:29:48 +00:00
Rob Hogan bb9d7ad9a9 Update Podfile.lock
Changelog: [Internal]
2024-12-30 23:26:03 +00:00
React Native Bot 9d472ae28d Release 0.77.0-rc.5
#publish-packages-to-npm&next
2024-12-30 19:07:27 +00:00
zhongwuzw 0bcb0c2b2f Fabric: Fixes Numeric TextInput not triggering onSubmitEditing (#48276)
Summary:
Fixes https://github.com/facebook/react-native/issues/48259 . The paper code like :
https://github.com/facebook/react-native/blob/2fee13094b3d384c071978776fd8b7cff0b6530f/packages/react-native/Libraries/Text/TextInput/RCTBaseTextInputView.mm#L777-L784

## Changelog:

[IOS] [FIXED] - Fabric: Fixes Numeric TextInput not triggering onSubmitEditing

Pull Request resolved: https://github.com/facebook/react-native/pull/48276

Test Plan: Repro in https://github.com/facebook/react-native/issues/48259

Reviewed By: cipolleschi

Differential Revision: D67274739

Pulled By: javache

fbshipit-source-id: 57396c6e1a8ef96a1b29cae4a9aa9b5f48f6080b
2024-12-30 12:16:25 +00:00
kirillzyusko d34032b6c0 fix: do not overwrite external inputAccessoryView on Fabric (#48339)
Summary:
If 3rd party libs are using `inputAccessoryView` - the current code can easily break it. Whenever props gets changed we call `setDefaultInputAccessoryView` which will simply overwrite the current `inputAccessoryView` (which is highly undesirable).

The same fix on paper was made ~7 years ago: https://github.com/facebook/react-native/commit/bf3698323d81508fc77174df2b1ffe5fb03224e7

## Changelog:

<!-- Help reviewers and the release process by writing your own changelog entry.

Pick one each for the category and type tags:

[IOS] [FIXED] - Fixed problem with accessory view & 3rd party libs

For more details, see:
https://reactnative.dev/contributing/changelogs-in-pull-requests

Pull Request resolved: https://github.com/facebook/react-native/pull/48339

Test Plan: Make sure `inputAccessoryView` functionality works as before

Reviewed By: javache

Differential Revision: D67451188

Pulled By: cipolleschi

fbshipit-source-id: bc3fa82ae15f8acedfd0b4e17bdea69cbd8c8a8d
2024-12-30 12:16:21 +00:00
Riccardo Cipolleschi 489b22c797 [LOCAL][RN][0.77] Bump Android Caches (#48373) 2024-12-24 11:30:24 +00:00
Rob Hogan 5b8ed374da Update Podfile.lock
Changelog: [Internal]
2024-12-24 11:10:08 +00:00
React Native Bot b0d9ef80da Release 0.77.0-rc.4
#publish-packages-to-npm&next
2024-12-23 16:50:28 +00:00
Thomas Nardone e3970a4bb3 Restore subclipping view removal (#48329)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/48329

With the call to `removeView()` removed from `ReactViewClippingManager` in https://github.com/facebook/react-native/pull/47634, we're seeing views erroneously sticking around in the layout.

While `removeViewWithSubviewClippingEnabled()` calls `removeViewsInLayout()`, it does not trigger the corresponding side effects of [removeView()](https://android.googlesource.com/platform/frameworks/base/+/refs/tags/android-15.0.0_r9/core/java/android/view/ViewGroup.java#5501):
```
public void removeView(View view) {
  if (removeViewInternal(view)) {
--> requestLayout();
--> invalidate(true);
  }
}
```
To compensate, flip `removeViewsInLayout()` to [`removeViews()`](https://android.googlesource.com/platform/frameworks/base/+/refs/tags/android-15.0.0_r9/core/java/android/view/ViewGroup.java#5562), which will ensure layout.

Changelog: [Android][Fixed] Restore layout/invalidate during ReactViewClippingManager.removeViewAt()

Reviewed By: javache

Differential Revision: D67398971

fbshipit-source-id: b100db468cc3be6ddc6edd6c6d078a8a0b59a2c1
2024-12-23 12:14:51 +00:00
Riccardo Cipolleschi 4370860c8c [LOCAL] Bump podfile.lock 2024-12-17 11:36:23 +00:00
React Native Bot 0840eab8ed Release 0.77.0-rc.3
#publish-packages-to-npm&next
2024-12-17 10:01:53 +00:00
Nicola Corti e7c44903e1 Gradle to 8.11.1 (#48026)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/48026

This should mitigate this particular issue we're seeing on Windows:
- https://github.com/facebook/react-native/issues/46210

Changelog:
[Android] [Changed] - Gradle to 8.11.1

Reviewed By: javache

Differential Revision: D66600321

fbshipit-source-id: d58437485222e189d90bcf4d6b41ca956449ed22
2024-12-16 15:35:32 +00:00
Riccardo Cipolleschi 6b22412069 [LOCAL] Add missing imports in ReactViewGroup.java and remove unused symbols 2024-12-16 15:32:04 +00:00
Riccardo Cipolleschi c4c52fb2bd [LOCAL] Fix badly resolved conflict on ReactViewGroup 2024-12-16 14:56:11 +00:00
Riccardo Cipolleschi d4941c7c6d [LOCAL][RN][CI] Add Maestro tests for the Old Arch in Template App (#48045) 2024-12-16 14:24:37 +00:00
Nicola Corti 125b0f47d1 Revert "Include autolinkin.h in OnLoad.cpp only if it exists (#47875)"
This reverts commit 5b2bbb84b1.
2024-12-16 14:23:56 +00:00
Kacper Kafara 81aaf46c67 Fix handling removal of transitioning views (#47634)
Summary:
Related PR in `react-native-screens`:

* https://github.com/software-mansion/react-native-screens/pull/2495

Additional context:
   * [my detailed explanation of **one of the issues**](https://github.com/software-mansion/react-native-screens/pull/2495#issuecomment-2478915818)
   * [Android Developer: ViewGroup.startViewTransition docs](https://developer.android.com/reference/android/view/ViewGroup#startViewTransition(android.view.View))

On Android view groups can be marked as "transitioning" with a `ViewGroup.startViewTransition` call. This effectively ensures, that in case a view group is marked with this call and its children are removed, they will be still drawn until `endViewTransition` is not called.

This mechanism is implemented in Android by [keeping track of "transitioning" children in auxiliary `mTransitioningViews` array](https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/view/ViewGroup.java#7178). Then when such "transitioning" child is removed, [it is removed from children array](https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/view/ViewGroup.java#5595) but it's [parent-child relationship is not cleared](https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/view/ViewGroup.java#5397) and it is still retained in the auxiliary array.

Having that established we can proceed with problem description.

https://github.com/user-attachments/assets/d0356bf5-2f17-4b06-ba53-bfca659a1071

<details>
<summary>Full code</summary>

```javascript
import { NavigationContainer } from 'react-navigation/native';
import React from 'react';
import { createNativeStackNavigator } from 'react-navigation/native-stack';
import { enableScreens } from 'react-native-screens';
import {
  StyleSheet,
  Text,
  View,
  FlatList,
  Button,
  ViewProps,
  Image,
  FlatListProps,
  findNodeHandle,
} from 'react-native';

enableScreens(true);

function Item({ children, ...props }: ViewProps) {
  return (
    <View style={styles.item} {...props}>
      <Image source={require('../assets/trees.jpg')} style={styles.image} />
      <Text style={styles.text}>{children}</Text>
    </View>
  );
}

function Home({ navigation }: any) {
  return (
    <View style={styles.container}>
      <Button title="Go to List" onPress={() => navigation.navigate('List')} />
    </View>
  );
}

function ListScreenSimplified({secondVisible}: {secondVisible?: (visible: boolean) => void}) {
  const containerRef = React.useRef<View>(null);
  const innerViewRef = React.useRef<View>(null);
  const childViewRef = React.useRef<View>(null);

  React.useEffect(() => {
    if (containerRef.current != null) {
      const tag = findNodeHandle(containerRef.current);
      console.log(`Container has tag [${tag}]`);
    }
    if (innerViewRef.current != null) {
      const tag = findNodeHandle(innerViewRef.current);
      console.log(`InnerView has tag [${tag}]`);
    }
    if (childViewRef.current != null) {
      const tag = findNodeHandle(childViewRef.current);
      console.log(`ChildView has tag [${tag}]`);
    }
  }, [containerRef.current, innerViewRef.current, childViewRef.current]);

  return (
    <View
      ref={containerRef}
      style={{ flex: 1, backgroundColor: 'slateblue', overflow: 'hidden' }}
      removeClippedSubviews={false}>
      <View ref={innerViewRef} removeClippedSubviews style={{ height: '100%' }}>
        <View ref={childViewRef} style={{ backgroundColor: 'pink', width: '100%', height: 50 }} removeClippedSubviews={false}>
          {secondVisible && (<Button title='Hide second' onPress={() => secondVisible(false)} />)}
        </View>
      </View>
    </View>
  );
}

function ParentFlatlist(props: Partial<FlatListProps<number>>) {
  return (
    <FlatList
      data={Array.from({ length: 30 }).fill(0) as number[]}
      renderItem={({ index }) => {
        if (index === 10) {
          return <NestedFlatlist key={index} />;
        } else if (index === 15) {
          return <ExtraNestedFlatlist key={index} />;
        } else if (index === 20) {
          return <NestedFlatlist key={index} horizontal />;
        } else if (index === 25) {
          return <ExtraNestedFlatlist key={index} horizontal />;
        } else {
          return <Item key={index}>List item {index + 1}</Item>;
        }
      }}
      {...props}
    />
  );
}

function NestedFlatlist(props: Partial<FlatListProps<number>>) {
  return (
    <FlatList
      style={[styles.nestedList, props.style]}
      data={Array.from({ length: 10 }).fill(0) as number[]}
      renderItem={({ index }) => (
        <Item key={'nested' + index}>Nested list item {index + 1}</Item>
      )}
      {...props}
    />
  );
}

function ExtraNestedFlatlist(props: Partial<FlatListProps<number>>) {
  return (
    <FlatList
      style={styles.nestedList}
      data={Array.from({ length: 10 }).fill(0) as number[]}
      renderItem={({ index }) =>
        index === 4 ? (
          <NestedFlatlist key={index} style={{ backgroundColor: '#d24729' }} />
        ) : (
          <Item key={'nested' + index}>Nested list item {index + 1}</Item>
        )
      }
      {...props}
    />
  );
}

const Stack = createNativeStackNavigator();

export default function App(): React.JSX.Element {
  return (
    <NavigationContainer>
      <Stack.Navigator screenOptions={{ animation: 'slide_from_right' }}>
        <Stack.Screen name="Home" component={Home} />
        <Stack.Screen name="List" component={ListScreenSimplified}/>
      </Stack.Navigator>
    </NavigationContainer>
  );
}

export function AppSimple(): React.JSX.Element {
  const [secondVisible, setSecondVisible] = React.useState(false);

  return (
    <View style={{ flex: 1, backgroundColor: 'lightsalmon' }}>
      {!secondVisible && (
        <View style={{ flex: 1, backgroundColor: 'lightblue' }} >
          <Button title='Show second' onPress={() => setSecondVisible(true)} />
        </View>
      )}
      {secondVisible && (
        <ListScreenSimplified secondVisible={setSecondVisible} />
      )}
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
  },
  nestedList: {
    backgroundColor: '#FFA07A',
  },
  item: {
    flexDirection: 'row',
    alignItems: 'center',
    padding: 10,
    gap: 10,
  },
  text: {
    fontSize: 24,
    fontWeight: 'bold',
    color: 'black',
  },
  image: {
    width: 50,
    height: 50,
  },
});

```

</details>

Explanation (copied from [here](https://github.com/software-mansion/react-native-screens/pull/2495#issuecomment-2478915818)):

I've debugged this for a while now & I have good understanding of what's going on. This bug is caused by our usage of `startViewTransition` and its implications. We use it well, however React does not account for case that some view might be in transition. Error mechanism is as follows:

1. Let's have initially simple stack with two screens: "A, B". This is component rendered under "B":

```javascript
    <View //<-- ContainerView (CV)
      removeClippedSubviews={false}
      style={{ flex: 1, backgroundColor: 'slateblue', overflow: 'hidden' }}>
      <View removeClippedSubviews style={{ height: '100%' }}> // <--- IntermediateView (IV)
        <View removeClippedSubviews={false} style={{ backgroundColor: 'pink', width: '100%', height: 50 }} /> // <--- ChildView (ChV)
      </View>
    </View>
```

2. We press the back button.
3. We're on Fabric, therefore subtree of B gets destroyed before B itself is unmounted -> in our commit hook we detect that the screen B will be unmounted & we mark every node under B as transitioning by calling `startViewTransition`.
4. React Mounting stage starts, view hierarchy is disassembled in bottom-up fashion (leafs first).
5. ReactViewGroupManager receives MountItem to detach ChV from IV.
6. A call to [`IV.removeView(ChV)` is made](https://github.com/facebook/react-native/blob/9c11d7ca68c5c62ab7bab9919161d8417e96b28b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactClippingViewManager.kt#L58-L73), which effectively removes ChV from `IV.children`, ***HOWEVER*** it does not clear `ChV.parent`, meaning that after the call, `ChV.parent == IV`. This happens, due to view being marked as in-transition by our call to `startViewTransition`. If the view is not marked as in-transition this parent-child relationship is removed.
7. IV has `removeClippedSubviews` enabled, therefore a [call to `IV.removeViewWithSubviewsClippingEnabled(ChV)` is made](https://github.com/facebook/react-native/blob/9c11d7ca68c5c62ab7bab9919161d8417e96b28b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactClippingViewManager.kt#L68). [This function](https://github.com/facebook/react-native/blob/9c11d7ca68c5c62ab7bab9919161d8417e96b28b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewGroup.java#L726-L744) does effectively two things:
    1. if the ChV has parent (interpretation: it has not yet been detached from parent), we compute it's index in `IV.children` (Android.ViewGroup's state) and remove it from the array,
    2. remove the ChV from `mAllChildren` array (this is state maintained by ReactViewGroup for purposes of implementing the "subview clipping" mechanism".

The crash happens in 7.1, because ChV has been removed from `IV.children` in step 6, but the parent-child relationship has not been broken up there. Under usual circumstances (this is my hypothesis now, yet unconfirmed) 7.1 does not execute, because `ChV.parent` is nulled in step no. 6.

Transitions. On Fabric, when some subtree is unmounted, views in the subtree are unmounted in bottom-up order. This leads to uncomfortable situation, where our components (react-native-screens), who want to drive & manage transitions are notified that their children will be removed after the subtrees mounted in screen subviews are already disassembled. **If we start animation in this very moment we will have staggering effect of white flash** [(issue)](https://github.com/software-mansion/react-native-screens/issues/1685) (we animate just the screen with white background without it's children). This was not a problem on Paper, because the order of subtree disassembling was opposite - top-down. While we've managed to workaround this issue on Fabric using `MountingTransactionObserving` protocol on iOS and a commit hook on Android (we can inspect mutations in incoming transaction before it starts being applied) we still need to prevent view hierarchy from being disassembled in the middle of transition (on Paper this has also been less of an issue) - and this is where `startViewTransition` comes in. It allows us to draw views throughout transition after React Native removes them from HostTree model. On iOS we exchange subtree for its snapshot for transition time, however this approach isn't feasible on Android, because [snapshots do not capture shadows](https://stackoverflow.com/questions/42212600/android-screenshot-of-view-with-shadow).

[Android does not expose a method to verify whether a view is in transition](https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/view/ViewGroup.java#7162) (it has `package` visibility), therefore we need to retrieve this information with some workaround. I see two posibilities:

* first approach would be to override `startViewTransition` & `endViewTransition` in ReactViewGroup and keep the state on whether the view is transitioning there,
* second possible approach would be as follows: we can check for "transitioning" view by checking whether a view has parent but is not it's parent child (this **should** be reliable),

Having information on whether the view is in transition or not, we can prevent multiple removals of the same view in every call site (currently only in `removeViewAt` if `parent.removeClippingSubviews == true`).

Another option would be to do just as this PR does: having in mind this "transitioning" state we can pass a flag to `removeViewWithSubviewClippingEnabled` and prevent duplicated removal from parent if we already know that this has been requested.

I can also add override of this method:

```java
  /*package*/ void removeViewWithSubviewClippingEnabled(View view) {
    this.removeViewWithSubviewClippingEnabled(view, false);
  }
```

to make this parameter optional.

[ANDROID] [FIXED] - Handle removal of in-transition views.

Pull Request resolved: https://github.com/facebook/react-native/pull/47634

Test Plan: WIP WIP

Reviewed By: javache

Differential Revision: D66539065

Pulled By: tdn120

fbshipit-source-id: cf1add67000ebd1b5dfdb2048461a55deac10b16
2024-12-16 14:23:31 +00:00
Riccardo Cipolleschi 621f13f9a2 Skip hidden folders when looking for third party components (#48182)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/48182

Maintainers from SVG reached out because of an edge case they inencountered when generating the ComponentProvider. In their setup, they had a `.git` folder in the repo and the algorithm was spending a lot of time crawling the git folder.

In general, we should avoid crawling hidden folders.

This change fix that.

## Changelog:
[General][Fixed] - Skip hidden folders when looking for third party components.

Reviewed By: javache

Differential Revision: D66959345

fbshipit-source-id: 992a79f3cff22cd6a459e0272c8140bc329888da
2024-12-16 14:19:51 +00:00
Ben Handanyan b153ec8af7 Enable hermes debugger by configuration type instead of configuration name (#48174)
Summary:
Fixes an [issue](https://github.com/facebook/react-native/issues/48168) where only iOS configurations with "Debug" in the name are configured to use the hermes debugger.

## Changelog:

<!-- Help reviewers and the release process by writing your own changelog entry.

Pick one each for the category and type tags:

For more details, see:
https://reactnative.dev/contributing/changelogs-in-pull-requests
-->

[IOS] [FIXED] - Enable hermes debugger by configuration type instead of configuration name

Pull Request resolved: https://github.com/facebook/react-native/pull/48174

Test Plan:
Added new test scenarios that all pass:
```
ruby -Itest packages/react-native/scripts/cocoapods/__tests__/utils-test.rb
Loaded suite packages/react-native/scripts/cocoapods/__tests__/utils-test
Started
Finished in 0.336047 seconds.
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
56 tests, 149 assertions, 0 failures, 0 errors, 0 pendings, 0 omissions, 0 notifications
100% passed
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
166.64 tests/s, 443.39 assertions/s
```

In a personal project with the following configurations:
```
project 'ReactNativeProject', {
    'Local' => :debug,
    'Development' => :release,
    'Staging' => :release,
    'Production' => :release,
  }
```
I added the following to my Podfile:
```
installer.pods_project.targets.each do |target|
    target.build_configurations.each do |config|
        puts "#{config.name} is debug? #{config.type == :debug}"
    end
end
```
To confirm that my logic is correct:
```
Local is debug? true
Development is debug? false
Staging is debug? false
Production is debug? false
```

Reviewed By: robhogan

Differential Revision: D66962860

Pulled By: cipolleschi

fbshipit-source-id: 7bd920e123c9064c8a1b5d45df546ff5d2a7d8be
2024-12-16 14:18:58 +00:00
Eric Rozell 840382d22f Disable weak event emitter in AttributedString for Mac Catalyst (#48225)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/48225

Fixes https://github.com/facebook/react-native/issues/47762

The weak event emitter in AttributedString attributes is causing a serialization error when typing into a TextInput in a Mac Catalyst build. We can resolve this by not putting the event emitters in the attributed string, but this is likely to cause other issues with event handling for nested <Text> components.

## Changelog

[iOS][Fixed] - Workaround for Mac Catalyst TextInput crash due to serialization attempt of WeakEventEmitter

Reviewed By: NickGerleman

Differential Revision: D66664583

fbshipit-source-id: efdfbcb0db4d5e6b9bf7c14f9bbb221faae2d724
2024-12-16 14:16:20 +00:00
Blake Friedman 77b9c55b22 Update Podfile.lock
Changelog: [Internal]
2024-12-10 01:41:19 +00:00
React Native Bot c5b53fdfab Release 0.77.0-rc.2
#publish-packages-to-npm&next
2024-12-09 23:47:06 +00:00
zhongwuzw db798c18d6 Fabric: Post RCTInstanceDidLoadBundle notification after bundle loaded (#48082)
Summary:
Fixes https://github.com/facebook/react-native/issues/47949

## Changelog:

[IOS] [FIXED] - Fabric: Post RCTInstanceDidLoadBundle notification after bundle loaded

Pull Request resolved: https://github.com/facebook/react-native/pull/48082

Test Plan: Post RCTInstanceDidLoadBundle notification after bundle loaded

Reviewed By: philIip

Differential Revision: D66754060

Pulled By: cipolleschi

fbshipit-source-id: d30f0ed73e127936082e6f91e137b9b4013c6651
2024-12-09 15:50:24 +00:00
Riccardo Cipolleschi d4d1788cc5 Exclude mapping generation of core component (#48145)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/48145

While writing the docs for 0.77, I found an edge case in the generation of the RCTThirdPartyComponentProvider:
* If the app has the `codegenConfig` field set in the `package.json`
* And it does not have the `ios.componentProvider` field is not provided

Codegen was generating the mapping for the react-native core components. That's not expected as, in that case, it should only generate components that are declared in the app or in libraries.

This change fixes this edge case.

## Changelog:
[Internal] - Exclude mapping generation of core component

Reviewed By: blakef

Differential Revision: D66875080

fbshipit-source-id: 65fe10381729ec7808efec70feacf2a55f0056e9
2024-12-09 15:49:31 +00:00
Tim Yung a78d74ca20 RN: Backout "Scheduling Animated End Callbacks in Microtask" (#48132)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/48132

Backs out D63573322 and D65645981, reverting the change that makes callbacks passed to `animation.start(<callback>)` scheduled for execution in a microtask.

This is being reverted becuase the latency introduced by the current macro and pending micro tasks can introduce visible latency artifacts that diminish the fidelity of animations.

Changelog:
[General][Changed] - Reverts #47503. (~~Callbacks passed to `animation.start(<callback>)` will be scheduled for execution in a microtask. Previously, there were certain scenarios in which the callback could be synchronously executed by `start`.~~)

Reviewed By: javache

Differential Revision: D66852804

fbshipit-source-id: 08434b9876813fe9e8b189b6b467198933843bf0
2024-12-09 15:48:53 +00:00
Nicola Corti d81be20084 Fix crash on HeadlessJsTaskService on old architecture (#48124)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/48124

Fixes https://github.com/facebook/react-native/issues/47592

The logic in HeadlessJsTaskService is broken. We should not check whether `getReactContext` is null or not.
Instead we should use the `enableBridgelessArchitecture` feature flag to understand if New Architecture was enabled or not.

The problem we were having is that `HeadlessJsTaskService` was attempting to load the New Architecture even if the user would have it turned off. The Service would then die attempting to load `libappmodules.so` which was correctly missing.

Changelog:
[Android] [Fixed] - Fix crash on HeadlessJsTaskService on old architecture

Reviewed By: javache

Differential Revision: D66826271

fbshipit-source-id: 2b8418e0b01b65014cdbfd0ec2f843420a15f9db
2024-12-09 15:48:20 +00:00
Nicola Corti 6cde3df040 Re-introduce the deprecated constructor on ReactModuleInfo (#48090)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/48090

This alleviates a breaking change on `ReactModuleInfo` constructor.
While the ctor was deprecated, we realized that there are more than 250 usages in OSS.
We'll need to properly communicate this removal before we do it.

Changelog:
[Android] [Fixed] - Re-introduce the deprecated constructor on ReactModuleInfo

Reviewed By: cipolleschi

Differential Revision: D66755541

fbshipit-source-id: 3673d8f2af278d55491cea89f1594d368513e3d8
2024-12-09 15:46:44 +00:00
zhongwuzw 19bff65415 Fabric: Fixes insets not adjust when keyboard disappear (#47924)
Summary:
Fixes https://github.com/facebook/react-native/issues/47731 .

## Changelog:

[IOS] [FIXED] - Fabric: Fixes insets not adjust when keyboard disappear

Pull Request resolved: https://github.com/facebook/react-native/pull/47924

Test Plan: Demo in https://github.com/facebook/react-native/issues/47731

Reviewed By: blakef

Differential Revision: D66651865

Pulled By: cipolleschi

fbshipit-source-id: a75afbd1a7651f0c77022d913f910821c482fcf7
2024-12-09 15:46:39 +00:00
Riccardo Cipolleschi 88c5b4f8ed Do not install CMake on Windows machine (#48122)
Summary:
GHA to build HermesC for windows are failing because the machines comes with a different CMake version already.
Let's try not to install Cmake and use the one provided by the machine.

## Changelog:
[Internal] -

Pull Request resolved: https://github.com/facebook/react-native/pull/48122

Test Plan: GHA {F1973187648}

Reviewed By: alanleedev

Differential Revision: D66825216

Pulled By: cipolleschi

fbshipit-source-id: 9a9376a5409e192195a6b6cc25b4d58cb47f15da
2024-12-06 10:45:46 +00:00
Rob Hogan 1c3160c7d0 Update Podfile.lock 2024-12-04 10:59:58 +00:00
React Native Bot b19898e259 Release 0.77.0-rc.1
#publish-packages-to-npm&next
2024-12-04 09:51:19 +00:00
Alex Hunt 22072bffaa Update debugger-frontend from b61aae3...6b80704 (#48042)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/48042

Changelog: [Internal] - Update `react-native/debugger-frontend` from b61aae3...6b80704

Resyncs `react-native/debugger-frontend` from GitHub - see `rn-chrome-devtools-frontend` [changelog](https://github.com/facebookexperimental/rn-chrome-devtools-frontend/compare/b61aae3ccc6e2684dfbf1e2a06b0f985b459f11f...6b80704fd50ea0bf10f5f5da5a4343de29aff8b2).

Reviewed By: blakef

Differential Revision: D66651149

fbshipit-source-id: 6848eebb4b7c04c7c04ae1f784fc39785945bf7b
2024-12-02 15:45:30 +00:00
Pieter De Baets d663fc4397 Restore deprecated TurboReactPackage (#48039)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/48039

This class was removed in D66127067 but was marked as DeprecatedInNewArchitecture and not Deprecated, which limited the signal we gave to developers to move away from this.

Restore for now to e

Changelog: [Android][Fixed] Reverted removal of TurboReactPackage

Reviewed By: rshest

Differential Revision: D66648209

fbshipit-source-id: 165f9390b4874e69353612b929d87b0c495588af
2024-12-02 15:45:12 +00:00
Vojtech Novak 70f6cec5d7 fix IOException in BuildCodegenCLITask (#48008)
Summary:
building RN tester with 0.77 rc-0 doesn't work now because of `java.io.IOException:  No such file or directory` on line 48.

`buildDirectory` is a Gradle property representing a file

https://github.com/facebook/react-native/pull/47552 removes this file altogether so feel free to close if that one is the "right one"

## Changelog:

[ANDROID] [FIXED] - fix IOException in `BuildCodegenCLITask`

Pull Request resolved: https://github.com/facebook/react-native/pull/48008

Test Plan: After this change, building RN tester works.

Reviewed By: cortinico

Differential Revision: D66650038

Pulled By: robhogan

fbshipit-source-id: 11cd83493fa118c6b79d11c9113228dd3971a803
2024-12-02 14:31:10 +00:00
zhongwuzw b79ec10b78 Fabric: Fixes Modal onRequestClose not called (#48037)
Summary:
Fixes https://github.com/facebook/react-native/issues/48030 .

## Changelog:

[IOS] [FIXED] - Fabric: Fixes Modal onRequestClose not called

Pull Request resolved: https://github.com/facebook/react-native/pull/48037

Test Plan: Repro please see  https://github.com/facebook/react-native/issues/48030.

Reviewed By: cortinico

Differential Revision: D66647232

Pulled By: cipolleschi

fbshipit-source-id: 773517dfe45f6f2e6348cda225e972fbac05edc2
2024-12-02 14:30:45 +00:00
Rob Hogan ee4333812c Fix Animated on JSC: Object.hasOwn -> obj.hasOwnProperty (#48035)
Summary:
https://github.com/facebook/react-native/pull/46385 introduced use of `Object.hasOwn` as an incidental detail of some `Animated` performance improvements.

Unfortunately, `Object.hasOwn` is not present in the version of JSC shipped with Android, nor the built in iOS JSC until iOS 15.4, which is greater than React Native's minimum version (13.4).

Instead:
 - Use `obj.hasOwnProperty(prop)` for known objects that have the `Object` prototype.
 - Otherwise, use `Object.hasOwn` where it is defined.
 - Lastly, fall back to `Object.prototype.hasOwnProperty.call(obj, prop)`, which is compatible with passed `null`-prototype objects.

Fixes https://github.com/facebook/react-native/issues/47963

Intend to pick for RN 0.77.

## Changelog:

[GENERAL][FIXED] Replace Object.hasOwn usages to fix Animated on JSC

Pull Request resolved: https://github.com/facebook/react-native/pull/48035

Test Plan:
- Run `rn-tester` on Android with Hermes disabled.
 - Verify the FlatList->Basic example redboxes before this change, and works after it.

Reviewed By: yungsters

Differential Revision: D66638379

Pulled By: robhogan

fbshipit-source-id: 51ac525851b41adea3bf3cc41349225138e1f2fe
2024-12-02 14:28:14 +00:00
Hugo FOYART 9388a7a699 fix: FormData filename in content-disposition (#46543)
Summary:
This Pull Request fixes a regression introduced in https://github.com/facebook/react-native/commit/7c7e9e6571c1f702213e9ffbb40921cd5a1a786b, which adds a `filename*` attribute to the `content-disposition` of a FormData part. However, as the [MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition#directives) states, there is no `filename*` attribute for the `content-disposition` header in case of a form data.

The `filename*` attribute would break the parsing of form data in the request, such as in frameworks like `Next.js` which uses the web implementation of [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request).

Fixes https://github.com/facebook/react-native/issues/44737

## Changelog:

<!-- Help reviewers and the release process by writing your own changelog entry.

Pick one each for the category and type tags:

[ANDROID|GENERAL|IOS|INTERNAL] [BREAKING|ADDED|CHANGED|DEPRECATED|REMOVED|FIXED|SECURITY] - Message

For more details, see:
https://reactnative.dev/contributing/changelogs-in-pull-requests
-->

[General] [Fixed] - Remove non compliant `filename*` attribute in a FormData `content-disposition` header

Pull Request resolved: https://github.com/facebook/react-native/pull/46543

Test Plan:
- Clone the `react-native` repo
- Create a simple JS file that will act as a node server and execute it

```javascript
const http = require('http');

const server = http.createServer(async function (r, res) {
    const req = new Request(new URL(r.url, 'http://localhost:3000'), {
      headers: r.headers,
      method: r.method,
      body: r,
      duplex: 'half',
    });

    const fileData = await req.formData();

    console.log(fileData);
    res.writeHead(200);
    res.end();
});
server.listen(3000);
```

- Go to `packages/rn-tester`
- Add a `useEffect` in `js/RNTesterAppShared.js`

```javascript
React.useEffect(() => {
    const formData = new FormData();
    formData.append('file', {
      uri: 'https://www.gravatar.com/avatar',
      name: '测试photo/1.jpg',
      type: 'image/jpeg',
    });

    fetch('http://localhost:3000', {
      method: 'POST',
      body: formData,
    }).then(res => console.log(res.ok));
  });
```

- Run the app on iOS or Android
- The node server should output the file added to the FormData with an encoded name

Reviewed By: robhogan

Differential Revision: D66643317

Pulled By: yungsters

fbshipit-source-id: 0d531528005025bff303505363671e854c0a2b63
2024-12-02 14:27:17 +00:00
Pieter De Baets 8ab87c617e Fix stale reference to ReactViewGroup#mAllChildren (#47950)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/47950

`addInArray` may reallocate `mAllChildren` so it's not correct to store this reference.

Changelog: [Internal]

Reviewed By: tdn120

Differential Revision: D66474532

fbshipit-source-id: 90ce2fcbf8ff236501ed47b2acc413e54ef8b82a
2024-12-02 14:26:41 +00:00
Jakub Romanczyk 184da8907e refactor(community-cli-plugin): use node builtin fetch (#47397)
Summary:
Removed `node-fetch` in favour of node builtin fetch to get rid of the deprecated `punycode` warning when using Node 22.

`react-native/community-cli-plugin` already requires Node >= 18 where it was made available by default (without `--experimental-fetch` flag).

This change is similar to the one made in https://github.com/facebook/react-native/pull/45227

## Changelog:

[GENERAL] [CHANGED] - Drop node-fetch in favor of Node's built-in fetch from undici in `react-native/community-cli-plugin`

Pull Request resolved: https://github.com/facebook/react-native/pull/47397

Test Plan: tests pass

Reviewed By: blakef

Differential Revision: D66512595

Pulled By: NickGerleman

fbshipit-source-id: c4e01baf388f9fae8cea7b4bfe25034bff28b461
2024-12-02 14:25:53 +00:00
Rob Hogan abfa3a23dc Update Podfile.lock
Changelog: [Internal]
2024-11-26 16:57:20 +00:00
React Native Bot f6cce65bec Release 0.77.0-rc.0
#publish-packages-to-npm&next
2024-11-26 11:38:45 +00:00
Riccardo Cipolleschi 385318bf6a [LOCAL] Fix testing script to use debug versions of the Android APK 2024-11-25 19:09:40 +00:00
Rob Hogan 4cffff35e0 [LOCAL] Update Hermes 2024-11-25 14:02:19 +00:00
5117 changed files with 196720 additions and 367074 deletions
+4
View File
@@ -0,0 +1,4 @@
# Circle CI
This directory was home to the Circle CI configuration files.
In July 2024 we moved to GitHub Actions, and week this folder for backward compatibility, as we want to keep on using Circle CI for the release of React Native <= 0.74.
+13
View File
@@ -0,0 +1,13 @@
version: 2.1
workflows:
version: 2
stub:
jobs:
- circleci-stub
jobs:
circleci-stub:
docker:
- image: debian:bullseye
steps:
- run: echo "There is nothing here, just an empty job. Everything has been moved to GitHub Action"
+1 -2
View File
@@ -3,16 +3,15 @@
docs/generatedComponentApiDocs.js
packages/react-native/flow/
packages/react-native/sdks/
packages/react-native/ReactAndroid/build
packages/react-native/ReactAndroid/hermes-engine/build/
packages/react-native/Libraries/Renderer/*
packages/react-native/Libraries/vendor/**/*
node_modules/
packages/*/node_modules
packages/*/dist
packages/*/types_generated
packages/debugger-frontend/dist/**/*
packages/react-native-codegen/lib
tools/eslint/rules/sort-imports.js
**/Pods/*
**/*.macos.js
**/*.windows.js
+20 -50
View File
@@ -4,18 +4,21 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @noflow
* @format
*/
'use strict';
const path = require('node:path');
require('eslint-plugin-lint').load(path.join(__dirname, 'tools/eslint/rules'));
module.exports = {
root: true,
extends: ['@react-native'],
plugins: ['@react-native/monorepo', '@react-native/specs'],
plugins: ['@react-native/eslint-plugin-specs', 'lint'],
overrides: [
// overriding the JS config from @react-native/eslint-config to ensure
@@ -24,34 +27,12 @@ module.exports = {
files: ['*.js', '*.js.flow', '*.jsx'],
parser: 'hermes-eslint',
rules: {
'@react-native/monorepo/sort-imports': 'warn',
'eslint-comments/no-unlimited-disable': 'off',
'ft-flow/require-valid-file-annotation': ['error', 'always'],
'no-extra-boolean-cast': 'off',
'no-void': 'off',
// These rules are not required with hermes-eslint
'ft-flow/define-flow-type': 'off',
'ft-flow/use-flow-type': 'off',
// Flow handles these checks for us, so they aren't required
'no-undef': 'off',
'no-unreachable': 'off',
},
},
{
files: ['*.js', '*.jsx', '*.ts', '*.tsx'],
rules: {
'@react-native/no-deep-imports': 'off',
},
},
{
files: [
'./packages/react-native/Libraries/**/*.{js,flow}',
'./packages/react-native/src/**/*.{js,flow}',
'./packages/assets/registry.js',
],
parser: 'hermes-eslint',
rules: {
'@react-native/monorepo/no-commonjs-exports': 'warn',
'ft-flow/define-flow-type': 0,
'ft-flow/use-flow-type': 0,
'lint/sort-imports': 1,
// flow handles this check for us, so it's not required
'no-undef': 0,
},
},
{
@@ -61,17 +42,15 @@ module.exports = {
{
files: ['package.json'],
rules: {
'@react-native/monorepo/react-native-manifest': 'error',
'lint/react-native-manifest': 2,
},
},
{
files: ['flow-typed/**/*.js', 'packages/react-native/flow/**/*'],
files: ['flow-typed/**/*.js'],
rules: {
'@react-native/monorepo/valid-flow-typed-signature': 'error',
'ft-flow/require-valid-file-annotation': 'off',
'no-shadow': 'off',
'no-unused-vars': 'off',
quotes: 'off',
'lint/valid-flow-typed-signature': 2,
'no-unused-vars': 0,
quotes: 0,
},
},
{
@@ -80,14 +59,11 @@ module.exports = {
'packages/react-native/src/**/*.js',
],
rules: {
'@react-native/monorepo/no-haste-imports': 'error',
'@react-native/monorepo/no-react-default-imports': 'error',
'@react-native/monorepo/no-react-named-type-imports': 'error',
'@react-native/monorepo/no-react-native-imports': 'error',
'@react-native/monorepo/no-react-node-imports': 'error',
'@react-native/monorepo/require-extends-error': 'error',
'@react-native/platform-colors': 'error',
'@react-native/specs/react-native-modules': 'error',
'@react-native/platform-colors': 2,
'@react-native/specs/react-native-modules': 2,
'lint/no-haste-imports': 2,
'lint/no-react-native-imports': 2,
'lint/require-extends-error': 2,
},
},
{
@@ -138,11 +114,5 @@ module.exports = {
],
},
},
{
files: ['**/__tests__/**'],
rules: {
'@react-native/monorepo/no-react-native-imports': 'off',
},
},
],
};
+3 -10
View File
@@ -2,14 +2,11 @@
; Ignore build cache folder
<PROJECT_ROOT>/packages/react-native/sdks/.*
; Ignore fb_internal modules
<PROJECT_ROOT>/packages/react-native/src/fb_internal/.*
; Ignore the codegen e2e tests
<PROJECT_ROOT>/packages/react-native-codegen/e2e/__test_fixtures__/modules/NativeEnumTurboModule.js
; Ignore the Dangerfile
<PROJECT_ROOT>/private/react-native-bots/dangerfile.js
<PROJECT_ROOT>/packages/react-native-bots/dangerfile.js
; Ignore "BUCK" generated dirs
<PROJECT_ROOT>/\.buckd/
@@ -27,7 +24,7 @@
<PROJECT_ROOT>/packages/.*/dist
; helloworld
<PROJECT_ROOT>/private/helloworld/ios/Pods/
<PROJECT_ROOT>/packages/helloworld/ios/Pods/
; Ignore rn-tester Pods
<PROJECT_ROOT>/packages/rn-tester/Pods/
@@ -69,10 +66,6 @@ module.name_mapper='^react-native/\(.*\)$' -> '<PROJECT_ROOT>/packages/react-nat
module.name_mapper='^@react-native/dev-middleware$' -> '<PROJECT_ROOT>/packages/dev-middleware'
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\)$' -> '<PROJECT_ROOT>/packages/react-native/Libraries/Image/RelativeImageStub'
module.system.haste.module_ref_prefix=m#
react.runtime=automatic
suppress_type=$FlowIssue
suppress_type=$FlowFixMe
suppress_type=$FlowFixMeProps
@@ -102,4 +95,4 @@ untyped-import
untyped-type-import
[version]
^0.273.1
^0.254.2
-1
View File
@@ -1 +0,0 @@
*.js.flow linguist-language=JavaScript
+8 -8
View File
@@ -19,10 +19,10 @@ body:
* Please [search for similar issues](https://github.com/facebook/react-native/issues) in our issue tracker.
Make sure that your issue:
* Have a **valid reproducer** (See [How to report a bug](https://reactnative.dev/contributing/how-to-report-a-bug)).
* Have a **valid reproducer** (either a [Expo Snack](https://snack.expo.dev/) or a [empty project from template](https://github.com/react-native-community/reproducer-react-native).
* Is tested against the [**latest stable**](https://github.com/facebook/react-native/releases/) of React Native.
🚨 IMPORTANT: Due to the extreme number of bugs we receive, issues **without a reproducer** or for an [**unsupported versions**](https://github.com/reactwg/react-native-releases#which-versions-are-currently-supported) of React Native **will be closed**.
Due to the extreme number of bugs we receive, we will be looking **ONLY** into issues with a reproducer, and on [supported versions](https://github.com/reactwg/react-native-releases#which-versions-are-currently-supported) of React Native.
- type: textarea
id: description
attributes:
@@ -69,10 +69,10 @@ body:
- type: textarea
id: react-native-info
attributes:
label: Output of `npx @react-native-community/cli info`
description: Run `npx @react-native-community/cli info` in your terminal, copy and paste the results here.
label: Output of `npx react-native info`
description: Run `npx react-native info` in your terminal, copy and paste the results here.
placeholder: |
Paste the output of `npx @react-native-community/cli info` here. The output looks like:
Paste the output of `npx react-native info` here. The output looks like:
...
System:
OS: macOS 14.1.1
@@ -83,7 +83,7 @@ body:
path: /bin/zsh
Binaries:
Node: ...
version: 22.14.0
version: 18.14.0
...
render: text
validations:
@@ -109,8 +109,8 @@ body:
- type: input
id: reproducer
attributes:
label: MANDATORY Reproducer
description: A link to either a failing RNTesterPlayground.js file, an Expo Snack or a public repository from [this template](https://github.com/react-native-community/reproducer-react-native) that reproduces this bug. Reproducers are **mandatory**, issues without a reproducer will be closed.
label: Reproducer
description: A link to a Expo Snack or a public repository that reproduces this bug, using [this template](https://github.com/react-native-community/reproducer-react-native). Reproducers are **mandatory**.
placeholder: "https://github.com/<myuser>/<myreproducer>"
validations:
required: true
@@ -48,10 +48,10 @@ body:
- type: textarea
id: react-native-info
attributes:
label: Output of `npx @react-native-community/cli info`
description: Run `npx @react-native-community/cli info` in your terminal, copy and paste the results here.
label: Output of `npx react-native info`
description: Run `npx react-native info` in your terminal, copy and paste the results here.
placeholder: |
Paste the output of `npx @react-native-community/cli info` here. The output looks like:
Paste the output of `npx react-native info` here. The output looks like:
...
System:
OS: macOS 14.1.1
@@ -62,7 +62,7 @@ body:
path: /bin/zsh
Binaries:
Node: ...
version: 22.14.0
version: 18.14.0
...
render: text
validations:
@@ -16,10 +16,10 @@ body:
Do not attempt to open a bug in this category if you're not using the New Architecture as your bug will be closed.
Make sure that your issue:
* Have a **valid reproducer** (See [How to report a bug](https://reactnative.dev/contributing/how-to-report-a-bug)).
* Have a **valid reproducer** (either a [Expo Snack](https://snack.expo.dev/) or a [empty project from template](https://github.com/react-native-community/reproducer-react-native).
* Is tested against the [**latest stable**](https://github.com/facebook/react-native/releases/) of React Native.
🚨 IMPORTANT: Due to the extreme number of bugs we receive, issues **without a reproducer** or for an [**unsupported versions**](https://github.com/reactwg/react-native-releases#which-versions-are-currently-supported) of React Native **will be closed**.
Due to the extreme number of bugs we receive, we will be looking **ONLY** into issues with a reproducer, and on [supported versions](https://github.com/reactwg/react-native-releases#which-versions-are-currently-supported) of React Native.
- type: textarea
id: description
attributes:
@@ -81,10 +81,10 @@ body:
- type: textarea
id: react-native-info
attributes:
label: Output of `npx @react-native-community/cli info`
description: Run `npx @react-native-community/cli info` in your terminal, copy and paste the results here.
label: Output of `npx react-native info`
description: Run `npx react-native info` in your terminal, copy and paste the results here.
placeholder: |
Paste the output of `npx @react-native-community/cli info` here. The output looks like:
Paste the output of `npx react-native info` here. The output looks like:
...
System:
OS: macOS 14.1.1
@@ -95,7 +95,7 @@ body:
path: /bin/zsh
Binaries:
Node: ...
version: 22.14.0
version: 18.14.0
...
render: text
validations:
@@ -121,8 +121,8 @@ body:
- type: input
id: reproducer
attributes:
label: MANDATORY Reproducer
description: A link to either a failing RNTesterPlayground.js file, an Expo Snack or a public repository from [this template](https://github.com/react-native-community/reproducer-react-native) that reproduces this bug. Reproducers are **mandatory**, issues without a reproducer will be closed.
label: Reproducer
description: A link to a Expo Snack or a public repository that reproduces this bug, using [this template](https://github.com/react-native-community/reproducer-react-native). Reproducers are **mandatory**.
placeholder: "https://github.com/<myuser>/<myreproducer>"
validations:
required: true
+23 -12
View File
@@ -7,8 +7,6 @@ inputs:
run-e2e-tests:
default: 'false'
description: If we need to build to run E2E tests. If yes, we need to build also x86.
gradle-cache-encryption-key:
description: "The encryption key needed to store the Gradle Configuration cache"
runs:
using: composite
steps:
@@ -18,7 +16,7 @@ runs:
- name: Setup node.js
uses: ./.github/actions/setup-node
- name: Install node dependencies
uses: ./.github/actions/yarn-install
uses: ./.github/actions/yarn-install-with-cache
- name: Set React Native Version
shell: bash
run: node ./scripts/releases/set-rn-artifacts-version.js --build-type ${{ inputs.release-type }}
@@ -26,15 +24,14 @@ runs:
uses: ./.github/actions/setup-gradle
with:
cache-read-only: "false"
cache-encryption-key: ${{ inputs.gradle-cache-encryption-key }}
- name: Restore Android ccache
uses: actions/cache/restore@v4
with:
path: /github/home/.cache/ccache
key: v1-ccache-android-${{ github.job }}-${{ github.ref }}
key: v2-ccache-android-${{ github.job }}-${{ github.ref }}
restore-keys: |
v1-ccache-android-${{ github.job }}-
v1-ccache-android-
v2-ccache-android-${{ github.job }}-
v2-ccache-android-
- name: Show ccache stats
shell: bash
run: ccache -s -v
@@ -63,7 +60,7 @@ runs:
uses: actions/cache/save@v4
with:
path: /github/home/.cache/ccache
key: v1-ccache-android-${{ github.job }}-${{ github.ref }}
key: v2-ccache-android-${{ github.job }}-${{ github.ref }}
- name: Show ccache stats
shell: bash
run: ccache -s -v
@@ -86,13 +83,27 @@ runs:
if: ${{ always() }}
uses: actions/upload-artifact@v4.3.4
with:
name: rntester-debug
path: packages/rn-tester/android/app/build/outputs/apk/debug/
name: rntester-hermes-debug
path: packages/rn-tester/android/app/build/outputs/apk/hermes/debug/
compression-level: 0
- name: Upload RNTester APK - hermes-release
if: ${{ always() }}
uses: actions/upload-artifact@v4.3.4
with:
name: rntester-release
path: packages/rn-tester/android/app/build/outputs/apk/release/
name: rntester-hermes-release
path: packages/rn-tester/android/app/build/outputs/apk/hermes/release/
compression-level: 0
- name: Upload RNTester APK - jsc-debug
if: ${{ always() }}
uses: actions/upload-artifact@v4.3.4
with:
name: rntester-jsc-debug
path: packages/rn-tester/android/app/build/outputs/apk/jsc/debug/
compression-level: 0
- name: Upload RNTester APK - jsc-release
if: ${{ always() }}
uses: actions/upload-artifact@v4.3.4
with:
name: rntester-jsc-release
path: packages/rn-tester/android/app/build/outputs/apk/jsc/release/
compression-level: 0
@@ -47,7 +47,7 @@ runs:
fi
- name: Yarn- Install Dependencies
if: ${{ steps.check_if_apple_artifacts_are_there.outputs.ARTIFACTS_EXIST != 'true' }}
uses: ./.github/actions/yarn-install
uses: ./.github/actions/yarn-install-with-cache
- name: Slice cache macosx
if: ${{ steps.check_if_apple_artifacts_are_there.outputs.ARTIFACTS_EXIST != 'true' }}
uses: actions/download-artifact@v4
@@ -10,8 +10,6 @@ inputs:
runs:
using: composite
steps:
- name: Setup xcode
uses: ./.github/actions/setup-xcode
- name: Restore Hermes workspace
uses: ./.github/actions/restore-hermes-workspace
- name: Hermes apple cache
@@ -14,23 +14,23 @@ runs:
uses: actions/download-artifact@v4
with:
name: hermes-workspace
path: 'D:\tmp\hermes'
path: 'C:\tmp\hermes'
- name: Set up workspace
shell: powershell
run: |
mkdir -p D:\tmp\hermes\osx-bin
mkdir -p C:\tmp\hermes\osx-bin
mkdir -p .\packages\react-native\sdks\hermes
cp -r -Force D:\tmp\hermes\hermes\* .\packages\react-native\sdks\hermes\.
cp -r -Force C:\tmp\hermes\hermes\* .\packages\react-native\sdks\hermes\.
cp -r -Force .\packages\react-native\sdks\hermes-engine\utils\* .\packages\react-native\sdks\hermes\.
- name: Windows cache
uses: actions/cache@v4
with:
key: v3-hermes-${{ github.job }}-windows-${{ inputs.hermes-version }}-${{ inputs.react-native-version }}
path: |
D:\tmp\hermes\win64-bin\
D:\tmp\hermes\hermes\icu\
D:\tmp\hermes\hermes\deps\
D:\tmp\hermes\hermes\build_release\
C:\tmp\hermes\win64-bin\
C:\tmp\hermes\hermes\icu\
C:\tmp\hermes\hermes\deps\
C:\tmp\hermes\hermes\build_release\
- name: setup-msbuild
uses: microsoft/setup-msbuild@v1.3.2
- name: Set up workspace
@@ -83,4 +83,4 @@ runs:
uses: actions/upload-artifact@v4.3.4
with:
name: hermes-win64-bin
path: D:\tmp\hermes\win64-bin\
path: C:\tmp\hermes\win64-bin\
+1 -24
View File
@@ -11,9 +11,6 @@ inputs:
required: false
description: The GHA npm token, required only to publish to npm
default: ''
gradle-cache-encryption-key:
description: The encryption key needed to store the Gradle Configuration cache
runs:
using: composite
steps:
@@ -101,35 +98,15 @@ runs:
cp ${{ inputs.hermes-ws-dir }}/hermes-runtime-darwin/hermes-ios-Release.tar.gz ./packages/react-native/ReactAndroid/external-artifacts/artifacts/hermes-ios-release.tar.gz
cp ${{ inputs.hermes-ws-dir }}/dSYM/Debug/hermes.framework.dSYM ./packages/react-native/ReactAndroid/external-artifacts/artifacts/hermes-framework-dSYM-debug.tar.gz
cp ${{ inputs.hermes-ws-dir }}/dSYM/Release/hermes.framework.dSYM ./packages/react-native/ReactAndroid/external-artifacts/artifacts/hermes-framework-dSYM-release.tar.gz
- name: Download ReactNativeDependencies
uses: actions/download-artifact@v4
with:
pattern: ReactNativeDependencies*
path: ./packages/react-native/ReactAndroid/external-artifacts/artifacts
merge-multiple: true
- name: Download ReactCore artifacts
uses: actions/download-artifact@v4
with:
pattern: ReactCore*
path: ./packages/react-native/ReactAndroid/external-artifacts/artifacts
merge-multiple: true
- name: Print Artifacts Directory
shell: bash
run: ls -lR ./packages/react-native/ReactAndroid/external-artifacts/artifacts/
- name: Setup node.js
uses: ./.github/actions/setup-node
- name: Setup gradle
uses: ./.github/actions/setup-gradle
with:
cache-encryption-key: ${{ inputs.gradle-cache-encryption-key }}
- name: Install dependencies
uses: ./.github/actions/yarn-install
uses: ./.github/actions/yarn-install-with-cache
- name: Build packages
shell: bash
run: yarn build
- name: Build types
shell: bash
run: yarn build-types
# Continue with publish steps
- name: Set npm credentials
if: ${{ inputs.release-type == 'release' ||
+1 -1
View File
@@ -15,7 +15,7 @@ runs:
using: composite
steps:
- name: Yarn install
uses: ./.github/actions/yarn-install
uses: ./.github/actions/yarn-install-with-cache
- name: Configure Git
shell: bash
run: |
+7 -21
View File
@@ -4,7 +4,7 @@ inputs:
node-version:
description: "The node.js version to use"
required: false
default: "22"
default: "18"
github-token:
description: "The GitHub token used by pull-bot"
required: true
@@ -15,8 +15,9 @@ runs:
uses: ./.github/actions/setup-node
with:
node-version: ${{ inputs.node-version }}
- name: Run yarn install
uses: ./.github/actions/yarn-install
- name: Yarn install
shell: bash
run: yarn install --non-interactive --frozen-lockfile
- name: Run linters against modified files (analysis-bot)
shell: bash
run: yarn lint-ci
@@ -25,16 +26,10 @@ runs:
GITHUB_PR_NUMBER: ${{ github.event.number }}
- name: Lint code
shell: bash
run: ./.github/workflow-scripts/exec_swallow_error.sh yarn lint --format junit -o ./reports/junit/eslint/results.xml
run: ./scripts/circleci/exec_swallow_error.sh yarn lint --format junit -o ./reports/junit/eslint/results.xml
- name: Lint java
shell: bash
run: ./.github/workflow-scripts/exec_swallow_error.sh yarn lint-java --check
- name: Lint file structure
shell: bash
run: ./.github/workflow-scripts/lint_files.sh
- name: Verify not committing repo after running build
shell: bash
run: yarn run build --validate
run: ./scripts/circleci/exec_swallow_error.sh yarn lint-java --check
- name: Run flowcheck
shell: bash
run: yarn flow-check
@@ -43,16 +38,7 @@ runs:
run: yarn test-typescript
- name: Check license
shell: bash
run: ./.github/workflow-scripts/check_license.sh
run: ./scripts/circleci/check_license.sh
- name: Check formatting
shell: bash
run: yarn run format-check
- name: Lint markdown
shell: bash
run: yarn run lint-markdown
- name: Build types
shell: bash
run: yarn build-types
- name: Run typescript check of generated types
shell: bash
run: yarn test-generated-typescript
+12 -7
View File
@@ -1,5 +1,5 @@
name: Maestro E2E Android
description: Runs E2E Tests on Android using Maestro
description: Runs E2E Tests on iOS using Maestro
inputs:
app-path:
required: true
@@ -7,6 +7,9 @@ inputs:
app-id:
required: true
description: The id of the app to test
jsengine:
required: true
description: The js engine we are using
maestro-flow:
required: true
description: the folder that contains the maestro tests
@@ -22,13 +25,17 @@ inputs:
required: false
default: "."
description: The directory from which metro should be started
architecture:
required: false
default: "NewArch"
description: The react native architecture to test
runs:
using: composite
steps:
- name: Installing Maestro
shell: bash
run: export MAESTRO_VERSION=1.40.0; curl -Ls "https://get.maestro.mobile.dev" | bash
run: export MAESTRO_VERSION=1.36.0; curl -Ls "https://get.maestro.mobile.dev" | bash
- name: Set up JDK 17
if: ${{ inputs.install-java == 'true' }}
uses: actions/setup-java@v4
@@ -54,10 +61,8 @@ runs:
with:
api-level: 24
arch: x86
ram-size: '8192M'
heap-size: '4096M'
ram-size: '4096M'
disk-size: '10G'
cores: '4'
disable-animations: false
avd-name: e2e_emulator
script: node .github/workflow-scripts/maestro-android.js ${{ inputs.app-path }} ${{ inputs.app-id }} ${{ inputs.maestro-flow }} ${{ inputs.flavor }} ${{ inputs.working-directory }}
@@ -72,7 +77,7 @@ runs:
uses: actions/upload-artifact@v4.3.4
if: always()
with:
name: e2e_android_${{ steps.normalize-app-id.outputs.app-id }}_report_${{ inputs.flavor }}_NewArch
name: e2e_android_${{ steps.normalize-app-id.outputs.app-id }}_report_${{ inputs.jsengine }}_${{ inputs.flavor }}_${{ inputs.architecture }}
path: |
report.xml
screen.mp4
@@ -80,5 +85,5 @@ runs:
if: steps.run-tests.outcome == 'failure'
uses: actions/upload-artifact@v4.3.4
with:
name: maestro-logs-android-${{ steps.normalize-app-id.outputs.app-id }}-${{ inputs.flavor }}-NewArch
name: maestro-logs-android-${{ steps.normalize-app-id.outputs.app-id }}-${{ inputs.jsengine }}-${{ inputs.flavor }}-${{ inputs.architecture }}
path: /tmp/MaestroLogs
+57 -16
View File
@@ -7,6 +7,9 @@ inputs:
app-id:
required: true
description: The id of the app to test
jsengine:
required: true
description: The js engine we are using
maestro-flow:
required: true
description: the folder that contains the maestro tests
@@ -18,32 +21,31 @@ inputs:
required: false
default: "."
description: The directory from which metro should be started
architecture:
required: false
default: "NewArch"
description: The react native architecture to test
runs:
using: composite
steps:
- name: Installing Maestro
shell: bash
run: export MAESTRO_VERSION=1.40.0; curl -Ls "https://get.maestro.mobile.dev" | bash
run: export MAESTRO_VERSION=1.36.0; curl -Ls "https://get.maestro.mobile.dev" | bash
- name: Installing Maestro dependencies
shell: bash
run: |
brew tap facebook/fb
brew install facebook/fb/idb-companion
brew install facebook/fb/idb-companion jq
- name: Set up JDK 11
uses: actions/setup-java@v2
with:
java-version: '17'
distribution: 'zulu'
- name: Run yarn install
uses: ./.github/actions/yarn-install
- name: Start Metro in Debug
shell: bash
if: ${{ inputs.flavor == 'Debug' }}
run: |
# build codegen or we will see a redbox
./packages/react-native-codegen/scripts/oss/build.sh
cd ${{ inputs.working-directory }}
yarn start &
sleep 5 # to give metro time to load
@@ -55,18 +57,57 @@ runs:
# Maestro can fail in case of flakyness, we have some retry logic.
set +e
node .github/workflow-scripts/maestro-ios.js \
"${{ inputs.app-path }}" \
"${{ inputs.app-id }}" \
"${{ inputs.maestro-flow }}" \
"Hermes" \
"${{ inputs.flavor }}" \
"${{ inputs.working-directory }}"
echo "Launching iOS Simulator: iPhone 15 Pro"
xcrun simctl boot "iPhone 15 Pro"
echo "Installing app on Simulator"
xcrun simctl install booted "${{ inputs.app-path }}"
echo "Retrieving device UDID"
UDID=$(xcrun simctl list devices booted -j | jq -r '[.devices[]] | add | first | .udid')
echo "UDID is $UDID"
echo "Bring simulator in foreground"
open -a simulator
echo "Launch the app"
xcrun simctl launch $UDID ${{ inputs.app-id }}
if [[ ${{ inputs.flavor }} == 'Debug' ]]; then
# To give the app time to warm the metro's cache
sleep 20
fi
echo "Running tests with Maestro"
export MAESTRO_DRIVER_STARTUP_TIMEOUT=1500000 # 25 min. CI is extremely slow
# Add retries for flakyness
MAX_ATTEMPTS=5
CURR_ATTEMPT=0
RESULT=1
while [[ $CURR_ATTEMPT -lt $MAX_ATTEMPTS ]] && [[ $RESULT -ne 0 ]]; do
CURR_ATTEMPT=$((CURR_ATTEMPT+1))
echo "Attempt number $CURR_ATTEMPT"
echo "Start video record using pid: video_record_${{ inputs.jsengine }}_$CURR_ATTEMPT.pid"
xcrun simctl io booted recordVideo video_record_$CURR_ATTEMPT.mov & echo $! > video_record_${{ inputs.jsengine }}_$CURR_ATTEMPT.pid
echo '$HOME/.maestro/bin/maestro --udid=$UDID test ${{ inputs.maestro-flow }} --format junit -e APP_ID=${{ inputs.app-id }}'
$HOME/.maestro/bin/maestro --udid=$UDID test ${{ inputs.maestro-flow }} --format junit -e APP_ID=${{ inputs.app-id }} --debug-output /tmp/MaestroLogs
RESULT=$?
# Stop video
kill -SIGINT $(cat video_record_${{ inputs.jsengine }}_$CURR_ATTEMPT.pid)
done
exit $RESULT
- name: Store video record
if: always()
uses: actions/upload-artifact@v4.3.4
with:
name: e2e_ios_${{ inputs.app-id }}_report_${{ inputs.flavor }}_NewArch
name: e2e_ios_${{ inputs.app-id }}_report_${{ inputs.jsengine }}_${{ inputs.flavor }}_${{ inputs.architecture }}
path: |
video_record_1.mov
video_record_2.mov
@@ -78,5 +119,5 @@ runs:
if: failure() && steps.run-tests.outcome == 'failure'
uses: actions/upload-artifact@v4.3.4
with:
name: maestro-logs-${{ inputs.app-id }}-${{ inputs.flavor }}-NewArch
name: maestro-logs-${{ inputs.app-id }}-${{ inputs.jsengine }}-${{ inputs.flavor }}-${{ inputs.architecture }}
path: /tmp/MaestroLogs
@@ -69,7 +69,7 @@ runs:
- name: Yarn- Install Dependencies
if: ${{ steps.meaningful-cache.outputs.HERMES_CACHED != 'true' }}
uses: ./.github/actions/yarn-install
uses: ./.github/actions/yarn-install-with-cache
- name: Download Hermes tarball
if: ${{ steps.meaningful-cache.outputs.HERMES_CACHED != 'true' }}
+2 -5
View File
@@ -4,8 +4,6 @@ inputs:
cache-read-only:
description: "Whether the Gradle Cache should be in read-only mode so this job won't be allowed to write to it"
default: "true"
cache-encryption-key:
description: "The encryption key needed to store the Gradle Configuration cache"
runs:
using: "composite"
steps:
@@ -17,7 +15,6 @@ runs:
cache-read-only: ${{ (github.ref != 'refs/heads/main' && !contains(github.ref, '-stable')) || inputs.cache-read-only == 'true' }}
# Similarly, for those jobs we want to start with a clean cache so it doesn't grow without limits (this is the negation of the previous condition).
cache-write-only: ${{ (github.ref == 'refs/heads/main' || contains(github.ref, '-stable')) && inputs.cache-read-only != 'true' }}
# Temporarily disabling to try resolve a cache cleanup failure
# gradle-home-cache-cleanup: true
add-job-summary-as-pr-comment: on-failure
# Encryption key for the Gradle Configuration Cache.
# See https://docs.gradle.org/8.6/userguide/configuration_cache.html#config_cache:secrets:configuring_encryption_key
cache-encryption-key: ${{ inputs.cache-encryption-key }}
+1 -2
View File
@@ -4,7 +4,7 @@ inputs:
node-version:
description: 'The node.js version to use'
required: false
default: '22'
default: '18'
runs:
using: "composite"
steps:
@@ -12,4 +12,3 @@ runs:
uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node-version }}
cache: yarn
@@ -4,29 +4,25 @@ inputs:
hermes-version:
description: The version of hermes
required: true
flavor:
description: The flavor that is going to be built
default: Debug
use-frameworks:
description: Whether we are bulding with DynamicFrameworks or StaticLibraries
default: StaticLibraries
ruby-version:
description: The ruby version we are going to use
default: 2.6.10
runs:
using: composite
steps:
- name: See commands.yml with_xcodebuild_cache
shell: bash
run: echo "See commands.yml with_xcodebuild_cache"
- name: Prepare Xcodebuild cache
shell: bash
run: |
WEEK=$(date +"%U")
YEAR=$(date +"%Y")
echo "$WEEK-$YEAR" > /tmp/week_year
- name: Cache podfile lock
uses: actions/cache@v4
with:
path: packages/rn-tester/Podfile.lock
key: v13-podfilelock-${{ github.job }}-NewArch-${{ inputs.flavor }}-${{ inputs.use-frameworks }}-${{ inputs.ruby-version }}-${{ hashfiles('packages/rn-tester/Podfile') }}-${{ inputs.hermes-version }}
key: v11-podfilelock-${{ github.job }}-${{ hashfiles('packages/rn-tester/Podfile') }}-${{ hashfiles('/tmp/week_year') }}-${{ inputs.hermes-version }}
- name: Cache cocoapods
uses: actions/cache@v4
with:
path: packages/rn-tester/Pods
key: v15-cocoapods-${{ github.job }}-NewArch-${{ inputs.flavor }}-${{ inputs.use-frameworks }}-${{ inputs.ruby-version }}-${{ hashfiles('packages/rn-tester/Podfile.lock') }}-${{ hashfiles('packages/rn-tester/Podfile') }}-${{ inputs.hermes-version}}
key: v13-cocoapods-${{ github.job }}-${{ hashfiles('packages/rn-tester/Podfile.lock') }}-${{ hashfiles('packages/rn-tester/Podfile') }}-${{ inputs.hermes-version}}
+1 -1
View File
@@ -4,7 +4,7 @@ inputs:
xcode-version:
description: 'The xcode version to use'
required: false
default: '16.2.0'
default: '15.2'
runs:
using: "composite"
steps:
+34 -36
View File
@@ -1,9 +1,15 @@
name: test-ios-helloworld
description: Test iOS Hello World
inputs:
jsengine:
description: Which JavaScript engine to use. Must be one of "Hermes", "JSC".
default: Hermes
use-frameworks:
description: The dependency building and linking strategy to use. Must be one of "StaticLibraries", "DynamicFrameworks"
default: StaticLibraries
architecture:
description: The React Native architecture to Test. RNTester has always Fabric enabled, but we want to run integration test with the old arch setup. Must be one of "OldArch" or "NewArch"
default: OldArch
ruby-version:
description: The version of ruby that must be used
default: 2.6.10
@@ -35,57 +41,49 @@ runs:
shell: bash
run: ls -lR "$HERMES_WS_DIR"
- name: Run yarn
uses: ./.github/actions/yarn-install
uses: ./.github/actions/yarn-install-with-cache
- name: Setup ruby
uses: ruby/setup-ruby@v1
uses: ruby/setup-ruby@v1.170.0
with:
ruby-version: ${{ inputs.ruby-version }}
- name: Download ReactNativeDependencies
uses: actions/download-artifact@v4
with:
name: ReactNativeDependencies${{ inputs.flavor }}.xcframework.tar.gz
path: /tmp/third-party
- name: Print third-party folder
shell: bash
run: ls -lR /tmp/third-party
- name: Install iOS dependencies - Configuration ${{ inputs.flavor }};
- name: Install iOS dependencies - Configuration ${{ inputs.flavor }}; New Architecture ${{ inputs.architecture }}; JS Engine ${{ inputs.jsengine }}
shell: bash
run: |
cd private/helloworld
cd packages/helloworld
args=()
if [[ ${{ inputs.architecture }} == "OldArch" ]]; then
args+=(--arch old)
fi
if [[ ${{ inputs.use-frameworks }} == "DynamicFrameworks" ]]; then
args+=(--frameworks dynamic)
fi
# Tarball is restored with capital flavors suffix, but somehow the tarball name from JS at line 96 returns as lowercased.
# Let's ensure that the tarballs have the right names
if [[ ${{ inputs.jsengine }} == "JSC" ]]; then
args+=(--jsvm jsc)
yarn bootstrap ios "${args[@]}" | cat
else
# Tarball is restored with capital flavors suffix, but somehow the tarball name from JS at line 96 returns as lowercased.
# Let's ensure that the tarballs have the right names
if [[ -f "$HERMES_WS_DIR/hermes-runtime-darwin/hermes-ios-Debug.tar.gz" ]]; then
mv "$HERMES_WS_DIR/hermes-runtime-darwin/hermes-ios-Debug.tar.gz" "$HERMES_WS_DIR/hermes-runtime-darwin/hermes-ios-debug.tar.gz"
if [[ -f "$HERMES_WS_DIR/hermes-runtime-darwin/hermes-ios-Debug.tar.gz" ]]; then
mv "$HERMES_WS_DIR/hermes-runtime-darwin/hermes-ios-Debug.tar.gz" "$HERMES_WS_DIR/hermes-runtime-darwin/hermes-ios-debug.tar.gz"
fi
if [[ -f "$HERMES_WS_DIR/hermes-runtime-darwin/hermes-ios-Release.tar.gz" ]]; then
mv "$HERMES_WS_DIR/hermes-runtime-darwin/hermes-ios-Release.tar.gz" "$HERMES_WS_DIR/hermes-runtime-darwin/hermes-ios-release.tar.gz"
fi
BUILD_TYPE="${{ inputs.flavor }}"
TARBALL_FILENAME=$(node ../react-native/scripts/hermes/get-tarball-name.js --buildType "$BUILD_TYPE")
HERMES_PATH="$HERMES_WS_DIR/hermes-runtime-darwin/$TARBALL_FILENAME"
HERMES_ENGINE_TARBALL_PATH="$HERMES_PATH" yarn bootstrap ios "${args[@]}" | cat
fi
if [[ -f "$HERMES_WS_DIR/hermes-runtime-darwin/hermes-ios-Release.tar.gz" ]]; then
mv "$HERMES_WS_DIR/hermes-runtime-darwin/hermes-ios-Release.tar.gz" "$HERMES_WS_DIR/hermes-runtime-darwin/hermes-ios-release.tar.gz"
fi
BUILD_TYPE="${{ inputs.flavor }}"
TARBALL_FILENAME=$(node ../../packages/react-native/scripts/hermes/get-tarball-name.js --buildType "$BUILD_TYPE")
export HERMES_ENGINE_TARBALL_PATH="$HERMES_WS_DIR/hermes-runtime-darwin/$TARBALL_FILENAME"
export RCT_USE_LOCAL_RN_DEP="/tmp/third-party/ReactNativeDependencies${{ inputs.flavor }}.xcframework.tar.gz"
yarn bootstrap ios "${args[@]}" | cat
- name: Run Helloworld tests
- name: Build HelloWorld project
shell: bash
run: |
cd private/helloworld
yarn test
- name: Build HelloWorld project
shell: bash
run: |
cd private/helloworld
cd packages/helloworld
args=()
if [[ ${{ inputs.flavor }} == "Release" ]]; then
+27 -25
View File
@@ -1,9 +1,15 @@
name: test-ios-rntester
description: Test iOS RNTester
inputs:
jsengine:
description: Which JavaScript engine to use. Must be one of "Hermes", "JSC".
default: Hermes
use-frameworks:
description: The dependency building and linking strategy to use. Must be one of "StaticLibraries", "DynamicFrameworks"
default: StaticLibraries
architecture:
description: The React Native architecture to Test. RNTester has always Fabric enabled, but we want to run integration test with the old arch setup
default: NewArch
ruby-version:
description: The version of ruby that must be used
default: 2.6.10
@@ -35,14 +41,14 @@ runs:
- name: Setup node.js
uses: ./.github/actions/setup-node
- name: Run yarn
uses: ./.github/actions/yarn-install
uses: ./.github/actions/yarn-install-with-cache
- name: Download Hermes
uses: actions/download-artifact@v4
with:
name: hermes-darwin-bin-${{ inputs.flavor }}
path: ${{ inputs.hermes-tarball-artifacts-dir }}
- name: Setup ruby
uses: ruby/setup-ruby@v1
uses: ruby/setup-ruby@v1.170.0
with:
ruby-version: ${{ inputs.ruby-version }}
- name: Prepare IOS Tests
@@ -86,37 +92,42 @@ runs:
else
echo 'No Hermes tarball found.'
fi
- name: Download ReactNativeDependencies
uses: actions/download-artifact@v4
with:
name: ReactNativeDependencies${{ inputs.flavor }}.xcframework.tar.gz
path: /tmp/third-party/
- name: Print third-party folder
shell: bash
run: ls -lR /tmp/third-party
- name: Setup xcode build cache
uses: ./.github/actions/setup-xcode-build-cache
with:
hermes-version: ${{ inputs.hermes-version }}
use-frameworks: ${{ inputs.use-frameworks }}
flavor: ${{ inputs.flavor }}
ruby-version: ${{ inputs.ruby-version }}
- name: Install CocoaPods dependencies
shell: bash
run: |
export HERMES_ENGINE_TARBALL_PATH=$HERMES_ENGINE_TARBALL_PATH
if [[ ${{ inputs.jsengine }} == "JSC" ]]; then
export USE_HERMES=0
else
export HERMES_ENGINE_TARBALL_PATH=$HERMES_ENGINE_TARBALL_PATH
fi
if [[ ${{ inputs.use-frameworks }} == "DynamicFrameworks" ]]; then
export USE_FRAMEWORKS=dynamic
fi
export RCT_USE_LOCAL_RN_DEP="/tmp/third-party/ReactNativeDependencies${{ inputs.flavor }}.xcframework.tar.gz"
if [[ ${{ inputs.architecture }} == "NewArch" ]]; then
export RCT_NEW_ARCH_ENABLED=1
fi
cd packages/rn-tester
bundle install
bundle exec pod install
- name: Build RNTester
if: ${{ inputs.run-unit-tests != 'true' && inputs.run-e2e-tests == 'false' }}
shell: bash
run: |
xcodebuild build \
-workspace packages/rn-tester/RNTesterPods.xcworkspace \
-scheme RNTester \
-sdk iphonesimulator
- name: Build RNTester (E2E Tests)
shell: bash
if: ${{ inputs.run-e2e-tests == 'true' }}
run: |
xcodebuild \
-scheme "RNTester" \
@@ -127,10 +138,7 @@ runs:
-derivedDataPath "/tmp/RNTesterBuild"
echo "Print path to *.app file"
APP_PATH=$(find "/tmp/RNTesterBuild" -type d -name "*.app")
echo "App found at $APP_PATH"
echo "app-path=$APP_PATH" >> $GITHUB_ENV
find "/tmp/RNTesterBuild" -type d -name "*.app"
- name: "Run Tests: iOS Unit and Integration Tests"
if: ${{ inputs.run-unit-tests == 'true' }}
shell: bash
@@ -150,12 +158,6 @@ runs:
with:
name: xcresults
path: /Users/distiller/Library/Developer/Xcode/xcresults.tar.gz
- name: Upload RNTester App
if: ${{ inputs.use-frameworks == 'StaticLibraries' && inputs.ruby-version == '2.6.10' }} # This is needed to avoid conflicts with the artifacts
uses: actions/upload-artifact@v4.3.4
with:
name: RNTesterApp-NewArch-${{ inputs.flavor }}
path: ${{ env.app-path }}
- name: Store test results
if: ${{ inputs.run-unit-tests == 'true' }}
uses: actions/upload-artifact@v4.3.4
+2 -2
View File
@@ -4,7 +4,7 @@ inputs:
node-version:
description: "The node.js version to use"
required: false
default: "22"
default: "18"
runs:
using: composite
steps:
@@ -13,7 +13,7 @@ runs:
with:
node-version: ${{ inputs.node-version }}
- name: Yarn install
uses: ./.github/actions/yarn-install
uses: ./.github/actions/yarn-install-with-cache
- name: Run Tests - JavaScript Tests
shell: bash
run: node ./scripts/run-ci-javascript-tests.js --maxWorkers 2
@@ -21,10 +21,6 @@ runs:
cd /tmp/RNApp
yarn add ${{ inputs.library-npm-package }}
# iOS
- name: Setup xcode
if: ${{ inputs.platform == 'ios' }}
uses: ./.github/actions/setup-xcode
- name: Build iOS
shell: bash
if: ${{ inputs.platform == 'ios' }}
@@ -36,8 +32,6 @@ runs:
-workspace RNApp.xcworkspace \
-scheme RNApp \
-sdk iphonesimulator
# Android
- name: Setup Java for Android
if: ${{ inputs.platform == 'android' }}
uses: actions/setup-java@v2
@@ -0,0 +1,28 @@
name: yarn-install-with-cache
inputs:
update-cache:
description: Update the cache, only do this if you are update-node-modules-cache.yml
default: "false"
description: Only update node_modules if on main
runs:
using: composite
steps:
- name: Load node_modules from cache
# Restore for all branches, but save for 'main'.
uses: actions/cache/restore@v4
with:
path: node_modules/
key: node-modules-${{ hashFiles('package.json') }}
- name: Install dependencies
shell: bash
run: yarn install --non-interactive
- name: Save node_modules to the cache
if: github.ref == 'refs/heads/main' && inputs.update-cache == 'true'
uses: actions/cache/save@v4
with:
path: node_modules/
# We're assuming that variations on branches will slightly vary from main,
# so it's always important to run yarn install --non-interactive after this
# cache is restored.
key: node-modules-v1-${{ hashFiles('package.json') }}
enableCrossOsArchive: true
-20
View File
@@ -1,20 +0,0 @@
name: yarn-install
runs:
using: composite
steps:
- name: Install dependencies
shell: bash
run: |
MAX_ATTEMPTS=2
ATTEMPT=0
WAIT_TIME=20
while [ $ATTEMPT -lt $MAX_ATTEMPTS ]; do
yarn install --non-interactive --frozen-lockfile && break
echo "yarn install failed. Retrying in $WAIT_TIME seconds..."
sleep $WAIT_TIME
ATTEMPT=$((ATTEMPT + 1))
done
if [ $ATTEMPT -eq $MAX_ATTEMPTS ]; then
echo "All attempts to invoke yarn install failed - Aborting the workflow"
exit 1
fi
@@ -1,305 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/
const {
_verifyTagExists,
_extractChangelog,
_computeBody,
_createDraftReleaseOnGitHub,
} = require('../createDraftRelease');
const fs = require('fs');
const silence = () => {};
const mockFetch = jest.fn();
jest.mock('../utils.js', () => ({
log: silence,
}));
global.fetch = mockFetch;
describe('Create Draft Release', () => {
beforeEach(jest.clearAllMocks);
describe('#_verifyTagExists', () => {
it('throws if the tag does not exists', async () => {
const token = 'token';
mockFetch.mockReturnValueOnce(Promise.resolve({status: 404}));
await expect(_verifyTagExists('0.77.1')).rejects.toThrowError(
`Tag v0.77.1 does not exist`,
);
expect(mockFetch).toHaveBeenCalledTimes(1);
expect(mockFetch).toHaveBeenCalledWith(
'https://github.com/facebook/react-native/releases/tag/v0.77.1',
);
});
});
describe('#_extractChangelog', () => {
it(`extracts changelog from CHANGELOG.md`, async () => {
const mockedReturnValue = `# Changelog
## v0.77.2
- [PR #1234](https://github.com/facebook/react-native/pull/1234) - Some change
- [PR #5678](https://github.com/facebook/react-native/pull/5678) - Some other change
## v0.77.1
### Breaking Changes
- [PR #9012](https://github.com/facebook/react-native/pull/9012) - Some other change
#### Android
- [PR #3456](https://github.com/facebook/react-native/pull/3456) - Some other change
- [PR #3457](https://github.com/facebook/react-native/pull/3457) - Some other change
#### iOS
- [PR #3436](https://github.com/facebook/react-native/pull/3436) - Some other change
- [PR #3437](https://github.com/facebook/react-native/pull/3437) - Some other change
### Fixed
- [PR #9012](https://github.com/facebook/react-native/pull/9012) - Some other change
#### Android
- [PR #3456](https://github.com/facebook/react-native/pull/3456) - Some other change
#### iOS
- [PR #3437](https://github.com/facebook/react-native/pull/3437) - Some other change
## v0.77.0
- [PR #3456](https://github.com/facebook/react-native/pull/3456) - Some other change
## v0.76.0
- [PR #7890](https://github.com/facebook/react-native/pull/7890) - Some other change`;
jest.spyOn(fs, 'readFileSync').mockImplementationOnce(func => {
return mockedReturnValue;
});
const changelog = _extractChangelog('0.77.1');
expect(changelog).toEqual(`## v0.77.1
### Breaking Changes
- [PR #9012](https://github.com/facebook/react-native/pull/9012) - Some other change
#### Android
- [PR #3456](https://github.com/facebook/react-native/pull/3456) - Some other change
- [PR #3457](https://github.com/facebook/react-native/pull/3457) - Some other change
#### iOS
- [PR #3436](https://github.com/facebook/react-native/pull/3436) - Some other change
- [PR #3437](https://github.com/facebook/react-native/pull/3437) - Some other change
### Fixed
- [PR #9012](https://github.com/facebook/react-native/pull/9012) - Some other change
#### Android
- [PR #3456](https://github.com/facebook/react-native/pull/3456) - Some other change
#### iOS
- [PR #3437](https://github.com/facebook/react-native/pull/3437) - Some other change`);
});
it('does not extract changelog for rc.0', async () => {
const changelog = _extractChangelog('0.77.0-rc.0');
expect(changelog).toEqual('');
});
it('does not extract changelog for 0.X.0', async () => {
const changelog = _extractChangelog('0.77.0');
expect(changelog).toEqual('');
});
});
describe('#_computeBody', () => {
it('computes body for release', async () => {
const version = '0.77.1';
const changelog = `## v${version}
### Breaking Changes
- [PR #9012](https://github.com/facebook/react-native/pull/9012) - Some other change
#### Android
- [PR #3456](https://github.com/facebook/react-native/pull/3456) - Some other change
- [PR #3457](https://github.com/facebook/react-native/pull/3457) - Some other change
#### iOS
- [PR #3436](https://github.com/facebook/react-native/pull/3436) - Some other change
- [PR #3437](https://github.com/facebook/react-native/pull/3437) - Some other change`;
const body = _computeBody(version, changelog);
expect(body).toEqual(`${changelog}
---
Hermes dSYMS:
- [Debug](https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/${version}/react-native-artifacts-${version}-hermes-framework-dSYM-debug.tar.gz)
- [Release](https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/${version}/react-native-artifacts-${version}-hermes-framework-dSYM-release.tar.gz)
ReactNativeDependencies dSYMs:
- [Debug](https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/${version}/react-native-artifacts-${version}-reactnative-dependencies-dSYM-debug.tar.gz)
- [Release](https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/${version}/react-native-artifacts-${version}-reactnative-dependencies-dSYM-release.tar.gz)
---
You can file issues or pick requests against this release [here](https://github.com/reactwg/react-native-releases/issues/new/choose).
---
To help you upgrade to this version, you can use the [Upgrade Helper](https://react-native-community.github.io/upgrade-helper/) ⚛️.
---
View the whole changelog in the [CHANGELOG.md file](https://github.com/facebook/react-native/blob/main/CHANGELOG.md).`);
});
});
describe('#_createDraftReleaseOnGitHub', () => {
it('creates a draft release on GitHub', async () => {
const version = '0.77.1';
const url = 'https://api.github.com/repos/facebook/react-native/releases';
const token = 'token';
const headers = {
Accept: 'Accept: application/vnd.github+json',
'X-GitHub-Api-Version': '2022-11-28',
Authorization: `Bearer ${token}`,
};
const body = `Draft release body`;
const latest = true;
const fetchBody = JSON.stringify({
tag_name: `v${version}`,
name: `${version}`,
body: body,
draft: true,
prerelease: false,
make_latest: `${latest}`,
});
mockFetch.mockReturnValueOnce(
Promise.resolve({
status: 201,
json: () =>
Promise.resolve({
html_url:
'https://github.com/facebook/react-native/releases/tag/v0.77.1',
}),
}),
);
const response = await _createDraftReleaseOnGitHub(
version,
body,
latest,
token,
);
expect(mockFetch).toHaveBeenCalledTimes(1);
expect(mockFetch).toHaveBeenCalledWith(
`https://api.github.com/repos/facebook/react-native/releases`,
{
method: 'POST',
headers: headers,
body: fetchBody,
},
);
expect(response).toEqual(
'https://github.com/facebook/react-native/releases/tag/v0.77.1',
);
});
it('creates a draft release for prerelease on GitHub', async () => {
const version = '0.77.0-rc.2';
const url = 'https://api.github.com/repos/facebook/react-native/releases';
const token = 'token';
const headers = {
Accept: 'Accept: application/vnd.github+json',
'X-GitHub-Api-Version': '2022-11-28',
Authorization: `Bearer ${token}`,
};
const body = `Draft release body`;
const latest = true;
const fetchBody = JSON.stringify({
tag_name: `v${version}`,
name: `${version}`,
body: body,
draft: true,
prerelease: true,
make_latest: `${latest}`,
});
mockFetch.mockReturnValueOnce(
Promise.resolve({
status: 201,
json: () =>
Promise.resolve({
html_url:
'https://github.com/facebook/react-native/releases/tag/v0.77.1',
}),
}),
);
const response = await _createDraftReleaseOnGitHub(
version,
body,
latest,
token,
);
expect(mockFetch).toHaveBeenCalledTimes(1);
expect(mockFetch).toHaveBeenCalledWith(
`https://api.github.com/repos/facebook/react-native/releases`,
{
method: 'POST',
headers: headers,
body: fetchBody,
},
);
expect(response).toEqual(
'https://github.com/facebook/react-native/releases/tag/v0.77.1',
);
});
it('throws if the post failes', async () => {
const version = '0.77.0-rc.2';
const url = 'https://api.github.com/repos/facebook/react-native/releases';
const token = 'token';
const headers = {
Accept: 'Accept: application/vnd.github+json',
'X-GitHub-Api-Version': '2022-11-28',
Authorization: `Bearer ${token}`,
};
const body = `Draft release body`;
const latest = true;
const fetchBody = JSON.stringify({
tag_name: `v${version}`,
name: `${version}`,
body: body,
draft: true,
prerelease: true,
make_latest: `${latest}`,
});
mockFetch.mockReturnValueOnce(
Promise.resolve({
status: 401,
}),
);
await expect(
_createDraftReleaseOnGitHub(version, body, latest, token),
).rejects.toThrowError();
expect(mockFetch).toHaveBeenCalledTimes(1);
expect(mockFetch).toHaveBeenCalledWith(
`https://api.github.com/repos/facebook/react-native/releases`,
{
method: 'POST',
headers: headers,
body: fetchBody,
},
);
});
});
});
@@ -1,83 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/
const {extractIssueOncalls} = require('../extractIssueOncalls');
const userMap = {
'@g': '1785',
'@c': '1781',
'@s': '1272',
'@d': '1332',
'@m': '9555',
'@p': '6097',
'@f': '7565',
};
const schedule = {
'2025-04-01': ['@m', '@f'],
'2025-04-08': ['@g', '@d'],
};
describe('extractIssueOncalls', () => {
beforeEach(() => {
jest.clearAllMocks();
jest.useFakeTimers('modern');
});
afterEach(() => {
jest.useRealTimers();
});
it('extracts m and f on 6 of April', () => {
jest.setSystemTime(new Date(2025, 3, 6));
const oncalls = extractIssueOncalls(schedule, userMap);
expect(oncalls).toEqual([userMap['@m'], userMap['@f']]);
});
it('extracts m and f on 7 of April', () => {
jest.setSystemTime(new Date(2025, 3, 7));
const oncalls = extractIssueOncalls(schedule, userMap);
expect(oncalls).toEqual([userMap['@m'], userMap['@f']]);
});
it('extracts g and d on 8 of April', () => {
jest.setSystemTime(new Date(2025, 3, 8));
const oncalls = extractIssueOncalls(schedule, userMap);
expect(oncalls).toEqual([userMap['@g'], userMap['@d']]);
});
it('extracts g and d on 9 of April', () => {
jest.setSystemTime(new Date(2025, 3, 9));
const oncalls = extractIssueOncalls(schedule, userMap);
expect(oncalls).toEqual([userMap['@g'], userMap['@d']]);
});
it('extracts g and d on 10 of April', () => {
jest.setSystemTime(new Date(2025, 3, 10));
const oncalls = extractIssueOncalls(schedule, userMap);
expect(oncalls).toEqual([userMap['@g'], userMap['@d']]);
});
it('extracts g and d on 11 of April', () => {
jest.setSystemTime(new Date(2025, 3, 11));
const oncalls = extractIssueOncalls(schedule, userMap);
expect(oncalls).toEqual([userMap['@g'], userMap['@d']]);
});
it('extracts g and d on 12 of April', () => {
jest.setSystemTime(new Date(2025, 3, 12));
const oncalls = extractIssueOncalls(schedule, userMap);
expect(oncalls).toEqual([userMap['@g'], userMap['@d']]);
});
it('extracts g and d on 13 of April', () => {
jest.setSystemTime(new Date(2025, 3, 13));
const oncalls = extractIssueOncalls(schedule, userMap);
expect(oncalls).toEqual([userMap['@g'], userMap['@d']]);
});
});
@@ -1,251 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/
const {
generateChangelog,
_computePreviousVersionFrom,
_generateChangelog,
_pushCommit,
_createPR,
} = require('../generateChangelog');
const silence = () => {};
const mockGetNpmPackageInfo = jest.fn();
const mockExecSync = jest.fn();
const mockRun = jest.fn();
const mockFetch = jest.fn();
const mockExit = jest.fn();
jest.mock('../utils.js', () => ({
log: silence,
run: mockRun,
getNpmPackageInfo: mockGetNpmPackageInfo,
}));
process.exit = mockExit;
global.fetch = mockFetch;
describe('Generate Changelog', () => {
beforeEach(jest.clearAllMocks);
describe('_computePreviousVersionFrom', () => {
it('returns rc.0 when rc is 1', async () => {
const currentVersion = '0.78.0-rc.1';
const expectedVersion = '0.78.0-rc.0';
const receivedVersion = await _computePreviousVersionFrom(currentVersion);
expect(receivedVersion).toEqual(expectedVersion);
});
it('returns previous rc version when rc is > 1', async () => {
const currentVersion = '0.78.0-rc.5';
const expectedVersion = '0.78.0-rc.4';
const receivedVersion = await _computePreviousVersionFrom(currentVersion);
expect(receivedVersion).toEqual(expectedVersion);
});
it('returns previous patch version when rc is 0', async () => {
const currentVersion = '0.78.0-rc.0';
const expectedVersion = '0.77.1';
mockGetNpmPackageInfo.mockReturnValueOnce(
Promise.resolve({version: '0.77.1'}),
);
const receivedVersion = await _computePreviousVersionFrom(currentVersion);
expect(receivedVersion).toEqual(expectedVersion);
});
it('returns patch 0 when patch is 1', async () => {
const currentVersion = '0.78.1';
const expectedVersion = '0.78.0';
const receivedVersion = await _computePreviousVersionFrom(currentVersion);
expect(receivedVersion).toEqual(expectedVersion);
});
it('returns previous patch when patch is > 1', async () => {
const currentVersion = '0.78.5';
const expectedVersion = '0.78.4';
const receivedVersion = await _computePreviousVersionFrom(currentVersion);
expect(receivedVersion).toEqual(expectedVersion);
});
it('returns null when patch is 0', async () => {
const currentVersion = '0.78.0';
const receivedVersion = await _computePreviousVersionFrom(currentVersion);
expect(receivedVersion).toBeNull();
});
it("throws an error when the version can't be parsed", async () => {
const currentVersion = '0.78.0-rc0';
await expect(
_computePreviousVersionFrom(currentVersion),
).rejects.toThrow();
});
});
describe('_generateChangelog', () => {
it('calls git in the right order', async () => {
const currentVersion = '0.79.0-rc5';
const previousVersion = '0.79.0-rc4';
const token = 'token';
expectedCommandArgs = [
'@rnx-kit/rn-changelog-generator',
'--base',
`v${previousVersion}`,
'--compare',
`v${currentVersion}`,
'--repo',
'.',
'--changelog',
'./CHANGELOG.md',
'--token',
`${token}`,
];
_generateChangelog(previousVersion, currentVersion, token);
expect(mockRun).toHaveBeenCalledTimes(4);
expect(mockRun).toHaveBeenNthCalledWith(1, 'git checkout main');
expect(mockRun).toHaveBeenNthCalledWith(2, 'git fetch');
expect(mockRun).toHaveBeenNthCalledWith(3, 'git pull origin main');
expect(mockRun).toHaveBeenNthCalledWith(
4,
`npx ${expectedCommandArgs.join(' ')}`,
);
});
});
describe('_pushCommit', () => {
it('calls git in the right order', async () => {
const currentVersion = '0.79.0-rc5';
_pushCommit(currentVersion);
expect(mockRun).toHaveBeenCalledTimes(4);
expect(mockRun).toHaveBeenNthCalledWith(
1,
`git checkout -b changelog/v${currentVersion}`,
);
expect(mockRun).toHaveBeenNthCalledWith(2, 'git add CHANGELOG.md');
expect(mockRun).toHaveBeenNthCalledWith(
3,
`git commit -m "[RN][Changelog] Add changelog for v${currentVersion}"`,
);
expect(mockRun).toHaveBeenNthCalledWith(
4,
`git push origin changelog/v${currentVersion}`,
);
});
});
describe('_createPR', () => {
it('throws error when status is not 201', async () => {
const currentVersion = '0.79.0-rc5';
const token = 'token';
mockFetch.mockReturnValueOnce(Promise.resolve({status: 401}));
const headers = {
Accept: 'Accept: application/vnd.github+json',
'X-GitHub-Api-Version': '2022-11-28',
Authorization: `Bearer ${token}`,
};
const content = `
## Summary
Add Changelog for ${currentVersion}
## Changelog:
[Internal] - Add Changelog for ${currentVersion}
## Test Plan:
N/A`;
const body = {
title: `[RN][Changelog] Add changelog for v${currentVersion}`,
head: `changelog/v${currentVersion}`,
base: 'main',
body: content,
};
await expect(_createPR(currentVersion, token)).rejects.toThrow();
expect(mockFetch).toHaveBeenCalledTimes(1);
expect(mockFetch).toHaveBeenCalledWith(
'https://api.github.com/repos/facebook/react-native/pulls',
{
method: 'POST',
headers: headers,
body: JSON.stringify(body),
},
);
});
it('Returns the pr url', async () => {
const currentVersion = '0.79.0-rc5';
const token = 'token';
const expectedPrURL =
'https://github.com/facebook/react-native/pulls/1234';
const returnedObject = {
status: 201,
json: () => Promise.resolve({html_url: expectedPrURL}),
};
mockFetch.mockReturnValueOnce(Promise.resolve(returnedObject));
const headers = {
Accept: 'Accept: application/vnd.github+json',
'X-GitHub-Api-Version': '2022-11-28',
Authorization: `Bearer ${token}`,
};
const content = `
## Summary
Add Changelog for ${currentVersion}
## Changelog:
[Internal] - Add Changelog for ${currentVersion}
## Test Plan:
N/A`;
const body = {
title: `[RN][Changelog] Add changelog for v${currentVersion}`,
head: `changelog/v${currentVersion}`,
base: 'main',
body: content,
};
const receivedPrURL = await _createPR(currentVersion, token);
expect(mockFetch).toHaveBeenCalledTimes(1);
expect(mockFetch).toHaveBeenCalledWith(
'https://api.github.com/repos/facebook/react-native/pulls',
{
method: 'POST',
headers: headers,
body: JSON.stringify(body),
},
);
expect(receivedPrURL).toEqual(expectedPrURL);
});
});
});
@@ -1,189 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/
'use strict';
const {
prepareFailurePayload,
sendMessageToDiscord,
} = require('../notifyDiscord');
describe('prepareFailurePayload', () => {
it('should handle undefined failures', () => {
const message = prepareFailurePayload(undefined);
expect(message).toEqual({
content:
'⚠️ **React Native Nightly Integration Failures** ⚠️\n\nNo failures to report.',
});
});
it('should handle empty failures array', () => {
const message = prepareFailurePayload([]);
expect(message).toEqual({
content:
'⚠️ **React Native Nightly Integration Failures** ⚠️\n\nNo failures to report.',
});
});
it('should format a single failure correctly', () => {
const failures = [
{
library: 'react-native-reanimated',
platform: 'iOS',
},
];
const message = prepareFailurePayload(failures);
expect(message).toEqual({
content:
'⚠️ **React Native Nightly Integration Failures** ⚠️\n\nThe integration of libraries with React Native nightly failed for the following libraries:\n\n❌ [iOS] react-native-reanimated',
});
});
it('should sort multiple failures by platform and library name', () => {
const failures = [
{
library: 'react-native-reanimated',
platform: 'iOS',
},
{
library: 'react-native-gesture-handler',
platform: 'Android',
},
{
library: 'react-native-screens',
platform: 'iOS',
},
{
library: 'react-native-svg',
platform: 'Android',
},
];
const message = prepareFailurePayload(failures);
// The failures should be sorted: first Android (alphabetically), then iOS
// Within each platform, libraries should be sorted alphabetically
expect(message).toEqual({
content:
'⚠️ **React Native Nightly Integration Failures** ⚠️\n\nThe integration of libraries with React Native nightly failed for the following libraries:\n\n❌ [Android] react-native-gesture-handler\n❌ [Android] react-native-svg\n❌ [iOS] react-native-reanimated\n❌ [iOS] react-native-screens',
});
});
it('should handle failures with missing properties', () => {
const failures = [
{
// Missing library
platform: 'iOS',
},
{
library: 'react-native-gesture-handler',
// Missing platform
},
{
// Both missing
},
];
const message = prepareFailurePayload(failures);
expect(message).toEqual({
content:
'⚠️ **React Native Nightly Integration Failures** ⚠️\n\nThe integration of libraries with React Native nightly failed for the following libraries:\n\n❌ [iOS] Unknown\n❌ [Unknown] react-native-gesture-handler\n❌ [Unknown] Unknown',
});
});
});
describe('sendMessageToDiscord', () => {
// Store the original fetch function
const originalFetch = global.fetch;
// Setup and teardown for each test
beforeEach(() => {
// Mock the global fetch function
global.fetch = jest.fn();
// Silence console logs during tests
jest.spyOn(console, 'log').mockImplementation(() => {});
jest.spyOn(console, 'error').mockImplementation(() => {});
});
afterEach(() => {
// Restore the original fetch function
global.fetch = originalFetch;
// Restore console functions
jest.restoreAllMocks();
});
it('should throw an error if webhook URL is missing', async () => {
await expect(sendMessageToDiscord(null, {})).rejects.toThrow(
'Discord webhook URL is missing',
);
});
it('should send a message successfully', async () => {
// Mock a successful response
global.fetch.mockResolvedValueOnce({
ok: true,
status: 200,
});
const webhook = 'https://discord.com/api/webhooks/123/abc';
const message = {content: 'Test message'};
await expect(sendMessageToDiscord(webhook, message)).resolves.not.toThrow();
// Verify fetch was called with the right arguments
expect(global.fetch).toHaveBeenCalledWith(webhook, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(message),
});
// Verify console.log was called
expect(console.log).toHaveBeenCalledWith(
'Successfully sent message to Discord',
);
});
it('should throw an error if the response is not ok', async () => {
// Mock a failed response
global.fetch.mockResolvedValueOnce({
ok: false,
status: 400,
text: jest.fn().mockResolvedValueOnce('Bad Request'),
});
const webhook = 'https://discord.com/api/webhooks/123/abc';
const message = {content: 'Test message'};
await expect(sendMessageToDiscord(webhook, message)).rejects.toThrow(
'HTTP status code: 400',
);
// Verify console.error was called
expect(console.error).toHaveBeenCalledWith(
'Failed to send message to Discord: 400 Bad Request',
);
});
it('should throw an error if fetch fails', async () => {
// Mock a network error
const networkError = new Error('Network error');
global.fetch.mockRejectedValueOnce(networkError);
const webhook = 'https://discord.com/api/webhooks/123/abc';
const message = {content: 'Test message'};
await expect(sendMessageToDiscord(webhook, message)).rejects.toThrow(
'Network error',
);
});
});
@@ -15,7 +15,6 @@ const {
const mockRun = jest.fn();
const mockSleep = jest.fn();
const mockGetNpmPackageInfo = jest.fn();
const mockVerifyPublishedPackage = jest.fn();
const silence = () => {};
jest.mock('../utils.js', () => ({
@@ -25,10 +24,6 @@ jest.mock('../utils.js', () => ({
getNpmPackageInfo: mockGetNpmPackageInfo,
}));
jest.mock('../verifyPublishedPackage.js', () => ({
verifyPublishedPackage: mockVerifyPublishedPackage,
}));
const getMockGithub = () => ({
rest: {
actions: {
@@ -87,43 +82,77 @@ describe('#verifyPublishedTemplate', () => {
it("waits on npm updating for version and not 'latest'", async () => {
const NOT_LATEST = false;
const version = '0.77.0';
mockGetNpmPackageInfo
// template@<version>
.mockReturnValueOnce(Promise.reject('mock http/404'))
.mockReturnValueOnce(Promise.resolve());
mockSleep.mockReturnValueOnce(Promise.resolve()).mockImplementation(() => {
throw new Error('Should not be called again!');
});
const version = '0.77.0';
await verifyPublishedTemplate(version, NOT_LATEST);
expect(mockVerifyPublishedPackage).toHaveBeenCalledWith(
expect(mockGetNpmPackageInfo).toHaveBeenLastCalledWith(
'@react-native-community/template',
version,
null,
18,
);
});
it('waits on npm updating version and latest tag', async () => {
const IS_LATEST = true;
const version = '0.77.0';
mockGetNpmPackageInfo
// template@latest → unknown tag
.mockReturnValueOnce(Promise.reject('mock http/404'))
// template@latest != version → old tag
.mockReturnValueOnce(Promise.resolve({version: '0.76.5'}))
// template@latest == version → correct tag
.mockReturnValueOnce(Promise.resolve({version}));
mockSleep
.mockReturnValueOnce(Promise.resolve())
.mockReturnValueOnce(Promise.resolve())
.mockImplementation(() => {
throw new Error('Should not be called again!');
});
await verifyPublishedTemplate(version, IS_LATEST);
expect(mockVerifyPublishedPackage).toHaveBeenCalledWith(
expect(mockGetNpmPackageInfo).toHaveBeenCalledWith(
'@react-native-community/template',
version,
'latest',
18,
);
});
describe('retries', () => {
describe('timeouts', () => {
let mockProcess;
beforeEach(() => {
mockProcess = jest.spyOn(process, 'exit').mockImplementation(code => {
throw new Error(`process.exit(${code}) called!`);
});
});
afterEach(() => mockProcess.mockRestore());
it('will timeout if npm does not update package version after a set number of retries', async () => {
const RETRIES = 2;
mockGetNpmPackageInfo.mockReturnValue(Promise.reject('mock http/404'));
mockSleep.mockReturnValue(Promise.resolve());
await expect(() =>
verifyPublishedTemplate('0.77.0', true, RETRIES),
).rejects.toThrowError('process.exit(1) called!');
expect(mockGetNpmPackageInfo).toHaveBeenCalledTimes(RETRIES);
});
await verifyPublishedTemplate('0.77.0', true, RETRIES),
expect(mockVerifyPublishedPackage).toHaveBeenCalledWith(
'@react-native-community/template',
'0.77.0',
'latest',
2,
);
it('will timeout if npm does not update latest tag after a set number of retries', async () => {
const RETRIES = 7;
const IS_LATEST = true;
mockGetNpmPackageInfo.mockReturnValue(
Promise.resolve({version: '0.76.5'}),
);
mockSleep.mockReturnValue(Promise.resolve());
await expect(async () => {
await verifyPublishedTemplate('0.77.0', IS_LATEST, RETRIES);
}).rejects.toThrowError('process.exit(1) called!');
expect(mockGetNpmPackageInfo).toHaveBeenCalledTimes(RETRIES);
});
});
});
@@ -1,87 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/
const {verifyArtifactsAreOnMaven} = require('../verifyArtifactsAreOnMaven');
const mockSleep = jest.fn();
const silence = () => {};
const mockFetch = jest.fn();
const mockExit = jest.fn();
jest.mock('../utils.js', () => ({
log: silence,
sleep: mockSleep,
}));
process.exit = mockExit;
global.fetch = mockFetch;
describe('#verifyArtifactsAreOnMaven', () => {
beforeEach(jest.clearAllMocks);
it('waits for the packages to be published on maven when version has no v', async () => {
mockSleep.mockReturnValueOnce(Promise.resolve()).mockImplementation(() => {
throw new Error('Should not be called again!');
});
mockFetch
.mockReturnValueOnce(Promise.resolve({status: 404}))
.mockReturnValueOnce(Promise.resolve({status: 200}));
const version = '0.78.1';
await verifyArtifactsAreOnMaven(version);
expect(mockSleep).toHaveBeenCalledTimes(1);
expect(mockFetch).toHaveBeenCalledWith(
'https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/0.78.1',
);
});
it('waits for the packages to be published on maven, when version starts with v', async () => {
mockSleep.mockReturnValueOnce(Promise.resolve()).mockImplementation(() => {
throw new Error('Should not be called again!');
});
mockFetch
.mockReturnValueOnce(Promise.resolve({status: 404}))
.mockReturnValueOnce(Promise.resolve({status: 200}));
const version = 'v0.78.1';
await verifyArtifactsAreOnMaven(version);
expect(mockSleep).toHaveBeenCalledTimes(1);
expect(mockFetch).toHaveBeenCalledWith(
'https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/0.78.1',
);
});
it('passes immediately if packages are already on Maven', async () => {
mockFetch.mockReturnValueOnce(Promise.resolve({status: 200}));
const version = '0.78.1';
await verifyArtifactsAreOnMaven(version);
expect(mockSleep).toHaveBeenCalledTimes(0);
expect(mockFetch).toHaveBeenCalledWith(
'https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/0.78.1',
);
});
it('tries 90 times and then exits', async () => {
mockSleep.mockReturnValue(Promise.resolve());
mockFetch.mockReturnValue(Promise.resolve({status: 404}));
const version = '0.78.1';
await verifyArtifactsAreOnMaven(version);
expect(mockSleep).toHaveBeenCalledTimes(90);
expect(mockExit).toHaveBeenCalledWith(1);
expect(mockFetch).toHaveBeenCalledWith(
'https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/0.78.1',
);
});
});
@@ -1,135 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/
const {verifyPublishedPackage} = require('../verifyPublishedPackage');
const mockRun = jest.fn();
const mockSleep = jest.fn();
const mockGetNpmPackageInfo = jest.fn();
const silence = () => {};
const REACT_NATIVE_PACKAGE = 'react-native';
jest.mock('../utils.js', () => ({
log: silence,
run: mockRun,
sleep: mockSleep,
getNpmPackageInfo: mockGetNpmPackageInfo,
}));
describe('#verifyPublishedPackage', () => {
beforeEach(jest.clearAllMocks);
it("waits on npm updating for version and not 'latest'", async () => {
mockGetNpmPackageInfo
// template@<version>
.mockReturnValueOnce(Promise.reject('mock http/404'))
.mockReturnValueOnce(Promise.resolve());
mockSleep.mockReturnValueOnce(Promise.resolve()).mockImplementation(() => {
throw new Error('Should not be called again!');
});
const version = '0.78.0';
await verifyPublishedPackage(REACT_NATIVE_PACKAGE, version, null);
expect(mockGetNpmPackageInfo).toHaveBeenLastCalledWith(
REACT_NATIVE_PACKAGE,
version,
);
});
it('waits on npm updating version and latest tag', async () => {
const version = '0.78.0';
mockGetNpmPackageInfo
// template@latest → unknown tag
.mockReturnValueOnce(Promise.reject('mock http/404'))
// template@latest != version → old tag
.mockReturnValueOnce(Promise.resolve({version: '0.76.5'}))
// template@latest == version → correct tag
.mockReturnValueOnce(Promise.resolve({version}));
mockSleep
.mockReturnValueOnce(Promise.resolve())
.mockReturnValueOnce(Promise.resolve())
.mockImplementation(() => {
throw new Error('Should not be called again!');
});
await verifyPublishedPackage(REACT_NATIVE_PACKAGE, version, 'latest');
expect(mockGetNpmPackageInfo).toHaveBeenCalledWith(
REACT_NATIVE_PACKAGE,
'latest',
);
});
it('waits on npm updating version and next tag', async () => {
const version = '0.78.0-rc.0';
mockGetNpmPackageInfo
// template@latest → unknown tag
.mockReturnValueOnce(Promise.reject('mock http/404'))
// template@latest != version → old tag
.mockReturnValueOnce(Promise.resolve({version: '0.76.5'}))
// template@latest == version → correct tag
.mockReturnValueOnce(Promise.resolve({version}));
mockSleep
.mockReturnValueOnce(Promise.resolve())
.mockReturnValueOnce(Promise.resolve())
.mockImplementation(() => {
throw new Error('Should not be called again!');
});
await verifyPublishedPackage(REACT_NATIVE_PACKAGE, version, 'next');
expect(mockGetNpmPackageInfo).toHaveBeenCalledWith(
REACT_NATIVE_PACKAGE,
'next',
);
});
describe('timeouts', () => {
let mockProcess;
beforeEach(() => {
mockProcess = jest.spyOn(process, 'exit').mockImplementation(code => {
throw new Error(`process.exit(${code}) called!`);
});
});
afterEach(() => mockProcess.mockRestore());
it('will timeout if npm does not update package version after a set number of retries', async () => {
const RETRIES = 2;
mockGetNpmPackageInfo.mockReturnValue(Promise.reject('mock http/404'));
mockSleep.mockReturnValue(Promise.resolve());
await expect(() =>
verifyPublishedPackage(
REACT_NATIVE_PACKAGE,
'0.77.0',
'latest',
RETRIES,
),
).rejects.toThrowError('process.exit(1) called!');
expect(mockGetNpmPackageInfo).toHaveBeenCalledTimes(RETRIES);
});
it('will timeout if npm does not update latest tag after a set number of retries', async () => {
const RETRIES = 7;
const IS_LATEST = true;
mockGetNpmPackageInfo.mockReturnValue(
Promise.resolve({version: '0.76.5'}),
);
mockSleep.mockReturnValue(Promise.resolve());
await expect(async () => {
await verifyPublishedPackage(
REACT_NATIVE_PACKAGE,
'0.77.0',
'latest',
RETRIES,
);
}).rejects.toThrowError('process.exit(1) called!');
expect(mockGetNpmPackageInfo).toHaveBeenCalledTimes(RETRIES);
});
});
});
@@ -1,109 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/
const {verifyReleaseOnNpm} = require('../verifyReleaseOnNpm');
const mockVerifyPublishedPackage = jest.fn();
const silence = () => {};
jest.mock('../utils.js', () => ({
log: silence,
sleep: silence,
}));
jest.mock('../verifyPublishedPackage.js', () => ({
verifyPublishedPackage: mockVerifyPublishedPackage,
}));
describe('#verifyReleaseOnNPM', () => {
beforeEach(jest.clearAllMocks);
it("waits on npm updating for version and not 'latest'", async () => {
const NOT_LATEST = false;
const version = '0.78.0';
await verifyReleaseOnNpm(version, NOT_LATEST);
expect(mockVerifyPublishedPackage).toHaveBeenLastCalledWith(
'react-native',
version,
null,
18,
);
});
it('waits on npm updating version and latest tag', async () => {
const IS_LATEST = true;
const version = '0.78.0';
await verifyReleaseOnNpm(version, IS_LATEST);
expect(mockVerifyPublishedPackage).toHaveBeenCalledWith(
'react-native',
version,
'latest',
18,
);
});
it('waits on npm updating version, not latest and next tag', async () => {
const IS_LATEST = false;
const version = '0.78.0-rc.0';
await verifyReleaseOnNpm(version, IS_LATEST);
expect(mockVerifyPublishedPackage).toHaveBeenCalledWith(
'react-native',
version,
'next',
18,
);
});
it('waits on npm updating version, latest and next tag', async () => {
const IS_LATEST = true;
const version = '0.78.0-rc.0';
await verifyReleaseOnNpm(version, IS_LATEST);
expect(mockVerifyPublishedPackage).toHaveBeenCalledWith(
'react-native',
version,
'next',
18,
);
});
describe('timeouts', () => {
it('will timeout if npm does not update package version after a set number of retries', async () => {
const RETRIES = 2;
await verifyReleaseOnNpm('0.77.0', true, RETRIES),
expect(mockVerifyPublishedPackage).toHaveBeenCalledWith(
'react-native',
'0.77.0',
'latest',
2,
);
});
it('will timeout if npm does not update latest tag after a set number of retries', async () => {
const RETRIES = 7;
const IS_LATEST = true;
await verifyReleaseOnNpm('0.77.0', IS_LATEST, RETRIES);
expect(mockVerifyPublishedPackage).toHaveBeenCalledWith(
'react-native',
'0.77.0',
'latest',
7,
);
});
});
});
+7 -1
View File
@@ -95,6 +95,12 @@ module.exports = async (github, context, labelWithContext) => {
);
await requestAuthorFeedback();
return;
case 'Newer Patch Available':
await addComment(
`> [!TIP]\n` +
`> **Newer version available**: You are on a supported minor version, but it looks like there's a newer patch available - ${labelWithContext.newestPatch}. Please [upgrade](https://reactnative.dev/docs/upgrading) to the highest patch for your minor or latest and verify if the issue persists (alternatively, create a new project and repro the issue in it). If it does not repro, please let us know so we can close out this issue. This helps us ensure we are looking at issues that still exist in the most recent releases.`,
);
return;
case 'Needs: Version Info':
await addComment(
`> [!WARNING]\n` +
@@ -105,7 +111,7 @@ module.exports = async (github, context, labelWithContext) => {
case 'Needs: Repro':
await addComment(
`> [!WARNING]\n` +
`> **Missing reproducer**: We could not detect a reproducible example in your issue report. Reproducers are **mandatory** and we can accept only one of those as a valid reproducer: <br/><ul><li>For majority of bugs: send us a Pull Request with the [RNTesterPlayground.js](https://github.com/facebook/react-native/blob/main/packages/rn-tester/js/examples/Playground/RNTesterPlayground.js) edited to reproduce your bug.</li><li>If your bug is UI related: a [Snack](https://snack.expo.dev)</li><li> If your bug is build/upgrade related: a project using our [Reproducer Template](https://github.com/react-native-community/reproducer-react-native/generate)</li></ul><br/>You can read more about about it on our website: [How to report a bug](https://reactnative.dev/contributing/how-to-report-a-bug).`,
`> **Missing reproducer**: We could not detect a reproducible example in your issue report. Please provide either: <br/><ul><li>If your bug is UI related: a [Snack](https://snack.expo.dev)</li><li> If your bug is build/upgrade related: a project using our [Reproducer Template](https://github.com/react-native-community/reproducer-react-native/generate)</li><li>Otherwise send us a Pull Request with the [RNTesterPlayground.js](https://github.com/facebook/react-native/blob/main/packages/rn-tester/js/examples/Playground/RNTesterPlayground.js) edited to reproduce your bug.</li></ul>`,
);
await requestAuthorFeedback();
return;
-27
View File
@@ -1,27 +0,0 @@
#!/bin/bash
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
export GITHUB_OWNER=-facebook
export GITHUB_REPO=-react-native
{
echo eslint
npm run lint --silent -- --format=json
echo flow
npm run flow-check --silent --json
echo google-java-format
node scripts/lint-java.js --diff
} | node private/react-native-bots/code-analysis-bot.js
STATUS=$?
if [ $STATUS == 0 ]; then
echo "Code analyzed successfully."
else
echo "Code analysis failed, error status $STATUS."
fi
exit $STATUS
@@ -1,25 +0,0 @@
#!/bin/bash
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
GITHUB_OWNER=-facebook
GITHUB_REPO=-react-native
export GITHUB_OWNER
export GITHUB_REPO
if [ -x "$(command -v shellcheck)" ]; then
IFS=$'\n'
find . \
-type f \
-not -path "*node_modules*" \
-not -path "*third-party*" \
-name '*.sh' \
-exec sh -c 'shellcheck "$1"' -- {} \;
else
echo 'shellcheck is not installed. See https://github.com/facebook/react-native/wiki/Development-Dependencies#shellcheck for instructions.'
exit 1
fi
@@ -41,11 +41,6 @@ module.exports = async (github, context) => {
// Look for Snack or a GH repo associated with the user that added an issue or comment
const hasValidReproducer = entities.some(entity => {
const hasPullRequestRepoLink = containsPattern(
entity.body,
`https?:\/\/github\.com\/facebook\/react-native\/pull\/\d+\/?`,
);
const hasExpoSnackLink = containsPattern(
entity.body,
`https?:\\/\\/snack\\.expo\\.dev\\/[^\\s)\\]]+`,
@@ -55,7 +50,7 @@ module.exports = async (github, context) => {
entity.body,
`https?:\\/\\/github\\.com\\/(${entity.user.login})\\/[^/]+\\/?\\s?`,
);
return hasPullRequestRepoLink || hasExpoSnackLink || hasGithubRepoLink;
return hasExpoSnackLink || hasGithubRepoLink;
});
if (hasValidReproducer) {
@@ -1,121 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/
const fs = require('fs');
const path = require('path');
const {
prepareFailurePayload,
sendMessageToDiscord,
} = require('./notifyDiscord');
function readOutcomes() {
const baseDir = '/tmp';
let outcomes = [];
fs.readdirSync(baseDir).forEach(file => {
const fullPath = path.join(baseDir, file);
if (fullPath.endsWith('outcome') && fs.statSync(fullPath).isDirectory) {
fs.readdirSync(fullPath).forEach(subFile => {
const subFullPath = path.join(fullPath, subFile);
if (subFullPath.endsWith('outcome')) {
const [library, status] = String(fs.readFileSync(subFullPath, 'utf8'))
.trim()
.split(':');
const platform = subFile.includes('android') ? 'Android' : 'iOS';
console.log(
`[${platform}] ${library} completed with status ${status}`,
);
outcomes.push({
library: library.trim(),
platform,
status: status.trim(),
});
}
});
} else if (fullPath.endsWith('outcome')) {
const [library, status] = String(fs.readFileSync(fullPath, 'utf8'))
.trim()
.split(':');
const platform = file.includes('android') ? 'Android' : 'iOS';
console.log(`[${platform}] ${library} completed with status ${status}`);
outcomes.push({
library: library.trim(),
platform,
status: status.trim(),
});
}
});
return outcomes;
}
function printFailures(outcomes) {
console.log('Printing failures...');
let failedLibraries = [];
outcomes.forEach(entry => {
if (entry.status !== 'success') {
console.log(
`❌ [${entry.platform}] ${entry.library} failed with status ${entry.status}`,
);
failedLibraries.push({
library: entry.library,
platform: entry.platform,
});
}
});
return failedLibraries;
}
/**
* Sends a message to Discord with the list of failures.
* @param {string} webHook - The Discord webhook URL
* @param {Array<Object>} failures - List of failures to report
* @returns {Promise<void>} - A promise that resolves when the message is sent
*/
async function notifyDiscord(webHook, failures) {
if (!webHook) {
console.error('Discord webhook URL is missing');
return;
}
if (!failures || failures.length === 0) {
console.log('No failures to report to Discord');
return;
}
try {
// Use the prepareFailurePayload function to format the message
const message = prepareFailurePayload(failures);
// Use the sendMessageToDiscord function to send the message
await sendMessageToDiscord(webHook, message);
} catch (error) {
console.error('Error in notifyDiscord function:', error);
throw error;
}
}
async function collectResults(discordWebHook) {
const outcomes = readOutcomes();
const failures = printFailures(outcomes);
if (failures.length > 0) {
if (discordWebHook) {
console.log('Sending to discord');
await notifyDiscord(discordWebHook, failures);
} else {
console.log('Web hook not set');
}
process.exit(1);
}
console.log('✅ All tests passed!');
}
module.exports = {
collectResults,
notifyDiscord,
};
@@ -1,137 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/
const {log, run} = require('./utils');
const fs = require('fs');
function _headers(token) {
return {
Accept: 'Accept: application/vnd.github+json',
'X-GitHub-Api-Version': '2022-11-28',
Authorization: `Bearer ${token}`,
};
}
function _extractChangelog(version) {
if (version.endsWith('.0')) {
// for RC.0 and for the release of a new stable minor, the changelog is too long
// to be added in a release. The release body is usually something shorter.
// See for example the release for 0.76.0 or 0.77.0:
// 0.76: https://github.com/facebook/react-native/releases/tag/v0.76.0
// 0.77: https://github.com/facebook/react-native/releases/tag/v0.77.0
return '';
}
const changelog = String(fs.readFileSync('CHANGELOG.md', 'utf8')).split('\n');
const changelogStarts = changelog.indexOf(`## v${version}`);
let changelogEnds = changelogStarts;
// Scan the changelog to find the next version
for (var line = changelogStarts + 1; line < changelog.length; line++) {
if (changelog[line].startsWith('## ')) {
changelogEnds = line;
break;
}
}
return changelog.slice(changelogStarts, changelogEnds).join('\n').trim();
}
function _computeBody(version, changelog) {
return `${changelog}
---
Hermes dSYMS:
- [Debug](https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/${version}/react-native-artifacts-${version}-hermes-framework-dSYM-debug.tar.gz)
- [Release](https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/${version}/react-native-artifacts-${version}-hermes-framework-dSYM-release.tar.gz)
ReactNativeDependencies dSYMs:
- [Debug](https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/${version}/react-native-artifacts-${version}-reactnative-dependencies-dSYM-debug.tar.gz)
- [Release](https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/${version}/react-native-artifacts-${version}-reactnative-dependencies-dSYM-release.tar.gz)
---
You can file issues or pick requests against this release [here](https://github.com/reactwg/react-native-releases/issues/new/choose).
---
To help you upgrade to this version, you can use the [Upgrade Helper](https://react-native-community.github.io/upgrade-helper/) ⚛️.
---
View the whole changelog in the [CHANGELOG.md file](https://github.com/facebook/react-native/blob/main/CHANGELOG.md).`;
}
async function _verifyTagExists(version) {
const url = `https://github.com/facebook/react-native/releases/tag/v${version}`;
const response = await fetch(url);
if (response.status === 404) {
throw new Error(`Tag v${version} does not exist`);
}
}
async function _createDraftReleaseOnGitHub(version, body, latest, token) {
const url = 'https://api.github.com/repos/facebook/react-native/releases';
const method = 'POST';
const headers = _headers(token);
const fetchBody = JSON.stringify({
tag_name: `v${version}`,
name: `${version}`,
body: body,
draft: true, // NEVER CHANGE this value to false. If false, it will publish the release, and send a GH notification to all the subscribers.
prerelease: version.includes('-rc.') ? true : false,
make_latest: `${latest}`,
});
const response = await fetch(url, {
method,
headers,
body: fetchBody,
});
if (response.status !== 201) {
throw new Error(
`Failed to create the release: ${response.status} ${response.statusText}`,
);
}
const data = await response.json();
return data.html_url;
}
function moveToChangelogBranch(version) {
log(`Moving to changelog branch: changelog/v${version}`);
run(`git checkout -b changelog/v${version}`);
}
async function createDraftRelease(version, latest, token) {
if (version.startsWith('v')) {
version = version.substring(1);
}
_verifyTagExists(version);
moveToChangelogBranch(version);
const changelog = _extractChangelog(version);
const body = _computeBody(version, changelog);
const release = await _createDraftReleaseOnGitHub(
version,
body,
latest,
token,
);
log(`Created draft release: ${release}`);
}
module.exports = {
createDraftRelease,
// Exported for testing purposes
_verifyTagExists,
_extractChangelog,
_computeBody,
_createDraftReleaseOnGitHub,
};
@@ -1,61 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/
const fs = require('fs');
const MSEC_IN_DAY = 1000 * 60 * 60 * 24;
function formatUsers(users) {
console.log(`${users[0]} ${users[1]}`.trim());
}
function extractUsersFromScheduleAndDate(schedule, userMap, date) {
const year = date.getFullYear();
const month = date.getMonth() + 1; // 0 is January, 1 is February
const day = date.getDate();
const dateStr = `${year}-${month < 10 ? `0${month}` : month}-${day < 10 ? `0${day}` : day}`;
const user1 = userMap[schedule[dateStr][0]];
const user2 = userMap[schedule[dateStr][1]];
return [user1, user2];
}
function main() {
const configuration = process.argv[2];
const {userMap, schedule} = JSON.parse(configuration);
extractIssueOncalls(schedule, userMap);
}
function extractIssueOncalls(schedule, userMap) {
const now = new Date();
const dayOfTheWeek = now.getDay(); // 0 is Sunday, 1 is Monday, etc.
let users;
if (dayOfTheWeek === 2) {
// exact match in the schedule
users = extractUsersFromScheduleAndDate(schedule, userMap, now);
} else if (dayOfTheWeek < 2) {
// sunday
// go to the tuesday of the last week
const lastWeekTuesday = new Date(now - (5 + dayOfTheWeek) * MSEC_IN_DAY);
users = extractUsersFromScheduleAndDate(schedule, userMap, lastWeekTuesday);
} else if (dayOfTheWeek > 1) {
// go to the previous tuesday
const thisWeekTuesday = new Date(now - (dayOfTheWeek - 2) * MSEC_IN_DAY);
users = extractUsersFromScheduleAndDate(schedule, userMap, thisWeekTuesday);
}
formatUsers(users);
return users;
}
if (require.main === module) {
void main();
}
module.exports = {
extractIssueOncalls,
};
@@ -1,120 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/
const {log, getNpmPackageInfo, run} = require('./utils');
async function _computePreviousVersionFrom(version) {
log(`Computing previous version from: ${version}`);
const regex = /^0\.(\d+)\.(\d+)(-rc\.(\d+))?$/;
const match = version.match(regex);
if (!match) {
throw new Error(`Invalid version format: ${version}`);
}
const minor = match[1];
const patch = match[2];
const rc = match[4];
if (rc) {
if (Number(rc) > 0) {
return `0.${minor}.${patch}-rc.${Number(rc) - 1}`;
}
//fetch latest version on NPM
const latestPkg = await getNpmPackageInfo('react-native', 'latest');
return latestPkg.version;
} else {
if (Number(patch) === 0) {
// No need to generate the changelog for 0.X.0 as we already generated it from RCs
log(
`Skipping changelog generation for ${version} as we already have it from the RCs`,
);
return null;
}
return `0.${minor}.${Number(patch) - 1}`;
}
}
function _generateChangelog(previousVersion, version, token) {
log(`Generating changelog for ${version} from ${previousVersion}`);
run('git checkout main');
run('git fetch');
run('git pull origin main');
const generateChangelogComand = `npx @rnx-kit/rn-changelog-generator --base v${previousVersion} --compare v${version} --repo . --changelog ./CHANGELOG.md --token ${token}`;
run(generateChangelogComand);
}
function _pushCommit(version) {
log(`Pushing commit to changelog/v${version}`);
run(`git checkout -b changelog/v${version}`);
run('git add CHANGELOG.md');
run(`git commit -m "[RN][Changelog] Add changelog for v${version}"`);
run(`git push origin changelog/v${version}`);
}
async function _createPR(version, token) {
log('Creating changelog pr');
const url = 'https://api.github.com/repos/facebook/react-native/pulls';
const body = `
## Summary
Add Changelog for ${version}
## Changelog:
[Internal] - Add Changelog for ${version}
## Test Plan:
N/A`;
const response = await fetch(url, {
method: 'POST',
headers: {
Accept: 'Accept: application/vnd.github+json',
'X-GitHub-Api-Version': '2022-11-28',
Authorization: `Bearer ${token}`,
},
body: JSON.stringify({
title: `[RN][Changelog] Add changelog for v${version}`,
head: `changelog/v${version}`,
base: 'main',
body: body,
}),
});
if (response.status !== 201) {
throw new Error(
`Failed to create PR: ${response.status} ${response.statusText}`,
);
}
const data = await response.json();
return data.html_url;
}
async function generateChangelog(version, token) {
if (version.startsWith('v')) {
version = version.substring(1);
}
const previousVersion = await _computePreviousVersionFrom(version);
if (previousVersion) {
log(`Previous version is ${previousVersion}`);
_generateChangelog(previousVersion, version, token);
_pushCommit(version);
const prURL = await _createPR(version, token);
log(`Created PR: ${prURL}`);
}
}
module.exports = {
generateChangelog,
// Exported only for testing purposes:
_computePreviousVersionFrom,
_generateChangelog,
_pushCommit,
_createPR,
};
-12
View File
@@ -1,12 +0,0 @@
#!/bin/bash
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
set -e
if git ls-files | grep -E '\.npmignore$'; then
echo "Error: Found unexpected .npmignore file(s). Please use package.json#files instead."
exit 1
fi
+7 -50
View File
@@ -8,7 +8,6 @@
*/
const childProcess = require('child_process');
const fs = require('fs');
const usage = `
=== Usage ===
@@ -34,38 +33,6 @@ const MAESTRO_FLOW = args[2];
const IS_DEBUG = args[3] === 'debug';
const WORKING_DIRECTORY = args[4];
const MAX_ATTEMPTS = 3;
async function executeFlowWithRetries(flow, currentAttempt) {
try {
console.info(`Executing flow: ${flow}`);
const timeout = 1000 * 60 * 10; // 10 minutes
childProcess.execSync(
`MAESTRO_DRIVER_STARTUP_TIMEOUT=120000 $HOME/.maestro/bin/maestro test ${flow} --format junit -e APP_ID=${APP_ID} --debug-output /tmp/MaestroLogs`,
{stdio: 'inherit', timeout},
);
} catch (err) {
if (currentAttempt < MAX_ATTEMPTS) {
console.info(`Retrying...`);
await executeFlowWithRetries(flow, currentAttempt + 1);
} else {
throw err;
}
}
}
async function executeFlowInFolder(flowFolder) {
const files = fs.readdirSync(flowFolder);
for (const file of files) {
const filePath = `${flowFolder}/${file}`;
if (fs.lstatSync(filePath).isDirectory()) {
await executeFlowInFolder(filePath);
} else {
await executeFlowWithRetries(filePath, 0);
}
}
}
async function main() {
console.info('\n==============================');
console.info('Running tests for Android with the following parameters:');
@@ -90,19 +57,14 @@ async function main() {
});
metroProcess.unref();
console.info(`- Metro PID: ${metroProcess.pid}`);
console.info('Wait For Metro to Start');
await sleep(5000);
}
console.info('Wait For Metro to Start');
await sleep(5000);
console.info('Start the app');
childProcess.execSync(`adb shell monkey -p ${APP_ID} 1`, {stdio: 'ignore'});
if (IS_DEBUG) {
console.info('Wait For App to warm from Metro');
await sleep(10000);
}
console.info('Start recording to /sdcard/screen.mp4');
childProcess
.exec('adb shell screenrecord /sdcard/screen.mp4', {
@@ -114,15 +76,10 @@ async function main() {
console.info(`Start testing ${MAESTRO_FLOW}`);
let error = null;
try {
//check if MAESTRO_FLOW is a folder
if (
fs.existsSync(MAESTRO_FLOW) &&
fs.lstatSync(MAESTRO_FLOW).isDirectory()
) {
await executeFlowInFolder(MAESTRO_FLOW);
} else {
await executeFlowWithRetries(MAESTRO_FLOW, 0);
}
childProcess.execSync(
`MAESTRO_DRIVER_STARTUP_TIMEOUT=120000 $HOME/.maestro/bin/maestro test ${MAESTRO_FLOW} --format junit -e APP_ID=${APP_ID} --debug-output /tmp/MaestroLogs`,
{stdio: 'inherit'},
);
} catch (err) {
error = err;
} finally {
-176
View File
@@ -1,176 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/
const childProcess = require('child_process');
const fs = require('fs');
const usage = `
=== Usage ===
node maestro-android.js <path to app> <app_id> <maestro_flow> <flavor> <working_directory>
@param {string} appPath - Path to the app APK
@param {string} appId - App ID that needs to be launched
@param {string} maestroFlow - Path to the maestro flow to be executed
@param {string} jsengine - The JSEngine to use for the test
@param {string} flavor - Flavor of the app to be launched. Can be 'Release' or 'Debug'
@param {string} workingDirectory - Working directory from where to run Metro
==============
`;
const args = process.argv.slice(2);
if (args.length !== 6) {
throw new Error(`Invalid number of arguments.\n${usage}`);
}
const APP_PATH = args[0];
const APP_ID = args[1];
const MAESTRO_FLOW = args[2];
const JS_ENGINE = args[3];
const IS_DEBUG = args[4] === 'Debug';
const WORKING_DIRECTORY = args[5];
const MAX_ATTEMPTS = 5;
function launchSimulator(simulatorName) {
console.log(`Launching simulator ${simulatorName}`);
try {
childProcess.execSync(`xcrun simctl boot "${simulatorName}"`);
} catch (error) {
if (
!error.message.includes('Unable to boot device in current state: Booted')
) {
throw error;
}
}
}
function installAppOnSimulator(appPath) {
console.log(`Installing app at path ${appPath}`);
childProcess.execSync(`xcrun simctl install booted "${appPath}"`);
}
function extractSimulatorUDID() {
console.log('Retrieving device UDID');
const command = `xcrun simctl list devices booted -j | jq -r '[.devices[]] | add | first | .udid'`;
const udid = String(childProcess.execSync(command)).trim();
console.log(`UDID is ${udid}`);
return udid;
}
function bringSimulatorInForeground() {
console.log('Bringing simulator in foreground');
childProcess.execSync('open -a simulator');
}
function sleep(ms) {
return new Promise(resolve => {
setTimeout(resolve, ms);
});
}
async function launchAppOnSimulator(appId, udid, isDebug) {
console.log('Launch the app');
childProcess.execSync(`xcrun simctl launch "${udid}" "${appId}"`);
if (isDebug) {
console.log('Wait for metro to warm');
await sleep(20 * 1000);
}
}
function startVideoRecording(jsengine, currentAttempt) {
console.log(
`Start video record using pid: video_record_${jsengine}_${currentAttempt}.pid`,
);
const recordingArgs =
`simctl io booted recordVideo video_record_${jsengine}_${currentAttempt}.mov`.split(
' ',
);
const recordingProcess = childProcess.spawn('xcrun', recordingArgs, {
detached: true,
stdio: 'ignore',
});
return recordingProcess;
}
function stopVideoRecording(recordingProcess) {
if (!recordingProcess) {
console.log("Passed a null recording process. Can't kill it");
return;
}
console.log(`Stop video record using pid: ${recordingProcess.pid}`);
recordingProcess.kill('SIGINT');
}
function executeTestsWithRetries(
appId,
udid,
maestroFlow,
jsengine,
currentAttempt,
) {
const recProcess = startVideoRecording(jsengine, currentAttempt);
try {
const timeout = 1000 * 60 * 10; // 10 minutes
const command = `$HOME/.maestro/bin/maestro --udid="${udid}" test "${maestroFlow}" --format junit -e APP_ID="${appId}"`;
console.log(command);
childProcess.execSync(`MAESTRO_DRIVER_STARTUP_TIMEOUT=1500000 ${command}`, {
stdio: 'inherit',
timeout,
});
stopVideoRecording(recProcess);
} catch (error) {
// Can't put this in the finally block because it will be executed after the
// recursive call of executeTestsWithRetries
stopVideoRecording(recProcess);
if (currentAttempt < MAX_ATTEMPTS) {
executeTestsWithRetries(
appId,
udid,
maestroFlow,
jsengine,
currentAttempt + 1,
);
} else {
console.error(`Failed to execute flow after ${MAX_ATTEMPTS} attempts.`);
throw error;
}
}
}
async function main() {
console.info('\n==============================');
console.info('Running tests for iOS with the following parameters:');
console.info(`APP_PATH: ${APP_PATH}`);
console.info(`APP_ID: ${APP_ID}`);
console.info(`MAESTRO_FLOW: ${MAESTRO_FLOW}`);
console.info(`JS_ENGINE: ${JS_ENGINE}`);
console.info(`IS_DEBUG: ${IS_DEBUG}`);
console.info(`WORKING_DIRECTORY: ${WORKING_DIRECTORY}`);
console.info('==============================\n');
const simulatorName = 'iPhone 15 Pro';
launchSimulator(simulatorName);
installAppOnSimulator(APP_PATH);
const udid = extractSimulatorUDID();
bringSimulatorInForeground();
await launchAppOnSimulator(APP_ID, udid, IS_DEBUG);
executeTestsWithRetries(APP_ID, udid, MAESTRO_FLOW, JS_ENGINE, 1);
console.log('Test finished');
process.exit(0);
}
main();
-90
View File
@@ -1,90 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/
/**
* Sends a message to Discord using the webhook URL.
* @param {string} webHook - The Discord webhook URL
* @param {Object} message - The message to send
* @returns {Promise<void>} - A promise that resolves when the message is sent
*/
async function sendMessageToDiscord(webHook, message) {
if (!webHook) {
throw new Error('Discord webhook URL is missing');
}
// Send the request using fetch
const response = await fetch(webHook, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(message),
});
// Handle the response
if (response.ok) {
console.log('Successfully sent message to Discord');
return;
} else {
const errorText = await response.text();
console.error(
`Failed to send message to Discord: ${response.status} ${errorText}`,
);
throw new Error(`HTTP status code: ${response.status}`);
}
}
/**
* Prepares a formatted Discord message payload from a list of failures.
* @param {Array<Object>} failures - List of failures to format
* @returns {Object} - The formatted Discord message payload
*/
function prepareFailurePayload(failures) {
if (!failures || failures.length === 0) {
return {
content:
'⚠️ **React Native Nightly Integration Failures** ⚠️\n\nNo failures to report.',
};
}
// Sort failures by platform and then by library name
const sortedFailures = [...failures].sort((a, b) => {
// First sort by platform
const platformA = a.platform || 'Unknown';
const platformB = b.platform || 'Unknown';
if (platformA !== platformB) {
return platformA.localeCompare(platformB);
}
// Then sort by library name
const libraryA = a.library || 'Unknown';
const libraryB = b.library || 'Unknown';
return libraryA.localeCompare(libraryB);
});
// Format the failures into a message
const formattedFailures = sortedFailures
.map(failure => {
const library = failure.library || 'Unknown';
const platform = failure.platform || 'Unknown';
return `❌ [${platform}] ${library}`;
})
.join('\n');
return {
content: `⚠️ **React Native Nightly Integration Failures** ⚠️\n\nThe integration of libraries with React Native nightly failed for the following libraries:\n\n${formattedFailures}`,
};
}
// Export the functions using CommonJS syntax
module.exports = {
prepareFailurePayload,
sendMessageToDiscord,
};
+33 -15
View File
@@ -7,8 +7,7 @@
* @format
*/
const {run, sleep, log} = require('./utils.js');
const {verifyPublishedPackage} = require('./verifyPublishedPackage.js');
const {run, sleep, getNpmPackageInfo, log} = require('./utils.js');
const TAG_AS_LATEST_REGEX = /#publish-packages-to-npm&latest/;
@@ -54,7 +53,8 @@ module.exports.publishTemplate = async (github, version, dryRun = true) => {
});
};
const MAX_RETRIES = 3 * 6; // 18 attempts. Waiting between attempt: 10 s. Total time: 3 mins.
const SLEEP_S = 10;
const MAX_RETRIES = 3 * 6; // 3 minutes
const TEMPLATE_NPM_PKG = '@react-native-community/template';
/**
@@ -68,18 +68,36 @@ module.exports.verifyPublishedTemplate = async (
latest = false,
retries = MAX_RETRIES,
) => {
try {
if (version.startsWith('v')) {
version = version.slice(1);
log(`🔍 Is ${TEMPLATE_NPM_PKG}@${version} on npm?`);
let count = retries;
while (count-- > 0) {
try {
const json = await getNpmPackageInfo(
TEMPLATE_NPM_PKG,
latest ? 'latest' : version,
);
log(`🎉 Found ${TEMPLATE_NPM_PKG}@${version} on npm`);
if (!latest) {
return;
}
if (json.version === version) {
log(`🎉 ${TEMPLATE_NPM_PKG}@latest → ${version} on npm`);
return;
}
log(
`🐌 ${TEMPLATE_NPM_PKG}@latest → ${pkg.version} on npm and not ${version} as expected, retrying...`,
);
} catch (e) {
log(`Nope, fetch failed: ${e.message}`);
}
await verifyPublishedPackage(
TEMPLATE_NPM_PKG,
version,
latest ? 'latest' : null,
retries,
);
} catch (e) {
console.error(e.message);
process.exit(1);
await sleep(SLEEP_S);
}
let msg = `🚨 Timed out when trying to verify ${TEMPLATE_NPM_PKG}@${version} on npm`;
if (latest) {
msg += ' and latest tag points to this version.';
}
log(msg);
process.exit(1);
};
+11 -15
View File
@@ -9,25 +9,21 @@
const {execSync} = require('child_process');
const log = (...args) => console.log(...args);
function run(cmd) {
return execSync(cmd, 'utf8').toString().trim();
}
module.exports.run = run;
async function sleep(seconds) {
return new Promise(resolve => setTimeout(resolve, seconds * 1000));
}
module.exports.sleep = sleep;
async function getNpmPackageInfo(pkg, versionOrTag) {
return fetch(`https://registry.npmjs.org/${pkg}/${versionOrTag}`).then(resp =>
resp.json(),
);
}
module.exports.getNpmPackageInfo = getNpmPackageInfo;
async function sleep(seconds) {
return new Promise(resolve => setTimeout(resolve, seconds * 1000));
}
function run(cmd) {
return execSync(cmd, 'utf8').toString().trim();
}
module.exports = {
log,
getNpmPackageInfo,
sleep,
run,
};
module.exports.log = (...args) => console.log(...args);
@@ -1,42 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/
const {log, sleep} = require('./utils');
const SLEEP_S = 60; // 1 minute
const MAX_RETRIES = 90; // 90 attempts. Waiting between attempt: 1 min. Total time: 90 min.
const ARTIFACT_URL =
'https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/';
async function verifyArtifactsAreOnMaven(version, retries = MAX_RETRIES) {
if (version.startsWith('v')) {
version = version.substring(1);
}
const artifactUrl = `${ARTIFACT_URL}${version}`;
for (let currentAttempt = 1; currentAttempt <= retries; currentAttempt++) {
const response = await fetch(artifactUrl);
if (response.status !== 200) {
log(
`${currentAttempt}) Artifact's for version ${version} are not on maven yet.\nURL: ${artifactUrl}\nLet's wait a minute and try again.\n`,
);
await sleep(SLEEP_S);
} else {
return;
}
}
log(
`We waited 90 minutes for the artifacts to be on Maven. Check https://status.maven.org/ if there are issues wth the service.`,
);
process.exit(1);
}
module.exports = {verifyArtifactsAreOnMaven};
@@ -1,63 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/
const {log, getNpmPackageInfo, sleep} = require('./utils');
const SLEEP_S = 10;
const MAX_RETRIES = 3 * 6; // 18 attempts. Waiting between attempt: 10 s. Total time: 3 mins.
async function verifyPublishedPackage(
packageName,
version,
tag = null,
retries = MAX_RETRIES,
) {
log(`🔍 Is ${packageName}@${version} on npm?`);
let count = retries;
while (count-- > 0) {
try {
const json = await getNpmPackageInfo(packageName, tag ? tag : version);
log(`🎉 Found ${packageName}@${version} on npm`);
if (!tag) {
return;
}
// check for next tag
if (tag === 'next' && json.version === version) {
log(`🎉 ${packageName}@next → ${version} on npm`);
return;
}
// Check for latest tag
if (tag === 'latest' && json.version === version) {
log(`🎉 ${packageName}@latest → ${version} on npm`);
return;
}
log(
`🐌 ${packageName}@${tag}${json.version} on npm and not ${version} as expected, retrying...`,
);
} catch (e) {
log(`Nope, fetch failed: ${e.message}`);
}
await sleep(SLEEP_S);
}
let msg = `🚨 Timed out when trying to verify ${packageName}@${version} on npm`;
if (tag) {
msg += ` and ${tag} tag points to this version.`;
}
log(msg);
process.exit(1);
}
module.exports = {
verifyPublishedPackage,
};
@@ -1,30 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/
const {run, sleep, log} = require('./utils.js');
const {verifyPublishedPackage} = require('./verifyPublishedPackage.js');
const REACT_NATIVE_NPM_PKG = 'react-native';
const MAX_RETRIES = 3 * 6; // 18 attempts. Waiting between attempt: 10 s. Total time: 3 mins.
/**
* Will verify that @latest, @next and the @<version> have been published.
*
* NOTE: This will infinitely query each step until successful, make sure the
* calling job has a timeout.
*/
module.exports.verifyReleaseOnNpm = async (
version,
latest = false,
retries = MAX_RETRIES,
) => {
const tag = version.includes('-rc.') ? 'next' : latest ? 'latest' : null;
if (version.startsWith('v')) {
version = version.slice(1);
}
await verifyPublishedPackage(REACT_NATIVE_NPM_PKG, version, tag, retries);
};
+28
View File
@@ -45,6 +45,18 @@ module.exports = async (github, context) => {
if (!isVersionSupported(issueVersion, latestVersion)) {
return {label: 'Type: Unsupported Version'};
}
// We want to encourage users to repro the issue on the highest available patch for the given minor.
const latestPatchForVersion = getLatestPatchForVersion(
issueVersion,
recentReleases,
);
if (latestPatchForVersion > issueVersion.patch) {
return {
label: 'Newer Patch Available',
newestPatch: `${issueVersion.major}.${issueVersion.minor}.${latestPatchForVersion}`,
};
}
};
/**
@@ -75,6 +87,22 @@ function isVersionTooOld(actualVersion, latestVersion) {
);
}
// Assumes that releases are sorted in the order of recency (i.e. most recent releases are earlier in the list)
// This enables us to stop looking as soon as we find the first release with a matching major/minor version, since
// we know it's the most recent release, therefore the highest patch available.
function getLatestPatchForVersion(version, releases) {
for (releaseName of releases) {
const release = parseVersionFromString(releaseName);
if (
release &&
release.major == version.major &&
release.minor == version.minor
) {
return release.patch;
}
}
}
function getReactNativeVersionFromIssueBodyIfExists(issue) {
if (!issue || !issue.body) return;
const rnVersionRegex = /React Native Version[\r\n]+(?<version>.+)[\r\n]*/;
-41
View File
@@ -1,41 +0,0 @@
name: Bump Podfile.lock
on:
workflow_call: # this directive allow us to call this workflow from other workflows
jobs:
bump-podfile-lock:
runs-on: macos-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
fetch-tags: true
- name: Install dependencies
uses: ./.github/actions/yarn-install
- name: Configure git
run: |
git config --local user.email "bot@reactnative.dev"
git config --local user.name "React Native Bot"
- name: Extract branch name
run: |
TAG="${{ github.ref_name }}";
BRANCH_NAME=$(echo "$TAG" | sed -E 's/v([0-9]+\.[0-9]+)\.[0-9]+(-rc\.[0-9]+)?/\1-stable/')
echo "Branch Name is $BRANCH_NAME"
echo "BRANCH_NAME=$BRANCH_NAME" >> $GITHUB_ENV
- name: Checkout release branch
run: |
git checkout "$BRANCH_NAME"
git fetch
git pull origin "$BRANCH_NAME"
- name: Bump podfile.lock
run: |
cd packages/rn-tester
bundle install
bundle exec pod update hermes-engine --no-repo-update
- name: Commit changes
run: |
git add packages/rn-tester/Podfile.lock
git commit -m "[LOCAL] Bump Podfile.lock"
git push origin "$BRANCH_NAME"
+2 -2
View File
@@ -14,7 +14,7 @@ jobs:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: ./.github/actions/setup-node
- name: Use Node.js 18
uses: actions/setup-node@v4
- name: Trim the cache
run: node scripts/clean-gha-cache.js
+1 -3
View File
@@ -1,6 +1,6 @@
# This jobs runs every day 2 hours after the nightly job and its purpose is to report
# a failure in case the nightly failed to be published. We are going to hook this to an internal automation.
name: Check Nightlies
name: Check Nigthlies
on:
workflow_dispatch:
@@ -30,5 +30,3 @@ jobs:
test-libraries:
uses: ./.github/workflows/test-libraries-on-nightlies.yml
needs: check-nightly
secrets:
discord_webhook_url: ${{ secrets.NIGHTLY_DISCORD_WEBHOOK }}
@@ -1,29 +0,0 @@
name: Create Draft Release
on:
workflow_call:
jobs:
create-draft-release:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
fetch-tags: true
- name: Install dependencies
uses: ./.github/actions/yarn-install
- name: Configure Git
shell: bash
run: |
git config --local user.email "bot@reactnative.dev"
git config --local user.name "React Native Bot"
- name: Create draft release
uses: actions/github-script@v6
with:
script: |
const {createDraftRelease} = require('./.github/workflow-scripts/createDraftRelease.js');
const version = '${{ github.ref_name }}';
const {isLatest} = require('./.github/workflow-scripts/publishTemplate.js');
await createDraftRelease(version, isLatest(), '${{secrets.REACT_NATIVE_BOT_GITHUB_TOKEN}}');
+4 -5
View File
@@ -18,12 +18,11 @@ jobs:
if: github.repository == 'facebook/react-native'
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: ./.github/actions/setup-node
- name: Run yarn install
uses: ./.github/actions/yarn-install
- name: Run Yarn Install on Root
run: yarn install
working-directory: .
- name: Danger
run: yarn danger ci --use-github-checks --failOnErrors
working-directory: private/react-native-bots
working-directory: packages/react-native-bots
env:
DANGER_GITHUB_API_TOKEN: ${{ secrets.REACT_NATIVE_BOT_GITHUB_TOKEN }}
-28
View File
@@ -1,28 +0,0 @@
name: Generate Changelog
on:
workflow_call:
jobs:
generate-changelog:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
fetch-tags: true
- name: Install dependencies
uses: ./.github/actions/yarn-install
- name: Configure Git
shell: bash
run: |
git config --local user.email "bot@reactnative.dev"
git config --local user.name "React Native Bot"
- name: Generate Changelog
uses: actions/github-script@v6
with:
script: |
const {generateChangelog} = require('./.github/workflow-scripts/generateChangelog');
const version = '${{ github.ref_name }}';
await generateChangelog(version, '${{secrets.REACT_NATIVE_BOT_GITHUB_TOKEN}}');
-45
View File
@@ -1,45 +0,0 @@
name: Monitor React Native New Issues
on:
schedule:
- cron: "0 0,6,12,18 * * *"
workflow_dispatch:
# Reminder for when we have to update the schedule (before Jan 2026):
# the secrets.ONCALL_SCHEDULE secret must be on a single line and must have all the `"` escaped as `\"`.
# Only a meta engineer can update it through the OSS internal portal.
jobs:
monitor-issues:
runs-on: ubuntu-latest
if: github.repository == 'facebook/react-native'
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Node.js
uses: ./.github/actions/setup-node
- name: Install dependencies
uses: ./.github/actions/yarn-install
- name: Extract next oncall
run: |
ONCALLS=$(node ./.github/workflow-scripts/extractIssueOncalls.js "${{ secrets.ONCALL_SCHEDULE }}")
ONCALL1=$(echo $ONCALLS | cut -d ' ' -f 1)
ONCALL2=$(echo $ONCALLS | cut -d ' ' -f 2)
echo "oncall1=$ONCALL1" >> $GITHUB_ENV
echo "oncall2=$ONCALL2" >> $GITHUB_ENV
- name: Print oncalls
run: |
echo "oncall1: ${{ env.oncall1 }}"
echo "oncall2: ${{ env.oncall2 }}"
- name: Monitor New Issues
uses: react-native-community/repo-monitor@v1.0.1
with:
task: "monitor-issues"
git_secret: ${{ secrets.GITHUB_TOKEN }}
notifier: "discord"
fetch_data_interval: 6
repo_owner: "facebook"
repo_name: "react-native"
discord_webhook_url: "${{ secrets.DISCORD_WEBHOOK_URL }}"
discord_id_type: "user"
discord_ids: "${{ env.oncall1 }},${{ env.oncall2 }}"
+7 -20
View File
@@ -40,7 +40,7 @@ jobs:
hermes-version-file: ${{ env.HERMES_VERSION_FILE }}
build_hermesc_apple:
runs-on: macos-14
runs-on: macos-13
needs: prepare_hermes_workspace
env:
HERMES_WS_DIR: /tmp/hermes
@@ -80,7 +80,7 @@ jobs:
react-native-version: ${{ needs.prepare_hermes_workspace.outputs.react-native-version }}
build_hermes_macos:
runs-on: macos-14
runs-on: macos-13
needs: [build_apple_slices_hermes, prepare_hermes_workspace]
env:
HERMES_WS_DIR: /tmp/hermes
@@ -100,15 +100,6 @@ jobs:
react-native-version: ${{ needs.prepare_hermes_workspace.outputs.react-native-version }}
flavor: ${{ matrix.flavor }}
prebuild_apple_dependencies:
uses: ./.github/workflows/prebuild-ios-dependencies.yml
secrets: inherit
prebuild_react_native_core:
uses: ./.github/workflows/prebuild-ios-core.yml
secrets: inherit
needs: [prebuild_apple_dependencies, build_hermes_macos]
build_hermesc_linux:
runs-on: ubuntu-latest
needs: prepare_hermes_workspace
@@ -128,9 +119,9 @@ jobs:
runs-on: windows-2025
needs: prepare_hermes_workspace
env:
HERMES_WS_DIR: 'D:\tmp\hermes'
HERMES_TARBALL_ARTIFACTS_DIR: 'D:\tmp\hermes\hermes-runtime-darwin'
HERMES_OSXBIN_ARTIFACTS_DIR: 'D:\tmp\hermes\osx-bin'
HERMES_WS_DIR: 'C:\tmp\hermes'
HERMES_TARBALL_ARTIFACTS_DIR: 'C:\tmp\hermes\hermes-runtime-darwin'
HERMES_OSXBIN_ARTIFACTS_DIR: 'C:\tmp\hermes\osx-bin'
ICU_URL: "https://github.com/unicode-org/icu/releases/download/release-64-2/icu4c-64_2-Win64-MSVC2017.zip"
MSBUILD_DIR: 'C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\MSBuild\Current\Bin'
CMAKE_DIR: 'C:\Program Files\CMake\bin'
@@ -162,7 +153,6 @@ jobs:
uses: ./.github/actions/build-android
with:
release-type: ${{ needs.set_release_type.outputs.RELEASE_TYPE }}
gradle-cache-encryption-key: ${{ secrets.GRADLE_CACHE_ENCRYPTION_KEY }}
build_npm_package:
runs-on: 8-core-ubuntu
@@ -174,15 +164,13 @@ jobs:
build_hermesc_linux,
build_hermesc_windows,
build_android,
prebuild_apple_dependencies,
prebuild_react_native_core,
]
container:
image: reactnativecommunity/react-native-android:latest
env:
TERM: "dumb"
GRADLE_OPTS: "-Dorg.gradle.daemon=false"
# By default we only build ARM64 to save time/resources. For release/nightlies, we override this value to build all archs.
# By default we only build ARM64 to save time/resources. For release/nightlies/prealpha, we override this value to build all archs.
ORG_GRADLE_PROJECT_reactNativeArchitectures: "arm64-v8a"
env:
HERMES_WS_DIR: /tmp/hermes
@@ -194,10 +182,9 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Build and Publish NPM Package
- name: Build and Publish NPM PAckage
uses: ./.github/actions/build-npm-package
with:
hermes-ws-dir: ${{ env.HERMES_WS_DIR }}
release-type: ${{ needs.set_release_type.outputs.RELEASE_TYPE }}
gha-npm-token: ${{ env.GHA_NPM_TOKEN }}
gradle-cache-encryption-key: ${{ secrets.GRADLE_CACHE_ENCRYPTION_KEY }}
-212
View File
@@ -1,212 +0,0 @@
name: Prebuild iOS Dependencies
on:
workflow_call: # this directive allow us to call this workflow from other workflows
jobs:
build-rn-slice:
runs-on: macos-14
strategy:
fail-fast: false
matrix:
flavor: ['Debug', 'Release']
slice: [
'ios',
'ios-simulator',
'mac-catalyst',
]
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Restore cache if present
id: restore-ios-slice
uses: actions/cache/restore@v4
with:
key: v3-ios-core-${{ matrix.slice }}-${{ matrix.flavor }}-${{ hashFiles('packages/react-native/Package.swift') }}-${{ hashFiles('packages/react-native/scripts/ios-prebuild/setup.js') }}
path: packages/react-native/
- name: Setup node.js
if: steps.restore-ios-slice.outputs.cache-hit != 'true'
uses: ./.github/actions/setup-node
- name: Setup xcode
if: steps.restore-ios-slice.outputs.cache-hit != 'true'
uses: ./.github/actions/setup-xcode
with:
xcode-version: '16.2.0'
- name: Yarn Install
if: steps.restore-ios-slice.outputs.cache-hit != 'true'
uses: ./.github/actions/yarn-install
- name: Download Hermes
if: steps.restore-ios-slice.outputs.cache-hit != 'true'
uses: actions/download-artifact@v4
with:
name: hermes-darwin-bin-${{ matrix.flavor }}
path: /tmp/hermes/hermes-runtime-darwin
- name: Extract Hermes
if: steps.restore-ios-slice.outputs.cache-hit != 'true'
shell: bash
run: |
HERMES_TARBALL_ARTIFACTS_DIR=/tmp/hermes/hermes-runtime-darwin
if [ ! -d $HERMES_TARBALL_ARTIFACTS_DIR ]; then
echo "Hermes tarball artifacts dir not present ($HERMES_TARBALL_ARTIFACTS_DIR)."
exit 0
fi
TARBALL_FILENAME=$(node ./packages/react-native/scripts/hermes/get-tarball-name.js --buildType "${{ matrix.flavor }}")
TARBALL_PATH=$HERMES_TARBALL_ARTIFACTS_DIR/$TARBALL_FILENAME
echo "Looking for $TARBALL_FILENAME in $HERMES_TARBALL_ARTIFACTS_DIR"
echo "$TARBALL_PATH"
if [ ! -f $TARBALL_PATH ]; then
echo "Hermes tarball not present ($TARBALL_PATH). Build Hermes from source."
exit 0
fi
echo "Found Hermes tarball at $TARBALL_PATH"
echo "HERMES_ENGINE_TARBALL_PATH=$TARBALL_PATH" >> $GITHUB_ENV
- name: Download ReactNativeDependencies
uses: actions/download-artifact@v4
with:
name: ReactNativeDependencies${{ matrix.flavor }}.xcframework.tar.gz
path: /tmp/third-party/
- name: Extract ReactNativeDependencies
if: steps.restore-ios-slice.outputs.cache-hit != 'true'
shell: bash
run: |
# Extract ReactNativeDependencies
tar -xzf /tmp/third-party/ReactNativeDependencies${{ matrix.flavor }}.xcframework.tar.gz -C /tmp/third-party/
# Create destination folder
mkdir -p packages/react-native/third-party/
# Move the XCFramework in the destination directory
mv /tmp/third-party/packages/react-native/third-party/ReactNativeDependencies.xcframework packages/react-native/third-party/ReactNativeDependencies.xcframework
VERSION=$(jq -r '.version' package.json)
echo "$VERSION-${{matrix.flavor}}" > "packages/react-native/third-party/version.txt"
cat "packages/react-native/third-party/version.txt"
# Check destination directory
ls -lR packages/react-native/third-party/
- name: Setup the workspace
if: steps.restore-ios-slice.outputs.cache-hit != 'true'
shell: bash
run: |
cd packages/react-native
node scripts/ios-prebuild.js -s -f "${{ matrix.flavor }}"
- name: Build React Native
if: steps.restore-ios-slice.outputs.cache-hit != 'true'
shell: bash
run: |
# This is going to be replaced by a CLI script
cd packages/react-native
node scripts/ios-prebuild -b -f "${{ matrix.flavor }}" -p "${{ matrix.slice }}"
- name: Upload headers
uses: actions/upload-artifact@v4
with:
name: prebuild-ios-core-headers-${{ matrix.flavor }}-${{ matrix.slice }}
path:
packages/react-native/.build/headers
- name: Upload artifacts
uses: actions/upload-artifact@v4.3.4
with:
name: prebuild-ios-core-slice-${{ matrix.flavor }}-${{ matrix.slice }}
path: |
packages/react-native/.build/output/spm/${{ matrix.flavor }}/Build/Products
- name: Save Cache
uses: actions/cache/save@v4
if: ${{ github.ref == 'refs/heads/main' }} # To avoid that the cache explode
with:
key: v3-ios-core-${{ matrix.slice }}-${{ matrix.flavor }}-${{ hashFiles('packages/react-native/Package.swift') }}-${{ hashFiles('packages/react-native/scripts/ios-prebuild/setup.js') }}
path: |
packages/react-native/.build/output/spm/${{ matrix.flavor }}/Build/Products
packages/react-native/.build/headers
compose-xcframework:
runs-on: macos-14
needs: [build-rn-slice]
strategy:
fail-fast: false
matrix:
flavor: ['Debug', 'Release']
env:
REACT_ORG_CODE_SIGNING_P12_CERT: ${{ secrets.REACT_ORG_CODE_SIGNING_P12_CERT }}
REACT_ORG_CODE_SIGNING_P12_CERT_PWD: ${{ secrets.REACT_ORG_CODE_SIGNING_P12_CERT_PWD }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Restore cache if present
id: restore-ios-xcframework
uses: actions/cache/restore@v4
with:
path: packages/react-native/.build/output/xcframeworks
key: v2-ios-core-xcframework-${{ matrix.flavor }}-${{ hashFiles('packages/react-native/Package.swift') }}-${{ hashFiles('packages/react-native/scripts/ios-prebuild/setup.js') }}
- name: Setup node.js
if: steps.restore-ios-xcframework.outputs.cache-hit != 'true'
uses: ./.github/actions/setup-node
- name: Setup xcode
if: steps.restore-ios-xcframework.outputs.cache-hit != 'true'
uses: ./.github/actions/setup-xcode
with:
xcode-version: '16.2.0'
- name: Yarn Install
if: steps.restore-ios-xcframework.outputs.cache-hit != 'true'
uses: ./.github/actions/yarn-install
- name: Download slice artifacts
if: steps.restore-ios-xcframework.outputs.cache-hit != 'true'
uses: actions/download-artifact@v4
with:
pattern: prebuild-ios-core-slice-${{ matrix.flavor }}-*
path: packages/react-native/.build/output/spm/${{ matrix.flavor }}/Build/Products
merge-multiple: true
- name: Download headers
if: steps.restore-ios-xcframework.outputs.cache-hit != 'true'
uses: actions/download-artifact@v4
with:
pattern: prebuild-ios-core-headers-${{ matrix.flavor }}-*
path: packages/react-native/.build/headers
merge-multiple: true
- name: Setup Keychain
if: ${{ steps.restore-ios-xcframework.outputs.cache-hit != 'true' && env.REACT_ORG_CODE_SIGNING_P12_CERT != '' }}
uses: apple-actions/import-codesign-certs@v3 # https://github.com/marketplace/actions/import-code-signing-certificates
with:
p12-file-base64: ${{ secrets.REACT_ORG_CODE_SIGNING_P12_CERT }}
p12-password: ${{ secrets.REACT_ORG_CODE_SIGNING_P12_CERT_PWD }}
- name: Create XCFramework
if: ${{ steps.restore-ios-xcframework.outputs.cache-hit != 'true' && env.REACT_ORG_CODE_SIGNING_P12_CERT == '' }}
run: |
cd packages/react-native
node scripts/ios-prebuild -c -f "${{ matrix.flavor }}"
- name: Create and Sign XCFramework
if: ${{ steps.restore-ios-xcframework.outputs.cache-hit != 'true' && env.REACT_ORG_CODE_SIGNING_P12_CERT != '' }}
run: |
cd packages/react-native
node scripts/ios-prebuild -c -f "${{ matrix.flavor }}" -i "React Org"
- name: Compress and Rename XCFramework
if: steps.restore-ios-xcframework.outputs.cache-hit != 'true'
run: |
cd packages/react-native/.build/output/xcframeworks/${{matrix.flavor}}
tar -cz -f ../ReactCore${{matrix.flavor}}.xcframework.tar.gz React.xcframework
- name: Compress and Rename dSYM
if: steps.restore-ios-xcframework.outputs.cache-hit != 'true'
run: |
cd packages/react-native/.build/output/xcframeworks/${{matrix.flavor}}/Symbols
tar -cz -f ../../ReactCore${{ matrix.flavor }}.framework.dSYM.tar.gz .
- name: Upload XCFramework Artifact
uses: actions/upload-artifact@v4
with:
name: ReactCore${{ matrix.flavor }}.xcframework.tar.gz
path: packages/react-native/.build/output/xcframeworks/ReactCore${{matrix.flavor}}.xcframework.tar.gz
- name: Upload dSYM Artifact
uses: actions/upload-artifact@v4
with:
name: ReactCore${{ matrix.flavor }}.framework.dSYM.tar.gz
path: packages/react-native/.build/output/xcframeworks/ReactCore${{matrix.flavor}}.framework.dSYM.tar.gz
- name: Save cache if present
if: ${{ github.ref == 'refs/heads/main' }} # To avoid that the cache explode
uses: actions/cache/save@v4
with:
path: |
packages/react-native/.build/output/xcframeworks/ReactCore${{matrix.flavor}}.xcframework.tar.gz
packages/react-native/.build/output/xcframeworks/ReactCore${{matrix.flavor}}.framework.dSYM.tar.gz
key: v2-ios-core-xcframework-${{ matrix.flavor }}-${{ hashFiles('packages/react-native/Package.swift') }}-${{ hashFiles('packages/react-native/scripts/ios-prebuild/setup.js') }}
@@ -1,202 +0,0 @@
name: Prebuild iOS Dependencies
on:
workflow_call: # this directive allow us to call this workflow from other workflows
jobs:
prepare_workspace:
name: Prepare workspace
runs-on: macos-14
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup node.js
uses: ./.github/actions/setup-node
- name: Restore cache if present
id: restore-ios-prebuilds
uses: actions/cache/restore@v4
with:
path: packages/react-native/third-party/
key: v2-ios-dependencies-${{ hashfiles('scripts/releases/ios-prebuild/configuration.js') }}
enableCrossOsArchive: true
- name: Yarn Install
if: steps.restore-ios-prebuilds.outputs.cache-hit != 'true'
uses: ./.github/actions/yarn-install
- name: Prepare Dependencies
if: steps.restore-ios-prebuilds.outputs.cache-hit != 'true'
run: |
node scripts/releases/prepare-ios-prebuilds.js -s
- name: Generate Package.swift
if: steps.restore-ios-prebuilds.outputs.cache-hit != 'true'
run: |
node scripts/releases/prepare-ios-prebuilds.js -w
- name: Upload Artifacts
uses: actions/upload-artifact@v4.3.4
with:
name: ios-prebuilds-workspace
path: packages/react-native/third-party/
- name: Save Cache
uses: actions/cache/save@v4
if: ${{ github.ref == 'refs/heads/main' }} # To avoid that the cache explode
with:
key: v2-ios-dependencies-${{ hashfiles('scripts/releases/ios-prebuild/configuration.js') }}
enableCrossOsArchive: true
path: packages/react-native/third-party/
build-apple-slices:
name: Build Apple Slice
runs-on: macos-14
needs: [prepare_workspace]
strategy:
fail-fast: false
matrix:
flavor: ['Debug', 'Release']
slice: ['ios',
'ios-simulator',
'macos',
'mac-catalyst',
'tvos',
'tvos-simulator',
'xros',
'xros-simulator']
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup node.js
uses: ./.github/actions/setup-node
- name: Setup xcode
uses: ./.github/actions/setup-xcode
with:
xcode-version: '16.1'
- name: Restore slice folder
id: restore-slice-folder
uses: actions/cache/restore@v4
with:
path: packages/react-native/third-party/.build/Build/Products
key: v2-ios-dependencies-slice-folder-${{ matrix.slice }}-${{ matrix.flavor }}-${{ hashfiles('scripts/releases/ios-prebuild/configuration.js') }}
- name: Yarn Install
if: steps.restore-slice-folder.outputs.cache-hit != 'true'
uses: ./.github/actions/yarn-install
- name: Restore workspace
if: steps.restore-slice-folder.outputs.cache-hit != 'true'
uses: actions/download-artifact@v4
with:
name: ios-prebuilds-workspace
path: packages/react-native/third-party/
- name: Print third-party folder structure
run: ls -lR packages/react-native/third-party
- name: Install VisionOS
if: ${{ steps.restore-slice-folder.outputs.cache-hit != 'true' && (matrix.slice == 'xros' || matrix.slice == 'xros-simulator') }}
run: |
# https://github.com/actions/runner-images/issues/10559
sudo xcodebuild -runFirstLaunch
sudo xcrun simctl list
sudo xcodebuild -downloadPlatform visionOS
sudo xcodebuild -runFirstLaunch
- name: Build slice ${{ matrix.slice }} for ${{ matrix.flavor }}
if: steps.restore-slice-folder.outputs.cache-hit != 'true'
run: node scripts/releases/prepare-ios-prebuilds.js -b -p ${{ matrix.slice }} -r ${{ matrix.flavor }}
- name: Upload Artifacts
uses: actions/upload-artifact@v4.3.4
with:
name: prebuild-slice-${{ matrix.flavor }}-${{ matrix.slice }}
path: |
packages/react-native/third-party/.build/Build/Products
- name: Save Cache
uses: actions/cache/save@v4
if: ${{ github.ref == 'refs/heads/main' }} # To avoid that the cache explode
with:
key: v2-ios-dependencies-slice-folder-${{ matrix.slice }}-${{ matrix.flavor }}-${{ hashfiles('scripts/releases/ios-prebuild/configuration.js') }}
enableCrossOsArchive: true
path: |
packages/react-native/third-party/.build/Build/Products
create-xcframework:
name: Prepare XCFramework
runs-on: macos-14
needs: [build-apple-slices]
strategy:
fail-fast: false
matrix:
flavor: [Debug, Release]
env:
REACT_ORG_CODE_SIGNING_P12_CERT: ${{ secrets.REACT_ORG_CODE_SIGNING_P12_CERT }}
REACT_ORG_CODE_SIGNING_P12_CERT_PWD: ${{ secrets.REACT_ORG_CODE_SIGNING_P12_CERT_PWD }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup node.js
uses: ./.github/actions/setup-node
- name: Setup xcode
uses: ./.github/actions/setup-xcode
with:
xcode-version: '16.1'
- name: Restore XCFramework
id: restore-xcframework
uses: actions/cache/restore@v4
with:
path: |
packages/react-native/third-party/
key: v2-ios-dependencies-xcframework-${{ matrix.flavor }}-${{ hashfiles('scripts/releases/ios-prebuild/configuration.js') }}
# If cache hit, we already have our binary. We don't need to do anything.
- name: Yarn Install
if: steps.restore-xcframework.outputs.cache-hit != 'true'
uses: ./.github/actions/yarn-install
- name: Restore workspace
if: steps.restore-xcframework.outputs.cache-hit != 'true'
uses: actions/download-artifact@v4
with:
name: ios-prebuilds-workspace
path: packages/react-native/third-party/
- name: Download slices
if: steps.restore-xcframework.outputs.cache-hit != 'true'
uses: actions/download-artifact@v4
with:
pattern: prebuild-slice-${{ matrix.flavor }}-*
path: packages/react-native/third-party/.build/Build/Products
merge-multiple: true
- name: Setup Keychain
if: ${{ steps.restore-xcframework.outputs.cache-hit != 'true' && env.REACT_ORG_CODE_SIGNING_P12_CERT != '' }}
uses: apple-actions/import-codesign-certs@v3 # https://github.com/marketplace/actions/import-code-signing-certificates
with:
p12-file-base64: ${{ secrets.REACT_ORG_CODE_SIGNING_P12_CERT }}
p12-password: ${{ secrets.REACT_ORG_CODE_SIGNING_P12_CERT_PWD }}
- name: Create XCFramework
if: ${{ steps.restore-xcframework.outputs.cache-hit != 'true' && env.REACT_ORG_CODE_SIGNING_P12_CERT == '' }}
run: node scripts/releases/prepare-ios-prebuilds.js -c
- name: Create and Sign XCFramework
if: ${{ steps.restore-xcframework.outputs.cache-hit != 'true' && env.REACT_ORG_CODE_SIGNING_P12_CERT != '' }}
run: node scripts/releases/prepare-ios-prebuilds.js -c -i "React Org"
- name: Compress and Rename XCFramework
if: steps.restore-xcframework.outputs.cache-hit != 'true'
run: |
tar -cz -f packages/react-native/third-party/ReactNativeDependencies${{ matrix.flavor }}.xcframework.tar.gz \
packages/react-native/third-party/ReactNativeDependencies.xcframework
- name: Show Symbol folder content
if: steps.restore-xcframework.outputs.cache-hit != 'true'
run: ls -lR packages/react-native/third-party/Symbols
- name: Compress and Rename dSYM
if: steps.restore-xcframework.outputs.cache-hit != 'true'
run: |
tar -cz -f packages/react-native/third-party/Symbols/ReactNativeDependencies${{ matrix.flavor }}.framework.dSYM.tar.gz \
packages/react-native/third-party/Symbols/ReactNativeDependencies.framework.dSYM
- name: Upload XCFramework Artifact
uses: actions/upload-artifact@v4
with:
name: ReactNativeDependencies${{ matrix.flavor }}.xcframework.tar.gz
path: packages/react-native/third-party/ReactNativeDependencies${{ matrix.flavor }}.xcframework.tar.gz
- name: Upload dSYM Artifact
uses: actions/upload-artifact@v4
with:
name: ReactNativeDependencies${{ matrix.flavor }}.framework.dSYM.tar.gz
path: |
packages/react-native/third-party/Symbols/ReactNativeDependencies${{ matrix.flavor }}.framework.dSYM.tar.gz
- name: Save XCFramework in Cache
if: ${{ github.ref == 'refs/heads/main' }} # To avoid that the cache explode
uses: actions/cache/save@v4
with:
path: |
packages/react-native/third-party/ReactNativeDependencies${{ matrix.flavor }}.xcframework.tar.gz
packages/react-native/third-party/ReactNativeDependencies${{ matrix.flavor }}.framework.dSYM.tar.gz
key: v2-ios-dependencies-xcframework-${{ matrix.flavor }}-${{ hashfiles('scripts/releases/ios-prebuild/configuration.js') }}
@@ -17,11 +17,9 @@ jobs:
- name: Setup node.js
uses: ./.github/actions/setup-node
- name: Run Yarn Install
uses: ./.github/actions/yarn-install
uses: ./.github/actions/yarn-install-with-cache
- name: Build packages
run: yarn build
- name: Build types
run: yarn build-types
- name: Set NPM auth token
run: echo "//registry.npmjs.org/:_authToken=$GHA_NPM_TOKEN" > ~/.npmrc
- name: Find and publish all bumped packages
+7 -73
View File
@@ -38,7 +38,7 @@ jobs:
hermes-version-file: ${{ env.HERMES_VERSION_FILE }}
build_hermesc_apple:
runs-on: macos-14
runs-on: macos-13
needs: prepare_hermes_workspace
env:
HERMES_WS_DIR: /tmp/hermes
@@ -77,7 +77,7 @@ jobs:
react-native-version: ${{ needs.prepare_hermes_workspace.outputs.react-native-version }}
build_hermes_macos:
runs-on: macos-14
runs-on: macos-13
needs: [build_apple_slices_hermes, prepare_hermes_workspace]
env:
HERMES_WS_DIR: /tmp/hermes
@@ -97,15 +97,6 @@ jobs:
react-native-version: ${{ needs.prepare_hermes_workspace.outputs.react-native-version }}
flavor: ${{ matrix.flavor }}
prebuild_apple_dependencies:
uses: ./.github/workflows/prebuild-ios-dependencies.yml
secrets: inherit
prebuild_react_native_core:
uses: ./.github/workflows/prebuild-ios-core.yml
secrets: inherit
needs: [prebuild_apple_dependencies, build_hermes_macos]
build_hermesc_linux:
runs-on: ubuntu-latest
needs: prepare_hermes_workspace
@@ -125,9 +116,9 @@ jobs:
runs-on: windows-2025
needs: prepare_hermes_workspace
env:
HERMES_WS_DIR: 'D:\tmp\hermes'
HERMES_TARBALL_ARTIFACTS_DIR: 'D:\tmp\hermes\hermes-runtime-darwin'
HERMES_OSXBIN_ARTIFACTS_DIR: 'D:\tmp\hermes\osx-bin'
HERMES_WS_DIR: 'C:\tmp\hermes'
HERMES_TARBALL_ARTIFACTS_DIR: 'C:\tmp\hermes\hermes-runtime-darwin'
HERMES_OSXBIN_ARTIFACTS_DIR: 'C:\tmp\hermes\osx-bin'
ICU_URL: "https://github.com/unicode-org/icu/releases/download/release-64-2/icu4c-64_2-Win64-MSVC2017.zip"
MSBUILD_DIR: 'C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\MSBuild\Current\Bin'
CMAKE_DIR: 'C:\Program Files\CMake\bin'
@@ -140,27 +131,6 @@ jobs:
hermes-version: ${{ needs.prepare_hermes_workspace.outputs.hermes-version }}
react-native-version: ${{ needs.prepare_hermes_workspace.outputs.react-native-version }}
build_android:
runs-on: 8-core-ubuntu
needs: [set_release_type]
container:
image: reactnativecommunity/react-native-android:latest
env:
TERM: "dumb"
GRADLE_OPTS: "-Dorg.gradle.daemon=false"
ORG_GRADLE_PROJECT_SIGNING_PWD: ${{ secrets.ORG_GRADLE_PROJECT_SIGNING_PWD }}
ORG_GRADLE_PROJECT_SIGNING_KEY: ${{ secrets.ORG_GRADLE_PROJECT_SIGNING_KEY }}
ORG_GRADLE_PROJECT_SONATYPE_USERNAME: ${{ secrets.ORG_GRADLE_PROJECT_SONATYPE_USERNAME }}
ORG_GRADLE_PROJECT_SONATYPE_PASSWORD: ${{ secrets.ORG_GRADLE_PROJECT_SONATYPE_PASSWORD }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Build Android
uses: ./.github/actions/build-android
with:
release-type: ${{ needs.set_release_type.outputs.RELEASE_TYPE }}
gradle-cache-encryption-key: ${{ secrets.GRADLE_CACHE_ENCRYPTION_KEY }}
build_npm_package:
runs-on: 8-core-ubuntu
needs:
@@ -170,16 +140,13 @@ jobs:
build_hermes_macos,
build_hermesc_linux,
build_hermesc_windows,
build_android,
prebuild_apple_dependencies,
prebuild_react_native_core,
]
container:
image: reactnativecommunity/react-native-android:latest
env:
TERM: "dumb"
GRADLE_OPTS: "-Dorg.gradle.daemon=false"
# By default we only build ARM64 to save time/resources. For release/nightlies, we override this value to build all archs.
# By default we only build ARM64 to save time/resources. For release/nightlies/prealpha, we override this value to build all archs.
ORG_GRADLE_PROJECT_reactNativeArchitectures: "arm64-v8a"
env:
HERMES_WS_DIR: /tmp/hermes
@@ -195,13 +162,12 @@ jobs:
with:
fetch-depth: 0
fetch-tags: true
- name: Build and Publish NPM Package
- name: Build and Publish NPM PAckage
uses: ./.github/actions/build-npm-package
with:
hermes-ws-dir: ${{ env.HERMES_WS_DIR }}
release-type: ${{ needs.set_release_type.outputs.RELEASE_TYPE }}
gha-npm-token: ${{ env.GHA_NPM_TOKEN }}
gradle-cache-encryption-key: ${{ secrets.GRADLE_CACHE_ENCRYPTION_KEY }}
- name: Publish @react-native-community/template
id: publish-template-to-npm
uses: actions/github-script@v6
@@ -227,35 +193,3 @@ jobs:
-H "Accept: application/vnd.github.v3+json" \
-H "Authorization: Bearer $REACT_NATIVE_BOT_GITHUB_TOKEN" \
-d "{\"event_type\": \"publish\", \"client_payload\": { \"version\": \"${{ github.ref_name }}\" }}"
- name: Verify Release is on NPM
timeout-minutes: 3
uses: actions/github-script@v6
with:
github-token: ${{ secrets.REACT_NATIVE_BOT_GITHUB_TOKEN }}
script: |
const {verifyReleaseOnNpm} = require('./.github/workflow-scripts/verifyReleaseOnNpm.js');
const {isLatest} = require('./.github/workflow-scripts/publishTemplate.js');
const version = "${{ github.ref_name }}";
await verifyReleaseOnNpm(version, isLatest());
- name: Verify that artifacts are on Maven
uses: actions/github-script@v6
with:
script: |
const {verifyArtifactsAreOnMaven} = require('./.github/workflow-scripts/verifyArtifactsAreOnMaven.js');
const version = "${{ github.ref_name }}";
await verifyArtifactsAreOnMaven(version);
generate_changelog:
needs: build_npm_package
uses: ./.github/workflows/generate-changelog.yml
secrets: inherit
bump_podfile_lock:
needs: build_npm_package
uses: ./.github/workflows/bump-podfile-lock.yml
secrets: inherit
create_draft_release:
needs: generate_changelog
uses: ./.github/workflows/create-draft-release.yml
secrets: inherit
-19
View File
@@ -1,19 +0,0 @@
name: Retry workflow
# Based on https://stackoverflow.com/a/78314483
on:
workflow_dispatch:
inputs:
run_id:
required: true
jobs:
rerun:
runs-on: ubuntu-latest
steps:
- name: rerun ${{ inputs.run_id }}
env:
GH_REPO: ${{ github.repository }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh run watch ${{ inputs.run_id }} > /dev/null 2>&1
gh run rerun ${{ inputs.run_id }} --failed
+106 -129
View File
@@ -56,7 +56,7 @@ jobs:
hermes-version-file: ${{ env.HERMES_VERSION_FILE }}
build_hermesc_apple:
runs-on: macos-14
runs-on: macos-13
needs: prepare_hermes_workspace
env:
HERMES_WS_DIR: /tmp/hermes
@@ -96,7 +96,7 @@ jobs:
react-native-version: ${{ needs.prepare_hermes_workspace.outputs.react-native-version }}
build_hermes_macos:
runs-on: macos-14
runs-on: macos-13
needs: [build_apple_slices_hermes, prepare_hermes_workspace]
env:
HERMES_WS_DIR: /tmp/hermes
@@ -116,19 +116,10 @@ jobs:
react-native-version: ${{ needs.prepare_hermes_workspace.outputs.react-native-version }}
flavor: ${{ matrix.flavor }}
prebuild_apple_dependencies:
uses: ./.github/workflows/prebuild-ios-dependencies.yml
secrets: inherit
prebuild_react_native_core:
uses: ./.github/workflows/prebuild-ios-core.yml
secrets: inherit
needs: [prebuild_apple_dependencies, build_hermes_macos]
test_ios_rntester_ruby_3_2_0:
runs-on: macos-14
runs-on: macos-13
needs:
[build_apple_slices_hermes, prepare_hermes_workspace, build_hermes_macos, prebuild_apple_dependencies]
[build_apple_slices_hermes, prepare_hermes_workspace, build_hermes_macos]
env:
HERMES_WS_DIR: /tmp/hermes
HERMES_TARBALL_ARTIFACTS_DIR: /tmp/hermes/hermes-runtime-darwin
@@ -143,29 +134,32 @@ jobs:
react-native-version: ${{ needs.prepare_hermes_workspace.outputs.react-native-version }}
test_ios_rntester_dynamic_frameworks:
runs-on: macos-14
runs-on: macos-13
needs:
[build_apple_slices_hermes, prepare_hermes_workspace, build_hermes_macos, prebuild_apple_dependencies]
[build_apple_slices_hermes, prepare_hermes_workspace, build_hermes_macos]
env:
HERMES_WS_DIR: /tmp/hermes
HERMES_TARBALL_ARTIFACTS_DIR: /tmp/hermes/hermes-runtime-darwin
continue-on-error: true
strategy:
fail-fast: false
matrix:
jsengine: [Hermes, JSC]
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Run it
uses: ./.github/actions/test-ios-rntester
with:
jsengine: ${{ matrix.jsengine }}
use-frameworks: DynamicFrameworks
hermes-version: ${{ needs.prepare_hermes_workspace.outputs.hermes-version }}
react-native-version: ${{ needs.prepare_hermes_workspace.outputs.react-native-version }}
test_ios_rntester:
runs-on: macos-14-large
runs-on: macos-13
needs:
[build_apple_slices_hermes, prepare_hermes_workspace, build_hermes_macos, prebuild_apple_dependencies]
[build_apple_slices_hermes, prepare_hermes_workspace, build_hermes_macos]
env:
HERMES_WS_DIR: /tmp/hermes
HERMES_TARBALL_ARTIFACTS_DIR: /tmp/hermes/hermes-runtime-darwin
@@ -173,6 +167,35 @@ jobs:
strategy:
fail-fast: false
matrix:
jsengine: [Hermes, JSC]
architecture: [NewArch, OldArch]
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Run it
uses: ./.github/actions/test-ios-rntester
with:
jsengine: ${{ matrix.jsengine }}
architecture: ${{ matrix.architecture }}
run-unit-tests: "false"
use-frameworks: StaticLibraries
hermes-version: ${{ needs.prepare_hermes_workspace.outputs.hermes-version }}
react-native-version: ${{ needs.prepare_hermes_workspace.outputs.react-native-version }}
test_e2e_ios_rntester:
if: ${{ github.ref == 'refs/heads/main' || contains(github.ref, 'stable') || inputs.run-e2e-tests }}
runs-on: macos-13-large
needs:
[build_apple_slices_hermes, prepare_hermes_workspace, build_hermes_macos]
env:
HERMES_WS_DIR: /tmp/hermes
HERMES_TARBALL_ARTIFACTS_DIR: /tmp/hermes/hermes-runtime-darwin
continue-on-error: true
strategy:
fail-fast: false
matrix:
jsengine: [Hermes, JSC]
architecture: [NewArch]
flavor: [Debug, Release]
steps:
- name: Checkout
@@ -180,57 +203,37 @@ jobs:
- name: Run it
uses: ./.github/actions/test-ios-rntester
with:
jsengine: ${{ matrix.jsengine }}
architecture: ${{ matrix.architecture }}
run-unit-tests: "false"
use-frameworks: StaticLibraries
hermes-version: ${{ needs.prepare_hermes_workspace.outputs.hermes-version }}
react-native-version: ${{ needs.prepare_hermes_workspace.outputs.react-native-version }}
run-e2e-tests: "true"
flavor: ${{ matrix.flavor }}
test_e2e_ios_rntester:
if: ${{ github.ref == 'refs/heads/main' || contains(github.ref, 'stable') || inputs.run-e2e-tests }}
runs-on: macos-14-large
needs:
[test_ios_rntester]
env:
HERMES_WS_DIR: /tmp/hermes
HERMES_TARBALL_ARTIFACTS_DIR: /tmp/hermes/hermes-runtime-darwin
strategy:
fail-fast: false
matrix:
flavor: [Debug, Release]
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: ./.github/actions/setup-node
- name: Download App
uses: actions/download-artifact@v4
with:
name: RNTesterApp-NewArch-${{ matrix.flavor }}
path: /tmp/RNTesterBuild/RNTester.app
- name: Check downloaded folder content
run: ls -lR /tmp/RNTesterBuild
- name: Setup xcode
uses: ./.github/actions/setup-xcode
- name: Run E2E Tests
uses: ./.github/actions/maestro-ios
with:
app-path: "/tmp/RNTesterBuild/RNTester.app"
app-path: "/tmp/RNTesterBuild/Build/Products/${{ matrix.flavor }}-iphonesimulator/RNTester.app"
app-id: com.meta.RNTester.localDevelopment
jsengine: ${{ matrix.jsengine }}
maestro-flow: ./packages/rn-tester/.maestro/
flavor: ${{ matrix.flavor }}
test_e2e_ios_templateapp:
if: ${{ github.ref == 'refs/heads/main' || contains(github.ref, 'stable') || inputs.run-e2e-tests }}
runs-on: macos-14-large
needs: [build_npm_package, prebuild_apple_dependencies]
runs-on: macos-13-large
needs: build_npm_package
env:
HERMES_WS_DIR: /tmp/hermes
HERMES_TARBALL_ARTIFACTS_DIR: /tmp/hermes/hermes-runtime-darwin
continue-on-error: true
strategy:
fail-fast: false
matrix:
jsengine: [Hermes, JSC]
flavor: [Debug, Release]
architecture: [OldArch, NewArch]
steps:
- name: Checkout
uses: actions/checkout@v4
@@ -239,9 +242,9 @@ jobs:
- name: Setup node.js
uses: ./.github/actions/setup-node
- name: Run yarn
uses: ./.github/actions/yarn-install
uses: ./.github/actions/yarn-install-with-cache
- name: Setup ruby
uses: ruby/setup-ruby@v1
uses: ruby/setup-ruby@v1.170.0
with:
ruby-version: 2.6.10
- name: Download Hermes
@@ -256,14 +259,6 @@ jobs:
path: /tmp/react-native-tmp
- name: Print /tmp folder
run: ls -lR /tmp/react-native-tmp
- name: Download ReactNativeDependencies
uses: actions/download-artifact@v4
with:
name: ReactNativeDependencies${{ matrix.flavor }}.xcframework.tar.gz
path: /tmp/third-party
- name: Print third-party folder
shell: bash
run: ls -lR /tmp/third-party
- name: Prepare artifacts
run: |
REACT_NATIVE_PKG=$(find /tmp/react-native-tmp -type f -name "*.tgz")
@@ -284,8 +279,10 @@ jobs:
cd /tmp/RNTestProject/ios
bundle install
NEW_ARCH_ENABLED=1
export RCT_USE_LOCAL_RN_DEP=/tmp/third-party/ReactNativeDependencies${{ matrix.flavor }}.xcframework.tar.gz
if [[ ${{ matrix.architecture }} == "OldArch" ]]; then
echo "Disable the New Architecture"
NEW_ARCH_ENABLED=0
fi
HERMES_ENGINE_TARBALL_PATH=$HERMES_PATH RCT_NEW_ARCH_ENABLED=$NEW_ARCH_ENABLED bundle exec pod install
xcodebuild \
@@ -300,25 +297,30 @@ jobs:
with:
app-path: "/tmp/RNTestProject/Build/Products/${{ matrix.flavor }}-iphonesimulator/RNTestProject.app"
app-id: org.reactjs.native.example.RNTestProject
jsengine: ${{ matrix.jsengine }}
maestro-flow: ./scripts/e2e/.maestro/
flavor: ${{ matrix.flavor }}
working-directory: /tmp/RNTestProject
architecture: ${{ matrix.architecture }}
test_e2e_android_templateapp:
if: ${{ github.ref == 'refs/heads/main' || contains(github.ref, 'stable') || inputs.run-e2e-tests }}
runs-on: 4-core-ubuntu
needs: build_npm_package
continue-on-error: true
strategy:
fail-fast: false
matrix:
jsengine: [Hermes, JSC]
flavor: [debug, release]
architecture: [OldArch, NewArch]
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup node.js
uses: ./.github/actions/setup-node
- name: Run yarn
uses: ./.github/actions/yarn-install
uses: ./.github/actions/yarn-install-with-cache
- name: Set up JDK 17
uses: actions/setup-java@v2
with:
@@ -357,6 +359,11 @@ jobs:
cd /tmp/RNTestProject
echo "react.internal.mavenLocalRepo=$MAVEN_LOCAL" >> android/gradle.properties
if [[ ${{matrix.architecture}} == "OldArch" ]]; then
echo "Disabling the New Architecture"
sed -i 's/newArchEnabled=true/newArchEnabled=false/' android/gradle.properties
fi
# Build
cd android
CAPITALIZED_FLAVOR=$(echo "${{ matrix.flavor }}" | awk '{print toupper(substr($0, 1, 1)) substr($0, 2)}')
@@ -364,14 +371,15 @@ jobs:
- name: Run E2E Tests
uses: ./.github/actions/maestro-android
timeout-minutes: 60
with:
app-path: /tmp/RNTestProject/android/app/build/outputs/apk/${{ matrix.flavor }}/app-${{ matrix.flavor }}.apk
app-id: com.rntestproject
jsengine: ${{ matrix.jsengine }}
maestro-flow: ./scripts/e2e/.maestro/
install-java: 'false'
flavor: ${{ matrix.flavor }}
working-directory: /tmp/RNTestProject
architecture: ${{ matrix.architecture }}
build_hermesc_linux:
runs-on: ubuntu-latest
@@ -392,9 +400,9 @@ jobs:
runs-on: windows-2025
needs: prepare_hermes_workspace
env:
HERMES_WS_DIR: 'D:\tmp\hermes'
HERMES_TARBALL_ARTIFACTS_DIR: 'D:\tmp\hermes\hermes-runtime-darwin'
HERMES_OSXBIN_ARTIFACTS_DIR: 'D:\tmp\hermes\osx-bin'
HERMES_WS_DIR: 'C:\tmp\hermes'
HERMES_TARBALL_ARTIFACTS_DIR: 'C:\tmp\hermes\hermes-runtime-darwin'
HERMES_OSXBIN_ARTIFACTS_DIR: 'C:\tmp\hermes\osx-bin'
ICU_URL: "https://github.com/unicode-org/icu/releases/download/release-64-2/icu4c-64_2-Win64-MSVC2017.zip"
MSBUILD_DIR: 'C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\MSBuild\Current\Bin'
CMAKE_DIR: 'C:\Program Files\CMake\bin'
@@ -425,15 +433,17 @@ jobs:
with:
release-type: ${{ needs.set_release_type.outputs.RELEASE_TYPE }}
run-e2e-tests: ${{ github.ref == 'refs/heads/main' || contains(github.ref, 'stable') || inputs.run-e2e-tests }}
gradle-cache-encryption-key: ${{ secrets.GRADLE_CACHE_ENCRYPTION_KEY }}
test_e2e_android_rntester:
if: ${{ github.ref == 'refs/heads/main' || contains(github.ref, 'stable') || inputs.run-e2e-tests }}
runs-on: 4-core-ubuntu
# Temporarily disable RNTester tests on Android as they are quite flaky and they make CI always red
# if: ${{ github.ref == 'refs/heads/main' || contains(github.ref, 'stable') || inputs.run-e2e-tests }}
if: ${{ contains(github.ref, 'stable') || inputs.run-e2e-tests }}
runs-on: ubuntu-latest
needs: [build_android]
strategy:
fail-fast: false
matrix:
jsengine: [hermes, jsc]
flavor: [debug, release]
steps:
- name: Checkout
@@ -441,20 +451,20 @@ jobs:
- name: Setup node.js
uses: ./.github/actions/setup-node
- name: Install node dependencies
uses: ./.github/actions/yarn-install
uses: ./.github/actions/yarn-install-with-cache
- name: Download APK
uses: actions/download-artifact@v4
with:
name: rntester-${{ matrix.flavor }}
path: ./packages/rn-tester/android/app/build/outputs/apk/${{ matrix.flavor }}/
name: rntester-${{ matrix.jsengine }}-${{ matrix.flavor }}
path: ./packages/rn-tester/android/app/build/outputs/apk/${{ matrix.jsengine }}/${{ matrix.flavor }}/
- name: Print folder structure
run: ls -lR ./packages/rn-tester/android/app/build/outputs/apk/${{ matrix.flavor }}/
run: ls -lR ./packages/rn-tester/android/app/build/outputs/apk/${{ matrix.jsengine }}/${{ matrix.flavor }}/
- name: Run E2E Tests
uses: ./.github/actions/maestro-android
timeout-minutes: 60
with:
app-path: ./packages/rn-tester/android/app/build/outputs/apk/${{ matrix.flavor }}/app-x86-${{ matrix.flavor }}.apk
app-path: ./packages/rn-tester/android/app/build/outputs/apk/${{ matrix.jsengine }}/${{ matrix.flavor }}/app-${{ matrix.jsengine }}-x86-${{ matrix.flavor }}.apk
app-id: com.facebook.react.uiapp
jsengine: ${{ matrix.jsengine }}
maestro-flow: ./packages/rn-tester/.maestro
flavor: ${{ matrix.flavor }}
@@ -468,8 +478,6 @@ jobs:
build_hermesc_linux,
build_hermesc_windows,
build_android,
prebuild_apple_dependencies,
prebuild_react_native_core,
]
container:
image: reactnativecommunity/react-native-android:latest
@@ -486,7 +494,6 @@ jobs:
with:
hermes-ws-dir: ${{ env.HERMES_WS_DIR }}
release-type: ${{ needs.set_release_type.outputs.RELEASE_TYPE }}
gradle-cache-encryption-key: ${{ secrets.GRADLE_CACHE_ENCRYPTION_KEY }}
test_android_helloworld:
runs-on: 4-core-ubuntu
@@ -506,6 +513,8 @@ jobs:
fail-fast: false
matrix:
flavor: [Debug, Release]
architecture: [NewArch, OldArch]
jsengine: [Hermes, JSC]
steps:
- name: Checkout
uses: actions/checkout@v4
@@ -523,18 +532,22 @@ jobs:
path: /tmp/maven-local
- name: Setup gradle
uses: ./.github/actions/setup-gradle
with:
cache-encryption-key: ${{ secrets.GRADLE_CACHE_ENCRYPTION_KEY }}
- name: Run yarn install
uses: ./.github/actions/yarn-install
uses: ./.github/actions/yarn-install-with-cache
- name: Prepare the Helloworld application
shell: bash
run: node ./scripts/e2e/init-project-e2e.js --useHelloWorld --pathToLocalReactNative "$GITHUB_WORKSPACE/build/$(cat build/react-native-package-version)"
- name: Build the Helloworld application for ${{ matrix.flavor }} with Architecture set to New Architecture.
- name: Build the Helloworld application for ${{ matrix.flavor }} with Architecture set to ${{ matrix.architecture }}, and using the ${{ matrix.jsengine }} JS engine.
shell: bash
run: |
cd private/helloworld/android
cd packages/helloworld/android
args=()
if [[ ${{ matrix.architecture }} == "OldArch" ]]; then
args+=(--arch old)
fi
if [[ ${{ matrix.jsengine }} == "JSC" ]]; then
args+=(--jsvm jsc)
fi
if [[ ${{ matrix.flavor }} == "Release" ]]; then
args+=(--prod)
fi
@@ -542,13 +555,13 @@ jobs:
- name: Upload artifact
uses: actions/upload-artifact@v4.3.4
with:
name: helloworld-apk-${{ matrix.flavor }}-NewArch-hermes
path: ./private/helloworld/android/app/build/outputs/apk/
name: helloworld-apk-${{ matrix.flavor }}-${{ matrix.architecture }}-${{ matrix.jsengine }}
path: ./packages/helloworld/android/app/build/outputs/apk/
compression-level: 0
test_ios_helloworld_with_ruby_3_2_0:
runs-on: macos-14
needs: [prepare_hermes_workspace, build_hermes_macos, prebuild_apple_dependencies] # prepare_hermes_workspace must be there because we need its reference to retrieve a couple of outputs
runs-on: macos-13
needs: [prepare_hermes_workspace, build_hermes_macos] # prepare_hermes_workspace must be there because we need its reference to retrieve a couple of outputs
env:
PROJECT_NAME: iOSTemplateProject
HERMES_WS_DIR: /tmp/hermes
@@ -559,20 +572,23 @@ jobs:
- uses: ./.github/actions/test-ios-helloworld
with:
ruby-version: 3.2.0
architecture: NewArch
flavor: Debug
hermes-version: ${{ needs.prepare_hermes_workspace.outputs.hermes-version }}
react-native-version: ${{ needs.prepare_hermes_workspace.outputs.react-native-version }}
test_ios_helloworld:
runs-on: macos-14
needs: [prepare_hermes_workspace, build_hermes_macos, prebuild_apple_dependencies] # prepare_hermes_workspace must be there because we need its reference to retrieve a couple of outputs
runs-on: macos-13
needs: [prepare_hermes_workspace, build_hermes_macos] # prepare_hermes_workspace must be there because we need its reference to retrieve a couple of outputs
strategy:
matrix:
flavor: [Debug, Release]
jsengine: [Hermes, JSC]
use_frameworks: [StaticLibraries, DynamicFrameworks]
exclude:
# This config is tested with Ruby 3.2.0. Let's not double test it.
- flavor: Debug
jsengine: Hermes
use_frameworks: StaticLibraries
env:
PROJECT_NAME: iOSTemplateProject
@@ -584,6 +600,7 @@ jobs:
- uses: ./.github/actions/test-ios-helloworld
with:
flavor: ${{ matrix.flavor }}
jsengine: ${{ matrix.jsengine }}
use-frameworks: ${{ matrix.use_frameworks }}
hermes-version: ${{ needs.prepare_hermes_workspace.outputs.hermes-version }}
react-native-version: ${{ needs.prepare_hermes_workspace.outputs.react-native-version }}
@@ -593,7 +610,7 @@ jobs:
strategy:
fail-fast: false
matrix:
node-version: ["24", "22"]
node-version: ["20", "18"]
steps:
- name: Checkout
uses: actions/checkout@v4
@@ -609,47 +626,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Run all the linters
- name: Run all the Linters
uses: ./.github/actions/lint
with:
github-token: ${{ env.GH_TOKEN }}
# This job should help with the E2E flakyness.
# In case E2E tests fails, it launches a new retry-workflow workflow, passing the current run_id as input.
# The retry-workflow reruns only the failed jobs of the current test-all workflow using
# ```
# gh run rerun ${{ inputs.run_id }} --failed
# ```
# From https://stackoverflow.com/a/78314483 it seems like that adding the extra workflow
# rather then calling directly this command should improve stability of this solution.
# This is exactly the same as rerunning failed tests from the GH UI, but automated.
rerun-failed-jobs:
runs-on: ubuntu-latest
needs: [test_e2e_ios_rntester, test_e2e_android_rntester, test_e2e_ios_templateapp, test_e2e_android_templateapp]
if: always()
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Rerun failed jobs in the current workflow
env:
GH_TOKEN: ${{ github.token }}
run: |
SHOULD_RETRY=${{fromJSON(github.run_attempt) < 3}}
if [[ $SHOULD_RETRY == "false" ]]; then
exit 0
fi
RNTESTER_ANDROID_FAILED=${{ needs.test_e2e_android_rntester.result == 'failure' }}
TEMPLATE_ANDROID_FAILED=${{ needs.test_e2e_android_templateapp.result == 'failure' }}
RNTESTER_IOS_FAILED=${{ needs.test_e2e_ios_rntester.result == 'failure' }}
TEMPLATE_IOS_FAILED=${{ needs.test_e2e_ios_templateapp.result == 'failure' }}
echo "RNTESTER_ANDROID_FAILED: $RNTESTER_ANDROID_FAILED"
echo "TEMPLATE_ANDROID_FAILED: $TEMPLATE_ANDROID_FAILED"
echo "RNTESTER_IOS_FAILED: $RNTESTER_IOS_FAILED"
echo "TEMPLATE_IOS_FAILED: $TEMPLATE_IOS_FAILED"
if [[ $RNTESTER_ANDROID_FAILED == "true" || $TEMPLATE_ANDROID_FAILED == "true" || $RNTESTER_IOS_FAILED == "true" || $TEMPLATE_IOS_FAILED == "true" ]]; then
echo "Rerunning failed jobs in the current workflow"
gh workflow run retry-workflow.yml -F run_id=${{ github.run_id }}
fi
@@ -2,25 +2,11 @@ name: Test Libraries on Nightlies
on:
workflow_call:
secrets:
discord_webhook_url:
required: true
# We use the matrix.library entry to specify the dependency we want to use
# The key is used directly as the <pkg> in the `yarn add <pkg>` command.
jobs:
runner-setup:
test-library-on-nightly-android:
name: "[Android] ${{ matrix.library }}"
runs-on: ubuntu-latest
outputs:
runners: '{"ios":"macos-14-large", "android": "ubuntu-latest"}'
steps:
- run: echo no-op
test-library-on-nightly:
name: "[${{ matrix.platform }}] ${{ matrix.library }}"
needs: runner-setup
runs-on: ${{ fromJSON(needs.runner-setup.outputs.runners)[matrix.platform] }}
continue-on-error: true
strategy:
matrix:
@@ -33,68 +19,57 @@ jobs:
"react-native-image-picker",
"react-native-linear-gradient",
"@react-native-masked-view/masked-view",
# "react-native-maps", React Native Maps with the New Arch support has a complex cocoapods setup for iOS. It needs a dedicated workflow.
"react-native-maps",
"@react-native-community/netinfo",
"react-native-reanimated@nightly react-native-worklets@nightly", #reanimated requires worklet to be explicitly installed as a separate package
"react-native-reanimated",
"react-native-svg",
"react-native-video",
"react-native-webview",
"react-native-mmkv",
"react-native-screens",
"react-native-pager-view",
"@react-native-community/slider",
# additional OSS libs used internally
"scandit-react-native-datacapture-barcode scandit-react-native-datacapture-core",
"react-native-contacts",
"react-native-device-info",
"react-native-email-link",
"@dr.pogodin/react-native-fs",
"react-native-permissions",
"react-native-vector-icons",
"react-native-masked-view",
"@react-native-community/image-editor",
"@react-native-community/slider"
]
platform: [ios, android]
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Node.js
uses: ./.github/actions/setup-node
- name: Test ${{ matrix.library }}
id: run-test
- name: Test ${{ inputs.library-name }}
uses: ./.github/actions/test-library-on-nightly
with:
library-npm-package: ${{ matrix.library }}
platform: ${{ matrix.platform}}
- name: Save outcome
id: save-outcome
if: always()
run: |
LIB_FOLDER=$(echo "${{matrix.library}}" | tr ' ' '_' | tr '/' '_')
echo "${{matrix.library}}: ${{steps.run-test.outcome}}" > "/tmp/$LIB_FOLDER-${{ matrix.platform }}-outcome"
echo "lib_folder=$LIB_FOLDER" >> $GITHUB_OUTPUT
- name: Upload Artifact
if: always()
uses: actions/upload-artifact@v4
with:
name: ${{ steps.save-outcome.outputs.lib_folder }}-${{ matrix.platform }}-outcome
path: /tmp/${{ steps.save-outcome.outputs.lib_folder }}-${{ matrix.platform }}-outcome
platform: android
collect-results:
runs-on: ubuntu-latest
needs: [test-library-on-nightly]
test-library-on-nightly-ios:
name: "[iOS] ${{ matrix.library }}"
runs-on: macos-13-large
continue-on-error: true
strategy:
matrix:
library: [
"react-native-async-storage",
"react-native-blob-util",
"@react-native-clipboard/clipboard",
"@react-native-community/datetimepicker",
"react-native-gesture-handler",
"react-native-image-picker",
"react-native-linear-gradient",
"@react-native-masked-view/masked-view",
"react-native-maps",
"@react-native-community/netinfo",
"react-native-reanimated",
"react-native-svg",
"react-native-video",
"react-native-webview",
"react-native-mmkv",
"react-native-screens",
"react-native-pager-view",
"@react-native-community/slider"
]
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Restore outcomes
uses: actions/download-artifact@v4
- name: Test ${{ inputs.library-name }}
uses: ./.github/actions/test-library-on-nightly
with:
pattern: '*-outcome'
path: /tmp
- name: Collect failures
uses: actions/github-script@v6
with:
script: |
const {collectResults} = require('./.github/workflow-scripts/collectNightlyOutcomes.js');
await collectResults('${{secrets.discord_webhook_url}}');
library-npm-package: ${{ matrix.library }}
platform: ios
@@ -0,0 +1,45 @@
name: Trigger E2E Tests on Comment
# This workflow is used to automatically trigger E2E tests when a comment is made
# containing the text "/run-e2e-tests".
on:
issue_comment:
types: [created]
permissions:
contents: read
jobs:
trigger-e2e-tests:
name: Trigger E2E Tests
runs-on: ubuntu-latest
if: github.event.issue.pull_request != '' && contains(github.event.comment.body, '/test-e2e')
steps:
# This is needed because of https://github.com/actions/runner-images/issues/6283
# TL;DR: brew is not in the PATH anymore.
- name: Setup Homebrew
uses: Homebrew/actions/setup-homebrew@master
- name: Install jq
run: brew install jq
- name: Run E2E Tests
run: |
# Github does not provide the branch of a PR when a comment on a PR is made
# So, given the issue number, which is the PR number, we can retrieve the branch with
# a quick API call
echo "Retrieving branch"
BRANCH=$(curl -L \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer $GITHUB_TOKEN" \
-H "X-GitHub-Api-Version: 2022-11-28" \
https://api.github.com/repos/facebook/react-native/pulls/$PR_NUMBER | jq -r '.head.ref')
echo "Trigger Test All workflow for branch $BRANCH"
curl -L \
-X POST \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer $GITHUB_TOKEN" \
-H "X-GitHub-Api-Version: 2022-11-28" \
https://api.github.com/repos/facebook/react-native/actions/workflows/test-all.yml/dispatches \
-d "{\"ref\": \"$BRANCH\", \"inputs\": {\"run-e2e-tests\": \"true\"}}"
env:
GITHUB_TOKEN: ${{ secrets.REACT_NATIVE_BOT_GITHUB_TOKEN }}
PR_NUMBER: ${{ github.event.issue.number }}
@@ -0,0 +1,18 @@
name: Update node modules cache
on:
workflow_dispatch:
push:
branches:
- main
jobs:
update_node_modules_cache:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install yarn dependencies and update cache
uses: ./.github/actions/yarn-install-with-cache
with:
update-cache: "true"
+11 -29
View File
@@ -30,7 +30,6 @@ project.xcworkspace
/packages/rn-tester/android/app/gradle/
/packages/rn-tester/android/app/gradlew
/packages/rn-tester/android/app/gradlew.bat
/packages/react-native/build/
/packages/react-native/ReactAndroid/build/
/packages/react-native/ReactAndroid/.cxx/
/packages/react-native/ReactAndroid/gradle/
@@ -40,8 +39,8 @@ project.xcworkspace
/packages/react-native/ReactAndroid/external-artifacts/artifacts/
/packages/react-native/ReactAndroid/hermes-engine/build/
/packages/react-native/ReactAndroid/hermes-engine/.cxx/
/private/helloworld/android/app/build/
/private/helloworld/android/build/
/packages/helloworld/android/app/build/
/packages/helloworld/android/build/
/packages/react-native-popup-menu-android/android/build/
/packages/react-native-test-library/android/build/
@@ -69,7 +68,6 @@ local.properties
*.iml
/packages/react-native/android/*
!/packages/react-native/android/README.md
.kotlin/
# Node
node_modules
@@ -108,33 +106,23 @@ package-lock.json
# Ruby Gems (Bundler)
/packages/react-native/vendor
/private/helloworld/vendor
/packages/helloworld/vendor
.ruby-version
/**/.ruby-version
vendor/
# iOS / CocoaPods
/private/helloworld/ios/build/
/private/helloworld/ios/Pods/
/private/helloworld/ios/Podfile.lock
/packages/rn-tester/bin/
/packages/rn-tester/cache/
/packages/rn-tester/extensions/
/packages/rn-tester/gems/
/packages/rn-tester/specifications/
/packages/helloworld/ios/build/
/packages/helloworld/ios/Pods/
/packages/helloworld/ios/Podfile.lock
/packages/rn-tester/Gemfile.lock
/packages/**/RCTLegacyInteropComponents.mm
/packages/sourcemap.ios.map
# Ignore RNTester specific Pods, but keep the __offline_mirrors__ here.
/packages/rn-tester/Pods/*
!/packages/rn-tester/Pods/__offline_mirrors_hermes__
!/packages/rn-tester/Pods/__offline_mirrors_jsc__
# Swift Package build folder
/packages/react-native/.build
/packages/react-native/.swiftpm
# @react-native/codegen
/packages/react-native/React/FBReactNativeSpec/
/packages/react-native-codegen/lib
@@ -144,7 +132,7 @@ vendor/
/**/RCTThirdPartyFabricComponentsProvider.*
# @react-native/codegen-typescript-test
/private/react-native-codegen-typescript-test/lib
/packages/react-native-codegen-typescript-test/lib
# Additional SDKs
/packages/react-native/sdks/download
@@ -152,11 +140,6 @@ vendor/
/packages/react-native/sdks/hermesc
/packages/react-native/sdks/hermes-engine/hermes-engine-from-local-source-dir.tar.gz
# iOS prebuilds
/packages/react-native/third-party/
fix_*.patch
*.xcframework
# Visual Studio Code (config dir - if present, this merges user defined
# workspace settings on top of react-native.code-workspace)
/.vscode
@@ -170,9 +153,8 @@ fix_*.patch
# Temporary files created by Metro to check the health of the file watcher
.metro-health-check*
# Jest Integration
/private/react-native-fantom/build/
/private/react-native-fantom/tester/build/
# CircleCI
.circleci/generated_config.yml
# [Experimental] Generated TS type definitions
/packages/**/types_generated/
# Jest Integration
/jest/integration/build/
-43
View File
@@ -1,43 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/
import relativeLinksRule from 'markdownlint-rule-relative-links';
const config = {
config: {
default: false,
'heading-increment': true,
'no-reversed-links': true,
'no-missing-space-atx': true,
'no-duplicate-heading': {
siblings_only: true,
},
'single-title': true,
'no-trailing-punctuation': true,
'no-space-in-emphasis': true,
'no-space-in-code': true,
'no-space-in-links': true,
'fenced-code-language': true,
'first-line-heading': true,
'no-empty-links': true,
'no-alt-text': true,
'link-fragments': true,
'table-column-count': true,
// The rest of default rules are already handled by prettier
// Custom rules
'relative-links': true,
},
globs: ['**/__docs__/*.md'],
ignores: ['**/node_modules', '__docs__/README-template.md'],
customRules: [relativeLinksRule],
};
export default config;
-1
View File
@@ -7,6 +7,5 @@
packages/*/dist
vendor
packages/**/types_generated/
packages/react-native-codegen/e2e/__test_fixtures__/modules/NativeEnumTurboModule.js
-10
View File
@@ -21,16 +21,6 @@
"options": {
"parser": "hermes"
}
},
{
"files": [
"**/__docs__/*.md"
],
"options": {
"parser": "markdown",
"proseWrap": "always",
"requirePragma": false
}
}
]
}
+2 -2
View File
@@ -1174,7 +1174,7 @@ Heads-up: the Facebook internal team is [currently working on a rewrite of some
- Restrict `WebView` to only http(s) URLs: ([634e7e11e3](https://github.com/facebook/react-native/commit/634e7e11e3ad39e0b13bf20cc7722c0cfd3c3e28), [23f8f7aecb](https://github.com/facebook/react-native/commit/23f8f7aecb1f21f4f5e44fb9e4a7456ea97935c9))
- Node 8 is now the minimum required version ([c1e6f27823](https://github.com/facebook/react-native/commit/c1e6f278237e84c8ed26d3d2eb45035f250e2d40))
- Upgrade React to v16.4.1, sync React Renderer to revision ae14317 ([c749d951ad](https://github.com/facebook/react-native/commit/c749d951ada829c6f6fb76f35e68142e61054433))
- Update new project template's Flow config to fix `Cannot resolve module X` issue due to removal of `@providesModule` ([843a433e87](https://github.com/facebook/react-native/commit/843a433e87b0ccaa64ab70d07e22bffbabad8045))
- Update new project template's Flow config to fix `Cannot resolve module X` isse due to removal of `@providesModule` ([843a433e87](https://github.com/facebook/react-native/commit/843a433e87b0ccaa64ab70d07e22bffbabad8045))
- Upgrade Flow to v0.75 ([3bed272a62](https://github.com/facebook/react-native/commit/3bed272a620ac806a6142327013265ea8138641a), [bc2f12c68c](https://github.com/facebook/react-native/commit/bc2f12c68cf8cfdf8c060354e84392fd9a3645d8), [6264b6932a](https://github.com/facebook/react-native/commit/6264b6932a08e1cefd83c4536ff7839d91938730))
- Upgrade Flow definitions ([f8b4850425](https://github.com/facebook/react-native/commit/f8b4850425f115c8a23dead7ec0716b61663aed6))
- Upgrade Prettier to v1.13.6 ([29fb2a8e90](https://github.com/facebook/react-native/commit/29fb2a8e90fa3811f9485d4b89d9dbcfffea93a6), [bc2f12c68c](https://github.com/facebook/react-native/commit/bc2f12c68cf8cfdf8c060354e84392fd9a3645d8))
@@ -1745,7 +1745,7 @@ Below is a list of the remaining, low-level changes that made it into this relea
- Changes to RCTShadowView to increase RCTSurface performance ([f96f9c5fd6](https://github.com/facebook/react-native/commit/f96f9c5fd692000f561e87cba68642ef7daf43e7) by [@shergin](https://github.com/shergin))
- Designated methods to control dirty propagation ([af226ef949](https://github.com/facebook/react-native/commit/af226ef949f3a21ef68a6e6b9fbd4cc06fa05152) by [@shergin](https://github.com/shergin))
- Add missing tvOS header ([49cbca7464](https://github.com/facebook/react-native/commit/49cbca7464e27c34105122459ae29cc3b1247513) by [@grabbou](https://github.com/grabbou))
- On Android, separate logic to initialize JS from starting the app ([4996b9aeb4](https://github.com/facebook/react-native/commit/4996b9aeb4127892b7579b45927dec14833b8b4d) by [@axe-fb](https://github.com/axe-fb))
- On Android, seperate logic to initialize JS from starting the app ([4996b9aeb4](https://github.com/facebook/react-native/commit/4996b9aeb4127892b7579b45927dec14833b8b4d) by [@axe-fb](https://github.com/axe-fb))
- ♻️ JS linting was cleaned up: removed unused libs, strengthened the rules, removed unneeded rules, prevent disabled tests, and more ([2815ada238](https://github.com/facebook/react-native/commit/2815ada23872fc28dc8dd5a9833962cb73f452eb), [183c316f4c](https://github.com/facebook/react-native/commit/183c316f4c869804b88cffe40614c84ac0a472d0), [9c67e749d8](https://github.com/facebook/react-native/commit/9c67e749d8f63cf14ece201ec19eee4676f96a85), [79902f99b8](https://github.com/facebook/react-native/commit/79902f99b81f538042b38a857182c2e5adbfd006), [9a36872f0c](https://github.com/facebook/react-native/commit/9a36872f0c7ba03a92fabf65e4d659d6861ea786), [67a3c42d1a](https://github.com/facebook/react-native/commit/67a3c42d1a29b6fa1375f7445d1c9b4429939bae), [b826596700](https://github.com/facebook/react-native/commit/b82659670041d0e472f68c0a14b3ef5b962db09b), [a1a0a69546](https://github.com/facebook/react-native/commit/a1a0a6954635141ce6c167816b67674aa5c31062), and [11a495cb32](https://github.com/facebook/react-native/commit/11a495cb3235ebbc2ad890e92ec612fd5316bffb) by [@TheSavior](https://github.com/TheSavior))
- 👷 Separate JS lint and flow checks from tests ([5ea5683d01](https://github.com/facebook/react-native/commit/5ea5683d01487b49c814fca6e11a818b9a777239) by [@hramos](https://github.com/hramos))
- 👷 Fix Buck in build config to enable CI ([796122d8f3](https://github.com/facebook/react-native/commit/796122d8f3b825c0bf0c138c662f3477f8bab123), [7c3a61f3b6](https://github.com/facebook/react-native/commit/7c3a61f3b610e219fd798eccd330deb9a2471253), [82b123e744](https://github.com/facebook/react-native/commit/82b123e744b87cc59c96b4e82af9ed03981b4f42) by [@grabbou](https://github.com/grabbou))
-1347
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -28,7 +28,7 @@ Examples of contributing to the release include being a [community releaser](htt
### Current partners:
* **[Coinbase](https://www.coinbase.com/):** Publishes [posts](https://blog.coinbase.com/tagged/react-native) advocating React Native usage. Supports `@react-native-community/datetimepicker` and other community modules to migrate to the new architecture. Supports releases in testing and feedback.
* **[Callstack](https://callstack.com/):** Maintains [React Native Community CLI](https://github.com/react-native-community/cli), develops [RNEF](https://rnef.dev), [Re.Pack](https://re-pack.dev) and [other community libraries](https://github.com/callstack). Hosts [React Universe Conf](https://www.reactuniverseconf.com/) and [React Universe On Air](https://www.callstack.com/podcast).
* **[Callstack](https://callstack.com/):** Maintains the [React Native CLI](https://github.com/react-native-community/react-native-cli) and [other community libraries](https://github.com/callstack), organizes [React Native EU](https://react-native.eu/) and hosts [The React Native Show podcast](https://www.callstack.com/podcast-react-native-show)
* **[Expo](https://expo.dev/):** Builds [Expo Go and SDK](https://github.com/expo/expo), [Snack](https://snack.expo.dev/), and [Expo Application Services](https://expo.dev/eas). Maintains [React Native Directory](https://reactnative.directory/), stewards [React Navigation](https://reactnavigation.org/) along with other partners.
* **[Infinite Red](https://infinite.red/):** Maintains the [ignite cli/boilerplate](https://github.com/infinitered/ignite), organizes [Chain React Conf](https://cr.infinite.red/), hosts the [React Native Radio podcast](https://reactnativeradio.com), publishes the [React Native Newsletter](https://reactnativenewsletter.com)
* **[Meta](https://opensource.fb.com/):** Oversees the React Native product and maintains the [React Native core repo](https://reactnative.dev/)
+1 -7
View File
@@ -6,10 +6,4 @@ ruby ">= 2.6.10"
gem 'cocoapods', '~> 1.13', '!= 1.15.0', '!= 1.15.1'
gem 'activesupport', '>= 6.1.7.5', '< 7.1.0'
gem 'xcodeproj', '< 1.26.0'
gem 'concurrent-ruby', '<= 1.3.4'
# Ruby 3.4.0 has removed some libraries from the standard library.
gem 'bigdecimal'
gem 'logger'
gem 'benchmark'
gem 'mutex_m'
gem 'concurrent-ruby', '< 1.3.4'
-10
View File
@@ -14,8 +14,6 @@ GEM
httpclient (~> 2.8, >= 2.8.3)
json (>= 1.5.1)
atomos (0.1.3)
benchmark (0.4.0)
bigdecimal (3.1.9)
claide (1.1.0)
cocoapods (1.14.2)
addressable (~> 2.8)
@@ -67,10 +65,8 @@ GEM
i18n (1.14.1)
concurrent-ruby (~> 1.0)
json (2.6.3)
logger (1.6.5)
minitest (5.20.0)
molinillo (0.8.0)
mutex_m (0.3.0)
nanaimo (0.3.0)
nap (1.1.0)
netrc (0.11.0)
@@ -94,13 +90,7 @@ PLATFORMS
DEPENDENCIES
activesupport (>= 6.1.7.5, < 7.1.0)
benchmark
bigdecimal
cocoapods (~> 1.13, != 1.15.1, != 1.15.0)
concurrent-ruby (<= 1.3.4)
logger
mutex_m
xcodeproj (< 1.26.0)
RUBY VERSION
ruby 3.2.0p0
+393
View File
@@ -0,0 +1,393 @@
Attribution 4.0 International
=======================================================================
Creative Commons Corporation ("Creative Commons") is not a law firm and
does not provide legal services or legal advice. Distribution of
Creative Commons public licenses does not create a lawyer-client or
other relationship. Creative Commons makes its licenses and related
information available on an "as-is" basis. Creative Commons gives no
warranties regarding its licenses, any material licensed under their
terms and conditions, or any related information. Creative Commons
disclaims all liability for damages resulting from their use to the
fullest extent possible.
Using Creative Commons Public Licenses
Creative Commons public licenses provide a standard set of terms and
conditions that creators and other rights holders may use to share
original works of authorship and other material subject to copyright
and certain other rights specified in the public license below. The
following considerations are for informational purposes only, are not
exhaustive, and do not form part of our licenses.
Considerations for licensors: Our public licenses are
intended for use by those authorized to give the public
permission to use material in ways otherwise restricted by
copyright and certain other rights. Our licenses are
irrevocable. Licensors should read and understand the terms
and conditions of the license they choose before applying it.
Licensors should also secure all rights necessary before
applying our licenses so that the public can reuse the
material as expected. Licensors should clearly mark any
material not subject to the license. This includes other CC-
licensed material, or material used under an exception or
limitation to copyright. More considerations for licensors:
wiki.creativecommons.org/Considerations_for_licensors
Considerations for the public: By using one of our public
licenses, a licensor grants the public permission to use the
licensed material under specified terms and conditions. If
the licensor's permission is not necessary for any reason--for
example, because of any applicable exception or limitation to
copyright--then that use is not regulated by the license. Our
licenses grant only permissions under copyright and certain
other rights that a licensor has authority to grant. Use of
the licensed material may still be restricted for other
reasons, including because others have copyright or other
rights in the material. A licensor may make special requests,
such as asking that all changes be marked or described.
Although not required by our licenses, you are encouraged to
respect those requests where reasonable. More_considerations
for the public:
wiki.creativecommons.org/Considerations_for_licensees
=======================================================================
Creative Commons Attribution 4.0 International Public License
By exercising the Licensed Rights (defined below), You accept and agree
to be bound by the terms and conditions of this Creative Commons
Attribution 4.0 International Public License ("Public License"). To the
extent this Public License may be interpreted as a contract, You are
granted the Licensed Rights in consideration of Your acceptance of
these terms and conditions, and the Licensor grants You such rights in
consideration of benefits the Licensor receives from making the
Licensed Material available under these terms and conditions.
Section 1 -- Definitions.
a. Adapted Material means material subject to Copyright and Similar
Rights that is derived from or based upon the Licensed Material
and in which the Licensed Material is translated, altered,
arranged, transformed, or otherwise modified in a manner requiring
permission under the Copyright and Similar Rights held by the
Licensor. For purposes of this Public License, where the Licensed
Material is a musical work, performance, or sound recording,
Adapted Material is always produced where the Licensed Material is
synched in timed relation with a moving image.
b. Adapter's License means the license You apply to Your Copyright
and Similar Rights in Your contributions to Adapted Material in
accordance with the terms and conditions of this Public License.
c. Copyright and Similar Rights means copyright and/or similar rights
closely related to copyright including, without limitation,
performance, broadcast, sound recording, and Sui Generis Database
Rights, without regard to how the rights are labeled or
categorized. For purposes of this Public License, the rights
specified in Section 2(b)(1)-(2) are not Copyright and Similar
Rights.
d. Effective Technological Measures means those measures that, in the
absence of proper authority, may not be circumvented under laws
fulfilling obligations under Article 11 of the WIPO Copyright
Treaty adopted on December 20, 1996, and/or similar international
agreements.
e. Exceptions and Limitations means fair use, fair dealing, and/or
any other exception or limitation to Copyright and Similar Rights
that applies to Your use of the Licensed Material.
f. Licensed Material means the artistic or literary work, database,
or other material to which the Licensor applied this Public
License.
g. Licensed Rights means the rights granted to You subject to the
terms and conditions of this Public License, which are limited to
all Copyright and Similar Rights that apply to Your use of the
Licensed Material and that the Licensor has authority to license.
h. Licensor means the individual(s) or entity(ies) granting rights
under this Public License.
i. Share means to provide material to the public by any means or
process that requires permission under the Licensed Rights, such
as reproduction, public display, public performance, distribution,
dissemination, communication, or importation, and to make material
available to the public including in ways that members of the
public may access the material from a place and at a time
individually chosen by them.
j. Sui Generis Database Rights means rights other than copyright
resulting from Directive 96/9/EC of the European Parliament and of
the Council of 11 March 1996 on the legal protection of databases,
as amended and/or succeeded, as well as other essentially
equivalent rights anywhere in the world.
k. You means the individual or entity exercising the Licensed Rights
under this Public License. Your has a corresponding meaning.
Section 2 -- Scope.
a. License grant.
1. Subject to the terms and conditions of this Public License,
the Licensor hereby grants You a worldwide, royalty-free,
non-sublicensable, non-exclusive, irrevocable license to
exercise the Licensed Rights in the Licensed Material to:
a. reproduce and Share the Licensed Material, in whole or
in part; and
b. produce, reproduce, and Share Adapted Material.
2. Exceptions and Limitations. For the avoidance of doubt, where
Exceptions and Limitations apply to Your use, this Public
License does not apply, and You do not need to comply with
its terms and conditions.
3. Term. The term of this Public License is specified in Section
6(a).
4. Media and formats; technical modifications allowed. The
Licensor authorizes You to exercise the Licensed Rights in
all media and formats whether now known or hereafter created,
and to make technical modifications necessary to do so. The
Licensor waives and/or agrees not to assert any right or
authority to forbid You from making technical modifications
necessary to exercise the Licensed Rights, including
technical modifications necessary to circumvent Effective
Technological Measures. For purposes of this Public License,
simply making modifications authorized by this Section 2(a)
(4) never produces Adapted Material.
5. Downstream recipients.
a. Offer from the Licensor -- Licensed Material. Every
recipient of the Licensed Material automatically
receives an offer from the Licensor to exercise the
Licensed Rights under the terms and conditions of this
Public License.
b. No downstream restrictions. You may not offer or impose
any additional or different terms or conditions on, or
apply any Effective Technological Measures to, the
Licensed Material if doing so restricts exercise of the
Licensed Rights by any recipient of the Licensed
Material.
6. No endorsement. Nothing in this Public License constitutes or
may be construed as permission to assert or imply that You
are, or that Your use of the Licensed Material is, connected
with, or sponsored, endorsed, or granted official status by,
the Licensor or others designated to receive attribution as
provided in Section 3(a)(1)(A)(i).
b. Other rights.
1. Moral rights, such as the right of integrity, are not
licensed under this Public License, nor are publicity,
privacy, and/or other similar personality rights; however, to
the extent possible, the Licensor waives and/or agrees not to
assert any such rights held by the Licensor to the limited
extent necessary to allow You to exercise the Licensed
Rights, but not otherwise.
2. Patent and trademark rights are not licensed under this
Public License.
3. To the extent possible, the Licensor waives any right to
collect royalties from You for the exercise of the Licensed
Rights, whether directly or through a collecting society
under any voluntary or waivable statutory or compulsory
licensing scheme. In all other cases the Licensor expressly
reserves any right to collect such royalties.
Section 3 -- License Conditions.
Your exercise of the Licensed Rights is expressly made subject to the
following conditions.
a. Attribution.
1. If You Share the Licensed Material (including in modified
form), You must:
a. retain the following if it is supplied by the Licensor
with the Licensed Material:
i. identification of the creator(s) of the Licensed
Material and any others designated to receive
attribution, in any reasonable manner requested by
the Licensor (including by pseudonym if
designated);
ii. a copyright notice;
iii. a notice that refers to this Public License;
iv. a notice that refers to the disclaimer of
warranties;
v. a URI or hyperlink to the Licensed Material to the
extent reasonably practicable;
b. indicate if You modified the Licensed Material and
retain an indication of any previous modifications; and
c. indicate the Licensed Material is licensed under this
Public License, and include the text of, or the URI or
hyperlink to, this Public License.
2. You may satisfy the conditions in Section 3(a)(1) in any
reasonable manner based on the medium, means, and context in
which You Share the Licensed Material. For example, it may be
reasonable to satisfy the conditions by providing a URI or
hyperlink to a resource that includes the required
information.
3. If requested by the Licensor, You must remove any of the
information required by Section 3(a)(1)(A) to the extent
reasonably practicable.
4. If You Share Adapted Material You produce, the Adapter's
License You apply must not prevent recipients of the Adapted
Material from complying with this Public License.
Section 4 -- Sui Generis Database Rights.
Where the Licensed Rights include Sui Generis Database Rights that
apply to Your use of the Licensed Material:
a. for the avoidance of doubt, Section 2(a)(1) grants You the right
to extract, reuse, reproduce, and Share all or a substantial
portion of the contents of the database;
b. if You include all or a substantial portion of the database
contents in a database in which You have Sui Generis Database
Rights, then the database in which You have Sui Generis Database
Rights (but not its individual contents) is Adapted Material; and
c. You must comply with the conditions in Section 3(a) if You Share
all or a substantial portion of the contents of the database.
For the avoidance of doubt, this Section 4 supplements and does not
replace Your obligations under this Public License where the Licensed
Rights include other Copyright and Similar Rights.
Section 5 -- Disclaimer of Warranties and Limitation of Liability.
a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.
b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.
c. The disclaimer of warranties and limitation of liability provided
above shall be interpreted in a manner that, to the extent
possible, most closely approximates an absolute disclaimer and
waiver of all liability.
Section 6 -- Term and Termination.
a. This Public License applies for the term of the Copyright and
Similar Rights licensed here. However, if You fail to comply with
this Public License, then Your rights under this Public License
terminate automatically.
b. Where Your right to use the Licensed Material has terminated under
Section 6(a), it reinstates:
1. automatically as of the date the violation is cured, provided
it is cured within 30 days of Your discovery of the
violation; or
2. upon express reinstatement by the Licensor.
For the avoidance of doubt, this Section 6(b) does not affect any
right the Licensor may have to seek remedies for Your violations
of this Public License.
c. For the avoidance of doubt, the Licensor may also offer the
Licensed Material under separate terms or conditions or stop
distributing the Licensed Material at any time; however, doing so
will not terminate this Public License.
d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
License.
Section 7 -- Other Terms and Conditions.
a. The Licensor shall not be bound by any additional or different
terms or conditions communicated by You unless expressly agreed.
b. Any arrangements, understandings, or agreements regarding the
Licensed Material not stated herein are separate from and
independent of the terms and conditions of this Public License.
Section 8 -- Interpretation.
a. For the avoidance of doubt, this Public License does not, and
shall not be interpreted to, reduce, limit, restrict, or impose
conditions on any use of the Licensed Material that could lawfully
be made without permission under this Public License.
b. To the extent possible, if any provision of this Public License is
deemed unenforceable, it shall be automatically reformed to the
minimum extent necessary to make it enforceable. If the provision
cannot be reformed, it shall be severed from this Public License
without affecting the enforceability of the remaining terms and
conditions.
c. No term or condition of this Public License will be waived and no
failure to comply consented to unless expressly agreed to by the
Licensor.
d. Nothing in this Public License constitutes or may be interpreted
as a limitation upon, or waiver of, any privileges and immunities
that apply to the Licensor or You, including from the legal
processes of any jurisdiction or authority.
=======================================================================
Creative Commons is not a party to its public licenses.
Notwithstanding, Creative Commons may elect to apply one of its public
licenses to material it publishes and in those instances will be
considered the "Licensor." Except for the limited purpose of indicating
that material is shared under a Creative Commons public license or as
otherwise permitted by the Creative Commons policies published at
creativecommons.org/policies, Creative Commons does not authorize the
use of the trademark "Creative Commons" or any other trademark or logo
of Creative Commons without its prior written consent including,
without limitation, in connection with any unauthorized modifications
to any of its public licenses or any other arrangements,
understandings, or agreements concerning use of licensed material. For
the avoidance of doubt, this paragraph does not form part of the public
licenses.
Creative Commons may be contacted at creativecommons.org.
+7 -1
View File
@@ -13,6 +13,9 @@
<a href="https://github.com/facebook/react-native/blob/HEAD/LICENSE">
<img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="React Native is released under the MIT license." />
</a>
<a href="https://circleci.com/gh/facebook/react-native">
<img src="https://circleci.com/gh/facebook/react-native.svg?style=shield" alt="Current CircleCI build status." />
</a>
<a href="https://www.npmjs.org/package/react-native">
<img src="https://img.shields.io/npm/v/react-native?color=brightgreen&label=npm%20package" alt="Current npm package version." />
</a>
@@ -64,7 +67,7 @@ React Native is developed and supported by many companies and individual core co
## 📋 Requirements
React Native apps may target iOS 15.1 and Android 7.0 (API 24) or newer. You may use Windows, macOS, or Linux as your development operating system, though building and running iOS apps is limited to macOS. Tools like [Expo](https://expo.dev) can be used to work around this.
React Native apps may target iOS 13.4 and Android 6.0 (API 23) or newer. You may use Windows, macOS, or Linux as your development operating system, though building and running iOS apps is limited to macOS. Tools like [Expo](https://expo.dev) can be used to work around this.
## 🎉 Building your first React Native app
@@ -138,4 +141,7 @@ Larger discussions and proposals are discussed in [**@react-native-community/dis
React Native is MIT licensed, as found in the [LICENSE][l] file.
React Native documentation is Creative Commons licensed, as found in the [LICENSE-docs][ld] file.
[l]: https://github.com/facebook/react-native/blob/main/LICENSE
[ld]: https://github.com/facebook/react-native/blob/main/LICENSE-docs
-94
View File
@@ -1,94 +0,0 @@
# React Native Technical Documentation Guidelines
_This is a document about documentation (hence the file name)._
## Motivation
The goals of this documentation are:
1. To make it easier for people to understand and contribute to the React Native
architecture.
2. To ensure the architecture is easy to maintain, with clearly scoped
subsystems that are easy to reason about and change.
## Strategy
Our documentation supports the following use cases:
1. (Exploration based) I want to understand how React Native works and learn
about its different parts. I want to explore.
2. (Goal based) I want to solve a problem and change something specific, so I
want to understand what I should change and how the system I need to change
works. I need to understand how other systems depend on this.
To support the first case, we provide a single entrypoint for the whole
documentation, which will be the first step in a tree of docs with links to
parents and children:
- `<root>/__docs__/README.md` (with links to subsystems 1, 2, etc.)
- `Subsystem 1/__docs__/README.md` (with links to root and subsystems 1.1,
1.2, etc.)
- `Subsystem 1.1/__docs__/README.md` (with links to subsystem 1 and
subsystems 1.1.1, 1.1.2, etc.)
- `Subsystem 1.2/__docs__/README.md`
- `Subsystem 2/__docs__/README.md`
This structure will make it possible for the user to navigate across the
documentation organically, just following links within the documents themselves.
To support the second use case, focusing on a specific subsystem, we will
describe what are the relationships between that subsystem and others, to make
sure that changes to its API are understood, and that usages of other subsystems
are considered.
The use of the `__docs__` directory (inspired by Python) has 2 goals:
1. Make the documentation easy to find in the directory, by generally appearing
at the top of the directory (similar to `__tests__`).
2. Grouping the documentation itself and its assets (images, diagrams, etc.).
## Guidelines
### Content
Use [this template](./README-template.md) to write the documentation for a
subsystem, adding the appropriate subsections depending on what that
documentation requires. Only diverge from this structure if it is strictly
necessary (removing unnecessary or empty sections is fine).
Include supporting images and diagrams in the documentation. Those assets should
be placed in the same `__docs__` directory as the `README.md` file. Use relative
paths to link to the assets in those directories.
If you include Excalidraw diagrams, make sure to export an SVG image from the
website using the "Embedded scene" option, so the original diagram is included
in the file and can be re-uploaded to Excalidraw for future modifications. Use
the extension `.excalidraw.svg` to signal this.
### Granularity
The level of granularity in the definition of the subsystems should be enough to
correctly describe how React Native works, but not so detailed that any changes
in the code require changes in the documentation.
Examples:
- Requires updating the docs:
- Adding a new major feature or API.
- Adding a new relevant dependency. Adding a dependency to helper functions
does not count as relevant.
- Does NOT require updating the docs:
- Internal implementation details that do not change how the system works or
interacts with others.
- Making a minor API or feature change.
- Internal refactors, even if they create new modules or introduce
dependencies to external helpers.
### Location
When a specific subsystem exists in multiple directories (e.g.:
platform-specific ones, C++, JavaScript, etc.):
1. Choose one of them to place the canonical documentation (in order of
preference, JavaScript -> C++ -> platform).
2. Create specific files in the rest linking to the canonical one.
-36
View File
@@ -1,36 +0,0 @@
# _Subsystem name_
[🏠 Home](path/to/main/__docs__/README.md)
_Description of the subsystem with the necessary context._
## 🚀 Usage
_Explanation of how the subsystem is used._
## 📐 Design
_Explain how the subsystem is designed, relevant implementation details, etc.
Ideally include an Excalidraw diagram._
## 🔗 Relationship with other systems
### Part of
- _A single bullet for the parent subsystem. Link to the documentation of that
subsystem if it exists._
### Part of this
- _One bullet point for each subsystem that is part of this one. Link to the
documentation of those subsystems if it exists._
### Used by this
- _One bullet point for each subsystem used by this one, explaining why it uses
it and how. Link to the documentation of those subsystems if it exists._
### Uses this
- _One bullet point for each subsystem using this one, explaining why it uses it
and how. Link to the documentation of those subsystems if it exists._
-109
View File
@@ -1,109 +0,0 @@
# React Native Technical Documentation
The React Native technical documentation describes how React Native works
internally, the subsystems it is composed of, how they work and how they
interact with each other.
The intended audience is people who want to learn about the internals of React
Native and contribute to it. **End users of React Native are meant to use the
[public website](https://reactnative.dev) instead** (its code can be found
[here](https://github.com/facebook/react-native-website)).
For details on how we approach technical documentation in this repository, see
[GUIDELINES.md](./GUIDELINES.md).
## 🚀 Usage
This repository is not meant to be consumed directly by end users. Instead, it
creates several packages that are published to the NPM registry for direct
consumption by end users and frameworks.
This repository uses a monorepo approach, and public packages can be found in
the [`packages`](../packages/) directory (the ones that do not contain
`"private": true` in their `package.json` file).
The most important package is the
[`react-native`](https://www.npmjs.com/package/react-native) package, located in
[`packages/react-native`](../packages/react-native), which contains the public
JavaScript API.
This repository provides the Android and iOS versions of React Native. Versions
for other platforms are maintained in their own repositories.
## 📐 Design
TODO: Explain the different components of React Native at a high level.
## 🔗 Relationship with other systems
### Part of this
- Runtime
- Cross-platform
- [Feature Flags](../packages/react-native/src/private/featureflags/__docs__/README.md)
- Host / Instance / Bridgeless
- UI / Fabric
- Events
- Shadow Tree Lifecycle
- [Runtime Shadow Node Reference Update](../packages/react-native/ReactCommon/react/renderer/core/__docs__/RSNRU.md)
- [passChildrenWhenCloningPersistedNodes](../packages/react-native/ReactCommon/react/renderer/core/__docs__/passChildrenWhenCloning.md)
- Layout
- Mounting
- Native Modules / TurboModules
- JS Runtime
- [Event Loop](../packages/react-native/ReactCommon/react/renderer/runtimescheduler/__docs__/README.md)
- Globals and environment setup
- Error handling
- Developer Tools
- React DevTools
- LogBox
- Misc
- Web APIs
- DOM Traversal & Layout APIs
- [IntersectionObserver](../packages/react-native/src/private/webapis/intersectionobserver/__docs__/README.md)
- [MutationObserver](../packages/react-native/src/private/webapis/mutationobserver/__docs__/README.md)
- Performance & PerformanceObserver
- Timers
- Platform-specific
- Host Platform Interface
- Android
- UI
- [Events](../packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/events/__docs__/README.md)
- Mounting
- iOS
- UI
- Events
- Mounting
- Build system
- Android
- iOS
- C++
- JavaScript
- Metro
- Testing
- Android
- iOS
- C++
- JavaScript
- Flow
- TypeScript
- Jest
- ESLint
- Integration / E2E
- [Fantom](../private/react-native-fantom/__docs__/README.md)
- Tooling
- React Native DevTools
### Used by this
This repository has many different types of dependencies: build systems,
external packages to be used during development, external packages used at
runtime, etc.
### Uses this
The main use cases for this repository are:
1. Developing React Native itself.
2. Testing and releasing React Native.
3. Synchronizing forks like `react-native-windows` and `react-native-macos`.
+1 -2
View File
@@ -12,7 +12,6 @@ plugins {
alias(libs.plugins.download) apply false
alias(libs.plugins.kotlin.android) apply false
alias(libs.plugins.binary.compatibility.validator) apply true
alias(libs.plugins.android.test) apply false
}
val reactAndroidProperties = java.util.Properties()
@@ -95,7 +94,7 @@ tasks.register("build") {
tasks.register("publishAllToMavenTempLocal") {
description = "Publish all the artifacts to be available inside a Maven Local repository on /tmp."
dependsOn(":packages:react-native:ReactAndroid:publishAllPublicationsToMavenTempLocalRepository")
// We don't publish the external-artifacts to Maven Local as ci is using it via workspace.
// We don't publish the external-artifacts to Maven Local as CircleCI is using it via workspace.
dependsOn(
":packages:react-native:ReactAndroid:hermes-engine:publishAllPublicationsToMavenTempLocalRepository")
}
-4279
View File
File diff suppressed because it is too large Load Diff
@@ -6,6 +6,7 @@
*
* @flow strict
* @format
* @oncall react_native
*/
declare module '@react-native-community/cli-server-api' {
@@ -41,4 +42,6 @@ declare module '@react-native-community/cli-server-api' {
},
...
};
declare export const indexPageMiddleware: NextHandleFunction;
}
@@ -0,0 +1,29 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @format
* @oncall react_native
*/
declare module '@react-native-community/cli-tools' {
declare export class CLIError extends Error {
constructor(msg: string, originalError?: Error | mixed | string): this;
}
declare export const logger: $ReadOnly<{
debug: (...message: Array<string>) => void,
error: (...message: Array<string>) => void,
log: (...message: Array<string>) => void,
info: (...message: Array<string>) => void,
warn: (...message: Array<string>) => void,
...
}>;
declare export const version: $ReadOnly<{
logIfUpdateAvailable: (projectRoot: string) => Promise<void>,
}>;
}
@@ -6,18 +6,19 @@
*
* @flow
* @format
* @oncall react_native
*/
declare module '@react-native-community/cli-types' {
declare type CommandFunction<Args = Object> = (
declare export type CommandFunction<Args = Object> = (
argv: Array<string>,
ctx: Config,
args: Args,
) => Promise<void> | void;
declare type OptionValue = string | boolean | number;
declare export type OptionValue = string | boolean | number;
declare type CommandOption<T = (ctx: Config) => OptionValue> = {
declare export type CommandOption<T = (ctx: Config) => OptionValue> = {
name: string,
description?: string,
parse?: (val: string) => any,
@@ -8,6 +8,6 @@
* @format
*/
declare module '@tsconfig/node22/tsconfig.json' {
declare module '@tsconfig/node18/tsconfig.json' {
declare module.exports: any;
}
+1
View File
@@ -6,6 +6,7 @@
*
* @flow strict-local
* @format
* @oncall react_native
*/
declare module 'actual-request-url' {
+1 -6
View File
@@ -6,6 +6,7 @@
*
* @flow
* @format
* @oncall react_native
*/
// The sections between BEGIN GENERATED and END GENERATED are generated
@@ -1015,7 +1016,6 @@ declare module '@babel/traverse' {
isTSDeclareFunction(opts?: Opts): boolean;
isTSDeclareMethod(opts?: Opts): boolean;
isTSEntityName(opts?: Opts): boolean;
isTSEnumBody(opts?: Opts): boolean;
isTSEnumDeclaration(opts?: Opts): boolean;
isTSEnumMember(opts?: Opts): boolean;
isTSExportAssignment(opts?: Opts): boolean;
@@ -1053,7 +1053,6 @@ declare module '@babel/traverse' {
isTSSatisfiesExpression(opts?: Opts): boolean;
isTSStringKeyword(opts?: Opts): boolean;
isTSSymbolKeyword(opts?: Opts): boolean;
isTSTemplateLiteralType(opts?: Opts): boolean;
isTSThisType(opts?: Opts): boolean;
isTSTupleType(opts?: Opts): boolean;
isTSType(opts?: Opts): boolean;
@@ -1334,7 +1333,6 @@ declare module '@babel/traverse' {
assertTSDeclareFunction(opts?: Opts): void;
assertTSDeclareMethod(opts?: Opts): void;
assertTSEntityName(opts?: Opts): void;
assertTSEnumBody(opts?: Opts): void;
assertTSEnumDeclaration(opts?: Opts): void;
assertTSEnumMember(opts?: Opts): void;
assertTSExportAssignment(opts?: Opts): void;
@@ -1372,7 +1370,6 @@ declare module '@babel/traverse' {
assertTSSatisfiesExpression(opts?: Opts): void;
assertTSStringKeyword(opts?: Opts): void;
assertTSSymbolKeyword(opts?: Opts): void;
assertTSTemplateLiteralType(opts?: Opts): void;
assertTSThisType(opts?: Opts): void;
assertTSTupleType(opts?: Opts): void;
assertTSType(opts?: Opts): void;
@@ -1742,7 +1739,6 @@ declare module '@babel/traverse' {
TSDeclareFunction?: VisitNode<BabelNodeTSDeclareFunction, TState>,
TSDeclareMethod?: VisitNode<BabelNodeTSDeclareMethod, TState>,
TSEntityName?: VisitNode<BabelNodeTSEntityName, TState>,
TSEnumBody?: VisitNode<BabelNodeTSEnumBody, TState>,
TSEnumDeclaration?: VisitNode<BabelNodeTSEnumDeclaration, TState>,
TSEnumMember?: VisitNode<BabelNodeTSEnumMember, TState>,
TSExportAssignment?: VisitNode<BabelNodeTSExportAssignment, TState>,
@@ -1795,7 +1791,6 @@ declare module '@babel/traverse' {
TSSatisfiesExpression?: VisitNode<BabelNodeTSSatisfiesExpression, TState>,
TSStringKeyword?: VisitNode<BabelNodeTSStringKeyword, TState>,
TSSymbolKeyword?: VisitNode<BabelNodeTSSymbolKeyword, TState>,
TSTemplateLiteralType?: VisitNode<BabelNodeTSTemplateLiteralType, TState>,
TSThisType?: VisitNode<BabelNodeTSThisType, TState>,
TSTupleType?: VisitNode<BabelNodeTSTupleType, TState>,
TSType?: VisitNode<BabelNodeTSType, TState>,
+17 -57
View File
File diff suppressed because one or more lines are too long
+104
View File
@@ -0,0 +1,104 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @format
* @oncall react_native
*/
declare module 'chalk' {
declare type TemplateStringsArray = $ReadOnlyArray<string>;
declare type Level = $Values<{
None: 0,
Basic: 1,
Ansi256: 2,
TrueColor: 3,
...
}>;
declare type ChalkOptions = {
enabled?: boolean,
level?: Level,
};
declare type ColorSupport = {
level: Level,
hasBasic: boolean,
has256: boolean,
has16m: boolean,
};
declare type Chalk = {
(...text: string[]): string,
(text: TemplateStringsArray, ...placeholders: string[]): string,
Instance(options?: ChalkOptions): Chalk,
level: Level,
rgb(r: number, g: number, b: number): Chalk,
hsl(h: number, s: number, l: number): Chalk,
hsv(h: number, s: number, v: number): Chalk,
hwb(h: number, w: number, b: number): Chalk,
bgHex(color: string): Chalk,
bgKeyword(color: string): Chalk,
bgRgb(r: number, g: number, b: number): Chalk,
bgHsl(h: number, s: number, l: number): Chalk,
bgHsv(h: number, s: number, v: number): Chalk,
bgHwb(h: number, w: number, b: number): Chalk,
hex(color: string): Chalk,
keyword(color: string): Chalk,
+reset: Chalk,
+bold: Chalk,
+dim: Chalk,
+italic: Chalk,
+underline: Chalk,
+inverse: Chalk,
+hidden: Chalk,
+strikethrough: Chalk,
+visible: Chalk,
+black: Chalk,
+red: Chalk,
+green: Chalk,
+yellow: Chalk,
+blue: Chalk,
+magenta: Chalk,
+cyan: Chalk,
+white: Chalk,
+gray: Chalk,
+grey: Chalk,
+blackBright: Chalk,
+redBright: Chalk,
+greenBright: Chalk,
+yellowBright: Chalk,
+blueBright: Chalk,
+magentaBright: Chalk,
+cyanBright: Chalk,
+whiteBright: Chalk,
+bgBlack: Chalk,
+bgRed: Chalk,
+bgGreen: Chalk,
+bgYellow: Chalk,
+bgBlue: Chalk,
+bgMagenta: Chalk,
+bgCyan: Chalk,
+bgWhite: Chalk,
+bgBlackBright: Chalk,
+bgRedBright: Chalk,
+bgGreenBright: Chalk,
+bgYellowBright: Chalk,
+bgBlueBright: Chalk,
+bgMagentaBright: Chalk,
+bgCyanBright: Chalk,
+bgWhiteBrigh: Chalk,
supportsColor: ColorSupport,
};
declare module.exports: Chalk;
}
+1
View File
@@ -6,6 +6,7 @@
*
* @flow strict
* @format
* @oncall react_native
*/
declare module 'chrome-launcher' {
+1
View File
@@ -6,6 +6,7 @@
*
* @flow strict
* @format
* @oncall react_native
*/
declare module 'chromium-edge-launcher' {
+2 -1
View File
@@ -6,6 +6,7 @@
*
* @flow strict
* @format
* @oncall react_native
* @generated
*/
@@ -316,7 +317,7 @@ declare module 'commander' {
*/
formatHelp(cmd: Command, helper: Help): string;
}
export type HelpConfiguration = Partial<Help>;
export type HelpConfiguration = $Rest<Help, {...}>;
export interface ParseOptions {
from: 'node' | 'electron' | 'user';
}
+1
View File
@@ -6,6 +6,7 @@
*
* @flow strict
* @format
* @oncall react_native
*/
declare module 'connect' {

Some files were not shown because too many files have changed in this diff Show More