Skip to content

Commit 456aeeb

Browse files
committed
fix oldarch
1 parent e4aa23a commit 456aeeb

11 files changed

Lines changed: 399 additions & 19 deletions

File tree

.github/workflows/e2e_android.yml

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,3 +144,110 @@ jobs:
144144
path: Example/e2etest/artifacts
145145
if-no-files-found: ignore
146146
retention-days: 7
147+
148+
e2e-android-rn077-oldarch:
149+
runs-on: ubuntu-latest
150+
timeout-minutes: 35
151+
steps:
152+
- name: Checkout react-native-update
153+
uses: actions/checkout@v6
154+
with:
155+
submodules: recursive
156+
157+
- name: Checkout react-native-update-cli
158+
uses: actions/checkout@v6
159+
with:
160+
repository: reactnativecn/react-native-update-cli
161+
path: react-native-update-cli
162+
163+
- name: Setup Node.js
164+
uses: actions/setup-node@v6
165+
with:
166+
node-version: 24
167+
168+
- name: Setup Bun
169+
uses: oven-sh/setup-bun@v2
170+
with:
171+
bun-version: latest
172+
173+
- name: Cache Bun dependencies
174+
uses: actions/cache@v5
175+
with:
176+
path: ~/.bun/install/cache
177+
key: ${{ runner.os }}-bun-e2e-rn077-oldarch-${{ hashFiles('Example/e2etest/bun.lock', 'Example/e2etest/scripts/create-rn077-oldarch-project.js', 'react-native-update-cli/bun.lock') }}
178+
restore-keys: |
179+
${{ runner.os }}-bun-e2e-rn077-oldarch-
180+
${{ runner.os }}-bun-e2e-
181+
182+
- name: Setup Java
183+
uses: actions/setup-java@v5
184+
with:
185+
distribution: temurin
186+
java-version: '17'
187+
cache: gradle
188+
189+
- name: Enable KVM
190+
run: |
191+
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
192+
sudo udevadm control --reload-rules
193+
sudo udevadm trigger --name-match=kvm
194+
195+
- name: Create RN 0.77.3 old architecture e2e project
196+
run: node Example/e2etest/scripts/create-rn077-oldarch-project.js
197+
198+
- name: Install RN 0.77.3 old architecture dependencies
199+
run: cd .e2e-rn077-oldarch/AwesomeProject && bun install
200+
201+
- name: Link local react-native-update
202+
run: |
203+
set -euo pipefail
204+
E2E_DIR=".e2e-rn077-oldarch/AwesomeProject"
205+
export LOCAL_RNU_VERSION="$(node -p "require('./${E2E_DIR}/node_modules/react-native-update/package.json').version")"
206+
LOCAL_RNU_DIR="$PWD/$E2E_DIR/.e2e-artifacts/local-react-native-update"
207+
rm -rf "$LOCAL_RNU_DIR" "$E2E_DIR/node_modules/react-native-update"
208+
mkdir -p "$LOCAL_RNU_DIR"
209+
cp package.json react-native-update.podspec react-native.config.js expo-module.config.json "$LOCAL_RNU_DIR"/
210+
rsync -a --exclude='.git' src ios android cpp scripts "$LOCAL_RNU_DIR"/
211+
node -e "const fs = require('fs'); const file = process.env.LOCAL_RNU_DIR + '/package.json'; const pkg = JSON.parse(fs.readFileSync(file, 'utf8')); pkg.version = process.env.LOCAL_RNU_VERSION; fs.writeFileSync(file, JSON.stringify(pkg, null, 2) + '\n');"
212+
ln -s ../.e2e-artifacts/local-react-native-update "$E2E_DIR/node_modules/react-native-update"
213+
env:
214+
LOCAL_RNU_DIR: ${{ github.workspace }}/.e2e-rn077-oldarch/AwesomeProject/.e2e-artifacts/local-react-native-update
215+
216+
- name: Install react-native-update-cli dependencies
217+
run: cd react-native-update-cli && bun install --frozen-lockfile && bun run build
218+
219+
- name: Detox build (RN 0.77.3 old arch android.emu.release)
220+
env:
221+
DETOX_ANDROID_ARCHS: x86_64
222+
DETOX_AVD_NAME: api34
223+
run: cd .e2e-rn077-oldarch/AwesomeProject && E2E_PLATFORM=android bunx detox build --configuration android.emu.release
224+
225+
- name: Prepare local update artifacts
226+
env:
227+
E2E_PLATFORM: android
228+
RNU_CLI_ROOT: ${{ github.workspace }}/react-native-update-cli
229+
run: cd .e2e-rn077-oldarch/AwesomeProject && bun run prepare:e2e
230+
231+
- name: Detox test (RN 0.77.3 old arch android.emu.release)
232+
uses: reactivecircus/android-emulator-runner@v2
233+
env:
234+
DETOX_ANDROID_ARCHS: x86_64
235+
RNU_CLI_ROOT: ${{ github.workspace }}/react-native-update-cli
236+
RNU_E2E_SKIP_PREPARE: 'true'
237+
DETOX_AVD_NAME: api34
238+
with:
239+
api-level: 34
240+
target: google_apis
241+
arch: x86_64
242+
avd-name: api34
243+
emulator-boot-timeout: 900
244+
script: cd .e2e-rn077-oldarch/AwesomeProject && E2E_PLATFORM=android bunx detox test --configuration android.emu.release --headless --record-logs all --retries 1
245+
246+
- name: Upload Detox artifacts
247+
if: failure()
248+
uses: actions/upload-artifact@v7.0.1
249+
with:
250+
name: e2e-android-rn077-oldarch-detox-artifacts
251+
path: .e2e-rn077-oldarch/AwesomeProject/artifacts
252+
if-no-files-found: ignore
253+
retention-days: 7

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ Example/**/artifacts
4949
Example/**/.detox
5050
Example/**/coverage
5151
Example/testHotUpdate/artifacts
52+
.e2e-rn077-oldarch
5253

