Files
pipu-music/scripts/check-apk.test.mjs
T
mrleo1nidandClaude Opus 5.5 73e642c1ec
ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Successful in 2m56s
release / android (APK в релиз) (push) Successful in 4m53s
ci / mobile / mobile (Metro-бандл Android) (push) Successful in 3m27s
ci / docker / docker (образы собираются — их сервер собирает сам при обновлении) (push) Successful in 1m2s
ci / ts / ts (packages + web) (push) Successful in 1m48s
release / release (версия и заметки) (push) Successful in 8s
fix(mobile): APK starts on Android 15+ 16 KB page devices and emulators
On an Android 16 emulator the release APK crashed at launch with
"Cannot read property 'NativeModule' of undefined": libexpo-modules-core.so
was missing for the ABI the device picked. Two build defects:

- librnskia.so was linked with 4 KB segment alignment (Skia 2.0.0-next.4
  lacks ANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES), so 16 KB page devices cannot
  load it; a pnpm patch adds the flag
- the APK carried incomplete x86, x86_64 and armeabi-v7a folders from the
  React Native libraries; an Expo config plugin keeps only arm64-v8a, the ABI
  the native modules are built for (the APK also halves in size)

scripts/check-apk.mjs now fails the release on another ABI, a missing core
library or a library not aligned to 16 KB.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 03:22:13 +03:00

72 lines
2.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import assert from 'node:assert/strict';
import { test } from 'node:test';
import { checkApk, loadAlignment, REQUIRED } from './check-apk.mjs';
/** ELF64 с одним PT_LOAD и заданным выравниванием. */
function elf(align) {
const b = Buffer.alloc(64 + 56);
b.writeUInt32BE(0x7f454c46, 0);
b[4] = 2;
b.writeBigUInt64LE(64n, 0x20);
b.writeUInt16LE(56, 0x36);
b.writeUInt16LE(1, 0x38);
b.writeUInt32LE(1, 64);
b.writeBigUInt64LE(BigInt(align), 64 + 0x30);
return b;
}
/** ZIP без сжатия из пар [имя, содержимое]. */
function zip(files) {
const locals = [];
const central = [];
let offset = 0;
for (const [name, data] of files) {
const n = Buffer.from(name);
const lh = Buffer.alloc(30);
lh.writeUInt32LE(0x04034b50, 0);
lh.writeUInt32LE(data.length, 18);
lh.writeUInt32LE(data.length, 22);
lh.writeUInt16LE(n.length, 26);
locals.push(lh, n, data);
const ch = Buffer.alloc(46);
ch.writeUInt32LE(0x02014b50, 0);
ch.writeUInt32LE(data.length, 20);
ch.writeUInt32LE(data.length, 24);
ch.writeUInt16LE(n.length, 28);
ch.writeUInt32LE(offset, 42);
central.push(ch, n);
offset += 30 + n.length + data.length;
}
const cd = Buffer.concat(central);
const end = Buffer.alloc(22);
end.writeUInt32LE(0x06054b50, 0);
end.writeUInt16LE(files.length, 8);
end.writeUInt16LE(files.length, 10);
end.writeUInt32LE(cd.length, 12);
end.writeUInt32LE(offset, 16);
return Buffer.concat([...locals, cd, end]);
}
test('reads the smallest PT_LOAD alignment', () => {
assert.equal(loadAlignment(elf(16384)), 16384);
assert.equal(loadAlignment(elf(4096)), 4096);
});
test('accepts an arm64-only APK aligned to 16 KB', () => {
const apk = zip(REQUIRED.map((l) => [`lib/arm64-v8a/${l}`, elf(16384)]));
assert.deepEqual(checkApk(apk).problems, []);
});
test('rejects other ABIs, missing libraries and 4 KB alignment', () => {
const libs = REQUIRED.filter((l) => l !== 'libexpo-modules-core.so').map((l) => [
`lib/arm64-v8a/${l}`,
elf(l === 'librnskia.so' ? 4096 : 16384),
]);
const { problems } = checkApk(zip([...libs, ['lib/x86_64/libjsi.so', elf(16384)]]));
assert.equal(problems.length, 3);
assert.match(problems.join('\n'), /lib\/x86_64/);
assert.match(problems.join('\n'), /libexpo-modules-core\.so/);
assert.match(problems.join('\n'), /librnskia\.so.*4096/);
});