已合并
chore: add `patch:rn` script and update react-native patching instructions #1458
Arek Kasprzyk创建于 2025年8月22日
chore: add `patch:rn` script and update react-native patching instructions #1458
已合并
共 4 个文件变更+185-125
| @@ -1,56 +1,33 @@ | |||
| 1 | # Modifying and Upgrading React Native | 1 | # Modifying and Upgrading React Native |
| 2 | 2 | ||
| 3 | -## Definitions | ||
| 4 | - | ||
| 5 | -- RNC (React Native Core) — Unofficial name for the (ideally) platform-agnostic, reusable part of React Native. | ||
| 6 | - | ||
| 7 | ## Target Audience | 3 | ## Target Audience |
| 8 | 4 | ||
| 9 | -- RNOH maintainers who need to modify React Native to accomplish their tasks (RNOH contributors can't introduce changes to RNC) | ||
| 10 | - RNOH maintainers responsible for upgrading the React Native used by RNOH | 5 | - RNOH maintainers responsible for upgrading the React Native used by RNOH |
| 11 | - RN maintainers who want to help reduce friction between RN and RNOH | 6 | - RN maintainers who want to help reduce friction between RN and RNOH |
| 12 | 7 | ||
| 13 | ## Modifying React Native and Handling OpenHarmony-specific code | 8 | ## Modifying React Native and Handling OpenHarmony-specific code |
| 14 | 9 | ||
| 15 | -RNOH maintainers should avoid introducing changes to React Native. However, sometimes this isn't feasible or possible. In those situations, an RNOH maintainer can modify React Native by contributing to the unofficial, private RNC repository. | 10 | +RNOH maintainers should avoid introducing changes to React Native. However, sometimes this isn't feasible or possible. In those situations, an RNOH maintainer can use `pnpm patch:rn` command to update `react-native.patch`. |
| 16 | -The contribution should aim to modify RN in a platform-agnostic manner, because those changes should be submitted later to the React Native repo. At the same time, changes should be as small as possible to avoid generating conflicts when upgrading React Native. These changes should help create an interface between RN and Out-of-Tree RN platforms. OpenHarmony-specific changes should be kept in `<RNOH>/packages/react-native-harmony`. | 11 | +The modification should aim to modify RN in a platform-agnostic manner, because those changes should be submitted later to the React Native repo. At the same time, changes should be as small as possible to avoid generating conflicts when upgrading React Native. These changes create an interface between RN and Out-of-Tree RN platforms. OpenHarmony-specific changes should be kept in `<RNOH>/packages/react-native-harmony`. |
| 17 | -Once the contribution is merged, the RNOH maintainer should generate a new `react-native.patch` by running the following commands in the RNC repo: | ||
| 18 | 12 | ||
| 19 | -``` | 13 | +For debugging purposes, modify files directly in `<RNOH>/packages/react-native-harmony`, then use `pnpm patch:rn` and move the changes to `<RNOH>/packages/react-native`. |
| 20 | -yarn install | ||
| 21 | -yarn generate-patch | ||
| 22 | -``` | ||
| 23 | 14 | ||
| 24 | -The generated patch file should replace `<RNOH>/packages/react-native.patch`. RNOH and RN maintainers should work towards eliminating that patch. | 15 | +RNOH and RN maintainers should work towards eliminating that patch. |
| 25 | 16 | ||
| 26 | -### Which RNC branch should be used? | ||
| 27 | - | ||
| 28 | -`react-native-core/v<RN_VERSION>`. RN_VERSION can be found in `<RNOH>/packages/tester/package.json::dependencies::react-native`. In case of multiple branches for the same RN_VERSION, check the comment at the first line in `<RNOH>/packages/react-native.patch`. | ||
| 29 | - | ||
| 30 | -### Where can I find the RNC repo? | ||
| 31 | - | ||
| 32 | -If you are an RNOH contributor, you can't. If you are a new RNOH maintainer, ask other RNOH maintainers. | ||
| 33 | - | ||
| 34 | -### Why can't RNC be public? | ||
| 35 | - | ||
| 36 | -Bureaucracy... | ||
| 37 | 17 | ||
| 38 | ## Upgrading React Native used by RNOH | 18 | ## Upgrading React Native used by RNOH |
| 39 | 19 | ||
| 40 | RNOH maintainers should keep RNOH as close as possible to RN to simplify the RN upgrading process for RNOH library and application maintainers. The difficulty of upgrading RN grows exponentially with time. This is a complex task. It should be done by an experienced RNOH maintainer. | 20 | RNOH maintainers should keep RNOH as close as possible to RN to simplify the RN upgrading process for RNOH library and application maintainers. The difficulty of upgrading RN grows exponentially with time. This is a complex task. It should be done by an experienced RNOH maintainer. |
| 41 | 21 | ||
| 42 | -1. Create a temporary branch in the RNC repo, and cherry-pick patch commits from the current active branch. | 22 | +1. Request RNOH repo maintainers to create a new branch in the remote RNOH repo. |
| 43 | -1. Simplify patch commits to avoid resolving unnecessary conflicts (some commits may introduce changes and remove them later). Rebase that temporary branch and squash/delete commits if possible. | 23 | +1. Create and switch to a new branch (follow the naming convention already used in the repo, keep it private). |
| 44 | -1. Create a new branch `react-native-core/v<NEW_RN_VERSION>`, let's call it NEW RNC BRANCH. | 24 | +1. Update `<RNOH>/packages/react-native`. |
| 45 | -1. Cherry-pick simplified commits from the temporary branch to the NEW RNC BRANCH and resolve conflicts. | 25 | +1. Fix issues that may arise when trying to apply react-native.patch. |
| 46 | -1. Update `<RNOH>/packages/react-native` submodule to point to the same revision as the one used by the starting point of NEW RNC BRANCH. | ||
| 47 | -1. Regenerate the patch (as described [here](#modifying-react-native-and-handling-openharmony-specific-code)) and run pnpm init-ws (work directly on the NEW RNC BRANCH, consider creating an oneliner that generates a patch, copies it to the RNOH repo and runs `pnpm init-ws`). | ||
| 48 | 1. Resolve compilation errors (it may require many iterations). | 26 | 1. Resolve compilation errors (it may require many iterations). |
| 49 | 1. Resolve runtime issues - use concurrent and sequential tests in the tester app. | 27 | 1. Resolve runtime issues - use concurrent and sequential tests in the tester app. |
| 50 | -1. Resolve issues arising during reloads - keep reloading the tester application for ~2 minutes (memory leaks, deadlocks, and crashes can be detected with this approach). | 28 | +1. Resolve issues that may arrise during reloads - keep reloading the tester application for ~2 minutes (memory leaks, deadlocks, and crashes can be detected with this approach). |
| 51 | 1. Unlink failing libraries from `<RNOH>/tester-ecosystem` and document errors. | 29 | 1. Unlink failing libraries from `<RNOH>/tester-ecosystem` and document errors. |
| 52 | -1. Create a new branch for the Release Candidate in the RNOH repo (follow the naming convention already used in the repo). | 30 | +1. Rebase your local branch. |
| 53 | 1. Submit a Merge Request (MR) to that new branch. | 31 | 1. Submit a Merge Request (MR) to that new branch. |
| 54 | -1. Once the MR is merged, make the NEW RNC BRANCH default. | ||
| 55 | 32 | ||
| 56 | At this stage, RN is updated. However, it's likely RNOH isn't stable enough, and some new RN features may need to be implemented. RNOH with upgraded RN should be distributed gradually, but it's reasonable to publish a Release Candidate relatively quickly, so that RNOH library maintainers can start adapting incompatible libraries. Once the Release Candidate is public, notify maintainers of those libraries and send them documented errors, to speed up the process of migrating the RNOH ecosystem to the newer RN version. | 33 | At this stage, RN is updated. However, it's likely RNOH isn't stable enough, and some new RN features may need to be implemented. RNOH with upgraded RN should be distributed gradually, but it's reasonable to publish a Release Candidate relatively quickly, so that RNOH library maintainers can start adapting incompatible libraries. Once the Release Candidate is public, notify maintainers of those libraries and send them documented errors, to speed up the process of migrating the RNOH ecosystem to the newer RN version. |
| @@ -0,0 +1,83 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2025 Huawei Technologies Co., Ltd. | ||
| 3 | + * | ||
| 4 | + * This source code is licensed under the MIT license found in the | ||
| 5 | + * LICENSE file in the root directory of this source tree. | ||
| 6 | + */ | ||
| 7 | +import pathUtils from 'node:path'; | ||
| 8 | +import { execSync } from 'child_process'; | ||
| 9 | +import readline from 'node:readline/promises'; | ||
| 10 | + | ||
| 11 | +const REPO_ROOT_PATH = pathUtils.resolve(__dirname, '..'); | ||
| 12 | + | ||
| 13 | +const REACT_NATIVE_SUBMODULE_PATH = pathUtils.join( | ||
| 14 | + REPO_ROOT_PATH, | ||
| 15 | + 'packages', | ||
| 16 | + 'react-native' | ||
| 17 | +); | ||
| 18 | + | ||
| 19 | +const REACT_NATIVE_PATCH_PATH = pathUtils.join( | ||
| 20 | + REPO_ROOT_PATH, | ||
| 21 | + 'packages', | ||
| 22 | + 'react-native.patch' | ||
| 23 | +); | ||
| 24 | + | ||
| 25 | +(async () => { | ||
| 26 | + try { | ||
| 27 | + applyPatch(); | ||
| 28 | + stageReactNativeChanges(); | ||
| 29 | + await pressToContinue( | ||
| 30 | + `You can now modify "${pathUtils.relative(REPO_ROOT_PATH, REACT_NATIVE_SUBMODULE_PATH)}". Press any key to continue...` | ||
| 31 | + ); | ||
| 32 | + stageReactNativeChanges(); | ||
| 33 | + regeneratePatch(); | ||
| 34 | + } finally { | ||
| 35 | + cleanReactNativeSubmodule(); | ||
| 36 | + } | ||
| 37 | +})(); | ||
| 38 | + | ||
| 39 | +function applyPatch() { | ||
| 40 | + cleanReactNativeSubmodule(); | ||
| 41 | + execSync(`git apply --whitespace=fix ${REACT_NATIVE_PATCH_PATH}`, { | ||
| 42 | + cwd: REACT_NATIVE_SUBMODULE_PATH, | ||
| 43 | + }); | ||
| 44 | + console.log( | ||
| 45 | + `applied ${pathUtils.relative(REPO_ROOT_PATH, REACT_NATIVE_PATCH_PATH)} to ${pathUtils.relative(REPO_ROOT_PATH, REACT_NATIVE_SUBMODULE_PATH)}` | ||
| 46 | + ); | ||
| 47 | +} | ||
| 48 | + | ||
| 49 | +function stageReactNativeChanges() { | ||
| 50 | + execSync(`git add .`, { | ||
| 51 | + cwd: REACT_NATIVE_SUBMODULE_PATH, | ||
| 52 | + }); | ||
| 53 | + console.log( | ||
| 54 | + `staged changes in ${pathUtils.relative(REPO_ROOT_PATH, REACT_NATIVE_SUBMODULE_PATH)}` | ||
| 55 | + ); | ||
| 56 | +} | ||
| 57 | + | ||
| 58 | +async function pressToContinue(message: string) { | ||
| 59 | + const rl = readline.createInterface({ | ||
| 60 | + input: process.stdin, | ||
| 61 | + output: process.stdout, | ||
| 62 | + }); | ||
| 63 | + await rl.question(message); | ||
| 64 | + rl.close(); | ||
| 65 | +} | ||
| 66 | + | ||
| 67 | +function regeneratePatch() { | ||
| 68 | + execSync(`git diff --full-index HEAD > ${REACT_NATIVE_PATCH_PATH}`, { | ||
| 69 | + cwd: REACT_NATIVE_SUBMODULE_PATH, | ||
| 70 | + }); | ||
| 71 | + console.log( | ||
| 72 | + `regenerated ${pathUtils.relative(REPO_ROOT_PATH, REACT_NATIVE_PATCH_PATH)}` | ||
| 73 | + ); | ||
| 74 | +} | ||
| 75 | + | ||
| 76 | +function cleanReactNativeSubmodule() { | ||
| 77 | + execSync('git reset --hard HEAD && git clean -dfx', { | ||
| 78 | + cwd: REACT_NATIVE_SUBMODULE_PATH, | ||
| 79 | + }); | ||
| 80 | + console.log( | ||
| 81 | + `cleaned ${pathUtils.relative(REPO_ROOT_PATH, REACT_NATIVE_SUBMODULE_PATH)}` | ||
| 82 | + ); | ||
| 83 | +} | ||