5354
yarn-error.log
5455
Example/testHotUpdate/.yarn

Example/e2etest/README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,13 @@ E2E_PLATFORM=android detox test --configuration android.emu.release --headless -
2626
架构需要跟 AVD 保持一致:Apple Silicon 本地的 `api34` AVD 用
2727
`arm64-v8a`;GitHub `ubuntu-latest` x64 job 用 `x86_64` emulator 和
2828
`DETOX_ANDROID_ARCHS=x86_64`
29+
30+
RN 0.77.3 旧架构 Android 兼容性由 CI 运行时生成工程验证:
31+
32+
```sh
33+
node Example/e2etest/scripts/create-rn077-oldarch-project.js
34+
cd .e2e-rn077-oldarch/AwesomeProject
35+
bun install
36+
E2E_PLATFORM=android detox build --configuration android.emu.release
37+
E2E_PLATFORM=android detox test --configuration android.emu.release --headless --record-logs all
38+
```

Example/e2etest/e2e/globalSetup.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,11 @@ function findProjectRoot(...startDirs: string[]) {
4040
}
4141

4242
const projectRoot = findProjectRoot(process.cwd(), __dirname);
43+
const packageJson = JSON.parse(
44+
fs.readFileSync(path.join(projectRoot, 'package.json'), 'utf8'),
45+
) as { dependencies?: { 'react-native'?: string } };
46+
const reactNativeVersion =
47+
packageJson.dependencies?.['react-native'] || 'unknown';
4348
const artifactsRoot = path.join(projectRoot, '.e2e-artifacts');
4449
const pidFile = path.join(artifactsRoot, '.server.pid');
4550

@@ -177,7 +182,7 @@ async function warmServer(platform: 'ios' | 'android') {
177182
hash: '',
178183
buildTime: '0',
179184
cInfo: {
180-
rn: '0.85.2',
185+
rn: reactNativeVersion,
181186
os: platform,
182187
rnu: 'e2e-warmup',
183188
uuid: 'warmup',
Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
#!/usr/bin/env node
2+
3+
const fs = require('node:fs');
4+
const path = require('node:path');
5+
const { spawnSync } = require('node:child_process');
6+
7+
const RN_VERSION = '0.77.3';
8+
const RN_CLI_VERSION = '15.0.1';
9+
const appName = 'AwesomeProject';
10+
const scriptDir = __dirname;
11+
const baseE2eDir = path.resolve(scriptDir, '..');
12+
const repoRoot = path.resolve(baseE2eDir, '../..');
13+
const workspaceRoot = path.resolve(
14+
process.env.RNU_RN077_OLDARCH_ROOT ||
15+
path.join(repoRoot, '.e2e-rn077-oldarch'),
16+
);
17+
const projectRoot = path.join(workspaceRoot, appName);
18+
19+
function run(command, args, options = {}) {
20+
const result = spawnSync(command, args, {
21+
stdio: 'inherit',
22+
shell: process.platform === 'win32',
23+
...options,
24+
});
25+
26+
if (result.status !== 0) {
27+
throw new Error(
28+
`${command} ${args.join(' ')} failed with exit code ${result.status}`,
29+
);
30+
}
31+
}
32+
33+
function copyFromBase(relativePath) {
34+
fs.cpSync(path.join(baseE2eDir, relativePath), path.join(projectRoot, relativePath), {
35+
recursive: true,
36+
force: true,
37+
});
38+
}
39+
40+
function readText(relativePath) {
41+
return fs.readFileSync(path.join(projectRoot, relativePath), 'utf8');
42+
}
43+
44+
function writeText(relativePath, content) {
45+
fs.writeFileSync(path.join(projectRoot, relativePath), content);
46+
}
47+
48+
function createReactNativeProject() {
49+
fs.rmSync(workspaceRoot, { recursive: true, force: true });
50+
fs.mkdirSync(workspaceRoot, { recursive: true });
51+
52+
run(
53+
'npx',
54+
[
55+
`@react-native-community/cli@${RN_CLI_VERSION}`,
56+
'init',
57+
appName,
58+
'--version',
59+
RN_VERSION,
60+
'--skip-install',
61+
],
62+
{ cwd: workspaceRoot },
63+
);
64+
65+
fs.rmSync(path.join(projectRoot, '.git'), { recursive: true, force: true });
66+
}
67+
68+
function copyE2eHarness() {
69+
[
70+
'.detoxrc.js',
71+
'.detoxrc.ts',
72+
'.eslintrc.js',
73+
'.prettierrc.js',
74+
'.watchmanconfig',
75+
'app.json',
76+
'e2e',
77+
'index.js',
78+
'jest.config.js',
79+
'jest.setup.js',
80+
'metro.config.js',
81+
'scripts',
82+
'src',
83+
'tsconfig.json',
84+
'tsconfig.node.json',
85+
'update.json',
86+
].forEach(copyFromBase);
87+
88+
writeText(
89+
'babel.config.js',
90+
"module.exports = {\n presets: ['module:@react-native/babel-preset'],\n};\n",
91+
);
92+
}
93+
94+
function writePackageJson() {
95+
const generatedPackageJson = JSON.parse(readText('package.json'));
96+
97+
const packageJson = {
98+
name: 'e2etest-rn077-oldarch',
99+
version: generatedPackageJson.version,
100+
private: true,
101+
scripts: {
102+
android: 'react-native run-android',
103+
ios: 'react-native run-ios',
104+
start: 'react-native start',
105+
test: 'jest',
106+
server: 'bun scripts/local-e2e-server.ts',
107+
'prepare:e2e': 'node scripts/run-prepare-local-update-artifacts.js',
108+
'test:e2e:android':
109+
'E2E_PLATFORM=android detox test --configuration android.emu.release --headless --record-logs all',
110+
lint: 'eslint .',
111+
apk: 'cd android && ./gradlew assembleRelease',
112+
},
113+
dependencies: {
114+
react: '18.3.1',
115+
'react-native': RN_VERSION,
116+
'react-native-update': '^10.40.0',
117+
},
118+
devDependencies: {
119+
...generatedPackageJson.devDependencies,
120+
'@types/node': '^24.6.0',
121+
detox: '20.50.1',
122+
'ts-node': '^10.9.2',
123+
},
124+
engines: {
125+
node: '>=22',
126+
},
127+
trustedDependencies: ['detox', 'dtrace-provider'],
128+
};
129+
130+
writeText('package.json', `${JSON.stringify(packageJson, null, 2)}\n`);
131+
}
132+
133+
function configureAndroidOldArchitecture() {
134+
let gradleProperties = readText('android/gradle.properties');
135+
gradleProperties = gradleProperties.replace(
136+
/^newArchEnabled=.*$/m,
137+
'newArchEnabled=false',
138+
);
139+
writeText('android/gradle.properties', gradleProperties);
140+
141+
let appBuildGradle = readText('android/app/build.gradle');
142+
appBuildGradle = appBuildGradle.replace(
143+
/versionName "1\.0"/,
144+
[
145+
'versionName "1.0"',
146+
" testBuildType System.getProperty('testBuildType', 'debug')",
147+
' testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"',
148+
].join('\n'),
149+
);
150+
appBuildGradle = appBuildGradle.replace(
151+
/release \{\n\s+\/\/ Caution!/,
152+
'release {\n crunchPngs false\n // Caution!',
153+
);
154+
appBuildGradle = appBuildGradle.replace(
155+
/dependencies \{\n/,
156+
[
157+
'def detoxVersion = "20.50.1"',
158+
'',
159+
'repositories {',
160+
' maven {',
161+
' url("$rootDir/../node_modules/detox/Detox-android")',
162+
' }',
163+
'}',
164+
'',
165+
'dependencies {',
166+
].join('\n') + '\n',
167+
);
168+
appBuildGradle = appBuildGradle.replace(
169+
/implementation\("com\.facebook\.react:react-android"\)\n/,
170+
[
171+
'implementation("com.facebook.react:react-android")',
172+
'',
173+
' androidTestImplementation("com.wix:detox:${detoxVersion}")',
174+
' androidTestImplementation("junit:junit:4.13.2")',
175+
' androidTestImplementation("androidx.test:runner:1.6.2")',
176+
' androidTestImplementation("androidx.test:rules:1.6.1")',
177+
' androidTestImplementation("androidx.test.ext:junit:1.2.1")',
178+
'',
179+
].join('\n'),
180+
);
181+
writeText('android/app/build.gradle', appBuildGradle);
182+
183+
copyFromBase('android/app/src/androidTest/java/com/awesomeproject/DetoxTest.java');
184+
copyFromBase('android/app/src/main/res/xml/network_security_config.xml');
185+
186+
let manifest = readText('android/app/src/main/AndroidManifest.xml');
187+
manifest = manifest.replace(
188+
/android:theme="@style\/AppTheme"/,
189+
[
190+
'android:theme="@style/AppTheme"',
191+
' android:networkSecurityConfig="@xml/network_security_config"',
192+
].join('\n'),
193+
);
194+
writeText('android/app/src/main/AndroidManifest.xml', manifest);
195+
196+
let mainApplication = readText(
197+
'android/app/src/main/java/com/awesomeproject/MainApplication.kt',
198+
);
199+
mainApplication = mainApplication.replace(
200+
'import com.facebook.soloader.SoLoader\n',
201+
[
202+
'import com.facebook.soloader.SoLoader',
203+
'import cn.reactnative.modules.update.UpdateContext',
204+
'',
205+
].join('\n'),
206+
);
207+
mainApplication = mainApplication.replace(
208+
' override fun getJSMainModuleName(): String = "index"\n',
209+
[
210+
' override fun getJSMainModuleName(): String = "index"',
211+
'',
212+
' override fun getJSBundleFile(): String? = UpdateContext.getBundleUrl(this@MainApplication)',
213+
'',
214+
].join('\n'),
215+
);
216+
writeText(
217+
'android/app/src/main/java/com/awesomeproject/MainApplication.kt',
218+
mainApplication,
219+
);
220+
}
221+
222+
function main() {
223+
createReactNativeProject();
224+
copyE2eHarness();
225+
writePackageJson();
226+
configureAndroidOldArchitecture();
227+
console.log(`Created RN ${RN_VERSION} old architecture e2e project: ${projectRoot}`);
228+
}
229+
230+
main();

0 commit comments

Comments
 (0)