diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..fec85ca0 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,4 @@ +# cmd.exe misreads batch files with bare LF line ends (labels, long lines). +*.cmd text eol=crlf +# Shell scripts run on macOS and Linux only; a CRLF would break the shebang. +*.sh text eol=lf diff --git a/.github/workflows/campaign-windows-hermes.yml b/.github/workflows/campaign-windows-hermes.yml new file mode 100644 index 00000000..e5d3d869 --- /dev/null +++ b/.github/workflows/campaign-windows-hermes.yml @@ -0,0 +1,375 @@ +name: campaign windows hermes (temporary) + +# Beta campaign N-25 / MX-WIN / HOOK-13 (Task 13.7). TEMPORARY: delete after +# the first beta's campaign. Proves a Hermes release build on a Windows host, +# for the bare example and for the Expo example after `expo prebuild`: +# hermesc through hermesc-preserve-js.cmd (React Native runs hermesCommand +# with `cmd /c`, which cannot run the .sh), the composed map, the debug id in +# map and bytecode, and the source-map upload, which goes to a stub server on +# the runner. Never a real token, never a Bugsee endpoint. The APKs are kept +# as artifacts for the device lane (composed-debug-id on the WOD_LX1). + +on: + workflow_dispatch: + pull_request: + paths: + - .github/workflows/campaign-windows-hermes.yml + - .gitattributes + - packages/react-native/scripts/** + - packages/react-native/plugin/** + - examples/bare/android/** + - examples/expo/** + +concurrency: + group: campaign-windows-${{ github.ref }} + cancel-in-progress: true + +env: + # Synthetic, not the all-zero placeholder: the placeholder makes the hook + # skip the upload, and the upload is part of what this job proves. It only + # ever reaches the stub below. + BUGSEE_TOKEN_ANDROID: 11111111-2222-4333-8444-555555555555 + BUGSEE_TOKEN_IOS: 00000000-0000-4000-8000-000000000000 + BUGSEE_ENDPOINT: http://127.0.0.1:8777 + +jobs: + android-release: + name: android release (windows-latest) + runs-on: windows-latest + timeout-minutes: 75 + # Required proof for BLK-07 / Task 13.7 (N-25): a Hermes release build of + # the bare example on a Windows host, the wrapper's own tests run under + # Windows, and the guard that fails a leftover .sh hermesCommand with a + # clear message. React Native runs react.hermesCommand through `cmd /c` + # on Windows, so this job must stay blocking: a regression here is a + # broken Windows release build for customers. + defaults: + run: + shell: bash + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: '22.18' + - uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: 17 + - run: corepack enable + - run: yarn install --immutable + + # The wrapper's own tests, on Windows: the .cmd launcher run the way + # React Native runs it (cmd /c), with the repository's hermesc.exe. + - name: Wrapper tests (Windows) + run: yarn jest packages/react-native/scripts/__tests__/hermesc-preserve-js.test.js + + # The repo's own stand-in for the symbol API (the upload tests use it): + # POST /apps//symbols answers with a PUT URL on itself, and the + # PUT's zip is unpacked and its map's debug_id logged. + # The synthetic token makes write-credentials turn NDK symbol upload on; + # that is not what this job proves, so take it back out. + - name: Credentials (synthetic token, stub endpoint, no NDK upload) + run: | + yarn workspace bugsee-example-bare credentials + sed -i '/^plugin.ndk.enabled/d' examples/bare/android/bugsee.properties + + # The stub (the repo's own stand-in for the symbol API, as the upload + # tests use it) starts in this step so it lives as long as Gradle does. + # The job fails on Gradle's exit code. + - name: Release build (Hermes) against the stub symbol API + shell: pwsh + run: | + New-Item -ItemType File -Force "$env:RUNNER_TEMP/stub.log" | Out-Null + $stub = Start-Process node -PassThru -NoNewWindow -ArgumentList @( + 'packages/react-native/scripts/__tests__/fixtures/symbol-stub-server.js', + '--port', '8777', '--log', "$env:RUNNER_TEMP/stub.log") + Start-Sleep -Seconds 2 + Push-Location examples/bare/android + ./gradlew.bat :app:assembleRelease -PreactNativeArchitectures=arm64-v8a -PbugseeE2eDebuggable=true --stacktrace *> "$env:RUNNER_TEMP/gradle.log" + $code = $LASTEXITCODE + Pop-Location + Stop-Process -Id $stub.Id -ErrorAction SilentlyContinue + Get-Content "$env:RUNNER_TEMP/gradle.log" + exit $code + + - name: Composed map carries a debug id; the bytecode carries the same one + working-directory: examples/bare/android/app/build + run: | + map="$(find generated/sourcemaps -name 'index.android.bundle.map' | head -1)" + echo "composed map: $map" + node -e ' + const m = JSON.parse(require("fs").readFileSync(process.argv[1], "utf8")); + const id = m.debugId || m.debug_id; + if (!id) { console.error("FAIL: no debugId in the composed map"); process.exit(1); } + console.log("debugId " + id); + require("fs").writeFileSync(process.env.RUNNER_TEMP + "/debug-id", id); + ' "$map" + apk=outputs/apk/release/app-release.apk + unzip -o -q "$apk" assets/index.android.bundle -d "$RUNNER_TEMP/apk" + id="$(cat "$RUNNER_TEMP/debug-id")" + if grep -a -q "$id" "$RUNNER_TEMP/apk/assets/index.android.bundle"; then + echo "bytecode carries $id" + else + echo "FAIL: the packaged bytecode does not carry $id"; exit 1 + fi + + - name: The composed map reached the stub, with that debug id + if: always() + run: | + echo "--- stub requests"; cat "$RUNNER_TEMP/stub.log" + echo "--- hook lines"; grep -i "bugsee" "$RUNNER_TEMP/gradle.log" | head -40 || true + node -e ' + const fs = require("fs"); + const want = fs.existsSync(process.argv[2]) ? fs.readFileSync(process.argv[2], "utf8").trim() : ""; + if (!want) { console.error("FAIL: no composed-map debug id to look for"); process.exit(1); } + const puts = fs.readFileSync(process.argv[1], "utf8").split("\n").filter(Boolean) + .map((l) => JSON.parse(l)).filter((e) => e.method === "PUT"); + const hit = puts.some((e) => (e.entries || []).some((x) => x.debugId === want)); + if (!hit) { console.error("FAIL: no PUT carried a map with debug_id " + want); process.exit(1); } + console.log("stub received the map with debug_id " + want); + ' "$RUNNER_TEMP/stub.log" "$RUNNER_TEMP/debug-id" + + - uses: actions/upload-artifact@v4 + if: always() + with: + name: campaign-windows-hermes + path: | + examples/bare/android/app/build/outputs/apk/release/app-release.apk + examples/bare/android/app/build/generated/sourcemaps/** + ${{ runner.temp }}/gradle.log + ${{ runner.temp }}/stub.log + if-no-files-found: warn + + # The guard for builds wired by an earlier README: a hermesCommand that + # names the .sh fails the bundle task before hermesc, with the fix, + # instead of a NoSuchFileException on the .hbc. hermesCommand is a task + # input, so the bundle task reruns. + - name: A .sh hermesCommand on Windows fails with the fix + shell: pwsh + run: | + Add-Content examples/bare/android/app/build.gradle "`nreact { hermesCommand = file(`"../../node_modules/@bugsee/react-native/scripts/hermesc-preserve-js.sh`").absolutePath }" + Push-Location examples/bare/android + ./gradlew.bat :app:createBundleReleaseJsAndAssets -PreactNativeArchitectures=arm64-v8a -PbugseeE2eDebuggable=true *> "$env:RUNNER_TEMP/guard.log" + $code = $LASTEXITCODE + Pop-Location + Get-Content "$env:RUNNER_TEMP/guard.log" | Select-String -Pattern "Bugsee|FAILED|NoSuchFile" | Select-Object -First 20 + if ($code -eq 0) { Write-Error "FAIL: the bundle task passed with a .sh hermesCommand"; exit 1 } + if (-not (Select-String -Path "$env:RUNNER_TEMP/guard.log" -SimpleMatch "a shell script, but on Windows" -Quiet)) { Write-Error "FAIL: no Bugsee guard message"; exit 1 } + if (Select-String -Path "$env:RUNNER_TEMP/guard.log" -SimpleMatch "NoSuchFileException" -Quiet) { Write-Error "FAIL: hermesc ran before the guard"; exit 1 } + Write-Output "guard: failed with the fix, before hermesc" + # pwsh would otherwise exit with Gradle's (expected) non-zero code. + exit 0 + + # The same on the Expo example: the config plugin writes hermesCommand, the + # per-OS launcher, during `expo prebuild` on this Windows runner. + expo-android-release: + name: expo android release (windows-latest) + runs-on: windows-latest + timeout-minutes: 75 + defaults: + run: + shell: bash + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: '22.18' + - uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: 17 + - run: corepack enable + - run: yarn install --immutable + + # The synthetic token makes the hook upload the map (to the stub); + # uploadSymbols false keeps the Gradle plugin's own uploads off, which is + # not what this job proves. + - name: Expo prebuild (Android) with a synthetic token + working-directory: examples/expo + run: | + cat > app.config.js <<'JS' + module.exports = ({ config }) => ({ + ...config, + plugins: [['@bugsee/react-native', { appToken: process.env.BUGSEE_TOKEN_ANDROID, uploadSymbols: false }]], + }); + JS + CI=1 yarn expo prebuild --clean --no-install --platform android + grep -n 'hermesCommand' android/app/build.gradle + grep -qF '"/scripts/hermesc-preserve-js" + (System.getProperty("os.name").startsWith("Windows") ? ".cmd" : ".sh")' android/app/build.gradle + + - name: Release build (Hermes) against the stub symbol API + shell: pwsh + run: | + New-Item -ItemType File -Force "$env:RUNNER_TEMP/stub.log" | Out-Null + $stub = Start-Process node -PassThru -NoNewWindow -ArgumentList @( + 'packages/react-native/scripts/__tests__/fixtures/symbol-stub-server.js', + '--port', '8777', '--log', "$env:RUNNER_TEMP/stub.log") + Start-Sleep -Seconds 2 + Push-Location examples/expo/android + ./gradlew.bat :app:assembleRelease -PreactNativeArchitectures=arm64-v8a --stacktrace *> "$env:RUNNER_TEMP/gradle.log" + $code = $LASTEXITCODE + Pop-Location + Stop-Process -Id $stub.Id -ErrorAction SilentlyContinue + Get-Content "$env:RUNNER_TEMP/gradle.log" + exit $code + + - name: Composed map carries a debug id; the bytecode carries the same one + working-directory: examples/expo/android/app/build + run: | + map="$(find generated/sourcemaps -name 'index.android.bundle.map' | head -1)" + echo "composed map: $map" + node -e ' + const m = JSON.parse(require("fs").readFileSync(process.argv[1], "utf8")); + const id = m.debugId || m.debug_id; + if (!id) { console.error("FAIL: no debugId in the composed map"); process.exit(1); } + console.log("debugId " + id); + require("fs").writeFileSync(process.env.RUNNER_TEMP + "/debug-id", id); + ' "$map" + apk=outputs/apk/release/app-release.apk + unzip -o -q "$apk" assets/index.android.bundle -d "$RUNNER_TEMP/apk" + id="$(cat "$RUNNER_TEMP/debug-id")" + if grep -a -q "$id" "$RUNNER_TEMP/apk/assets/index.android.bundle"; then + echo "bytecode carries $id" + else + echo "FAIL: the packaged bytecode does not carry $id"; exit 1 + fi + + - name: The composed map reached the stub, with that debug id + if: always() + run: | + echo "--- stub requests"; cat "$RUNNER_TEMP/stub.log" + echo "--- hook lines"; grep -i "bugsee" "$RUNNER_TEMP/gradle.log" | head -40 || true + node -e ' + const fs = require("fs"); + const want = fs.existsSync(process.argv[2]) ? fs.readFileSync(process.argv[2], "utf8").trim() : ""; + if (!want) { console.error("FAIL: no composed-map debug id to look for"); process.exit(1); } + const puts = fs.readFileSync(process.argv[1], "utf8").split("\n").filter(Boolean) + .map((l) => JSON.parse(l)).filter((e) => e.method === "PUT"); + const hit = puts.some((e) => (e.entries || []).some((x) => x.debugId === want)); + if (!hit) { console.error("FAIL: no PUT carried a map with debug_id " + want); process.exit(1); } + console.log("stub received the map with debug_id " + want); + ' "$RUNNER_TEMP/stub.log" "$RUNNER_TEMP/debug-id" + + - uses: actions/upload-artifact@v4 + if: always() + with: + name: campaign-windows-hermes-expo + path: | + examples/expo/android/app/build/outputs/apk/release/app-release.apk + examples/expo/android/app/build/generated/sourcemaps/** + ${{ runner.temp }}/gradle.log + ${{ runner.temp }}/stub.log + if-no-files-found: warn + + # The bare release again, from a copy of the checkout whose path holds + # spaces and parentheses, as C:\Users\First Last\... or "Program Files + # (x86)" do: React Native runs hermesCommand through `cmd /c`, whose quote + # handling changes when the command path has spaces or ( ). The copy sits + # at a short root: under the workspace the New Architecture codegen objects + # pass Windows' 260-character path limit and CMake fails before any + # JavaScript step (run 37600186416, ninja mkdir "No such file or + # directory"), a React Native limit that has nothing to do with spaces. + android-release-spaced-path: + name: android release, path with spaces (windows-latest) + runs-on: windows-latest + timeout-minutes: 75 + env: + APP_ROOT: 'C:/s (x86)/b rn' + defaults: + run: + shell: bash + steps: + - uses: actions/checkout@v4 + - name: Copy the checkout to a path with spaces and parentheses + run: | + mkdir -p "$APP_ROOT" + cp -a . "$APP_ROOT/" + ls "$APP_ROOT" + - uses: actions/setup-node@v4 + with: + node-version: '22.18' + - uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: 17 + - run: corepack enable + - run: yarn install --immutable + working-directory: 'C:/s (x86)/b rn' + + - name: Credentials (synthetic token, stub endpoint, no NDK upload) + working-directory: 'C:/s (x86)/b rn' + run: | + pwd + yarn workspace bugsee-example-bare credentials + sed -i '/^plugin.ndk.enabled/d' examples/bare/android/bugsee.properties + + - name: Release build (Hermes) against the stub symbol API + shell: pwsh + run: | + New-Item -ItemType File -Force "$env:RUNNER_TEMP/stub.log" | Out-Null + $stub = Start-Process node -PassThru -NoNewWindow -ArgumentList @( + "`"$env:APP_ROOT/packages/react-native/scripts/__tests__/fixtures/symbol-stub-server.js`"", + '--port', '8777', '--log', "$env:RUNNER_TEMP/stub.log") + Start-Sleep -Seconds 2 + Push-Location "$env:APP_ROOT/examples/bare/android" + Write-Output "building in $((Get-Location).Path)" + ./gradlew.bat :app:assembleRelease -PreactNativeArchitectures=arm64-v8a -PbugseeE2eDebuggable=true --stacktrace *> "$env:RUNNER_TEMP/gradle.log" + $code = $LASTEXITCODE + Pop-Location + Stop-Process -Id $stub.Id -ErrorAction SilentlyContinue + Get-Content "$env:RUNNER_TEMP/gradle.log" + exit $code + + - name: Composed map carries a debug id; the bytecode carries the same one + working-directory: 'C:/s (x86)/b rn/examples/bare/android/app/build' + run: | + map="$(find generated/sourcemaps -name 'index.android.bundle.map' | head -1)" + echo "composed map: $map" + node -e ' + const m = JSON.parse(require("fs").readFileSync(process.argv[1], "utf8")); + const id = m.debugId || m.debug_id; + if (!id) { console.error("FAIL: no debugId in the composed map"); process.exit(1); } + console.log("debugId " + id); + require("fs").writeFileSync(process.env.RUNNER_TEMP + "/debug-id", id); + ' "$map" + apk=outputs/apk/release/app-release.apk + unzip -o -q "$apk" assets/index.android.bundle -d "$RUNNER_TEMP/apk" + id="$(cat "$RUNNER_TEMP/debug-id")" + if grep -a -q "$id" "$RUNNER_TEMP/apk/assets/index.android.bundle"; then + echo "bytecode carries $id" + else + echo "FAIL: the packaged bytecode does not carry $id"; exit 1 + fi + + - name: The composed map reached the stub, with that debug id + if: always() + run: | + echo "--- stub requests"; cat "$RUNNER_TEMP/stub.log" + echo "--- hook lines"; grep -i "bugsee" "$RUNNER_TEMP/gradle.log" | head -40 || true + node -e ' + const fs = require("fs"); + const want = fs.existsSync(process.argv[2]) ? fs.readFileSync(process.argv[2], "utf8").trim() : ""; + if (!want) { console.error("FAIL: no composed-map debug id to look for"); process.exit(1); } + const puts = fs.readFileSync(process.argv[1], "utf8").split("\n").filter(Boolean) + .map((l) => JSON.parse(l)).filter((e) => e.method === "PUT"); + const hit = puts.some((e) => (e.entries || []).some((x) => x.debugId === want)); + if (!hit) { console.error("FAIL: no PUT carried a map with debug_id " + want); process.exit(1); } + console.log("stub received the map with debug_id " + want); + ' "$RUNNER_TEMP/stub.log" "$RUNNER_TEMP/debug-id" + + - uses: actions/upload-artifact@v4 + if: always() + with: + name: campaign-windows-hermes-spaced + path: C:/s (x86)/b rn/examples/bare/android/app/build/outputs/apk/release/app-release.apk + if-no-files-found: warn + + # A second artifact: upload-artifact needs one root, and the logs are on D:. + - uses: actions/upload-artifact@v4 + if: always() + with: + name: campaign-windows-hermes-spaced-logs + path: | + ${{ runner.temp }}/gradle.log + ${{ runner.temp }}/stub.log + if-no-files-found: warn diff --git a/.gitignore b/.gitignore index 7a499efa..5d3127a9 100644 --- a/.gitignore +++ b/.gitignore @@ -45,3 +45,6 @@ packages/react-native-feedback/.bugsee-feedback-sources-version # Staged into each package by prepack (scripts/pack-native-versions.ts) and # removed by postpack; the repo-root file is the only source. packages/*/native-versions.json +examples/*/ios/build-spm/ +# `lib/` above is for build output; these are campaign generator steps. +!scripts/campaign/lib/ diff --git a/examples/bare/android/app/build.gradle b/examples/bare/android/app/build.gradle index 38d13c65..b5ff9d56 100644 --- a/examples/bare/android/app/build.gradle +++ b/examples/bare/android/app/build.gradle @@ -49,7 +49,9 @@ react { /* Hermes Commands */ // Preserve the JS bundle, then inject the composed map after hermesc. - hermesCommand = file("../../node_modules/@bugsee/react-native/scripts/hermesc-preserve-js.sh").absolutePath + // Windows runs hermesCommand through cmd, which cannot run the .sh. + hermesCommand = file("../../node_modules/@bugsee/react-native/scripts/hermesc-preserve-js" + + (System.getProperty("os.name").startsWith("Windows") ? ".cmd" : ".sh")).absolutePath // // The list of flags to pass to the Hermes compiler. By default is "-O", "-output-source-map" // hermesFlags = ["-O", "-output-source-map"] @@ -149,7 +151,7 @@ android { // createBundleJsAndAssets the hook injects the debug id into the // composed map and the bytecode (Hermes) or Metro's bundle and map (no Hermes), // then uploads the map when bugsee.properties holds a real token. It fails -// the bundle task if Hermes ran without hermesc-preserve-js.sh (hermesCommand +// the bundle task if Hermes ran without the preserve wrapper (hermesCommand // above). -PbugseeUploadSourcemaps=false turns the upload off. apply from: file("../../node_modules/@bugsee/react-native/scripts/bugsee-sourcemaps.gradle") diff --git a/examples/bare/scripts/run-ios.sh b/examples/bare/scripts/run-ios.sh index 07d41b57..9dce0f71 100755 --- a/examples/bare/scripts/run-ios.sh +++ b/examples/bare/scripts/run-ios.sh @@ -6,34 +6,137 @@ # BUGSEE_TOKEN_IOS required # BUGSEE_ENDPOINT optional; the bridge wants the /v2 suffix, which # App.tsx appends -# IOS_DEVICE_ID defaults to the iPhone XS Phase 1 was verified on +# IOS_DEVICE_ID defaults to the iPhone XS Phase 1 was verified on; +# only an e2e/device.ts allowlist entry is accepted # IOS_DEVELOPMENT_TEAM required for a device build; no default, because a # team id is per-developer +# IOS_CONFIGURATION Debug (default) or Release +# +# Campaign switches (N-27, MX-IOS-SPM), all optional: +# IOS_DELIVERY cocoapods (default) or spm. spm migrates ios/ with +# `npx react-native spm add --deintegrate --yes` +# (RN 0.87+ only), builds, asserts the embed, +# installs, then restores ios/ from git and re-runs +# pod install. IOS_SPM_KEEP=1 skips the restore. +# IOS_TARGET device (default) or simulator. simulator builds +# for iphonesimulator and installs on IOS_SIMULATOR_ID +# (default the campaign simulator 6FA9B3E8-...). The +# caller holds .device-lock (simulator) or +# .device-lock-xs (device), as for every device run. +# IOS_APP_DIR an app other than this example, e.g. a campaign +# app from scripts/campaign/gen-rn-app.sh. Its +# credentials are its own (campaign-config.json), so +# write-credentials is skipped and BUGSEE_TOKEN_IOS +# is not needed. +# IOS_LAUNCH 1: after installing, launch the app with its +# console attached for IOS_LAUNCH_SECONDS (default +# 40) and print the BUGSEE_E2E / Bugsee lines; exit +# non-zero unless IOS_LAUNCH_EXPECT (default +# "BUGSEE_E2E status=2", the contract of +# e2e/launch.test.ts) appears. The SDK banner is +# printed, never accepted: it comes before Launched. +# The device must already be held under its lock. +# Debug: the app is pointed at its own Metro with +# -RCT_jsLocation : (port: the app's +# campaign.port, else E2E_METRO_PORT, else 8081; host: +# localhost on the simulator, E2E_METRO_HOST or this +# Mac's en0 address on a device), Metro is started +# on that port if nothing answers there, and the +# wait defaults to 180 s. # # The SwiftPM delivery path is the other configuration; see README.md. set -euo pipefail cd "$(dirname "$0")/.." +EXAMPLE_DIR="$(pwd)" +APP_DIR="${IOS_APP_DIR:-$EXAMPLE_DIR}" +APP_DIR="$(cd "$APP_DIR" && pwd)" +DELIVERY="${IOS_DELIVERY:-cocoapods}" +TARGET="${IOS_TARGET:-device}" +case "$DELIVERY" in cocoapods|spm) ;; *) echo "IOS_DELIVERY must be cocoapods or spm" >&2; exit 2 ;; esac +case "$TARGET" in device|simulator) ;; *) echo "IOS_TARGET must be device or simulator" >&2; exit 2 ;; esac DEVICE="${IOS_DEVICE_ID:-345BA7FE-2C29-5722-892A-BFCB1FD34D0C}" -: "${IOS_DEVELOPMENT_TEAM:?set IOS_DEVELOPMENT_TEAM to your Apple Developer team id}" +SIMULATOR="${IOS_SIMULATOR_ID:-6FA9B3E8-26C7-4232-AA2C-537D9DF32957}" +if [[ "$TARGET" == device ]]; then + : "${IOS_DEVELOPMENT_TEAM:?set IOS_DEVELOPMENT_TEAM to your Apple Developer team id}" + # The same fail-closed allowlist the e2e uses: never another iPhone. + node -e ' + const id = process.argv[1].trim().toUpperCase(); + const src = require("fs").readFileSync(process.argv[2], "utf8"); + const allow = src.slice(src.indexOf("IOS_DEVICE_ALLOWLIST"), src.indexOf("IOS_DEVICE_REFUSED")); + if (/^D027034D/i.test(id) || !allow.includes(`\x27${id}\x27`)) { + console.error(`IOS_DEVICE_ID=${id} is not on the e2e/device.ts allowlist`); + process.exit(1); + }' "$DEVICE" "$EXAMPLE_DIR/e2e/device.ts" +fi # Debug by default; Release for the gated E2E_RELEASE=1 device cases (Task 7.5b). CONFIGURATION="${IOS_CONFIGURATION:-Debug}" -APP="ios/build/Build/Products/${CONFIGURATION}-iphoneos/BareExample.app" +SCHEME="$(basename "$(ls -d "$APP_DIR"/ios/*.xcodeproj | head -1)" .xcodeproj)" +SDK=iphoneos +[[ "$TARGET" == simulator ]] && SDK=iphonesimulator +BUILD_DIR="ios/build" +[[ "$DELIVERY" == spm ]] && BUILD_DIR="ios/build-spm" +APP="$APP_DIR/$BUILD_DIR/Build/Products/${CONFIGURATION}-${SDK}/${SCHEME}.app" + +if [[ "$APP_DIR" == "$EXAMPLE_DIR" ]]; then + node scripts/write-credentials.mjs +fi + +restore_cocoapods() { + local status=$? + set +e + echo "--- restoring CocoaPods delivery in $APP_DIR/ios" + if git -C "$APP_DIR" rev-parse --is-inside-work-tree >/dev/null 2>&1; then + git -C "$APP_DIR" checkout -- ios + git -C "$APP_DIR" clean -fdq -- ios + else + rm -rf "$APP_DIR/ios" + tar -xf "$SPM_BACKUP" -C "$APP_DIR" + fi + (cd "$APP_DIR/ios" && pod install >/dev/null) || { echo "restore: pod install failed"; exit 1; } + # The SPM build leaves xcshareddata/swiftpm/Package.resolved behind. + if git -C "$APP_DIR" rev-parse --is-inside-work-tree >/dev/null 2>&1; then + git -C "$APP_DIR" clean -fdq -- ios + fi + echo "--- restored" + exit "$status" +} -node scripts/write-credentials.mjs +if [[ "$DELIVERY" == spm ]]; then + if git -C "$APP_DIR" rev-parse --is-inside-work-tree >/dev/null 2>&1; then + # The restore is a git checkout of ios/, so refuse to start over edits + # it would throw away. + git -C "$APP_DIR" diff --quiet -- ios \ + || { echo "ios/ has uncommitted changes; commit or stash them before IOS_DELIVERY=spm" >&2; exit 1; } + else + # A campaign app outside git: keep a copy of ios/ (without Pods/build). + SPM_BACKUP="$(mktemp -t ios-cocoapods).tar" + tar -cf "$SPM_BACKUP" -C "$APP_DIR" --exclude ios/Pods --exclude 'ios/build*' ios + fi + [[ "${IOS_SPM_KEEP:-0}" == 1 ]] || trap restore_cocoapods EXIT + # --deintegrate leaves Podfile broken (README.md), hence the restore above. + (cd "$APP_DIR" && npx react-native spm add --deintegrate --yes) +else + (cd "$APP_DIR/ios" && pod install) +fi -(cd ios && pod install) +PROJECT_ARGS=(-workspace "$APP_DIR/ios/$SCHEME.xcworkspace") +[[ -d "$APP_DIR/ios/$SCHEME.xcworkspace" ]] || PROJECT_ARGS=(-project "$APP_DIR/ios/$SCHEME.xcodeproj") +DESTINATION="id=$DEVICE" +SIGNING=(DEVELOPMENT_TEAM="${IOS_DEVELOPMENT_TEAM:-}" CODE_SIGN_STYLE=Automatic -allowProvisioningUpdates) +if [[ "$TARGET" == simulator ]]; then + DESTINATION="id=$SIMULATOR" + SIGNING=() +fi xcodebuild \ - -workspace ios/BareExample.xcworkspace \ - -scheme BareExample \ + "${PROJECT_ARGS[@]}" \ + -scheme "$SCHEME" \ -configuration "$CONFIGURATION" \ - -destination "id=$DEVICE" \ - -derivedDataPath ios/build \ - DEVELOPMENT_TEAM="$IOS_DEVELOPMENT_TEAM" \ - CODE_SIGN_STYLE=Automatic \ - -allowProvisioningUpdates \ + -destination "$DESTINATION" \ + -derivedDataPath "$APP_DIR/$BUILD_DIR" \ + ${SIGNING[@]+"${SIGNING[@]}"} \ build # Before installing, not after: an app that builds without the framework is the @@ -55,7 +158,67 @@ node -e 'const [maj,min]=process.versions.node.split(".").map(Number); }' node ../../scripts/cli-assert-framework-embedded.ts "$APP" -xcrun devicectl device install app --device "$DEVICE" "$APP" +if [[ "$TARGET" == simulator ]]; then + xcrun simctl install "$SIMULATOR" "$APP" + echo + echo "Installed on simulator $SIMULATOR ($DELIVERY). Now: E2E_PLATFORM=ios E2E_IOS_TARGET=simulator yarn e2e" +else + xcrun devicectl device install app --device "$DEVICE" "$APP" + echo + echo "Installed on $DEVICE ($DELIVERY). Now: E2E_PLATFORM=ios E2E_IOS_TARGET=device yarn e2e" +fi -echo -echo "Installed on $DEVICE. Now: E2E_PLATFORM=ios E2E_IOS_TARGET=device yarn e2e" +if [[ "${IOS_LAUNCH:-0}" == 1 ]]; then + BUNDLE_ID="$(/usr/libexec/PlistBuddy -c 'Print :CFBundleIdentifier' "$APP/Info.plist")" + CONSOLE="$(mktemp -t run-ios-console)" + APP_ARGS=() + LAUNCH_SECONDS="${IOS_LAUNCH_SECONDS:-40}" + if [[ "$CONFIGURATION" == Debug ]]; then + METRO_PORT="$(cat "$APP_DIR/campaign.port" 2>/dev/null || echo "${E2E_METRO_PORT:-8081}")" + METRO_HOST=localhost + [[ "$TARGET" == device ]] && METRO_HOST="${E2E_METRO_HOST:-$(ipconfig getifaddr en0)}" + if ! curl -sf "http://localhost:$METRO_PORT/status" | grep -q running; then + if grep -q '"expo"' "$APP_DIR/app.json" 2>/dev/null; then + (cd "$APP_DIR" && CI=1 exec npx expo start --port "$METRO_PORT") >"$CONSOLE.metro" 2>&1 "$CONSOLE.metro" 2>&1 "$CONSOLE" 2>&1 "$CONSOLE" 2>&1 /dev/null || true + kill "$METRO_PID" 2>/dev/null || true + lsof -t -iTCP:"$METRO_PORT" -sTCP:LISTEN 2>/dev/null | xargs kill 2>/dev/null || true + fi + kill "$LAUNCH_PID" 2>/dev/null || true + wait "$LAUNCH_PID" 2>/dev/null || true + echo "--- console of $BUNDLE_ID ($CONFIGURATION, $DELIVERY, $TARGET)" + grep -E "BUGSEE_E2E|Bugsee|BugseeRN" "$CONSOLE" | cut -c1-240 | head -40 + if grep -qE "${IOS_LAUNCH_EXPECT:-BUGSEE_E2E status=2}" "$CONSOLE"; then + echo "launch: PASS" + else + echo "launch: FAIL (no match for ${IOS_LAUNCH_EXPECT:-BUGSEE_E2E status=2}; console kept at $CONSOLE)" + exit 1 + fi +fi diff --git a/examples/expo/App.js b/examples/expo/App.js index 861885f0..42b8bda2 100644 --- a/examples/expo/App.js +++ b/examples/expo/App.js @@ -1,8 +1,107 @@ +/** + * The Expo example (N-20): launches Bugsee and prints the markers the device + * e2e and scripts/campaign/launch-check.sh read, as examples/bare does: + * + * BUGSEE_E2E launching on rn= engine= scenario= + * BUGSEE_E2E status= () + * + * Credentials: campaign-config.json beside this file, which holds only the + * placeholder token and the dead endpoint. Never a real token. + * + * The scenario comes from the e2e deep link + * `bugsee-e2e://scenario/?nonce=` (app.json `scheme`), the channel + * examples/bare uses on Android. `launch` is the default. The N-01 smoke + * scenarios register in SCENARIOS below (additive edits only). + */ +/* global setInterval, clearInterval */ const React = require('react'); -const { Text, View } = require('react-native'); +const { Linking, Platform, Text, View } = require('react-native'); +const Bugsee = require('@bugsee/react-native'); + +const { default: bugsee, BugseeLaunchOptions, Status, createDefaultLaunchOptions } = Bugsee; + +const config = require('./campaign-config.json'); + +const STATUS_NAMES = { + [Status.Stopped]: 'Stopped', + [Status.Launching]: 'Launching', + [Status.Launched]: 'Launched', + [Status.Stopping]: 'Stopping', +}; + +/** Scenario name -> async (nonce) => void, run once Launched. */ +const SCENARIOS = { + launch: async () => undefined, +}; + +const SCENARIO_URI = /^bugsee-e2e:\/\/scenario\/([\w-]+)\?nonce=([0-9a-f]+)/; + +async function chooseScenario() { + const uri = await Linking.getInitialURL().catch(() => null); + const match = uri == null ? null : SCENARIO_URI.exec(uri); + return match ? { scenario: match[1], nonce: match[2] } : { scenario: 'launch', nonce: '-' }; +} + +function engine() { + return globalThis.HermesInternal ? 'hermes' : 'jsc'; +} function App() { - return React.createElement(View, null, React.createElement(Text, null, 'Bugsee Expo')); + const [status, setStatus] = React.useState(Status.Stopped); + + React.useEffect(() => { + let last = -1; + let cancelled = false; + const poll = setInterval(() => { + bugsee + .getStatus() + .then((next) => { + if (!cancelled && next !== last) { + last = next; + setStatus(next); + console.log(`BUGSEE_E2E status=${next} (${STATUS_NAMES[next]})`); + } + }) + .catch(() => undefined); + }, 100); + + (async () => { + const { scenario, nonce } = await chooseScenario(); + const rn = Platform.constants.reactNativeVersion; + console.log( + `BUGSEE_E2E launching on ${Platform.OS} rn=${rn.major}.${rn.minor}.${rn.patch} ` + + `engine=${engine()} scenario=${scenario}`, + ); + const options = createDefaultLaunchOptions(); + options.endpoint = config.endpoint; + const token = Platform.OS === 'ios' ? config.ios : config.android; + try { + const ok = await bugsee.launch(token, BugseeLaunchOptions.serialize(options)); + console.log(`BUGSEE_E2E launch() resolved ${ok}`); + } catch (e) { + console.log(`BUGSEE_E2E launch() rejected ${e instanceof Error ? e.message : String(e)}`); + return; + } + const run = SCENARIOS[scenario]; + if (run === undefined) { + console.log(`BUGSEE_E2E unknown scenario ${scenario}`); + return; + } + await run(nonce); + console.log(`BUGSEE_E2E scenario ${scenario} done nonce=${nonce}`); + })(); + + return () => { + cancelled = true; + clearInterval(poll); + }; + }, []); + + return React.createElement( + View, + { style: { flex: 1, alignItems: 'center', justifyContent: 'center' } }, + React.createElement(Text, null, `Bugsee Expo: ${STATUS_NAMES[status]}`), + ); } module.exports = App; diff --git a/examples/expo/app.json b/examples/expo/app.json index a2f85070..a324d08c 100644 --- a/examples/expo/app.json +++ b/examples/expo/app.json @@ -12,6 +12,7 @@ }, "plugins": [ "@bugsee/react-native" - ] + ], + "scheme": "bugsee-e2e" } } diff --git a/examples/expo/campaign-config.json b/examples/expo/campaign-config.json new file mode 100644 index 00000000..41ac6e29 --- /dev/null +++ b/examples/expo/campaign-config.json @@ -0,0 +1,5 @@ +{ + "ios": "00000000-0000-4000-8000-000000000000", + "android": "00000000-0000-4000-8000-000000000000", + "endpoint": "https://127.0.0.1:9" +} diff --git a/package.json b/package.json index 06a45d68..0bb29b20 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "assert:ios-embed": "node scripts/cli-assert-framework-embedded.ts", "mutate:src": "stryker run stryker.src.json", "mutate:scripts": "stryker run stryker.scripts.json", - "mutate:plugin": "stryker run stryker.plugin.json; status=$?; chmod 755 packages/react-native/scripts/compose-then-inject.js; exit $status" + "mutate:plugin": "stryker run stryker.plugin.json; status=$?; chmod 755 packages/react-native/scripts/compose-then-inject.js packages/react-native/scripts/hermesc-preserve-js.js; exit $status" }, "devDependencies": { "@eslint/js": "^10.0.1", diff --git a/packages/react-native/README.md b/packages/react-native/README.md index 5fe39595..c8d1643a 100644 --- a/packages/react-native/README.md +++ b/packages/react-native/README.md @@ -155,16 +155,28 @@ def bugseeDir = new File(["node", "--print", "require.resolve('@bugsee/react-nat react { // Copies the JS aside before hermesc compiles it, so the debug id can - // reach the bytecode. - hermesCommand = new File(new File(bugseeDir, "scripts"), "hermesc-preserve-js.sh").absolutePath + // reach the bytecode. React Native runs this command through cmd on + // Windows, so Windows gets the .cmd launcher and macOS and Linux the .sh. + // Keep it on one line: tools that apply this snippet read it as one. + hermesCommand = new File(new File(bugseeDir, "scripts"), System.getProperty("os.name").startsWith("Windows") ? "hermesc-preserve-js.cmd" : "hermesc-preserve-js.sh").absolutePath } // At the end of the file. Applying it twice is harmless. apply from: new File(new File(bugseeDir, "scripts"), "bugsee-sourcemaps.gradle") ``` -If Hermes compiles a bundle without going through `hermesc-preserve-js.sh`, -the bundle task fails rather than ship a release without a debug id. +The OS is checked when Gradle configures the build, so one `build.gradle` +works on macOS, Linux and Windows. Both launchers run +`scripts/hermesc-preserve-js.js` with `node`, which must be on the `PATH` (as +React Native's own bundling already needs). The wrapper exits non-zero when +hermesc writes no bytecode. + +If Hermes compiles a bundle without going through the wrapper, the bundle +task fails rather than ship a release without a debug id. On Windows a +`hermesCommand` that names a `.sh` (what earlier versions of this README +said) fails the bundle task before hermesc runs, with the line to use +instead: `cmd` cannot run a shell script. With Hermes off, the plain JS +bundle and Metro's map get the id instead. - **The upload needs a real token.** Set `app_token` in `android/bugsee.properties`, or `BUGSEE_APP_TOKEN`. Without a token, or with @@ -177,9 +189,20 @@ the bundle task fails rather than ship a release without a debug id. the path; the Gradle hook takes the path relative to the build directory. The three agree unless the bundle directory itself nests another `generated/assets`, which React Native never does. -- **Limitations.** The preserve wrapper is a shell script, so Hermes release - builds on a Windows host are not supported yet. The hook has not yet been - verified with Gradle's configuration cache. +- **Expo.** `expo prebuild` writes the same per-OS choice. A `hermesCommand` + that an earlier prebuild wrote (the `.sh` on every OS) is recognised by its + exact text and moved to the per-OS one; a `hermesCommand` of your own that + already names `hermesc-preserve-js` is left as it is. +- **Windows paths with spaces or brackets.** React Native runs + `hermesCommand` through `cmd /c`, which cuts an absolute path holding a + space together with `(` `)` (`C:\Program Files (x86)\…`), or `(` `)` alone + (`C:\Users\John(US)\…`). On Windows the hook gives the launcher to React + Native relative to the project root instead, as React Native gives its own + paths; the build is verified from `C:\s (x86)\b rn`. A project whose path is long + can still pass Windows' 260-character limit in React Native's own native + build, which has nothing to do with Bugsee. +- **Limitations.** The hook has not yet been verified with Gradle's + configuration cache. ## Android: Bugsee Gradle plugin diff --git a/packages/react-native/package.json b/packages/react-native/package.json index 3013026e..acc81761 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -17,7 +17,9 @@ "scripts/hermes-sourcemaps.js", "scripts/compose-then-inject.js", "scripts/bugsee-xcode.sh", + "scripts/hermesc-preserve-js.js", "scripts/hermesc-preserve-js.sh", + "scripts/hermesc-preserve-js.cmd", "scripts/bugsee-sourcemaps.gradle", "react-native.config.js", "*.podspec", diff --git a/packages/react-native/plugin/build/gradle.d.ts b/packages/react-native/plugin/build/gradle.d.ts index c781750f..7eb4cb94 100644 --- a/packages/react-native/plugin/build/gradle.d.ts +++ b/packages/react-native/plugin/build/gradle.d.ts @@ -33,7 +33,20 @@ export declare function ensureGradlePluginDeclared(projectBuildGradle: string, v * or no real token is configured. */ export declare function ensureAppAppliesPlugin(appBuildGradle: string, ndkVersion: string | null, log?: (message: string) => void): string; -export declare const HERMES_COMMAND_UNREWRITABLE = "@bugsee/react-native cannot edit android/app/build.gradle: react.hermesCommand spans several lines or shares its line with another statement, so it cannot be pointed at scripts/hermesc-preserve-js.sh. Put it alone on one line, or delete it, and prebuild again"; +/** + * The launcher for the host the build runs on: React Native runs + * hermesCommand through `cmd /c` on Windows, which cannot run a shell + * script. Chosen when Gradle configures, so one build.gradle serves a repo + * shared across macOS, Linux and Windows. + */ +export declare const HERMES_COMMAND_EXPR = "new File([\"node\", \"--print\", \"require.resolve('@bugsee/react-native/package.json')\"].execute(null, rootDir).text.trim()).getParentFile().getAbsolutePath() + \"/scripts/hermesc-preserve-js\" + (System.getProperty(\"os.name\").startsWith(\"Windows\") ? \".cmd\" : \".sh\")"; +/** + * The value earlier versions wrote: the shell launcher on every host, which + * a Windows build cannot run. Recognised only as this exact text, and moved + * to HERMES_COMMAND_EXPR. + */ +export declare const LEGACY_HERMES_COMMAND_EXPR = "new File([\"node\", \"--print\", \"require.resolve('@bugsee/react-native/package.json')\"].execute(null, rootDir).text.trim()).getParentFile().getAbsolutePath() + \"/scripts/hermesc-preserve-js.sh\""; +export declare const HERMES_COMMAND_UNREWRITABLE = "@bugsee/react-native cannot edit android/app/build.gradle: react.hermesCommand spans several lines or shares its line with another statement, so it cannot be pointed at scripts/hermesc-preserve-js.sh (.cmd on Windows). Put it alone on one line, or delete it, and prebuild again"; /** * `uploadSymbols: false` on Android: disables every `uploadBugsee*` task * (mapping, NDK symbols, build info) inside a marked block. On again diff --git a/packages/react-native/plugin/build/gradle.js b/packages/react-native/plugin/build/gradle.js index a03efa6e..b56d9be6 100644 --- a/packages/react-native/plugin/build/gradle.js +++ b/packages/react-native/plugin/build/gradle.js @@ -1,6 +1,6 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -exports.HERMES_COMMAND_UNREWRITABLE = exports.CANNOT_EDIT = void 0; +exports.HERMES_COMMAND_UNREWRITABLE = exports.LEGACY_HERMES_COMMAND_EXPR = exports.HERMES_COMMAND_EXPR = exports.CANNOT_EDIT = void 0; exports.ensureMavenCentral = ensureMavenCentral; exports.ensureGradlePluginDeclared = ensureGradlePluginDeclared; exports.ensureAppAppliesPlugin = ensureAppAppliesPlugin; @@ -879,21 +879,34 @@ function removeInsertedSymbolTable(source) { } return kept.join('\n'); } -const HERMES_COMMAND_EXPR = 'new File(["node", "--print", "require.resolve(\'@bugsee/react-native/package.json\')"].execute(null, rootDir).text.trim()).getParentFile().getAbsolutePath() + "/scripts/hermesc-preserve-js.sh"'; +const BUGSEE_PACKAGE_DIR_EXPR = 'new File(["node", "--print", "require.resolve(\'@bugsee/react-native/package.json\')"].execute(null, rootDir).text.trim()).getParentFile().getAbsolutePath()'; +/** + * The launcher for the host the build runs on: React Native runs + * hermesCommand through `cmd /c` on Windows, which cannot run a shell + * script. Chosen when Gradle configures, so one build.gradle serves a repo + * shared across macOS, Linux and Windows. + */ +exports.HERMES_COMMAND_EXPR = `${BUGSEE_PACKAGE_DIR_EXPR} + "/scripts/hermesc-preserve-js" + (System.getProperty("os.name").startsWith("Windows") ? ".cmd" : ".sh")`; +/** + * The value earlier versions wrote: the shell launcher on every host, which + * a Windows build cannot run. Recognised only as this exact text, and moved + * to HERMES_COMMAND_EXPR. + */ +exports.LEGACY_HERMES_COMMAND_EXPR = `${BUGSEE_PACKAGE_DIR_EXPR} + "/scripts/hermesc-preserve-js.sh"`; const SOURCEMAPS_SCRIPT = 'scripts/bugsee-sourcemaps.gradle'; /** First line of the hook this plugin writes. */ const SOURCEMAPS_HOOK_MARKER = '// bugsee-sourcemaps: debug ids and source-map upload for release bundles (@bugsee/react-native).'; /** * The hook is the package's own Gradle script, so the bare example and Expo * apps run the same code: inject after compose, upload, fail the bundle - * task when Hermes skipped hermesc-preserve-js.sh. + * task when Hermes skipped the preserve wrapper. */ const SOURCEMAPS_APPLY = `apply from: new File(new File(["node", "--print", "require.resolve('@bugsee/react-native/package.json')"].execute(null, rootDir).text.trim()).getParentFile(), "${SOURCEMAPS_SCRIPT}")`; /** First line of the inline hook earlier versions wrote. */ const LEGACY_HOOK_MARKER = '// After compose-source-maps.js. Release variants only; debug does not bundle.'; /** The line both earlier inline hooks have after their comments; user code does not. */ const LEGACY_HOOK_FINGERPRINT = 'def bugseeHermesSourcemaps = '; -exports.HERMES_COMMAND_UNREWRITABLE = `${exports.CANNOT_EDIT} ${APP_GRADLE}: react.hermesCommand spans several lines or shares its line with another statement, so it cannot be pointed at scripts/hermesc-preserve-js.sh. Put it alone on one line, or delete it, and prebuild again`; +exports.HERMES_COMMAND_UNREWRITABLE = `${exports.CANNOT_EDIT} ${APP_GRADLE}: react.hermesCommand spans several lines or shares its line with another statement, so it cannot be pointed at scripts/hermesc-preserve-js.sh (.cmd on Windows). Put it alone on one line, or delete it, and prebuild again`; /** Brackets all close, something is there, and it does not end in an operator. */ function completeExpression(code) { let depth = 0; @@ -912,11 +925,14 @@ const CONTINUATION = /^(\?\.|\.|\?|:|\+|-|\*|\/|&&|\|\|)/; const HERMES_COMMAND = /^([ \t]*)hermesCommand(\s*=(?!=)|\.set\()/; const REACT_BLOCK = /^\s*react\s*\{\s*$/; /** - * Points react.hermesCommand at hermesc-preserve-js.sh. Only the setting at - * the top level of the react block counts: one in a comment, a string, - * another block or a nested block is left alone. A one-line - * `hermesCommand = …` or `hermesCommand.set(…)` is rewritten, a trailing - * comment kept. Anything this cannot read with certainty (a value that + * Points react.hermesCommand at the preserve launcher for the host. Only the + * setting at the top level of the react block counts: one in a comment, a + * string, another block or a nested block is left alone. A value that already + * names hermesc-preserve-js is kept (this plugin's, or the user's own), except + * the exact value earlier versions wrote, which is moved to the per-host one. + * Any other one-line `hermesCommand = …` or `hermesCommand.set(…)` is + * rewritten, a trailing comment kept. Anything this cannot read with + * certainty (a value that * continues on the next line, an open bracket or multi-line string, nothing * after `=`, another statement after `;`) is refused. A react block without * the setting gets one. With no react block the file is left alone, and the @@ -940,7 +956,15 @@ function rewriteHermesCommand(source) { continue; } found = true; - if (line.raw.includes('hermesc-preserve-js.sh')) { + // The value as written, up to a trailing comment. + const valueEnd = line.raw.slice(0, line.commentAt ?? line.raw.length).trimEnd().length; + const written = line.raw.slice(match[0].length, valueEnd).trim(); + // A setter call never equals it: its value carries the closing bracket. + if (written === exports.LEGACY_HERMES_COMMAND_EXPR) { + lines[i] = `${match[1]}hermesCommand = ${exports.HERMES_COMMAND_EXPR}${line.raw.slice(valueEnd)}`; + continue; + } + if (line.raw.includes('hermesc-preserve-js')) { continue; } // Comments are spaces in the mask, so the whole masked line is the value. @@ -951,8 +975,7 @@ function rewriteHermesCommand(source) { throw new Error(exports.HERMES_COMMAND_UNREWRITABLE); } // The value is replaced; what follows it (whitespace, a comment, the CR) is kept. - const valueEnd = line.raw.slice(0, line.commentAt ?? line.raw.length).trimEnd().length; - lines[i] = `${match[1]}hermesCommand = ${HERMES_COMMAND_EXPR}${line.raw.slice(valueEnd)}`; + lines[i] = `${match[1]}hermesCommand = ${exports.HERMES_COMMAND_EXPR}${line.raw.slice(valueEnd)}`; } if (!found) { const reactLine = s.lines[react]; @@ -961,7 +984,7 @@ function rewriteHermesCommand(source) { } const opener = reactLine.raw; const indent = opener.slice(0, opener.length - opener.trimStart().length); - lines.splice(react + 1, 0, `${indent} hermesCommand = ${HERMES_COMMAND_EXPR}${crOf(eol)}`); + lines.splice(react + 1, 0, `${indent} hermesCommand = ${exports.HERMES_COMMAND_EXPR}${crOf(eol)}`); } return lines.join('\n'); } diff --git a/packages/react-native/plugin/build/gradle.js.map b/packages/react-native/plugin/build/gradle.js.map index 05466073..90d9f902 100644 --- a/packages/react-native/plugin/build/gradle.js.map +++ b/packages/react-native/plugin/build/gradle.js.map @@ -1 +1 @@ -{"version":3,"file":"gradle.js","sourceRoot":"","sources":["../src/gradle.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"gradle.js","sourceRoot":"","sources":["../src/gradle.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/packages/react-native/plugin/src/__tests__/gradle-corpus.test.ts b/packages/react-native/plugin/src/__tests__/gradle-corpus.test.ts index d97d91d8..5372717b 100644 --- a/packages/react-native/plugin/src/__tests__/gradle-corpus.test.ts +++ b/packages/react-native/plugin/src/__tests__/gradle-corpus.test.ts @@ -47,6 +47,19 @@ beforeAll(() => { EXPR = line.replace(/^\s*hermesCommand = /, ''); }); +/** The value the plugin wrote before Task 13.7: the shell launcher on every OS. */ +const LEGACY_EXPR = + 'new File(["node", "--print", "require.resolve(\'@bugsee/react-native/package.json\')"].execute(null, rootDir).text.trim()).getParentFile().getAbsolutePath() + "/scripts/hermesc-preserve-js.sh"'; + +/** The earlier line is gone and the per-OS one stands `count` times. */ +const expectMigrated = + (count: number) => + (output: string): void => { + expect(output).not.toContain(LEGACY_EXPR); + expect(countLines(output, `hermesCommand = ${EXPR}`)).toBe(count); + expect(EXPR).toContain('(System.getProperty("os.name").startsWith("Windows") ? ".cmd" : ".sh")'); + }; + const EXCLUDE_BLOCK = [ 'configurations.configureEach {', " exclude group: 'com.bugsee', module: 'bugsee-android-ndk'", @@ -769,6 +782,92 @@ const appCases: Case[] = [ segments: [u(REACT_APPLY), u(reactOpen), u(' hermesCommand = foo(bar\n )'), u(reactClose)], refuse: true, }, + + // --- Task 13.7: the .sh-on-every-OS line an earlier prebuild wrote --- + { + name: 'the hermesCommand an earlier prebuild wrote moves to the per-OS launcher', + segments: [u(REACT_APPLY), u(reactOpen), h(` hermesCommand = ${LEGACY_EXPR}`), u(reactClose), u(userDeps)], + check: expectMigrated(1), + }, + { + name: 'the earlier prebuild line keeps a trailing comment the user added', + segments: [u(REACT_APPLY), u(reactOpen), h(` hermesCommand = ${LEGACY_EXPR} // mine`, ' // mine'), u(reactClose)], + check: (output) => { + expect(output).not.toContain(LEGACY_EXPR); + expect(output).toContain(` hermesCommand = ${EXPR} // mine\n`); + }, + }, + { + name: 'the earlier prebuild line, CRLF', + segments: [u(REACT_APPLY), u(reactOpen), h(` hermesCommand = ${LEGACY_EXPR}`), u(reactClose), u(userDeps)], + crlf: true, + check: (output) => { + expectMigrated(1)(output); + expect(output).toContain(` hermesCommand = ${EXPR}\r\n`); + }, + }, + { + name: 'the earlier prebuild line at tab indentation, among other react settings', + segments: [ + u(REACT_APPLY), + u(reactOpen), + u('\tbundleCommand = "export:embed"'), + h(`\thermesCommand = ${LEGACY_EXPR}`), + u('\t// hermesCommand = "$rootDir/my-custom-hermesc/bin/hermesc"'), + u(reactClose), + ], + check: (output) => { + expectMigrated(1)(output); + expect(output).toContain(`\thermesCommand = ${EXPR}\n`); + }, + }, + { + name: 'a user hermesCommand that names the .sh itself is the user\'s, and stays', + segments: [ + u('def bugseeDir = new File(["node", "--print", "require.resolve(\'@bugsee/react-native/package.json\')"].execute(null, rootDir).text.trim()).getParentFile()'), + u(REACT_APPLY), + u(reactOpen), + u(' hermesCommand = new File(new File(bugseeDir, "scripts"), "hermesc-preserve-js.sh").absolutePath'), + u(reactClose), + ], + check: (output) => expect(output).not.toContain(EXPR), + }, + { + name: 'a user hermesCommand that names the .cmd stays', + segments: [u(REACT_APPLY), u(reactOpen), u(' hermesCommand = "C:/tools/hermesc-preserve-js.cmd"'), u(reactClose)], + check: (output) => expect(output).not.toContain(EXPR), + }, + { + name: 'the earlier expression with more after it is not the plugin\'s line, and stays', + segments: [u(REACT_APPLY), u(reactOpen), u(` hermesCommand = ${LEGACY_EXPR} + ""`), u(reactClose)], + check: (output) => expect(output).not.toContain(EXPR), + }, + { + name: 'the earlier expression in a setter call is not the plugin\'s line, and stays', + segments: [u(REACT_APPLY), u(reactOpen), u(` hermesCommand.set(${LEGACY_EXPR})`), u(reactClose)], + check: (output) => expect(output).not.toContain(EXPR), + }, + { + name: 'the earlier expression with a statement after ; is not the plugin\'s line, and stays', + segments: [u(REACT_APPLY), u(reactOpen), u(` hermesCommand = ${LEGACY_EXPR}; bundleCommand = "x"`), u(reactClose)], + check: (output) => expect(output).not.toContain(EXPR), + }, + { + name: 'the earlier prebuild line inside a block comment stays as written', + segments: [u(REACT_APPLY), u(reactOpen), u(` /*\n hermesCommand = ${LEGACY_EXPR}\n */`), u(reactClose)], + check: (output) => { + expect(output).toContain(` hermesCommand = ${LEGACY_EXPR}\n`); + expect(countLines(output, `hermesCommand = ${EXPR}`)).toBe(1); + }, + }, + { + name: 'the earlier prebuild line in another block stays as written', + segments: [u(REACT_APPLY), u(`myTool {\n hermesCommand = ${LEGACY_EXPR}\n}`), u(reactOpen), u(reactClose)], + check: (output) => { + expect(output).toContain(` hermesCommand = ${LEGACY_EXPR}\n`); + expect(countLines(output, `hermesCommand = ${EXPR}`)).toBe(1); + }, + }, ]; // --- Re-review 4: anything else on an anchor line, and user code shaped like Bugsee's lines --- diff --git a/packages/react-native/plugin/src/__tests__/plugin.test.ts b/packages/react-native/plugin/src/__tests__/plugin.test.ts index 9b29515e..2ffe3286 100644 --- a/packages/react-native/plugin/src/__tests__/plugin.test.ts +++ b/packages/react-native/plugin/src/__tests__/plugin.test.ts @@ -553,7 +553,7 @@ describe('Android Gradle edits', () => { const hermes = next.split('\n').filter((line) => /^\s*hermesCommand\s*=/.test(line)); expect(hermes).toEqual([ - ' hermesCommand = new File(["node", "--print", "require.resolve(\'@bugsee/react-native/package.json\')"].execute(null, rootDir).text.trim()).getParentFile().getAbsolutePath() + "/scripts/hermesc-preserve-js.sh"', + ' hermesCommand = new File(["node", "--print", "require.resolve(\'@bugsee/react-native/package.json\')"].execute(null, rootDir).text.trim()).getParentFile().getAbsolutePath() + "/scripts/hermesc-preserve-js" + (System.getProperty("os.name").startsWith("Windows") ? ".cmd" : ".sh")', ]); expect(hermes[0]).not.toContain('../../node_modules'); expect(hermes[0]).not.toContain('hermes-compiler'); @@ -577,7 +577,7 @@ describe('Android Gradle edits', () => { expect(buildTypeBody(off, 'release')).toContain('minifyEnabled enableMinifyInReleaseBuilds'); expect(buildTypeBody(off, 'debug')).not.toContain('ndk'); expect(buildTypeBody(off, 'release')).not.toContain('ndk'); - expect(off).toContain('hermesc-preserve-js.sh'); + expect(off).toContain('"/scripts/hermesc-preserve-js" + (System.getProperty("os.name").startsWith("Windows") ? ".cmd" : ".sh")'); expect(off.match(/bugsee-sourcemaps\.gradle/g)).toHaveLength(1); expect(ensureAppAppliesPlugin(off, null)).toBe(off); }); @@ -593,7 +593,7 @@ describe('Android Gradle edits', () => { writeFileSync(join(app, 'node_modules/react-native/package.json'), '{"name":"react-native"}\n'); writeFileSync(join(nested, 'package.json'), '{"name":"hermes-compiler"}\n'); const hermesc = join(nested, 'hermesc', osbin, bin); - writeFileSync(hermesc, '#!/bin/sh\nexit 0\n'); + writeFileSync(hermesc, '#!/bin/sh\nout=""; prev=""\nfor a in "$@"; do [ "$prev" = "-out" ] && out="$a"; prev="$a"; done\nprintf HBC > "$out"\n'); chmodSync(hermesc, 0o755); const shipped = join(app, 'node_modules/react-native/sdks/hermesc', osbin, bin); mkdirSync(join(app, 'node_modules/react-native/sdks/hermesc', osbin), { recursive: true }); @@ -639,7 +639,7 @@ describe('Android Gradle edits', () => { mkdirSync(join(rn, 'sdks/hermesc', osbin), { recursive: true }); writeFileSync(join(rn, 'package.json'), '{"name":"react-native"}\n'); const hermesc = join(rn, 'sdks/hermesc', osbin, bin); - writeFileSync(hermesc, '#!/bin/sh\nexit 0\n'); + writeFileSync(hermesc, '#!/bin/sh\nout=""; prev=""\nfor a in "$@"; do [ "$prev" = "-out" ] && out="$a"; prev="$a"; done\nprintf HBC > "$out"\n'); chmodSync(hermesc, 0o755); expect(existsSync(join(app, 'node_modules/hermes-compiler'))).toBe(false); diff --git a/packages/react-native/plugin/src/__tests__/transforms.test.ts b/packages/react-native/plugin/src/__tests__/transforms.test.ts index 31e0adaa..22ffd218 100644 --- a/packages/react-native/plugin/src/__tests__/transforms.test.ts +++ b/packages/react-native/plugin/src/__tests__/transforms.test.ts @@ -47,7 +47,7 @@ const SYMBOL_BLOCK = (indent: string): string => .join('\n'); const HERMES_COMMAND = - 'hermesCommand = new File(["node", "--print", "require.resolve(\'@bugsee/react-native/package.json\')"].execute(null, rootDir).text.trim()).getParentFile().getAbsolutePath() + "/scripts/hermesc-preserve-js.sh"'; + 'hermesCommand = new File(["node", "--print", "require.resolve(\'@bugsee/react-native/package.json\')"].execute(null, rootDir).text.trim()).getParentFile().getAbsolutePath() + "/scripts/hermesc-preserve-js" + (System.getProperty("os.name").startsWith("Windows") ? ".cmd" : ".sh")'; describe('ensureMavenCentral, exactly', () => { const header = lines( diff --git a/packages/react-native/plugin/src/__tests__/with-bugsee.test.ts b/packages/react-native/plugin/src/__tests__/with-bugsee.test.ts index bfa9bba0..c4e9495b 100644 --- a/packages/react-native/plugin/src/__tests__/with-bugsee.test.ts +++ b/packages/react-native/plugin/src/__tests__/with-bugsee.test.ts @@ -114,7 +114,7 @@ describe('withBugsee through the Expo mod compiler', () => { expect(app).toContain('apply plugin: "com.bugsee.android.gradle"'); expect(app).toContain(`implementation "com.bugsee:bugsee-android-ndk:${baked.sdk}"`); expect(app).toContain('"scripts/bugsee-sourcemaps.gradle")'); - expect(app).toContain('hermesc-preserve-js.sh'); + expect(app).toContain('"/scripts/hermesc-preserve-js" + (System.getProperty("os.name").startsWith("Windows") ? ".cmd" : ".sh")'); expect(app).not.toContain('bugsee-upload-symbols-off:'); expect(read('gradleProperties')).not.toContain('bugseeUploadSourcemaps'); expect(read('manifest')).not.toContain('com.bugsee.app-token'); diff --git a/packages/react-native/plugin/src/gradle.ts b/packages/react-native/plugin/src/gradle.ts index 54d5ea53..9c1e6ae1 100644 --- a/packages/react-native/plugin/src/gradle.ts +++ b/packages/react-native/plugin/src/gradle.ts @@ -1031,8 +1031,23 @@ function removeInsertedSymbolTable(source: string): string { return kept.join('\n'); } -const HERMES_COMMAND_EXPR = - 'new File(["node", "--print", "require.resolve(\'@bugsee/react-native/package.json\')"].execute(null, rootDir).text.trim()).getParentFile().getAbsolutePath() + "/scripts/hermesc-preserve-js.sh"'; +const BUGSEE_PACKAGE_DIR_EXPR = + 'new File(["node", "--print", "require.resolve(\'@bugsee/react-native/package.json\')"].execute(null, rootDir).text.trim()).getParentFile().getAbsolutePath()'; + +/** + * The launcher for the host the build runs on: React Native runs + * hermesCommand through `cmd /c` on Windows, which cannot run a shell + * script. Chosen when Gradle configures, so one build.gradle serves a repo + * shared across macOS, Linux and Windows. + */ +export const HERMES_COMMAND_EXPR = `${BUGSEE_PACKAGE_DIR_EXPR} + "/scripts/hermesc-preserve-js" + (System.getProperty("os.name").startsWith("Windows") ? ".cmd" : ".sh")`; + +/** + * The value earlier versions wrote: the shell launcher on every host, which + * a Windows build cannot run. Recognised only as this exact text, and moved + * to HERMES_COMMAND_EXPR. + */ +export const LEGACY_HERMES_COMMAND_EXPR = `${BUGSEE_PACKAGE_DIR_EXPR} + "/scripts/hermesc-preserve-js.sh"`; const SOURCEMAPS_SCRIPT = 'scripts/bugsee-sourcemaps.gradle'; @@ -1043,7 +1058,7 @@ const SOURCEMAPS_HOOK_MARKER = /** * The hook is the package's own Gradle script, so the bare example and Expo * apps run the same code: inject after compose, upload, fail the bundle - * task when Hermes skipped hermesc-preserve-js.sh. + * task when Hermes skipped the preserve wrapper. */ const SOURCEMAPS_APPLY = `apply from: new File(new File(["node", "--print", "require.resolve('@bugsee/react-native/package.json')"].execute(null, rootDir).text.trim()).getParentFile(), "${SOURCEMAPS_SCRIPT}")`; @@ -1053,7 +1068,7 @@ const LEGACY_HOOK_MARKER = '// After compose-source-maps.js. Release variants on /** The line both earlier inline hooks have after their comments; user code does not. */ const LEGACY_HOOK_FINGERPRINT = 'def bugseeHermesSourcemaps = '; -export const HERMES_COMMAND_UNREWRITABLE = `${CANNOT_EDIT} ${APP_GRADLE}: react.hermesCommand spans several lines or shares its line with another statement, so it cannot be pointed at scripts/hermesc-preserve-js.sh. Put it alone on one line, or delete it, and prebuild again`; +export const HERMES_COMMAND_UNREWRITABLE = `${CANNOT_EDIT} ${APP_GRADLE}: react.hermesCommand spans several lines or shares its line with another statement, so it cannot be pointed at scripts/hermesc-preserve-js.sh (.cmd on Windows). Put it alone on one line, or delete it, and prebuild again`; /** Brackets all close, something is there, and it does not end in an operator. */ function completeExpression(code: string): boolean { @@ -1075,11 +1090,14 @@ const HERMES_COMMAND = /^([ \t]*)hermesCommand(\s*=(?!=)|\.set\()/; const REACT_BLOCK = /^\s*react\s*\{\s*$/; /** - * Points react.hermesCommand at hermesc-preserve-js.sh. Only the setting at - * the top level of the react block counts: one in a comment, a string, - * another block or a nested block is left alone. A one-line - * `hermesCommand = …` or `hermesCommand.set(…)` is rewritten, a trailing - * comment kept. Anything this cannot read with certainty (a value that + * Points react.hermesCommand at the preserve launcher for the host. Only the + * setting at the top level of the react block counts: one in a comment, a + * string, another block or a nested block is left alone. A value that already + * names hermesc-preserve-js is kept (this plugin's, or the user's own), except + * the exact value earlier versions wrote, which is moved to the per-host one. + * Any other one-line `hermesCommand = …` or `hermesCommand.set(…)` is + * rewritten, a trailing comment kept. Anything this cannot read with + * certainty (a value that * continues on the next line, an open bracket or multi-line string, nothing * after `=`, another statement after `;`) is refused. A react block without * the setting gets one. With no react block the file is left alone, and the @@ -1103,7 +1121,15 @@ function rewriteHermesCommand(source: string): string { continue; } found = true; - if (line.raw.includes('hermesc-preserve-js.sh')) { + // The value as written, up to a trailing comment. + const valueEnd = line.raw.slice(0, line.commentAt ?? line.raw.length).trimEnd().length; + const written = line.raw.slice(match[0].length, valueEnd).trim(); + // A setter call never equals it: its value carries the closing bracket. + if (written === LEGACY_HERMES_COMMAND_EXPR) { + lines[i] = `${match[1] as string}hermesCommand = ${HERMES_COMMAND_EXPR}${line.raw.slice(valueEnd)}`; + continue; + } + if (line.raw.includes('hermesc-preserve-js')) { continue; } // Comments are spaces in the mask, so the whole masked line is the value. @@ -1114,7 +1140,6 @@ function rewriteHermesCommand(source: string): string { throw new Error(HERMES_COMMAND_UNREWRITABLE); } // The value is replaced; what follows it (whitespace, a comment, the CR) is kept. - const valueEnd = line.raw.slice(0, line.commentAt ?? line.raw.length).trimEnd().length; lines[i] = `${match[1] as string}hermesCommand = ${HERMES_COMMAND_EXPR}${line.raw.slice(valueEnd)}`; } if (!found) { diff --git a/packages/react-native/scripts/__tests__/hermes-sourcemaps.test.js b/packages/react-native/scripts/__tests__/hermes-sourcemaps.test.js index 86fbe898..bc81c2a6 100644 --- a/packages/react-native/scripts/__tests__/hermes-sourcemaps.test.js +++ b/packages/react-native/scripts/__tests__/hermes-sourcemaps.test.js @@ -182,7 +182,7 @@ fs.writeFileSync(out, JSON.stringify({ version: 3, file: 'recomposed.js', mappin fs.writeFileSync(packaged, 'console.log("bugsee-fixture")\n'); const hermesc = path.join(dir, 'hermesc'); - fs.writeFileSync(hermesc, '#!/bin/sh\nexit 0\n'); + fs.writeFileSync(hermesc, '#!/bin/sh\nout=""; prev=""\nfor a in "$@"; do [ "$prev" = "-out" ] && out="$a"; prev="$a"; done\nprintf HBC > "$out"\n'); fs.chmodSync(hermesc, 0o755); const preserve = path.join(__dirname, '..', 'hermesc-preserve-js.sh'); const saved = cp.spawnSync( @@ -804,7 +804,7 @@ describe('preserve directory paths', () => { const packaged = path.join(assets, 'index.android.bundle'); fs.writeFileSync(packaged, 'console.log("bugsee-fixture")\n'); const hermesc = path.join(root, 'hermesc'); - fs.writeFileSync(hermesc, '#!/bin/sh\nexit 0\n'); + fs.writeFileSync(hermesc, '#!/bin/sh\nout=""; prev=""\nfor a in "$@"; do [ "$prev" = "-out" ] && out="$a"; prev="$a"; done\nprintf HBC > "$out"\n'); fs.chmodSync(hermesc, 0o755); const saved = cp.spawnSync( path.join(__dirname, '..', 'hermesc-preserve-js.sh'), diff --git a/packages/react-native/scripts/__tests__/hermesc-preserve-js.test.js b/packages/react-native/scripts/__tests__/hermesc-preserve-js.test.js new file mode 100644 index 00000000..e1311fa0 --- /dev/null +++ b/packages/react-native/scripts/__tests__/hermesc-preserve-js.test.js @@ -0,0 +1,522 @@ +'use strict'; + +// The react.hermesCommand wrapper (Task 13.7): the work is in +// hermesc-preserve-js.js, run in-process here so mutation testing sees it; +// the .sh and .cmd launchers are run (or, off Windows, read) at the end. + +const cp = require('node:child_process'); +const fs = require('node:fs'); +const os = require('node:os'); +const path = require('node:path'); + +const { + bundleOf, + findHermesc, + hermescBin, + isExecutableFile, + isFile, + outputOf, + preserveDirOf, + resolveFrom, + run, + spawnInherit, +} = require('../hermesc-preserve-js'); +const { useScratchCwd } = require('./fixtures/scratch-cwd'); + +useScratchCwd(); + +const SCRIPTS = path.join(__dirname, '..'); +const WIN = process.platform === 'win32'; + +/** A stand-in hermesc (POSIX shell) that writes `content` to its -out (nothing when null). */ +function fakeHermesc(dir, content = 'HBC', status = 0) { + const file = path.join(dir, 'hermesc'); + fs.writeFileSync( + file, + `#!/bin/sh +out=""; prev="" +for a in "$@"; do [ "$prev" = "-out" ] && out="$a"; prev="$a"; done +${content === null ? '' : `printf '%s' '${content}' > "$out"`} +exit ${status} +`, + ); + fs.chmodSync(file, 0o755); + return file; +} + +describe('argument parsing', () => { + it('takes the value after the last -out', () => { + expect(outputOf(['-w', '-out', 'a.hbc', 'x.js', '-out', 'b.hbc'])).toBe('b.hbc'); + expect(outputOf(['-w', '-out', 'a.hbc', 'x.js'])).toBe('a.hbc'); + expect(outputOf(['x.js', '-out'])).toBeUndefined(); + // A trailing -out with nothing after it leaves the earlier value. + expect(outputOf(['-out', 'a.hbc', '-out'])).toBe('a.hbc'); + expect(outputOf(['-emit-binary', 'x.js'])).toBeUndefined(); + expect(outputOf([])).toBeUndefined(); + }); + + it('takes the first existing file that is neither a flag nor the output', () => { + const files = new Set(['out.hbc', 'a.js', 'b.js', '-weird']); + const isFile = (arg) => files.has(arg); + expect(bundleOf(['-w', '-weird', '-out', 'out.hbc', 'missing.js', 'a.js', 'b.js'], 'out.hbc', isFile)).toBe('a.js'); + expect(bundleOf(['out.hbc', 'b.js'], 'out.hbc', isFile)).toBe('b.js'); + expect(bundleOf(['out.hbc'], undefined, isFile)).toBe('out.hbc'); + expect(bundleOf(['-w', 'missing.js'], undefined, isFile)).toBeUndefined(); + }); + + it('names hermesc the way React Native does on each host', () => { + expect(hermescBin('win32')).toEqual({ osbin: 'win64-bin', bin: 'hermesc.exe' }); + expect(hermescBin('linux')).toEqual({ osbin: 'linux64-bin', bin: 'hermesc' }); + expect(hermescBin('darwin')).toEqual({ osbin: 'osx-bin', bin: 'hermesc' }); + expect(hermescBin('freebsd')).toEqual({ osbin: 'osx-bin', bin: 'hermesc' }); + }); +}); + +describe('findHermesc', () => { + const cwd = path.join(path.sep, 'app'); + const rnDir = path.join(cwd, 'node_modules', 'react-native'); + const compilerDir = path.join(rnDir, 'node_modules', 'hermes-compiler'); + const compiled = path.join(compilerDir, 'hermesc', 'osx-bin', 'hermesc'); + const shipped = path.join(rnDir, 'sdks', 'hermesc', 'osx-bin', 'hermesc'); + const sibling = path.join(cwd, 'node_modules', 'hermes-compiler', 'hermesc', 'osx-bin', 'hermesc'); + const built = path.join(cwd, 'node_modules', 'react-native', 'sdks', 'hermes', 'build', 'bin', 'hermesc'); + + /** resolve() that knows react-native from cwd and hermes-compiler from react-native only. */ + const resolver = ({ rn = true, compiler = true } = {}) => { + const calls = []; + const resolve = (request, from) => { + calls.push([request, from]); + if (request === 'react-native/package.json' && rn && from === cwd) return path.join(rnDir, 'package.json'); + if (request === 'hermes-compiler/package.json' && compiler && from === rnDir) { + return path.join(compilerDir, 'package.json'); + } + return null; + }; + return { resolve, calls }; + }; + const find = (present, opts = {}, env = {}, platform = 'darwin') => + findHermesc({ + env, + cwd, + platform, + isExecutable: (file) => present.includes(file), + resolve: resolver(opts).resolve, + }); + + it('prefers an executable BUGSEE_REAL_HERMESC', () => { + expect(find(['/x/hermesc', compiled], {}, { BUGSEE_REAL_HERMESC: '/x/hermesc' })).toBe('/x/hermesc'); + }); + + it('ignores a BUGSEE_REAL_HERMESC that is not executable', () => { + expect(find([compiled], {}, { BUGSEE_REAL_HERMESC: '/x/hermesc' })).toBe(compiled); + }); + + it('resolves hermes-compiler from the react-native package, then the shipped sdks/hermesc', () => { + const { resolve, calls } = resolver(); + expect( + findHermesc({ env: {}, cwd, platform: 'darwin', isExecutable: (f) => [compiled, shipped].includes(f), resolve }), + ).toBe(compiled); + expect(calls).toEqual([ + ['react-native/package.json', cwd], + ['hermes-compiler/package.json', rnDir], + ]); + expect(find([shipped, sibling])).toBe(shipped); + expect(find([shipped], { compiler: false })).toBe(shipped); + }); + + it('falls back to the app node_modules, hermes-compiler before a source build', () => { + expect(find([sibling, built], { rn: false })).toBe(sibling); + expect(find([built], { rn: false })).toBe(built); + expect(find([sibling], { compiler: false })).toBe(sibling); + expect(find([], { rn: false })).toBeNull(); + // Without react-native, the paths under it are never looked at. + expect(find([compiled, shipped], { rn: false })).toBeNull(); + }); + + it('takes the first candidate that exists, in order, and nothing else', () => { + expect(find(['/anything'], { rn: false }, {}, 'darwin')).toBeNull(); + const all = findHermesc({ env: {}, cwd, platform: 'darwin', isExecutable: () => true, resolve: () => null }); + expect(all).toBe(sibling); + }); + + it('uses the Windows directory and file name on Windows', () => { + const win = path.join(compilerDir, 'hermesc', 'win64-bin', 'hermesc.exe'); + expect(find([win, compiled], {}, {}, 'win32')).toBe(win); + }); + + it('resolves for real from the working directory', () => { + const app = fs.mkdtempSync(path.join(os.tmpdir(), 'bugsee-find-hermesc-')); + try { + const { osbin, bin } = hermescBin(process.platform); + const rn = path.join(app, 'node_modules', 'react-native'); + const compiler = path.join(rn, 'node_modules', 'hermes-compiler'); + fs.mkdirSync(path.join(compiler, 'hermesc', osbin), { recursive: true }); + fs.writeFileSync(path.join(rn, 'package.json'), '{"name":"react-native"}\n'); + fs.writeFileSync(path.join(compiler, 'package.json'), '{"name":"hermes-compiler"}\n'); + const real = path.join(compiler, 'hermesc', osbin, bin); + fs.writeFileSync(real, ''); + fs.chmodSync(real, 0o755); + const found = findHermesc({ env: {}, cwd: app, platform: process.platform, isExecutable: fs.existsSync }); + // .native: Windows temp paths can come back in 8.3 short form. + expect(fs.realpathSync.native(found)).toBe(fs.realpathSync.native(real)); + } finally { + fs.rmSync(app, { recursive: true, force: true }); + } + }); +}); + +describe('file helpers', () => { + let dir; + beforeEach(() => { + dir = fs.mkdtempSync(path.join(os.tmpdir(), 'bugsee-preserve-helpers-')); + }); + afterEach(() => { + fs.rmSync(dir, { recursive: true, force: true }); + }); + + it('resolveFrom resolves from the given directory, or gives null', () => { + fs.mkdirSync(path.join(dir, 'node_modules', 'pkg'), { recursive: true }); + fs.writeFileSync(path.join(dir, 'node_modules', 'pkg', 'package.json'), '{"name":"pkg"}'); + expect(fs.realpathSync.native(resolveFrom('pkg/package.json', dir))).toBe( + fs.realpathSync.native(path.join(dir, 'node_modules', 'pkg', 'package.json')), + ); + expect(resolveFrom('no-such-package-for-bugsee-tests/package.json', dir)).toBeNull(); + }); + + it('isFile and isExecutableFile say false, not undefined, for what is not there', () => { + const file = path.join(dir, 'f'); + fs.writeFileSync(file, 'x'); + expect(isFile(file)).toBe(true); + expect(isFile(dir)).toBe(false); + expect(isFile(path.join(dir, 'missing'))).toBe(false); + expect(isExecutableFile(path.join(dir, 'missing'))).toBe(false); + expect(isExecutableFile(dir)).toBe(false); + if (!WIN) { + expect(isExecutableFile(file)).toBe(false); + fs.chmodSync(file, 0o755); + expect(isExecutableFile(file)).toBe(true); + } + }); + + it('spawnInherit runs in the given directory and hands hermesc the build log', () => { + const result = spawnInherit(process.execPath, ['-e', 'process.exit(process.cwd() === process.argv[1] ? 7 : 8)', fs.realpathSync(dir)], fs.realpathSync(dir)); + expect(result.status).toBe(7); + // Inherited streams are not captured. + expect(result.stdout).toBeNull(); + expect(result.stderr).toBeNull(); + }); +}); + +describe('preserveDirOf', () => { + // Absolute on every host (a drive letter on Windows), as run() passes it. + const root = path.resolve(path.sep, 'app'); + const build = path.join(root, 'android', 'app', 'build'); + const js = path.join(build, 'generated', 'assets', 'react', 'release', 'index.android.bundle'); + const jsDir = path.dirname(js); + + it('maps generated/assets to intermediates/bugsee-sourcemaps', () => { + expect(preserveDirOf(js, {}, root)).toEqual({ + dir: path.join(build, 'intermediates', 'bugsee-sourcemaps', 'react', 'release'), + }); + }); + + it('refuses a bundle outside generated/assets', () => { + const elsewhere = path.join(root, 'out', 'index.android.bundle'); + expect(preserveDirOf(elsewhere, {}, root)).toEqual({ + error: `refusing to write preserve files beside the bundle (${path.dirname(elsewhere)})`, + }); + }); + + it('takes BUGSEE_PRESERVE_DIR, relative to the working directory', () => { + expect(preserveDirOf(js, { BUGSEE_PRESERVE_DIR: 'keep' }, build)).toEqual({ dir: path.join(build, 'keep') }); + // Beside the asset directory, not in it: a shared name prefix is fine. + expect(preserveDirOf(js, { BUGSEE_PRESERVE_DIR: `${jsDir}-keep` }, root)).toEqual({ dir: `${jsDir}-keep` }); + }); + + it('refuses a BUGSEE_PRESERVE_DIR in the packaged asset directory', () => { + const error = { error: 'refusing to write preserve files into the packaged asset directory' }; + expect(preserveDirOf(js, { BUGSEE_PRESERVE_DIR: jsDir }, root)).toEqual(error); + expect(preserveDirOf(js, { BUGSEE_PRESERVE_DIR: path.join(jsDir, 'sub') }, root)).toEqual(error); + }); +}); + +describe('run', () => { + let dir; + let assets; + let js; + let intermediates; + let lines; + const stderr = (line) => lines.push(line); + const argv = () => ['-w', '-emit-binary', '-out', `${js}.hbc`, js, '-O', '-output-source-map']; + + beforeEach(() => { + dir = fs.mkdtempSync(path.join(os.tmpdir(), 'bugsee-preserve-run-')); + assets = path.join(dir, 'build', 'generated', 'assets', 'react', 'release'); + intermediates = path.join(dir, 'build', 'intermediates', 'bugsee-sourcemaps', 'react', 'release'); + fs.mkdirSync(assets, { recursive: true }); + js = path.join(assets, 'index.android.bundle'); + fs.writeFileSync(js, 'console.log("bundle")\n'); + lines = []; + }); + + afterEach(() => { + fs.rmSync(dir, { recursive: true, force: true }); + }); + + /** A spawn stand-in that records its call and writes `content` to -out. */ + const spawnWriting = (content, result = { status: 0 }) => { + const calls = []; + const spawn = (file, args, cwd) => { + calls.push({ file, args, cwd, staleGone: !fs.existsSync(`${js}.hbc`) }); + if (content !== null) fs.writeFileSync(args[args.indexOf('-out') + 1], content); + return result; + }; + return { spawn, calls }; + }; + + it('preserves the JS, notes hermesc, runs it with the same arguments and exits 0', () => { + fs.writeFileSync(`${js}.hbc`, 'stale'); + const { spawn, calls } = spawnWriting('HBC'); + const code = run(argv(), { env: { BUGSEE_REAL_HERMESC: '/h/hermesc' }, cwd: dir, isExecutable: () => true, spawn, stderr }); + expect(code).toBe(0); + expect(lines).toEqual([]); + expect(calls).toEqual([{ file: '/h/hermesc', args: argv(), cwd: dir, staleGone: true }]); + expect(fs.readFileSync(path.join(intermediates, 'index.android.bundle.bugsee-js-source'), 'utf8')).toBe( + 'console.log("bundle")\n', + ); + expect(fs.readFileSync(path.join(intermediates, 'index.android.bundle.bugsee-hermesc'), 'utf8')).toBe('/h/hermesc\n'); + expect(fs.readdirSync(assets).sort()).toEqual(['index.android.bundle', 'index.android.bundle.hbc']); + }); + + it('reads relative arguments against the working directory, as React Native passes them on Windows', () => { + const rel = path.relative(dir, js); + const calls = []; + const relArgv = ['-w', '-out', `${rel}.hbc`, rel]; + const write = (file, args, cwd) => { + calls.push({ file, args, cwd }); + fs.writeFileSync(path.join(cwd, `${rel}.hbc`), 'HBC'); + return { status: 0 }; + }; + expect(run(relArgv, { env: { BUGSEE_REAL_HERMESC: '/h' }, cwd: dir, isExecutable: () => true, spawn: write, stderr })).toBe(0); + expect(calls).toEqual([{ file: '/h', args: relArgv, cwd: dir }]); + expect(fs.existsSync(path.join(intermediates, 'index.android.bundle.bugsee-js-source'))).toBe(true); + }); + + it('takes the host platform unless one is given', () => { + const win = path.join(dir, 'node_modules', 'hermes-compiler', 'hermesc', 'win64-bin', 'hermesc.exe'); + const { spawn, calls } = spawnWriting('HBC'); + const deps = { env: {}, cwd: dir, isExecutable: (f) => f === win, resolve: () => null, spawn, stderr }; + expect(run(argv(), { ...deps, platform: 'win32' })).toBe(0); + expect(calls.map((call) => call.file)).toEqual([win]); + expect(run(argv(), deps)).toBe(WIN ? 0 : 1); + }); + + it('writes its failures to stderr by default', () => { + const write = jest.spyOn(process.stderr, 'write').mockImplementation(() => true); + try { + expect(run(['-w'], { env: {}, cwd: dir })).toBe(1); + expect(write).toHaveBeenCalledWith('bugsee: hermesc wrapper could not find the JS bundle\n'); + } finally { + write.mockRestore(); + } + }); + + it('fails when hermesc exits 0 but writes no bytecode', () => { + const { spawn } = spawnWriting(null); + const code = run(argv(), { env: { BUGSEE_REAL_HERMESC: '/h/hermesc' }, cwd: dir, isExecutable: () => true, spawn, stderr }); + expect(code).toBe(1); + expect(lines).toEqual([`bugsee: hermesc exited 0 but wrote no bytecode to ${js}.hbc (/h/hermesc)\n`]); + }); + + it('fails when the bytecode is empty, or a stale file was all there was', () => { + const { spawn } = spawnWriting(''); + expect(run(argv(), { env: { BUGSEE_REAL_HERMESC: '/h' }, cwd: dir, isExecutable: () => true, spawn, stderr })).toBe(1); + fs.writeFileSync(`${js}.hbc`, 'stale from an earlier run'); + expect(run(argv(), { env: { BUGSEE_REAL_HERMESC: '/h' }, cwd: dir, isExecutable: () => true, spawn: spawnWriting(null).spawn, stderr })).toBe(1); + expect(lines).toEqual([ + `bugsee: hermesc exited 0 but wrote no bytecode to ${js}.hbc (/h)\n`, + `bugsee: hermesc exited 0 but wrote no bytecode to ${js}.hbc (/h)\n`, + ]); + }); + + it('passes on hermesc\'s own exit code', () => { + const { spawn } = spawnWriting('HBC', { status: 3 }); + expect(run(argv(), { env: { BUGSEE_REAL_HERMESC: '/h' }, cwd: dir, isExecutable: () => true, spawn, stderr })).toBe(3); + expect(lines).toEqual([]); + }); + + it('fails when hermesc is killed or cannot start', () => { + const killed = spawnWriting(null, { status: null, signal: 'SIGKILL' }).spawn; + expect(run(argv(), { env: { BUGSEE_REAL_HERMESC: '/h' }, cwd: dir, isExecutable: () => true, spawn: killed, stderr })).toBe(1); + const missing = () => ({ error: new Error('spawn /h ENOENT'), status: null }); + expect(run(argv(), { env: { BUGSEE_REAL_HERMESC: '/h' }, cwd: dir, isExecutable: () => true, spawn: missing, stderr })).toBe(1); + expect(lines).toEqual(['bugsee: hermesc was killed by SIGKILL (/h)\n', 'bugsee: hermesc failed to start (/h): spawn /h ENOENT\n']); + }); + + it('fails without running anything when no hermesc is found, after preserving the JS', () => { + const { spawn, calls } = spawnWriting('HBC'); + const code = run(argv(), { env: {}, cwd: dir, isExecutable: () => false, resolve: () => null, spawn, stderr }); + expect(code).toBe(1); + expect(calls).toEqual([]); + expect(lines).toEqual(['bugsee: hermesc binary not found\n']); + expect(fs.existsSync(path.join(intermediates, 'index.android.bundle.bugsee-js-source'))).toBe(true); + expect(fs.existsSync(path.join(intermediates, 'index.android.bundle.bugsee-hermesc'))).toBe(false); + }); + + it('fails when there is no bundle or no -out, and writes nothing', () => { + const { spawn, calls } = spawnWriting('HBC'); + const deps = { env: { BUGSEE_REAL_HERMESC: '/h' }, cwd: dir, isExecutable: () => true, spawn, stderr }; + expect(run(['-w', '-out', `${js}.hbc`, path.join(dir, 'missing.js')], deps)).toBe(1); + expect(run(['-w', js], deps)).toBe(1); + expect(calls).toEqual([]); + expect(lines).toEqual([ + 'bugsee: hermesc wrapper could not find the JS bundle\n', + 'bugsee: hermesc wrapper was given no -out, so it cannot check that bytecode was written\n', + ]); + expect(fs.existsSync(path.join(dir, 'build', 'intermediates'))).toBe(false); + }); + + it('refuses a bundle outside generated/assets and writes nothing', () => { + const other = path.join(dir, 'out', 'index.android.bundle'); + fs.mkdirSync(path.dirname(other)); + fs.writeFileSync(other, 'js'); + const { spawn, calls } = spawnWriting('HBC'); + expect( + run(['-out', `${other}.hbc`, other], { env: { BUGSEE_REAL_HERMESC: '/h' }, cwd: dir, isExecutable: () => true, spawn, stderr }), + ).toBe(1); + expect(calls).toEqual([]); + expect(lines).toEqual([`bugsee: refusing to write preserve files beside the bundle (${path.dirname(other)})\n`]); + expect(fs.readdirSync(path.dirname(other))).toEqual(['index.android.bundle']); + }); + + it('runs a real hermesc with the default helpers', () => { + if (WIN) return; // the stand-in is a shell script; the launcher test covers Windows. + const hermesc = fakeHermesc(dir); + expect(run(argv(), { env: { BUGSEE_REAL_HERMESC: hermesc }, cwd: dir, stderr })).toBe(0); + expect(fs.readFileSync(`${js}.hbc`, 'utf8')).toBe('HBC'); + expect(fs.readFileSync(path.join(intermediates, 'index.android.bundle.bugsee-hermesc'), 'utf8')).toBe(`${hermesc}\n`); + }); + + it('runs a real hermesc in the working directory, with relative arguments', () => { + if (WIN) return; + const hermesc = fakeHermesc(dir); + const rel = path.relative(dir, js); + expect(run(['-w', '-out', `${rel}.hbc`, rel], { env: { BUGSEE_REAL_HERMESC: hermesc }, cwd: dir, stderr })).toBe(0); + expect(fs.readFileSync(`${js}.hbc`, 'utf8')).toBe('HBC'); + }); + + it('fails with the default helpers when a real hermesc writes nothing, or cannot start', () => { + if (WIN) return; + const silent = fakeHermesc(dir, null); + expect(run(argv(), { env: { BUGSEE_REAL_HERMESC: silent }, cwd: dir, stderr })).toBe(1); + expect(lines).toEqual([`bugsee: hermesc exited 0 but wrote no bytecode to ${js}.hbc (${silent})\n`]); + // Not executable: not taken, and nothing else is there. + const plain = path.join(dir, 'plain'); + fs.writeFileSync(plain, ''); + expect(run(argv(), { env: { BUGSEE_REAL_HERMESC: plain }, cwd: dir, stderr })).toBe(1); + // A directory is not a hermesc either. + expect(run(argv(), { env: { BUGSEE_REAL_HERMESC: dir }, cwd: dir, stderr })).toBe(1); + expect(lines.slice(1)).toEqual(['bugsee: hermesc binary not found\n', 'bugsee: hermesc binary not found\n']); + }); +}); + +describe('launchers', () => { + let dir; + let js; + + beforeEach(() => { + dir = fs.mkdtempSync(path.join(os.tmpdir(), 'bugsee-preserve-launch-')); + const assets = path.join(dir, 'build', 'generated', 'assets', 'react', 'release'); + fs.mkdirSync(assets, { recursive: true }); + js = path.join(assets, 'index.android.bundle'); + fs.writeFileSync(js, 'console.log("bundle")\n'); + }); + + afterEach(() => { + fs.rmSync(dir, { recursive: true, force: true }); + }); + + /** + * As React Native runs it: on Windows `cmd /c ` + * (windowsAwareCommandLine), elsewhere the command itself. + */ + const launch = (hermesc) => { + const args = ['-w', '-emit-binary', '-out', `${js}.hbc`, js]; + const options = { cwd: dir, encoding: 'utf8', env: { ...process.env, BUGSEE_REAL_HERMESC: hermesc } }; + return WIN + ? cp.spawnSync('cmd', ['/c', path.join(SCRIPTS, 'hermesc-preserve-js.cmd'), ...args], options) + : cp.spawnSync(path.join(SCRIPTS, 'hermesc-preserve-js.sh'), args, options); + }; + + /** The repository's own hermesc: a script stand-in cannot be spawned on Windows without a shell. */ + const realHermesc = () => + findHermesc({ env: {}, cwd: path.join(SCRIPTS, '..'), platform: process.platform, isExecutable: fs.existsSync }); + + it('compiles through the launcher for this host', () => { + const result = launch(WIN ? realHermesc() : fakeHermesc(dir)); + expect([result.status, result.stderr]).toEqual([0, '']); + expect(fs.statSync(`${js}.hbc`).size).toBeGreaterThan(0); + expect( + fs.existsSync(path.join(dir, 'build', 'intermediates', 'bugsee-sourcemaps', 'react', 'release', 'index.android.bundle.bugsee-js-source')), + ).toBe(true); + }); + + it('passes a failing hermesc exit code through the launcher', () => { + if (WIN) { + fs.writeFileSync(js, 'function (\n'); + const result = launch(realHermesc()); + expect(result.status).not.toBe(0); + expect(fs.existsSync(`${js}.hbc`)).toBe(false); + return; + } + expect(launch(fakeHermesc(dir, 'HBC', 5)).status).toBe(5); + }); + + it('exits non-zero through the launcher when hermesc writes nothing', () => { + if (WIN) return; // needs a stand-in hermesc; run() covers it on every host. + const result = launch(fakeHermesc(dir, null)); + expect(result.status).toBe(1); + expect(result.stderr).toContain('bugsee: hermesc exited 0 but wrote no bytecode'); + }); + + it('exits non-zero through the launcher when node cannot find the script\'s hermesc', () => { + const result = launch(path.join(dir, 'no-such-hermesc')); + // Falls back to the search from the scratch directory, which has no node_modules. + expect(result.status).toBe(1); + expect(result.stderr).toContain('bugsee: hermesc binary not found'); + }); + + it('ships a .cmd that runs the same script with every argument and keeps its exit code', () => { + const text = fs.readFileSync(path.join(SCRIPTS, 'hermesc-preserve-js.cmd'), 'utf8'); + const lines = text.split('\r\n'); + // CRLF throughout (see .gitattributes), ending in one. + expect(text.replace(/\r\n/g, '')).not.toContain('\n'); + expect(lines[lines.length - 1]).toBe(''); + expect(lines[0]).toBe('@echo off'); + expect(lines.filter((line) => !/^rem /.test(line) && line !== '')).toEqual([ + '@echo off', + 'node "%~dp0hermesc-preserve-js.js" %*', + 'exit /b %ERRORLEVEL%', + ]); + }); + + it('ships an executable .sh that runs the same script', () => { + const sh = path.join(SCRIPTS, 'hermesc-preserve-js.sh'); + expect(fs.readFileSync(sh, 'utf8')).toContain('exec node "$dir/hermesc-preserve-js.js" "$@"'); + if (!WIN) { + expect(fs.statSync(sh).mode & 0o111).toBe(0o111); + } + }); + + it('lists both launchers and the script in the published files', () => { + const pkg = JSON.parse(fs.readFileSync(path.join(SCRIPTS, '..', 'package.json'), 'utf8')); + expect(pkg.files).toEqual( + expect.arrayContaining([ + 'scripts/hermesc-preserve-js.js', + 'scripts/hermesc-preserve-js.sh', + 'scripts/hermesc-preserve-js.cmd', + 'scripts/hermes-sourcemaps.js', + ]), + ); + }); +}); diff --git a/packages/react-native/scripts/__tests__/sourcemaps-gradle.test.js b/packages/react-native/scripts/__tests__/sourcemaps-gradle.test.js index f533818d..882cffa6 100644 --- a/packages/react-native/scripts/__tests__/sourcemaps-gradle.test.js +++ b/packages/react-native/scripts/__tests__/sourcemaps-gradle.test.js @@ -36,6 +36,7 @@ abstract class FakeBundle extends DefaultTask { @Internal abstract ListProperty getNodeExecutableAndArgs() @Internal abstract Property getHermesEnabled() @Internal abstract ListProperty getHermesFlags() + @Internal abstract Property getHermesCommand() @TaskAction void run() {} } @@ -67,13 +68,15 @@ components.variants << new FakeVariant(name: 'customRelease', outputs: [output(' // Starts with an "i": under the tr_TR locale this build runs in, String.toUpperCase() // would give "İnternalRelease", while RN's task name title-cases it to "InternalRelease". components.variants << new FakeVariant(name: 'internalRelease', outputs: [output('7.0', 70)]) +// Hermes on through a hermesCommand that names the shell launcher: fine off Windows. +components.variants << new FakeVariant(name: 'shellRelease', outputs: [output('8.0', 8)]) extensions.add('androidComponents', components) apply from: 'scripts/bugsee-sourcemaps.gradle' // A second apply (a bare app that copied the example and followed the README) is a no-op. apply from: 'scripts/bugsee-sourcemaps.gradle' -def bundle = { String variant, boolean hermes, String bundleDir = null -> +def bundle = { String variant, boolean hermes, String bundleDir = null, String hermesCommand = null -> // As React Native names the task: replaceFirstChar { it.titlecase() }. def cap = String.valueOf(Character.toTitleCase(variant.charAt(0))) + variant.substring(1) tasks.register("createBundle\${cap}JsAndAssets", FakeBundle) { t -> @@ -85,6 +88,7 @@ def bundle = { String variant, boolean hermes, String bundleDir = null -> t.nodeExecutableAndArgs.set([findProperty('nodePath').toString()]) t.hermesEnabled.set(hermes) t.hermesFlags.set(['-O', '-output-source-map']) + if (hermesCommand != null) t.hermesCommand.set(hermesCommand) } } bundle('freeRelease', true) @@ -96,6 +100,25 @@ bundle('StagingRelease', false) // Outside generated/assets: no preserve directory to map to, and none needed with Hermes off. bundle('customRelease', false, 'custom/js') bundle('internalRelease', false) +bundle('shellRelease', true, null, '/opt/bugsee/scripts/hermesc-preserve-js.SH') + +// The Windows hermesCommand rewrite, on this host's paths: [command, root]. +def cmdCases = [ + ['/s (x86)/b rn/node_modules/@bugsee/react-native/scripts/hermesc-preserve-js.cmd', '/s (x86)/b rn'], + ['/Users/John(US)/app/node_modules/@bugsee/react-native/scripts/hermesc-preserve-js.cmd', '/Users/John(US)/app'], + ['/plain/app/node_modules/@bugsee/react-native/scripts/hermesc-preserve-js.cmd', '/plain/app'], + ['/plain/app/tools/hermesc-preserve-js.cmd', '/plain/app'], + ['/p&q/app/x.cmd', '/p&q/app'], + ['/a b/x.cmd', '/c d/app'], + ['/a(b)/x.cmd', '/c/app'], + ['node_modules/x (1).cmd', '/app'], +] +tasks.register('bugseeCmdCases') { + def safe = project.extensions.extraProperties.get('bugseeCmdSafeCommand') + doLast { + cmdCases.each { c -> println "BUGSEE-CMD|\${c[0]}|\${safe(c[0], new File(c[1]))}" } + } +} `; // Records argv, then does what finish would to the files it was given. @@ -143,6 +166,10 @@ function argOf(argv, name) { fs.mkdirSync(preserve('freeRelease'), { recursive: true }); fs.writeFileSync(path.join(preserve('freeRelease'), 'index.android.bundle.bugsee-js-source'), 'js'); fs.writeFileSync(path.join(preserve('freeRelease'), 'index.android.bundle.bugsee-hermesc'), '/opt/hermesc\n'); + fs.mkdirSync(assets('shellRelease'), { recursive: true }); + fs.writeFileSync(path.join(assets('shellRelease'), 'index.android.bundle'), 'bundle'); + fs.mkdirSync(preserve('shellRelease'), { recursive: true }); + fs.writeFileSync(path.join(preserve('shellRelease'), 'index.android.bundle.bugsee-js-source'), 'js'); // Hermes off with a stale preserve file from an earlier Hermes build. fs.mkdirSync(preserve('paidRelease'), { recursive: true }); fs.writeFileSync(path.join(preserve('paidRelease'), 'index.android.bundle.bugsee-js-source'), 'stale'); @@ -165,6 +192,8 @@ function argOf(argv, name) { 'createBundleStagingReleaseJsAndAssets', 'createBundleCustomReleaseJsAndAssets', 'createBundleInternalReleaseJsAndAssets', + 'createBundleShellReleaseJsAndAssets', + 'bugseeCmdCases', ], { encoding: 'utf8', timeout: 10 * 60 * 1000 }, ); @@ -234,14 +263,18 @@ function argOf(argv, name) { expect(result.status).not.toBe(0); const output = `${result.stdout}${result.stderr}`; expect(output).toContain( - 'Bugsee: createBundleBrokenReleaseJsAndAssets compiled the bundle with Hermes, but hermesc did not run through hermesc-preserve-js.sh', + 'Bugsee: createBundleBrokenReleaseJsAndAssets compiled the bundle with Hermes, but hermesc did not run through the preserve wrapper', + ); + // The fix it names is the per-OS launcher. + expect(output).toContain( + 'System.getProperty("os.name").startsWith("Windows") ? "hermesc-preserve-js.cmd" : "hermesc-preserve-js.sh"', ); expect(output).toContain('hermesCommand'); expect(output).toContain('"Android source maps"'); expect(calls.some((argv) => argOf(argv, '--bundle').includes('/react/brokenRelease/'))).toBe(false); - // Only that task failed; --continue ran the other six, each once although + // Only that task failed; --continue ran the other seven, each once although // the script was applied twice. - expect(calls).toHaveLength(7); + expect(calls).toHaveLength(8); expect(output.match(/compiled the bundle with Hermes/g)).toHaveLength(1); }); @@ -250,6 +283,38 @@ function argOf(argv, name) { expect(`${result.stdout}${result.stderr}`).not.toContain('createBundleFreeReleaseJsAndAssets FAILED'); }); + it('off Windows, a hermesCommand naming a .sh is not refused', () => { + expect(argOf(callFor('shellRelease'), '--app-version')).toBe('8.0'); + expect(`${result.stdout}${result.stderr}`).not.toContain('a shell script, but on Windows'); + }); + + it('gives the Windows launcher relative to the root when cmd would cut its absolute path', () => { + const got = Object.fromEntries( + `${result.stdout}` + .split('\n') + .filter((line) => line.startsWith('BUGSEE-CMD|')) + .map((line) => line.split('|').slice(1)), + ); + expect(got).toEqual({ + // The campaign job's path: a space and ( ); @ is fine unquoted. + '/s (x86)/b rn/node_modules/@bugsee/react-native/scripts/hermesc-preserve-js.cmd': + 'node_modules/@bugsee/react-native/scripts/hermesc-preserve-js.cmd', + // ( ) and no space: unquoted, cmd would still cut it at the bracket. + '/Users/John(US)/app/node_modules/@bugsee/react-native/scripts/hermesc-preserve-js.cmd': + 'node_modules/@bugsee/react-native/scripts/hermesc-preserve-js.cmd', + '/plain/app/node_modules/@bugsee/react-native/scripts/hermesc-preserve-js.cmd': + 'node_modules/@bugsee/react-native/scripts/hermesc-preserve-js.cmd', + // Nothing cmd would cut: as written. + '/plain/app/tools/hermesc-preserve-js.cmd': '/plain/app/tools/hermesc-preserve-js.cmd', + '/p&q/app/x.cmd': 'x.cmd', + // The relative path would be cut too (a space, or ( )): as written. + '/a b/x.cmd': '/a b/x.cmd', + '/a(b)/x.cmd': '/a(b)/x.cmd', + // Already relative: as written. + 'node_modules/x (1).cmd': 'node_modules/x (1).cmd', + }); + }); + it('finishes a Hermes-off bundle outside generated/assets', () => { const custom = callFor('custom/js'); expect(argOf(custom, '--bundle')).toBe(path.join(dir, 'build/custom/js/index.android.bundle')); diff --git a/packages/react-native/scripts/bugsee-sourcemaps.gradle b/packages/react-native/scripts/bugsee-sourcemaps.gradle index 6e0e71e4..c6306212 100644 --- a/packages/react-native/scripts/bugsee-sourcemaps.gradle +++ b/packages/react-native/scripts/bugsee-sourcemaps.gradle @@ -7,8 +7,9 @@ // apply from: new File(new File(bugseeDir, "scripts"), "bugsee-sourcemaps.gradle") // // After each createBundleJsAndAssets: -// - Hermes on: hermesc must have run through hermesc-preserve-js.sh -// (react { hermesCommand = ... }). The debug id is injected into the +// - Hermes on: hermesc must have run through the preserve wrapper +// (react { hermesCommand = ... }: hermesc-preserve-js.sh on macOS and +// Linux, hermesc-preserve-js.cmd on Windows). The debug id is injected into the // composed map and the bytecode, then that map is uploaded. Without the // preserved JS the task fails: the bundle would ship without a debug id. // - Hermes off: the packaged bundle is Metro's JS and the map is Metro's; @@ -47,7 +48,7 @@ androidComponents.onVariants(androidComponents.selector().all()) { variant -> bugseeVariantVersions[key] = [output.versionName, output.versionCode] } -// hermesc-preserve-js.sh writes beside generated/assets/ in +// The preserve wrapper writes beside generated/assets/ in // intermediates/bugsee-sourcemaps/. Null when the bundle is elsewhere. def bugseePreserveDir = { File assetDir -> def relative = bugseeBuildDir.toPath().relativize(assetDir.toPath()) @@ -58,17 +59,76 @@ def bugseePreserveDir = { File assetDir -> .resolve(relative.subpath(2, relative.nameCount)).toFile() } +// The hermesCommand the README and expo prebuild write: the launcher for the +// host, so one build.gradle serves macOS, Linux and Windows. +def bugseeHermesCommandFix = + "react { hermesCommand = new File(new File(bugseeDir, \"scripts\"), " + + "System.getProperty(\"os.name\").startsWith(\"Windows\") ? \"hermesc-preserve-js.cmd\" : \"hermesc-preserve-js.sh\").absolutePath }" + def bugseeMissingPreserve = { String taskName -> "Bugsee: ${taskName} compiled the bundle with Hermes, but hermesc did not run through " + - "hermesc-preserve-js.sh, so the app would ship without a debug id and its source map could " + - "not be uploaded. Set react { hermesCommand = \"<@bugsee/react-native>/scripts/hermesc-preserve-js.sh\" } " + + "the preserve wrapper, so the app would ship without a debug id and its source map could " + + "not be uploaded. Set ${bugseeHermesCommandFix} " + "in this module's build.gradle (expo prebuild writes it). See " + "node_modules/@bugsee/react-native/README.md, \"Android source maps\"." } +// React Native runs hermesCommand through `cmd /c` on Windows, and cmd cannot +// run a shell script: it exits 0, no bytecode is written, and the task only +// fails later on a missing .hbc. Say why before that happens. +def bugseeShellOnWindows = { String taskName, String command -> + "Bugsee: ${taskName} would run hermesc through ${command}, a shell script, but on Windows " + + "React Native runs hermesCommand with cmd, which cannot run it, so no bytecode would be " + + "written. Set ${bugseeHermesCommandFix} in this module's build.gradle (expo prebuild " + + "writes it). See node_modules/@bugsee/react-native/README.md, \"Android source maps\"." +} + +// React Native runs hermesCommand through `cmd /c` on Windows, and cmd cuts a +// launcher path that holds a space or one of ( ) & < > ^ |. Quoted (Java +// quotes a path with a space), cmd strips the quotes unless the path is free +// of ( ) & < > @ ^ |, so "C:\Program Files (x86)\..." runs as "C:\Program". +// Unquoted (no space), ( ) still group: C:\Users\John(US)\... runs as +// "C:\Users\John". React Native passes its own paths relative to the project +// root and runs hermesc from there, so on Windows the launcher is given the +// same way whenever its absolute path holds any of those characters. The +// relative path goes unquoted, so @ in it is fine (node_modules\@bugsee). +// Another drive, or a relative path that holds a space or ( ) & < > ^ | too, +// keeps the command as written. Kept on the project for the hook's tests. +def bugseeCmdUnsafe = ~/[\s()&<>^|@]/ +def bugseeCmdUnsafeUnquoted = ~/[\s()&<>^|]/ +def bugseeCmdSafeCommand = { String command, File root -> + def file = new File(command) + if (!(command =~ bugseeCmdUnsafe) || !file.isAbsolute()) { + return command + } + try { + def relative = root.toPath().toAbsolutePath().normalize() + .relativize(file.toPath().toAbsolutePath().normalize()).toString() + return (relative =~ bugseeCmdUnsafeUnquoted) ? command : relative + } catch (IllegalArgumentException ignored) { + return command + } +} +project.extensions.extraProperties.set("bugseeCmdSafeCommand", bugseeCmdSafeCommand) +def bugseeReact = project.extensions.findByName("react") +if (System.getProperty("os.name").startsWith("Windows") && bugseeReact != null && bugseeReact.hermesCommand.isPresent()) { + def written = bugseeReact.hermesCommand.get() + def safe = bugseeCmdSafeCommand(written, bugseeReact.root.get().asFile) + if (safe != written) { + bugseeReact.hermesCommand.set(safe) + } +} + tasks.matching { task -> task.name.startsWith("createBundle") && task.name.endsWith("JsAndAssets") }.configureEach { bundleTask -> + bundleTask.doFirst { + def command = bundleTask.hermesCommand.getOrElse("") + if (bundleTask.hermesEnabled.get() && System.getProperty("os.name").startsWith("Windows") && + command.toLowerCase(Locale.ROOT).endsWith(".sh")) { + throw new GradleException(bugseeShellOnWindows(bundleTask.name, command)) + } + } bundleTask.doLast { def asset = bundleTask.bundleAssetName.get() def assetDir = bundleTask.jsBundleDir.get().asFile diff --git a/packages/react-native/scripts/hermesc-preserve-js.cmd b/packages/react-native/scripts/hermesc-preserve-js.cmd new file mode 100644 index 00000000..9c5e7e2c --- /dev/null +++ b/packages/react-native/scripts/hermesc-preserve-js.cmd @@ -0,0 +1,6 @@ +@echo off +rem react.hermesCommand on Windows, where React Native runs it through +rem "cmd /c". macOS and Linux use hermesc-preserve-js.sh. The work, and its +rem non-zero exit when no bytecode came out, is in hermesc-preserve-js.js. +node "%~dp0hermesc-preserve-js.js" %* +exit /b %ERRORLEVEL% diff --git a/packages/react-native/scripts/hermesc-preserve-js.js b/packages/react-native/scripts/hermesc-preserve-js.js new file mode 100755 index 00000000..5b088694 --- /dev/null +++ b/packages/react-native/scripts/hermesc-preserve-js.js @@ -0,0 +1,200 @@ +#!/usr/bin/env node +'use strict'; + +/** + * The work behind `react.hermesCommand`. React Native runs the launcher with + * hermesc's own arguments: hermesc-preserve-js.sh on macOS and Linux, + * hermesc-preserve-js.cmd on Windows (React Native runs every command there + * through `cmd /c`, which cannot run a shell script). Both start this file. + * + * Copy the JS bundle aside before hermesc replaces it with bytecode, note + * which hermesc compiled it, then run that hermesc. Injection happens later, + * on the composed map (bugsee-sourcemaps.gradle, hermes-sourcemaps.js). + * + * Exits non-zero, with a `bugsee:` line, whenever no bytecode came out: + * React Native moves `.hbc` over the bundle next, and a wrapper that + * exits 0 without it only fails later, far from the cause. + */ + +const fs = require('node:fs'); +const path = require('node:path'); +const { preserveDirFor } = require('./hermes-sourcemaps'); + +/** The value after the last `-out`, as hermesc reads it. */ +function outputOf(argv) { + let out; + for (let i = 1; i < argv.length; i += 1) { + if (argv[i - 1] === '-out') { + out = argv[i]; + } + } + return out; +} + +/** The first argument that is not a flag, not the output, and is a file. */ +function bundleOf(argv, out, isFile) { + return argv.find((arg) => !arg.startsWith('-') && arg !== out && isFile(arg)); +} + +/** React Native's own names: getHermesOSBin, getHermesCBin. */ +function hermescBin(platform) { + if (platform === 'win32') { + return { osbin: 'win64-bin', bin: 'hermesc.exe' }; + } + return { osbin: platform === 'linux' ? 'linux64-bin' : 'osx-bin', bin: 'hermesc' }; +} + +/** require.resolve from a directory, or null. */ +function resolveFrom(request, from) { + try { + return require.resolve(request, { paths: [from] }); + } catch { + return null; + } +} + +/** + * The hermesc React Native would have run. BUGSEE_REAL_HERMESC first. Expo + * SDK 57 resolves hermes-compiler from the react-native package, so a pnpm or + * Yarn workspace install under node_modules/react-native/node_modules is + * found. React Native 0.81 (Expo SDK 54) has no hermes-compiler package and + * ships sdks/hermesc beside react-native/package.json. Then the app's own + * node_modules. Null when none is there. + */ +function findHermesc({ env, cwd, platform, isExecutable, resolve = resolveFrom }) { + if (env.BUGSEE_REAL_HERMESC && isExecutable(env.BUGSEE_REAL_HERMESC)) { + return env.BUGSEE_REAL_HERMESC; + } + const { osbin, bin } = hermescBin(platform); + const candidates = []; + const rnPkg = resolve('react-native/package.json', cwd); + if (rnPkg) { + const rnDir = path.dirname(rnPkg); + const compilerPkg = resolve('hermes-compiler/package.json', rnDir); + if (compilerPkg) { + candidates.push(path.join(path.dirname(compilerPkg), 'hermesc', osbin, bin)); + } + candidates.push(path.join(rnDir, 'sdks', 'hermesc', osbin, bin)); + } + candidates.push( + path.join(cwd, 'node_modules', 'hermes-compiler', 'hermesc', osbin, bin), + path.join(cwd, 'node_modules', 'react-native', 'sdks', 'hermes', 'build', 'bin', bin), + ); + return candidates.find((candidate) => isExecutable(candidate)) ?? null; +} + +/** + * Where the preserved JS goes. The bundle directory is packaged as assets, + * so files beside the bundle would ship in the APK; intermediates are not. + */ +function preserveDirOf(js, env, cwd) { + const jsDir = path.dirname(js); + let dir; + if (env.BUGSEE_PRESERVE_DIR) { + dir = path.resolve(cwd, env.BUGSEE_PRESERVE_DIR); + } else { + dir = preserveDirFor(js); + if (dir === jsDir) { + return { error: `refusing to write preserve files beside the bundle (${jsDir})` }; + } + } + if (dir === jsDir || dir.startsWith(jsDir + path.sep)) { + return { error: 'refusing to write preserve files into the packaged asset directory' }; + } + return { dir }; +} + +function isExecutableFile(file) { + try { + fs.accessSync(file, fs.constants.X_OK); + return fs.statSync(file).isFile(); + } catch { + return false; + } +} + +function isFile(file) { + try { + return fs.statSync(file).isFile(); + } catch { + return false; + } +} + +/** hermesc's output goes to the build log, as when React Native runs it. */ +function spawnInherit(file, args, cwd) { + return require('node:child_process').spawnSync(file, args, { cwd, stdio: 'inherit' }); +} + +/** Returns the exit code. */ +function run(argv, deps = {}) { + const env = deps.env ?? process.env; + const cwd = deps.cwd ?? process.cwd(); + const platform = deps.platform ?? process.platform; + const isExecutable = deps.isExecutable ?? isExecutableFile; + const spawn = deps.spawn ?? spawnInherit; + const stderr = deps.stderr ?? ((line) => process.stderr.write(line)); + const fail = (message) => { + stderr(`bugsee: ${message}\n`); + return 1; + }; + + const out = outputOf(argv); + const bundle = bundleOf(argv, out, (arg) => isFile(path.resolve(cwd, arg))); + if (bundle === undefined) { + return fail('hermesc wrapper could not find the JS bundle'); + } + if (out === undefined) { + return fail('hermesc wrapper was given no -out, so it cannot check that bytecode was written'); + } + const js = path.resolve(cwd, bundle); + const preserve = preserveDirOf(js, env, cwd); + if (preserve.error) { + return fail(preserve.error); + } + fs.mkdirSync(preserve.dir, { recursive: true }); + const base = path.basename(js); + fs.copyFileSync(js, path.join(preserve.dir, `${base}.bugsee-js-source`)); + + const hermesc = findHermesc({ env, cwd, platform, isExecutable, resolve: deps.resolve }); + if (hermesc === null) { + return fail('hermesc binary not found'); + } + fs.writeFileSync(path.join(preserve.dir, `${base}.bugsee-hermesc`), `${hermesc}\n`); + + // A file left from an earlier run must not pass the check below. + const bytecode = path.resolve(cwd, out); + fs.rmSync(bytecode, { force: true }); + const result = spawn(hermesc, argv, cwd); + if (result.error) { + return fail(`hermesc failed to start (${hermesc}): ${result.error.message}`); + } + if (result.status !== 0) { + return result.status === null + ? fail(`hermesc was killed by ${result.signal} (${hermesc})`) + : result.status; + } + if (!isFile(bytecode) || fs.statSync(bytecode).size === 0) { + return fail(`hermesc exited 0 but wrote no bytecode to ${bytecode} (${hermesc})`); + } + return 0; +} + +module.exports = { + bundleOf, + findHermesc, + hermescBin, + isExecutableFile, + isFile, + outputOf, + preserveDirOf, + resolveFrom, + run, + spawnInherit, +}; + +// Stryker disable all: the command-line entry runs only in a child process (the launcher tests). +if (require.main === module) { + process.exitCode = run(process.argv.slice(2)); +} +// Stryker restore all diff --git a/packages/react-native/scripts/hermesc-preserve-js.sh b/packages/react-native/scripts/hermesc-preserve-js.sh index 2444541b..390aa810 100755 --- a/packages/react-native/scripts/hermesc-preserve-js.sh +++ b/packages/react-native/scripts/hermesc-preserve-js.sh @@ -1,118 +1,7 @@ -#!/bin/bash -# Android invokes this as `react.hermesCommand`. Copy the JS bundle aside -# before hermesc replaces it with bytecode, then run the real compiler. -# Injection happens later, on the composed map. - -set -euo pipefail - -out="" -prev="" -for arg in "$@"; do - if [[ "$prev" == "-out" ]]; then - out="$arg" - fi - prev="$arg" -done - -js="" -for arg in "$@"; do - if [[ "$arg" == -* ]]; then - continue - fi - if [[ -n "$out" && "$arg" == "$out" ]]; then - continue - fi - if [[ -f "$arg" ]]; then - js="$arg" - break - fi -done - -if [[ -z "$js" ]]; then - echo "bugsee: hermesc wrapper could not find the JS bundle" >&2 - exit 1 -fi - -# jsBundleDir is packaged as assets. Preserve files beside the bundle would -# ship in the APK. Intermediates are not. The last generated/assets in the -# path is the one under the build directory, which is where the Gradle hook -# (relative to the build directory) and hermes-sourcemaps.js look. -js_dir="$(dirname "$js")" -marker="/generated/assets/" -if [[ -n "${BUGSEE_PRESERVE_DIR:-}" ]]; then - preserve_dir="$BUGSEE_PRESERVE_DIR" -elif [[ "$js_dir" == *"$marker"* ]]; then - preserve_dir="${js_dir%"$marker"*}/intermediates/bugsee-sourcemaps/${js_dir##*"$marker"}" -else - echo "bugsee: refusing to write preserve files beside the bundle ($js_dir)" >&2 - exit 1 -fi -case "$preserve_dir" in - "$js_dir"|"$js_dir"/*) - echo "bugsee: refusing to write preserve files into the packaged asset directory" >&2 - exit 1 - ;; -esac -mkdir -p "$preserve_dir" -base="$(basename "$js")" -cp "$js" "${preserve_dir}/${base}.bugsee-js-source" - -find_hermesc() { - if [[ -n "${BUGSEE_REAL_HERMESC:-}" && -x "$BUGSEE_REAL_HERMESC" ]]; then - echo "$BUGSEE_REAL_HERMESC" - return - fi - local osbin="osx-bin" - local bin="hermesc" - case "$(uname -s)" in - Linux) osbin="linux64-bin" ;; - Darwin) osbin="osx-bin" ;; - MINGW*|MSYS*|CYGWIN*) osbin="win64-bin"; bin="hermesc.exe" ;; - esac - # Expo SDK 57 resolves hermes-compiler from the react-native package, so a - # pnpm or Yarn workspace install under node_modules/react-native/node_modules - # is found. React Native 0.81 (Expo SDK 54) has no hermes-compiler package - # and ships sdks/hermesc beside react-native/package.json. That path is next. - local pkg="" - if command -v node >/dev/null 2>&1; then - pkg="$(node --print "require.resolve('hermes-compiler/package.json', { paths: [require.resolve('react-native/package.json')] })" 2>/dev/null)" || pkg="" - fi - if [[ -n "$pkg" ]]; then - local resolved - resolved="$(dirname "$pkg")/hermesc/${osbin}/${bin}" - if [[ -x "$resolved" ]]; then - echo "$resolved" - return - fi - fi - local rn_pkg="" - if command -v node >/dev/null 2>&1; then - rn_pkg="$(node --print "require.resolve('react-native/package.json')" 2>/dev/null)" || rn_pkg="" - fi - if [[ -n "$rn_pkg" ]]; then - local shipped - shipped="$(dirname "$rn_pkg")/sdks/hermesc/${osbin}/${bin}" - if [[ -x "$shipped" ]]; then - echo "$shipped" - return - fi - fi - local candidate - for candidate in \ - "$PWD/node_modules/hermes-compiler/hermesc/${osbin}/${bin}" \ - "$PWD/node_modules/react-native/sdks/hermes/build/bin/${bin}" - do - if [[ -x "$candidate" ]]; then - echo "$candidate" - return - fi - done - return 1 -} - -real="$(find_hermesc)" || { - echo "bugsee: hermesc binary not found" >&2 - exit 1 -} -printf '%s\n' "$real" > "${preserve_dir}/${base}.bugsee-hermesc" -exec "$real" "$@" +#!/bin/sh +# react.hermesCommand on macOS and Linux. Windows runs the command through +# `cmd /c`, which cannot run this file: there it is hermesc-preserve-js.cmd. +# The work, and its non-zero exit when no bytecode came out, is in +# hermesc-preserve-js.js. +dir="$(cd "$(dirname "$0")" && pwd)" || exit 1 +exec node "$dir/hermesc-preserve-js.js" "$@" diff --git a/scripts/__tests__/campaign-readme-android.test.ts b/scripts/__tests__/campaign-readme-android.test.ts new file mode 100644 index 00000000..a18577a4 --- /dev/null +++ b/scripts/__tests__/campaign-readme-android.test.ts @@ -0,0 +1,69 @@ +// scripts/campaign/lib/readme-android.js applies the README's "Android source +// maps" snippet literally to a generated app (gen-rn-app.sh, N-20). The +// hermesCommand it copies must be the README's whole per-OS line. + +import * as cp from 'node:child_process'; +import * as fs from 'node:fs'; +import * as os from 'node:os'; +import * as path from 'node:path'; + +const REPO = path.join(__dirname, '..', '..'); +const SCRIPT = path.join(REPO, 'scripts', 'campaign', 'lib', 'readme-android.js'); + +function appWithReadme(readme: string): { app: string; gradle: string } { + const app = fs.mkdtempSync(path.join(os.tmpdir(), 'bugsee-readme-android-')); + const pkg = path.join(app, 'node_modules', '@bugsee', 'react-native'); + fs.mkdirSync(pkg, { recursive: true }); + fs.writeFileSync(path.join(pkg, 'package.json'), '{"name":"@bugsee/react-native"}\n'); + fs.writeFileSync(path.join(pkg, 'README.md'), readme); + const gradle = path.join(app, 'android', 'app', 'build.gradle'); + fs.mkdirSync(path.dirname(gradle), { recursive: true }); + fs.writeFileSync(gradle, 'apply plugin: "com.facebook.react"\n\nreact {\n autolinkLibrariesWithApp()\n}\n'); + return { app, gradle }; +} + +describe('readme-android.js', () => { + it('copies the README hermesCommand whole, with both launchers', () => { + const readme = fs.readFileSync(path.join(REPO, 'packages', 'react-native', 'README.md'), 'utf8'); + const { app, gradle } = appWithReadme(readme); + try { + const result = cp.spawnSync(process.execPath, [SCRIPT, gradle], { encoding: 'utf8' }); + expect([result.status, result.stderr]).toEqual([0, '']); + const line = fs + .readFileSync(gradle, 'utf8') + .split('\n') + .find((entry: string) => /^\s*hermesCommand = /.test(entry)); + expect(line).toBe( + ' hermesCommand = new File(new File(bugseeDir, "scripts"), System.getProperty("os.name").startsWith("Windows") ? "hermesc-preserve-js.cmd" : "hermesc-preserve-js.sh").absolutePath', + ); + } finally { + fs.rmSync(app, { recursive: true, force: true }); + } + }); + + it('refuses a README whose hermesCommand spans lines', () => { + const split = [ + '## Android source maps', + '', + '```groovy', + 'def bugseeDir = new File(["node", "--print", "x"].execute(null, rootDir).text.trim()).getParentFile()', + '', + 'react {', + ' hermesCommand = new File(new File(bugseeDir, "scripts"),', + ' cond ? "hermesc-preserve-js.cmd" : "hermesc-preserve-js.sh").absolutePath', + '}', + '', + 'apply from: new File(new File(bugseeDir, "scripts"), "bugsee-sourcemaps.gradle")', + '```', + '', + ].join('\n'); + const { app, gradle } = appWithReadme(split); + try { + const result = cp.spawnSync(process.execPath, [SCRIPT, gradle], { encoding: 'utf8' }); + expect(result.status).not.toBe(0); + expect(result.stderr).toContain('README hermesCommand is not one whole per-OS line'); + } finally { + fs.rmSync(app, { recursive: true, force: true }); + } + }); +}); diff --git a/scripts/__tests__/run-ios-configuration.test.ts b/scripts/__tests__/run-ios-configuration.test.ts index 68324474..d95a47ed 100644 --- a/scripts/__tests__/run-ios-configuration.test.ts +++ b/scripts/__tests__/run-ios-configuration.test.ts @@ -16,12 +16,17 @@ describe('run-ios.sh IOS_CONFIGURATION', () => { it('the embed check follows IOS_CONFIGURATION', () => { const body = code(script); expect(body).toMatch(/CONFIGURATION="\$\{IOS_CONFIGURATION:-Debug\}"/); + // The app path is built from the configuration and the SDK, and the SDK + // is iphoneos unless IOS_TARGET=simulator (N-27 switches). expect(body).toMatch( - /APP="ios\/build\/Build\/Products\/\$\{CONFIGURATION\}-iphoneos\/BareExample\.app"/, + /APP="\$APP_DIR\/\$BUILD_DIR\/Build\/Products\/\$\{CONFIGURATION\}-\$\{SDK\}\/\$\{SCHEME\}\.app"/, ); + expect(body).toMatch(/^SDK=iphoneos$/m); + expect(body).toMatch(/\[\[ "\$TARGET" == simulator \]\] && SDK=iphonesimulator/); + expect(body).toMatch(/^BUILD_DIR="ios\/build"$/m); expect(body).toMatch(/-configuration "\$CONFIGURATION"/); // The embed assertion must receive that same APP, not a hard-coded Debug path. expect(body).toMatch(/cli-assert-framework-embedded\.ts "\$APP"/); - expect(body).not.toMatch(/APP="ios\/build\/Build\/Products\/Debug-iphoneos/); + expect(body).not.toMatch(/Products\/Debug-iphoneos/); }); }); diff --git a/scripts/campaign/build-app.sh b/scripts/campaign/build-app.sh new file mode 100755 index 00000000..bed822ce --- /dev/null +++ b/scripts/campaign/build-app.sh @@ -0,0 +1,144 @@ +#!/usr/bin/env bash +# +# Builds a campaign app (generated by gen-rn-app.sh / gen-expo-app.sh) for one +# target. No device is touched, so no device lock is taken. +# +# build-app.sh ... +# +# Targets: +# android-debug :app:assembleDebug (arm64-v8a) +# android-release :app:assembleRelease (arm64-v8a, -PbugseeE2eDebuggable=true), +# then the 16 KB alignment check (check-16kb.sh): a +# Bugsee library out of alignment fails the target; +# only another vendor's records PASS-16KB-WARN +# ios-sim-debug Debug, iphonesimulator +# ios-sim-release Release, iphonesimulator +# ios-device-debug Debug, iphoneos, signed for IOS_DEVELOPMENT_TEAM +# ios-device-release Release, iphoneos, signed for IOS_DEVELOPMENT_TEAM +# all the five the plan asks for: android-debug, +# android-release, ios-sim-debug, ios-device-debug, +# ios-device-release +# +# Environment: +# IOS_DEVELOPMENT_TEAM default 7LJD5DCH7T (device targets only) +# ANDROID_HOME default ~/Library/Android/sdk +# +# Each target writes /logs/-.log and one result +# line to /logs/build-results.tsv: +# PASS|FAIL +set -uo pipefail + +# One function, so bash parses the whole script before running any of it. +main() { +HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +APP_DIR="$(cd "${1:?usage: build-app.sh ...}" && pwd)" || exit 2 +shift +APP="$(basename "$APP_DIR")" +LOGS="$(dirname "$APP_DIR")/logs" +mkdir -p "$LOGS" +RESULTS="$LOGS/build-results.tsv" +: "${ANDROID_HOME:=$HOME/Library/Android/sdk}" +export ANDROID_HOME +TEAM="${IOS_DEVELOPMENT_TEAM:-7LJD5DCH7T}" + +PORT="$(cat "$APP_DIR/campaign.port" 2>/dev/null || echo 8081)" +TARGETS=("$@") +if [[ "${TARGETS[0]:-}" == all ]]; then + TARGETS=(android-debug android-release ios-sim-debug ios-device-debug ios-device-release) +fi + +workspace() { ls -d "$APP_DIR"/ios/*.xcworkspace | head -1; } +scheme() { basename "$(workspace)" .xcworkspace; } + +record() { # target status seconds artifact + local sha="-" + [[ -e "$4" && -f "$4" ]] && sha="$(shasum -a 256 "$4" | cut -c1-16)" + [[ -d "$4" ]] && sha="$(find "$4" -type f -name "$(basename "$4" .app)" -exec shasum -a 256 {} \; | cut -c1-16)" + printf '%s\t%s\t%s\t%s\t%s\t%s\t%s\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$APP" "$1" "$2" "$3" "${4#$APP_DIR/}" "$sha" >>"$RESULTS" + echo "$APP $1: $2 (${3}s) ${4#$APP_DIR/}" +} + +# The embed check is a .ts CLI. Node strips types from a .ts entry point +# only from 22.18; an older Node dies with ERR_UNKNOWN_FILE_EXTENSION and exit +# 1, the same code a real embed miss uses. Check first (as run-ios.sh does) +# and report a too-old Node as tooling (rc 99), never as an embed FAIL. +embed_check() { # artifact log + node -e 'const [maj,min]=process.versions.node.split(".").map(Number); + if (maj<22 || (maj===22 && min<18)) { + console.error(`Node ${process.versions.node} cannot run the embed check; 22.18+ required.`); + process.exit(2); + }' >>"$2" 2>&1 || return 99 + node "$HERE/../cli-assert-framework-embedded.ts" "$1" >>"$2" 2>&1 +} + +xcode() { # configuration sdk destination log [extra settings...] + local cfg="$1" sdk="$2" dest="$3" log="$4" + shift 4 + xcodebuild -workspace "$(workspace)" -scheme "$(scheme)" -configuration "$cfg" \ + -sdk "$sdk" -destination "$dest" -derivedDataPath "$APP_DIR/ios/build" \ + "$@" build >"$log" 2>&1 +} + +overall=0 +for target in "${TARGETS[@]}"; do + log="$LOGS/$APP-$target.log" + start=$SECONDS + artifact="-" + case "$target" in + android-debug) + (cd "$APP_DIR/android" && ./gradlew :app:assembleDebug -PreactNativeArchitectures=arm64-v8a -PreactNativeDevServerPort="$PORT") >"$log" 2>&1 + rc=$? + artifact="$APP_DIR/android/app/build/outputs/apk/debug/app-debug.apk" + ;; + android-release) + (cd "$APP_DIR/android" && ./gradlew :app:assembleRelease -PreactNativeArchitectures=arm64-v8a -PbugseeE2eDebuggable=true) >"$log" 2>&1 + rc=$? + artifact="$APP_DIR/android/app/build/outputs/apk/release/app-release.apk" + if [[ $rc -eq 0 ]]; then + "$HERE/check-16kb.sh" "$artifact" >>"$log" 2>&1 || rc=$? + if [[ $rc -eq 4 ]]; then + record "$target" PASS-16KB-WARN $((SECONDS - start)) "$artifact" + grep "16 KB check" "$log" + continue + fi + fi + ;; + ios-sim-debug|ios-sim-release) + cfg=Debug; [[ "$target" == *release ]] && cfg=Release + xcode "$cfg" iphonesimulator "generic/platform=iOS Simulator" "$log" + rc=$? + artifact="$APP_DIR/ios/build/Build/Products/${cfg}-iphonesimulator/$(scheme).app" + # Same embed assertion as the device targets and run-ios.sh: a link or + # embed miss fails here, not later as a silent launch. + if [[ $rc -eq 0 ]]; then + embed_check "$artifact" "$log" || rc=$? + fi + ;; + ios-device-debug|ios-device-release) + cfg=Debug; [[ "$target" == *release ]] && cfg=Release + xcode "$cfg" iphoneos "generic/platform=iOS" "$log" \ + DEVELOPMENT_TEAM="$TEAM" CODE_SIGN_STYLE=Automatic -allowProvisioningUpdates + rc=$? + artifact="$APP_DIR/ios/build/Build/Products/${cfg}-iphoneos/$(scheme).app" + if [[ $rc -eq 0 ]]; then + embed_check "$artifact" "$log" || rc=$? + fi + ;; + *) echo "unknown target $target" >&2; exit 2 ;; + esac + if [[ $rc -eq 0 ]]; then + record "$target" PASS $((SECONDS - start)) "$artifact" + elif [[ $rc -eq 99 ]]; then + record "$target" TOOLING $((SECONDS - start)) "$artifact" + grep "cannot run the embed check" "$log" | tail -1 + overall=1 + else + record "$target" FAIL $((SECONDS - start)) "$artifact" + grep -E "error:|FAILED|What went wrong|^> |FAIL" "$log" | cut -c1-300 | head -15 + overall=1 + fi +done +exit $overall +} + +main "$@" diff --git a/scripts/campaign/check-16kb.sh b/scripts/campaign/check-16kb.sh new file mode 100755 index 00000000..0cbb23ce --- /dev/null +++ b/scripts/campaign/check-16kb.sh @@ -0,0 +1,80 @@ +#!/usr/bin/env bash +# +# N-24 / MX-16KB: Android 16 KB page-size readiness of a release APK or AAB. +# +# check-16kb.sh +# +# Two checks, both required by Google Play for targetSdk 35+ apps on +# 16 KB devices: +# 1. zip alignment: every uncompressed .so starts on a 16 KB boundary +# (`zipalign -c -P 16 -v 4`); +# 2. ELF alignment: every PT_LOAD segment of every .so in lib/arm64-v8a and +# lib/x86_64 has p_align >= 2**14 (16384). +# Prints one line per .so (Bugsee's own marked with *). Exit 1 when zipalign +# or a Bugsee library (libbugsee*, crashpad) fails; exit 4 when only another +# vendor's library fails (reported, not ours to fix). 32-bit ABIs are listed +# but not judged: 16 KB pages are a 64-bit-only requirement. +set -euo pipefail + +ARTIFACT="$(cd "$(dirname "${1:?usage: check-16kb.sh }")" && pwd)/$(basename "$1")" +# An AAB keeps its libraries in nested module zips this does not unpack: +# check the universal APK (bundletool build-apks --mode=universal) instead. +[[ "$ARTIFACT" == *.apk ]] || { echo "check-16kb.sh takes an APK; for an AAB check its universal APK" >&2; exit 2; } +: "${ANDROID_HOME:=$HOME/Library/Android/sdk}" + +BUILD_TOOLS="$(ls -d "$ANDROID_HOME"/build-tools/* | grep -v rc | sort -V | tail -1)" +NDK="$(ls -d "$ANDROID_HOME"/ndk/* | sort -V | tail -1)" +READELF="$(ls "$NDK"/toolchains/llvm/prebuilt/*/bin/llvm-readelf | head -1)" +[[ -x "$READELF" ]] || { echo "llvm-readelf not found under $NDK"; exit 2; } + +WORK="$(mktemp -d)" +trap 'rm -rf "$WORK"' EXIT +unzip -q "$ARTIFACT" '*.so' -d "$WORK" 2>/dev/null || true + +failures=0 +others=0 +examined=0 +echo "--- 16 KB check: $(basename "$ARTIFACT")" +if [[ "$ARTIFACT" == *.apk ]]; then + if "$BUILD_TOOLS/zipalign" -c -P 16 -v 4 "$ARTIFACT" >"$WORK/zipalign.txt" 2>&1; then + echo "zipalign -P 16: PASS ($(basename "$BUILD_TOOLS"))" + else + echo "zipalign -P 16: FAIL" + grep -v "(OK" "$WORK/zipalign.txt" | grep "\.so" | head -20 + failures=$((failures + 1)) + fi +fi + +while IFS= read -r so; do + rel="${so#$WORK/}" + abi="$(basename "$(dirname "$so")")" + # Smallest PT_LOAD alignment in the file. + min_align="$("$READELF" -lW "$so" | awk '$1=="LOAD"{print $NF}' | while read -r a; do printf '%d\n' "$a"; done | sort -n | head -1)" + mark=" " + case "$(basename "$so")" in libbugsee*|*crashpad*) mark="*" ;; esac + verdict=PASS + case "$abi" in + arm64-v8a|x86_64) + examined=$((examined + 1)) + if [[ -z "$min_align" || "$min_align" -lt 16384 ]]; then + verdict=FAIL + if [[ "$mark" == "*" ]]; then failures=$((failures + 1)); else others=$((others + 1)); fi + fi ;; + *) verdict=n/a ;; + esac + printf '%s %-5s align=%-6s %s\n' "$mark" "$verdict" "${min_align:-?}" "$rel" +done < <(find "$WORK" -name '*.so' | sort) + +if [[ "$examined" -eq 0 ]]; then + echo "16 KB check: FAIL (no 64-bit .so examined)" + exit 1 +fi +if [[ "$failures" -gt 0 ]]; then + echo "16 KB check: FAIL ($failures Bugsee/zip, $others other)" + exit 1 +fi +if [[ "$others" -gt 0 ]]; then + echo "16 KB check: Bugsee PASS, $others non-Bugsee librar$( [[ $others -eq 1 ]] && echo y || echo ies) FAIL" + exit 4 +fi +echo "16 KB check: PASS" diff --git a/scripts/campaign/emulators.sh b/scripts/campaign/emulators.sh new file mode 100755 index 00000000..836de246 --- /dev/null +++ b/scripts/campaign/emulators.sh @@ -0,0 +1,142 @@ +#!/usr/bin/env bash +# +# Android emulator sweep (campaign-rules: the SDK's minimum API, a mid +# version and API 36): creates the three AVDs and boots/stops them. +# +# emulators.sh create install the system images (if missing) and +# create the AVDs (idempotent) +# emulators.sh boot boot headless, wait for sys.boot_completed, +# print the serial. The caller holds +# .device-lock for as long as it is up. +# emulators.sh stop kill that emulator +# emulators.sh list the campaign AVDs and their images +# emulators.sh sweep ... for each AVD: take .device-lock, boot it, +# launch-check.sh each app (debug and release), +# stop it, release the lock +# +# The floor: the Bugsee Android SDK and the wrapper declare minSdk 21, but +# every React Native 0.81+ template sets minSdkVersion 24, so an RN app cannot +# be installed below API 24. API 24 is the lowest a campaign app runs on, and +# the sweep's "minimum". +# +# AVDs (all arm64-v8a, Google APIs, 4 GB RAM, cold boot, no snapshot): +# bugsee_api24 system-images;android-24;google_apis;arm64-v8a (floor) +# bugsee_api30 system-images;android-30;google_apis;arm64-v8a (mid) +# bugsee_api36 system-images;android-36;default;arm64-v8a (latest) +set -euo pipefail + +: "${ANDROID_HOME:=$HOME/Library/Android/sdk}" +export ANDROID_HOME +SDKMANAGER="$ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager" +AVDMANAGER="$ANDROID_HOME/cmdline-tools/latest/bin/avdmanager" +EMULATOR="$ANDROID_HOME/emulator/emulator" +ADB="$ANDROID_HOME/platform-tools/adb" + +AVDS="bugsee_api24 bugsee_api30 bugsee_api36" +image_of() { + case "$1" in + bugsee_api24) echo "system-images;android-24;google_apis;arm64-v8a" ;; + bugsee_api30) echo "system-images;android-30;google_apis;arm64-v8a" ;; + bugsee_api36) echo "system-images;android-36;default;arm64-v8a" ;; + *) echo "unknown AVD $1" >&2; exit 2 ;; + esac +} +port_of() { + case "$1" in + bugsee_api24) echo 5580 ;; + bugsee_api30) echo 5582 ;; + bugsee_api36) echo 5584 ;; + esac +} + +case "${1:-}" in + create) + for avd in $AVDS; do + image="$(image_of "$avd")" + dir="$ANDROID_HOME/$(echo "$image" | tr ';' '/')" + if [[ ! -f "$dir/system.img" ]]; then + yes | "$SDKMANAGER" "$image" >/dev/null + fi + if "$EMULATOR" -list-avds | grep -qx "$avd"; then + echo "$avd: exists" + continue + fi + echo no | "$AVDMANAGER" create avd -n "$avd" -k "$image" -d pixel_6 >/dev/null + cfg="$HOME/.android/avd/$avd.avd/config.ini" + { + echo "hw.ramSize=4096" + echo "hw.keyboard=yes" + echo "disk.dataPartition.size=6G" + } >>"$cfg" + echo "$avd: created ($image)" + done + ;; + boot) + avd="${2:?emulators.sh boot }" + image_of "$avd" >/dev/null + port="$(port_of "$avd")" + serial="emulator-$port" + nohup "$EMULATOR" -avd "$avd" -port "$port" -no-window -no-audio -no-snapshot \ + -no-boot-anim -gpu swiftshader_indirect >"/tmp/$avd.emulator.log" 2>&1 & + # Bounded: a wedged or broken AVD must not hold the shared device lock + # forever. On timeout, kill this emulator and exit 3 so a sweep can + # release the lock and move on. + deadline=$((SECONDS + ${BOOT_TIMEOUT:-600})) + until [[ "$("$ADB" -s "$serial" shell getprop sys.boot_completed 2>/dev/null | tr -d '\r')" == 1 ]]; do + if (( SECONDS >= deadline )); then + echo "$avd ($serial): no boot within ${BOOT_TIMEOUT:-600}s; see /tmp/$avd.emulator.log" >&2 + "$ADB" -s "$serial" emu kill >/dev/null 2>&1 || true + exit 3 + fi + sleep 3 + done + "$ADB" -s "$serial" shell settings put global window_animation_scale 0 || true + "$ADB" -s "$serial" shell settings put global transition_animation_scale 0 || true + "$ADB" -s "$serial" shell settings put global animator_duration_scale 0 || true + echo "$serial" + ;; + stop) + "$ADB" -s "${2:?emulators.sh stop }" emu kill + ;; + list) + for avd in $AVDS; do + printf '%s\t%s\t%s\n' "$avd" "$(image_of "$avd")" \ + "$("$EMULATOR" -list-avds | grep -qx "$avd" && echo created || echo missing)" + done + ;; + sweep) + shift + LOCK="${CAMPAIGN_DEVICE_LOCK:-/Volumes/External2TB/Projects/Bugsee/cross/bugsee-react-native/.device-lock}" + [[ -d "$(dirname "$LOCK")" ]] || { echo "lock directory $(dirname "$LOCK") does not exist; set CAMPAIGN_DEVICE_LOCK" >&2; exit 2; } + HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + overall=0 + for avd in $AVDS; do + until mkdir "$LOCK" 2>/dev/null; do sleep 30; done + serial="emulator-$(port_of "$avd")" + trap '"$ADB" -s "$serial" emu kill >/dev/null 2>&1; rmdir "$LOCK" 2>/dev/null' EXIT + if ! "$0" boot "$avd" >/dev/null; then + echo "$avd ($serial): FAIL (did not boot)" + overall=1 + rmdir "$LOCK" + trap - EXIT + continue + fi + echo "$avd ($serial): API $("$ADB" -s "$serial" shell getprop ro.build.version.sdk | tr -d '\r'), page size $("$ADB" -s "$serial" shell getconf PAGE_SIZE | tr -d '\r')" + for app in "$@"; do + for cfg in debug release; do + out="$(CAMPAIGN_LOCK_HELD=1 "$HERE/launch-check.sh" "$app" android "$cfg" "$serial" 2>&1)" || overall=1 + grep -E ": (PASS|FAIL)" <<<"$out" || echo "$(basename "$app") android $cfg: FAIL (no result line)" + done + done + "$ADB" -s "$serial" emu kill >/dev/null 2>&1 || true + sleep 5 + rmdir "$LOCK" + trap - EXIT + done + exit "$overall" + ;; + *) + sed -n '3,25p' "$0" + exit 2 + ;; +esac diff --git a/scripts/campaign/gen-expo-app.sh b/scripts/campaign/gen-expo-app.sh new file mode 100755 index 00000000..534c8ed4 --- /dev/null +++ b/scripts/campaign/gen-expo-app.sh @@ -0,0 +1,124 @@ +#!/usr/bin/env bash +# +# N-20 / MX-EXPO: one Expo app per SDK, built from the blank template, with +# @bugsee/react-native installed from the packed tarball, the config plugin in +# app.json as the package README shows (no options: the placeholder token +# never goes into native files), examples/expo's App.js (N-20 smoke wiring) +# and `expo prebuild --clean`. Outside this repo. +# +# gen-expo-app.sh [--plugin-options ''] [--with-feedback] +# +# 54 (RN 0.81), 55 (0.83), 56 (0.85) or 57 (0.86) +# --plugin-options the plugin's options object for a PLG-* variant, e.g. +# '{"uploadSymbols":false}' (never a real token) +# --with-feedback also install @bugsee/react-native-feedback (PLG-12) +# +# Output: $CAMPAIGN_APPS/expo/ (package/bundle id +# com.bugsee.campaign.expo), Metro port 8300+ in campaign.port, +# logs/gen-expo.log. Build with build-app.sh, launch with +# launch-check.sh, exactly as the bare campaign apps. +set -euo pipefail + +main() { + local REPO HERE SDK PLUGIN_OPTIONS="" FEEDBACK=0 + REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" + HERE="$REPO/scripts/campaign" + SDK="${1:?usage: gen-expo-app.sh <54|55|56|57> [--plugin-options json] [--with-feedback]}" + shift + while [[ $# -gt 0 ]]; do + case "$1" in + --plugin-options) PLUGIN_OPTIONS="$2"; shift 2 ;; + --with-feedback) FEEDBACK=1; shift ;; + *) echo "unknown argument $1" >&2; exit 2 ;; + esac + done + [[ "$SDK" =~ ^5[4-7]$ ]] || { echo "sdk must be 54..57" >&2; exit 2; } + + local APPS TARBALLS CORE_TGZ FB_TGZ DIR ID PORT GEN_LOG + APPS="${CAMPAIGN_APPS:-$(cd "$REPO/.." && pwd)/campaign-apps}" + TARBALLS="$APPS/tarballs" + CORE_TGZ="$(ls "$TARBALLS"/bugsee-react-native-[0-9]*.tgz | head -1)" + FB_TGZ="$(ls "$TARBALLS"/bugsee-react-native-feedback-[0-9]*.tgz | head -1)" + DIR="$APPS/expo$SDK" + ID="com.bugsee.campaign.expo$SDK" + PORT=$((8300 + SDK)) + mkdir -p "$APPS/logs" + GEN_LOG="$APPS/logs/gen-expo$SDK.log" + : >"$GEN_LOG" + log() { echo "$*" | tee -a "$GEN_LOG"; } + + rm -rf "$DIR" + log "== generating expo$SDK from blank@sdk-$SDK into $DIR" + (cd "$APPS" && npx --yes create-expo-app@latest "expo$SDK" --template "blank-typescript@sdk-$SDK" \ + --no-install --no-agents-md) >>"$GEN_LOG" 2>&1 + cd "$DIR" + npm install --no-audit --no-fund >>"$GEN_LOG" 2>&1 + log "-- template: expo $(node -p "require('expo/package.json').version"), react-native $(node -p "require('react-native/package.json').version")" + + npm install --no-audit --no-fund "$CORE_TGZ" >>"$GEN_LOG" 2>&1 + log "README install: npm install $(basename "$CORE_TGZ")" + if [[ "$FEEDBACK" -eq 1 ]]; then + npm install --no-audit --no-fund "$FB_TGZ" >>"$GEN_LOG" 2>&1 + log "feedback: npm install $(basename "$FB_TGZ")" + fi + # WORKAROUND W-1: native-versions.json is not in the tarball (gen-rn-app.sh). + cp "$REPO/native-versions.json" node_modules/native-versions.json + log "WORKAROUND W-1 native-versions.json copied to node_modules/ (not in the tarball)" + + # app.json: ids, the e2e scheme, and the plugin as the README shows it. + node -e ' + const fs = require("fs"); + const [id, opts] = process.argv.slice(1); + const app = JSON.parse(fs.readFileSync("app.json", "utf8")); + const e = app.expo; + e.ios = { ...(e.ios || {}), bundleIdentifier: id }; + e.android = { ...(e.android || {}), package: id }; + e.scheme = "bugsee-e2e"; + const plugin = opts ? ["@bugsee/react-native", JSON.parse(opts)] : "@bugsee/react-native"; + e.plugins = [...(e.plugins || []).filter((p) => (Array.isArray(p) ? p[0] : p) !== "@bugsee/react-native"), plugin]; + fs.writeFileSync("app.json", JSON.stringify(app, null, 2) + "\n"); + ' "$ID" "$PLUGIN_OPTIONS" + log "app.json: ids $ID, scheme bugsee-e2e, plugin @bugsee/react-native ${PLUGIN_OPTIONS:-(no options)}" + + # The app: examples/expo's App.js and index.js (N-20) and its placeholder + # config, in place of the template's App.tsx / index.ts. + rm -f App.tsx App.js index.ts + cp "$REPO/examples/expo/App.js" App.js + cp "$REPO/examples/expo/index.js" index.js + node -e 'const fs=require("fs"); const p=JSON.parse(fs.readFileSync("package.json","utf8")); p.main="index.js"; fs.writeFileSync("package.json", JSON.stringify(p,null,2)+"\n");' + cp "$REPO/examples/expo/campaign-config.json" campaign-config.json + log "app: examples/expo/App.js (N-20)" + + CI=1 npx expo prebuild --clean --no-install >>"$GEN_LOG" 2>&1 + log "expo prebuild --clean: ok" + # The config plugin's two load-bearing edits must be there (as in + # scripts/check-expo-prebuild.sh); a prebuild without them is a failure. + grep -q 'com.bugsee.android.gradle' android/app/build.gradle \ + || { log "FAIL: the config plugin did not apply the Bugsee Gradle plugin"; exit 1; } + log "plugin: Bugsee Gradle plugin applied" + grep -q 'bugsee-xcode.sh' ios/*.xcodeproj/project.pbxproj \ + || { log "FAIL: the config plugin did not wire bugsee-xcode.sh into the bundle phase"; exit 1; } + log "plugin: iOS bundle phase runs bugsee-xcode.sh" + + # Harness support, as gen-rn-app.sh: debuggable release, own Metro port. + node -e ' + const fs=require("fs"); const f="android/app/build.gradle"; let s=fs.readFileSync(f,"utf8"); + s=s.replace(/(\n\s*release \{\n)/, "$1 debuggable findProperty(\"bugseeE2eDebuggable\") == \"true\" // campaign harness\n"); + fs.writeFileSync(f,s);' + node "$HERE/lib/ios-log-mirror.js" ios | tee -a "$GEN_LOG" + node "$HERE/lib/metro-port.js" "$DIR" "$PORT" >>"$GEN_LOG" + log "harness: debuggable release flag, Metro port $PORT" + + (cd ios && pod install) >>"$GEN_LOG" 2>&1 + log "pod install: ok" + if node "$HERE/lib/workaround-fmt.js" ios | tee -a "$GEN_LOG" | grep -q NEEDS_POD_INSTALL; then + (cd ios && pod install) >>"$GEN_LOG" 2>&1 + log "WORKAROUND W-3 fmt 11.x built as C++17 (React Native + Xcode 26.4+), pod install again" + fi + + cp "$HERE/template/campaign-consumer-check.ts" . + npx tsc --noEmit >>"$GEN_LOG" 2>&1 && log "tsc --noEmit: clean" || { log "tsc --noEmit: FAILED"; exit 1; } + log "== done: $DIR" +} + +main "$@" diff --git a/scripts/campaign/gen-rn-app.sh b/scripts/campaign/gen-rn-app.sh new file mode 100755 index 00000000..c1af5366 --- /dev/null +++ b/scripts/campaign/gen-rn-app.sh @@ -0,0 +1,246 @@ +#!/usr/bin/env bash +# +# N-21 / N-22: generates one bare React Native app on a given minor and +# integrates @bugsee/react-native into it the way the package README says, +# from the packed tarball (scripts/campaign/pack.sh), outside this repo. +# +# gen-rn-app.sh [--engine hermes|jsc] [--pm npm|yarn|pnpm] +# [--readme-only] [--no-workarounds] [--smoke ] +# +# 0.81 .. 0.87 (0.87 is examples/bare; generate it only for a +# variant such as --engine jsc) +# --engine jsc N-22: Hermes off, through @react-native-community/javascriptcore +# wired as that package's README says. +# --pm N-23 / MX-PACK: the package manager the app installs with. +# npm (default); yarn = Yarn 4 with the node-modules linker +# (corepack); pnpm with nodeLinker: hoisted, which React +# Native itself requires (its Gradle settings include +# ../node_modules/@react-native/gradle-plugin). +# --readme-only apply only what packages/react-native/README.md says. +# Without it the deviations the README is missing (R-2, R-3 +# in campaign-prep-build.md) are applied too and each one is +# logged with a "DEVIATION" line, so a README bug is never +# silently papered over. +# --no-workarounds leave out the WORKAROUND steps for product bugs (W-*), +# to reproduce them. +# --smoke copy the N-01 smoke module (App.tsx, index.js and whatever +# else holds) over the generated app instead of the +# launch-check App in scripts/campaign/template. +# +# Environment: +# CAMPAIGN_APPS where apps go (default: /../campaign-apps) +# RN_CLI_VERSION @react-native-community/cli used for init (default 20.2.0) +# +# Output: $CAMPAIGN_APPS/rn0[-jsc]/, with package and bundle id +# com.bugsee.campaign.rn0[jsc], the placeholder token and the dead +# endpoint in campaign-config.json, and gen.log listing every step. +set -euo pipefail + +# One function, so bash parses the whole script before running any of it. +main() { +REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +HERE="$REPO/scripts/campaign" +MINOR="${1:?usage: gen-rn-app.sh [--engine jsc] [--readme-only] [--smoke dir]}" +shift +ENGINE=hermes +README_ONLY=0 +WORKAROUNDS=1 +SMOKE="" +PM=npm +while [[ $# -gt 0 ]]; do + case "$1" in + --engine) ENGINE="$2"; shift 2 ;; + --readme-only) README_ONLY=1; shift ;; + --no-workarounds) WORKAROUNDS=0; shift ;; + --smoke) SMOKE="$(cd "$2" && pwd)"; shift 2 ;; + --pm) PM="$2"; shift 2 ;; + *) echo "unknown argument $1" >&2; exit 2 ;; + esac +done +[[ "$ENGINE" == hermes || "$ENGINE" == jsc ]] || { echo "--engine hermes|jsc" >&2; exit 2; } +case "$PM" in npm|yarn|pnpm) ;; *) echo "--pm npm|yarn|pnpm" >&2; exit 2 ;; esac +[[ "$MINOR" =~ ^0\.8[1-7]$ ]] || { echo "minor must be 0.81..0.87" >&2; exit 2; } + +APPS="${CAMPAIGN_APPS:-$(cd "$REPO/.." && pwd)/campaign-apps}" +TARBALLS="$APPS/tarballs" +CORE_TGZ="$(ls "$TARBALLS"/bugsee-react-native-[0-9]*.tgz 2>/dev/null | head -1 || true)" +[[ -n "$CORE_TGZ" ]] || { echo "no tarball in $TARBALLS; run scripts/campaign/pack.sh first" >&2; exit 1; } + +NUM="${MINOR#0.}" +SUFFIX="" +CAP="" +# Each variant flag adds its own bit, so no two variants of one minor share +# a Metro port: 8100 + minor, +100 jsc, +200 yarn, +400 pnpm, +800 readme. +PORT=$((8100 + NUM)) +if [[ "$ENGINE" == jsc ]]; then SUFFIX="jsc"; CAP="Jsc"; PORT=$((PORT + 100)); fi +case "$PM" in + yarn) SUFFIX="${SUFFIX}yarn"; CAP="${CAP}Yarn"; PORT=$((PORT + 200)) ;; + pnpm) SUFFIX="${SUFFIX}pnpm"; CAP="${CAP}Pnpm"; PORT=$((PORT + 400)) ;; +esac +if [[ "$README_ONLY" -eq 1 ]]; then SUFFIX="${SUFFIX}readme"; CAP="${CAP}Readme"; PORT=$((PORT + 800)); fi +NAME="Rn0${NUM}${CAP}" # Rn081, Rn087Jsc +ID="com.bugsee.campaign.rn0${NUM}${SUFFIX}" +DIR="$APPS/rn0${NUM}${SUFFIX:+-$SUFFIX}" +VERSION="$(npm view "react-native@~${MINOR}.0" version --json | node -e 'const v=JSON.parse(require("fs").readFileSync(0,"utf8")); console.log(Array.isArray(v)?v[v.length-1]:v)')" + +mkdir -p "$APPS" +rm -rf "$DIR" +LOG_DIR="$APPS/logs" +mkdir -p "$LOG_DIR" +GEN_LOG="$APPS/logs/gen-rn0${NUM}${SUFFIX:+-$SUFFIX}.log" +: > "$GEN_LOG" +log() { echo "$*" | tee -a "$GEN_LOG"; } +deviation() { log "DEVIATION $*"; } + +log "== generating $NAME: react-native $VERSION, engine $ENGINE, id $ID" +log " into $DIR from $(basename "$CORE_TGZ")" + +# 1. The template, exactly as `npx @react-native-community/cli init` writes it. +(cd "$APPS" && npx --yes "@react-native-community/cli@${RN_CLI_VERSION:-20.2.0}" init "$NAME" \ + --version "$VERSION" --directory "$DIR" --package-name "$ID" \ + --skip-install --skip-git-init --install-pods false) >>"$GEN_LOG" 2>&1 +log "-- template written" + +cd "$DIR" +# The app is outside this repo, so none of the workspace's Yarn settings +# reach it. The README says `yarn add`; from a tarball every package manager +# resolves the same files. +pm_add() { + case "$PM" in + npm) npm install --no-audit --no-fund "$@" ;; + yarn) yarn add "$@" ;; + pnpm) pnpm add "$@" ;; + esac +} +case "$PM" in + npm) npm install --no-audit --no-fund >>"$GEN_LOG" 2>&1 ;; + yarn) + printf 'nodeLinker: node-modules\nenableTelemetry: false\n' > .yarnrc.yml + rm -f package-lock.json + COREPACK_ENABLE_DOWNLOAD_PROMPT=0 corepack use yarn@4.11.0 >>"$GEN_LOG" 2>&1 ;; + pnpm) + # pnpm 11 reads its settings from pnpm-workspace.yaml, not .npmrc. + printf 'nodeLinker: hoisted\n' > pnpm-workspace.yaml + pnpm install >>"$GEN_LOG" 2>&1 ;; +esac +log "-- template dependencies installed ($PM $($PM --version))" + +# 2. README "Install": the package (from the packed tarball, since it is not +# published yet). +if [[ "$PM" == pnpm && "$README_ONLY" -eq 0 ]]; then + # pnpm 10+ runs no dependency build script unless allowed, and pnpm 11 + # fails the install over it: @bugsee/cli has a postinstall. The README does + # not say so (R-4). + printf "allowBuilds:\n '@bugsee/cli': true\n" >> pnpm-workspace.yaml + deviation "R-4 pnpm: @bugsee/cli's postinstall allowed (allowBuilds in pnpm-workspace.yaml)" +fi +if [[ "$PM" == yarn ]]; then + pm_add "@bugsee/react-native@file:$CORE_TGZ" >>"$GEN_LOG" 2>&1 +else + pm_add "$CORE_TGZ" >>"$GEN_LOG" 2>&1 +fi +log "README install: $PM add $(basename "$CORE_TGZ")" + +# 3. Engine (N-22). JSC left core react-native; the community package's +# README is followed: gradle.properties, the pod install flags (step 8), +# and the runtime factory in AppDelegate / MainApplication. +if [[ "$ENGINE" == jsc ]]; then + pm_add @react-native-community/javascriptcore >>"$GEN_LOG" 2>&1 + node "$HERE/lib/use-jsc.js" "$DIR" >>"$GEN_LOG" + log "JSC: @react-native-community/javascriptcore $(node -p "require('@react-native-community/javascriptcore/package.json').version"): hermesEnabled=false, useThirdPartyJSC=true, runtime factories" +fi + +# 4. README "Android source maps": the two edits in android/app/build.gradle. +node "$HERE/lib/readme-android.js" android/app/build.gradle >>"$GEN_LOG" +log "README android: bugseeDir + react.hermesCommand + apply from bugsee-sourcemaps.gradle" + +# 5. Deviations: what examples/bare needs and the package README does not say. +if [[ "$README_ONLY" -eq 0 ]]; then + node "$HERE/lib/deviation-gradle-plugin.js" "$DIR/android" "$REPO/native-versions.json" >>"$GEN_LOG" + deviation "R-2 Bugsee Gradle plugin: mavenCentral() in pluginManagement, root plugins{} pin, app apply plugin, bugsee.properties (placeholder: no token)" + node "$HERE/lib/deviation-ios-bundle-phase.js" "$DIR/ios/$NAME.xcodeproj/project.pbxproj" >>"$GEN_LOG" + deviation "R-3 iOS bundle phase runs scripts/bugsee-xcode.sh instead of react-native-xcode.sh" +fi + +# 6. Harness support (not integration): a release build the e2e can run-as +# into, as examples/bare does with -PbugseeE2eDebuggable=true. +node -e ' + const fs=require("fs"); const f=process.argv[1]; let s=fs.readFileSync(f,"utf8"); + s=s.replace(/(release \{\n)/, "$1 debuggable findProperty(\"bugseeE2eDebuggable\") == \"true\" // campaign harness\n"); + fs.writeFileSync(f,s);' android/app/build.gradle +log "harness: release { debuggable findProperty(bugseeE2eDebuggable) }" + +# 7. The app: README "Use" (launch with options), the placeholder token and the +# dead endpoint, never a real one. +cat > campaign-config.json <<'JSON' +{ + "ios": "00000000-0000-4000-8000-000000000000", + "android": "00000000-0000-4000-8000-000000000000", + "endpoint": "https://127.0.0.1:9" +} +JSON +if [[ -n "$SMOKE" ]]; then + cp -R "$SMOKE"/. . + log "app: N-01 smoke module from $SMOKE" +else + cp "$HERE/template/App.tsx" App.tsx + log "app: launch-check App.tsx (scripts/campaign/template)" +fi +# tsconfig: resolveJsonModule for campaign-config.json. +node -e ' + const fs=require("fs"); + // Template tsconfigs are JSONC (comments, trailing commas). + const t=new Function("return (" + fs.readFileSync("tsconfig.json","utf8") + ")")(); + t.compilerOptions={...(t.compilerOptions||{}), resolveJsonModule:true}; + fs.writeFileSync("tsconfig.json", JSON.stringify(t,null,2)+"\n");' + +# 7a. Harness support: BUGSEE_E2E markers reach NSLog (lib/ios-log-mirror.js). +node "$HERE/lib/ios-log-mirror.js" ios | tee -a "$GEN_LOG" + +# 7b. Harness support: this app's own Metro port (lib/metro-port.js). +node "$HERE/lib/metro-port.js" "$DIR" "$PORT" >>"$GEN_LOG" +log "harness: Metro port $PORT (campaign.port, RCT_METRO_PORT on React-Core)" + +# 8. iOS: README says CocoaPods before 0.87, SPM from 0.87. +if [[ "$NUM" -ge 87 ]]; then + log "README ios: 0.87+ resolves the SDK through SPM (pod install still sets up React Native)" +fi +# WORKAROUND W-1 (product bug, see campaign-prep-build.md): the packed +# podspecs read ../../native-versions.json and the wrapper's +# android/build.gradle walks up for it, but the file is not in the +# tarball, so neither `pod install` nor Gradle configures from a real +# install. /node_modules/native-versions.json satisfies both lookups. +# --no-workarounds leaves it out, to reproduce the bug. +# Applied last: a later npm install prunes it from node_modules. +if [[ "$WORKAROUNDS" -eq 1 ]]; then + cp "$REPO/native-versions.json" node_modules/native-versions.json + PKG_REAL="$(node -p "require('fs').realpathSync(require('path').dirname(require.resolve('@bugsee/react-native/package.json')))")" + [[ -f "$PKG_REAL/../../native-versions.json" ]] || cp "$REPO/native-versions.json" "$PKG_REAL/../../native-versions.json" + log "WORKAROUND W-1 native-versions.json copied to node_modules/ (not in the tarball)" +fi + +POD_ENV=() +[[ "$ENGINE" == jsc ]] && POD_ENV=(USE_THIRD_PARTY_JSC=1 USE_HERMES=0) +(cd ios && env "${POD_ENV[@]+"${POD_ENV[@]}"}" pod install) >>"$GEN_LOG" 2>&1 +log "README ios: pod install" +if [[ "$WORKAROUNDS" -eq 1 ]] && node "$HERE/lib/workaround-fmt.js" ios | tee -a "$GEN_LOG" | grep -q NEEDS_POD_INSTALL; then + (cd ios && env "${POD_ENV[@]+"${POD_ENV[@]}"}" pod install) >>"$GEN_LOG" 2>&1 + log "WORKAROUND W-3 fmt 11.x built as C++17 (React Native + Xcode 26.4+), pod install again" +fi + +# 9. Consumer type-check (API-48/49): the app, plus campaign-consumer-check.ts +# (every public export, and every removed 6.x API a compile error), with +# the template's TypeScript and with TypeScript 6. +cp "$HERE/template/campaign-consumer-check.ts" . +TS_VERSION="$(node -p "require('typescript/package.json').version")" +# A failure is reported and the generator exits 3, but only after the app is +# complete: the type errors are the finding, the app still builds. +TSC_RC=0 +npx tsc --noEmit >>"$GEN_LOG" 2>&1 && log "tsc $TS_VERSION --noEmit: clean" || { log "tsc $TS_VERSION --noEmit: FAILED (see $GEN_LOG)"; TSC_RC=3; } +npx --yes -p typescript@6 tsc --noEmit >>"$GEN_LOG" 2>&1 && log "tsc 6 --noEmit: clean" || { log "tsc 6 --noEmit: FAILED (see $GEN_LOG)"; TSC_RC=3; } + +log "== done: $DIR" +return "$TSC_RC" +} + +main "$@" diff --git a/scripts/campaign/launch-check.sh b/scripts/campaign/launch-check.sh new file mode 100755 index 00000000..4ce0d174 --- /dev/null +++ b/scripts/campaign/launch-check.sh @@ -0,0 +1,181 @@ +#!/usr/bin/env bash +# +# Minimal launch check for a campaign app until the N-01 smoke harness runs +# against it: install the build, launch it, and wait for the app's own +# markers in the device log: +# +# BUGSEE_E2E launching on rn= engine= +# BUGSEE_E2E status=2 (Launched) within 10 s of "launching" +# +# launch-check.sh android [serial] +# launch-check.sh ios-sim +# +# android defaults to the WOD_LX1 (AMRJCP4718402860); pass an emulator serial +# (emulators.sh boot) for the sweep. ios-sim is the campaign simulator +# 6FA9B3E8-26C7-4232-AA2C-537D9DF32957. The iPhone XS is never driven from +# here: use examples/bare/scripts/run-ios.sh (IOS_APP_DIR, IOS_LAUNCH=1). +# +# Takes .device-lock (blocking, no timeout) for the whole install+launch and +# releases it on every exit. CAMPAIGN_LOCK_HELD=1 means the caller already +# holds .device-lock (emulators.sh sweep: the emulator is up for several +# launches) and this script neither takes nor releases it. A Debug build loads JS from this app's own Metro +# port (campaign.port), never 8081, so another lane's Metro is never used and +# the 8081 reverse another lane may hold is left alone. +# +# Exit 0 = PASS. One result line goes to /logs/launch-results.tsv. +set -uo pipefail + +APP_DIR="$(cd "${1:?usage: launch-check.sh android|ios-sim debug|release [serial]}" && pwd)" +TARGET="${2:?target android|ios-sim}" +CONFIG="${3:?debug|release}" +SERIAL="${4:-AMRJCP4718402860}" +SIMULATOR=6FA9B3E8-26C7-4232-AA2C-537D9DF32957 +# The campaign's shared device lock (campaign-rules.md); CAMPAIGN_DEVICE_LOCK +# overrides it on another machine. +LOCK="${CAMPAIGN_DEVICE_LOCK:-/Volumes/External2TB/Projects/Bugsee/cross/bugsee-react-native/.device-lock}" +APP="$(basename "$APP_DIR")" +LOGS="$(dirname "$APP_DIR")/logs" +mkdir -p "$LOGS" +OUT="$LOGS/$APP-launch-$TARGET-$CONFIG.log" +PORT="$(cat "$APP_DIR/campaign.port" 2>/dev/null || echo 8081)" +: "${ANDROID_HOME:=$HOME/Library/Android/sdk}" +ADB="$ANDROID_HOME/platform-tools/adb" +BUDGET="${LAUNCH_BUDGET:-180}" +[[ "$TARGET" == android && "$SERIAL" == emulator-* ]] && BUDGET="${LAUNCH_BUDGET:-300}" + +# A missing parent would make the mkdir below fail forever, like a held lock. +[[ -d "$(dirname "$LOCK")" ]] || { echo "lock directory $(dirname "$LOCK") does not exist; set CAMPAIGN_DEVICE_LOCK" >&2; exit 2; } +case "$TARGET" in android|ios-sim) ;; *) echo "target android|ios-sim" >&2; exit 2 ;; esac +case "$CONFIG" in debug|release) ;; *) echo "debug|release" >&2; exit 2 ;; esac +[[ "$SERIAL" == 00008140* || "$SERIAL" == D027034D* ]] && { echo "refused: never this device" >&2; exit 2; } + +android_id() { + node -e ' + const s=require("fs").readFileSync(process.argv[1],"utf8"); + const m=/applicationId\s*=?\s*["\x27]([^"\x27]+)["\x27]/.exec(s); console.log(m?m[1]:"");' "$APP_DIR/android/app/build.gradle" +} +ios_app() { + local cfg="Debug"; [[ "$CONFIG" == release ]] && cfg="Release" + ls -d "$APP_DIR"/ios/build/Build/Products/"$cfg"-iphonesimulator/*.app 2>/dev/null | head -1 +} + +METRO_PID="" +LOG_PID="" +RECORDED=0 +record_row() { # result + local engine rn + engine="$(grep -o 'engine=[a-z]*' "$OUT" 2>/dev/null | head -1)" + rn="$(grep -o 'rn=[0-9.]*' "$OUT" 2>/dev/null | head -1)" + printf '%s\t%s\t%s\t%s\t%s\t%s %s\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$APP" \ + "$TARGET:$( [[ $TARGET == android ]] && echo "$SERIAL" || echo sim)" "$CONFIG" "$1" "$rn" "$engine" \ + >>"$LOGS/launch-results.tsv" + RECORDED=1 +} +cleanup() { + # Every exit leaves a row: an install or Metro failure is a FAIL, not a gap. + [[ "$RECORDED" == 1 ]] || { record_row FAIL; echo "$APP $TARGET $CONFIG: FAIL (before launch, see $OUT)"; } + if [[ -n "$LOG_PID" ]]; then + kill "$LOG_PID" 2>/dev/null + wait "$LOG_PID" 2>/dev/null + fi + if [[ -n "$METRO_PID" ]]; then + pkill -P "$METRO_PID" 2>/dev/null + kill "$METRO_PID" 2>/dev/null + # npx's node children: whatever still listens on this app's port. + lsof -t -iTCP:"$PORT" -sTCP:LISTEN 2>/dev/null | xargs kill 2>/dev/null + fi + if [[ "$TARGET" == android && "$CONFIG" == debug ]]; then + "$ADB" -s "$SERIAL" reverse --remove "tcp:$PORT" 2>/dev/null + fi + [[ "${CAMPAIGN_LOCK_HELD:-0}" == 1 ]] || rmdir "$LOCK" 2>/dev/null +} + +start_metro() { + if [[ -f "$APP_DIR/app.json" ]] && grep -q '"expo"' "$APP_DIR/app.json"; then + (cd "$APP_DIR" && CI=1 exec npx expo start --port "$PORT") >"$OUT.metro" 2>&1 "$OUT.metro" 2>&1 >"$OUT" + return 1 +} + +: >"$OUT" +echo "== $APP $TARGET $CONFIG $( [[ $TARGET == android ]] && echo "$SERIAL" || echo "$SIMULATOR") port $PORT" | tee -a "$OUT" + +if [[ "${CAMPAIGN_LOCK_HELD:-0}" != 1 ]]; then + until mkdir "$LOCK" 2>/dev/null; do sleep 30; done +fi +trap cleanup EXIT + +result=FAIL +if [[ "$TARGET" == android ]]; then + ID="$(android_id)" + [[ -n "$ID" ]] || { echo "no applicationId in android/app/build.gradle" | tee -a "$OUT"; exit 1; } + APK="$APP_DIR/android/app/build/outputs/apk/$CONFIG/app-$CONFIG.apk" + echo "adb reverse before: $("$ADB" -s "$SERIAL" reverse --list | tr '\n' ' ')" >>"$OUT" + "$ADB" -s "$SERIAL" install -r "$APK" >>"$OUT" 2>&1 || { echo "install failed" | tee -a "$OUT"; exit 1; } + if [[ "$CONFIG" == debug ]]; then + start_metro || exit 1 + "$ADB" -s "$SERIAL" reverse "tcp:$PORT" "tcp:$PORT" >>"$OUT" + fi + "$ADB" -s "$SERIAL" shell am force-stop "$ID" + "$ADB" -s "$SERIAL" logcat -c || { echo "logcat -c failed" | tee -a "$OUT"; exit 1; } + # Straight to a file, detached from this script's stdout: a reader holding + # the caller's pipe open would keep the caller waiting after we exit. + "$ADB" -s "$SERIAL" logcat -v time "ReactNativeJS:V" "Bugsee:V" "BugseeRN:V" "AndroidRuntime:E" "*:S" \ + >>"$OUT" 2>&1 /dev/null 2>&1 +else + APP_PATH="$(ios_app)" + [[ -n "$APP_PATH" ]] || { echo "no simulator build" | tee -a "$OUT"; exit 1; } + ID="$(/usr/libexec/PlistBuddy -c 'Print :CFBundleIdentifier' "$APP_PATH/Info.plist")" + xcrun simctl bootstatus "$SIMULATOR" -b >/dev/null 2>&1 + xcrun simctl install "$SIMULATOR" "$APP_PATH" >>"$OUT" 2>&1 || { echo "install failed" | tee -a "$OUT"; exit 1; } + [[ "$CONFIG" == debug ]] && { start_metro || exit 1; } + xcrun simctl terminate "$SIMULATOR" "$ID" >/dev/null 2>&1 + xcrun simctl spawn "$SIMULATOR" log stream --style compact \ + --predicate 'eventMessage CONTAINS "BUGSEE_E2E" OR eventMessage CONTAINS "Bugsee"' >>"$OUT" 2>&1 >"$OUT" 2>&1 +fi + +launched_this_run() { + awk ' + /BUGSEE_E2E launching on/ { pid = ""; if (match($0, /\( *[0-9]+\)|\[[0-9]+:/)) pid = substr($0, RSTART, RLENGTH); gsub(/[^0-9]/, "", pid); seen = 1; next } + seen && /BUGSEE_E2E status=2/ { + p = ""; if (match($0, /\( *[0-9]+\)|\[[0-9]+:/)) p = substr($0, RSTART, RLENGTH); gsub(/[^0-9]/, "", p) + if (pid == "" || p == pid) { found = 1 } + } + END { exit found ? 0 : 1 } + ' "$OUT" +} + +deadline=$((SECONDS + BUDGET)) +while [[ $SECONDS -lt $deadline ]]; do + # Only this launch counts: status=2 after this launch's own + # "BUGSEE_E2E launching" line, from the same process (Android: logcat's + # "(pid)"; iOS: log stream's "[pid:"). + if launched_this_run; then result=PASS; break; fi + if grep -qE "FATAL EXCEPTION|launch\(\) rejected" "$OUT"; then break; fi + sleep 2 +done +sleep 2 +record_row "$result" +echo "$APP $TARGET $CONFIG: $result $(grep -o 'rn=[0-9.]*' "$OUT" | head -1) $(grep -o 'engine=[a-z]*' "$OUT" | head -1)" +grep -E "BUGSEE_E2E" "$OUT" | head -8 +[[ "$result" == PASS ]] diff --git a/scripts/campaign/lib/deviation-gradle-plugin.js b/scripts/campaign/lib/deviation-gradle-plugin.js new file mode 100644 index 00000000..a037cfac --- /dev/null +++ b/scripts/campaign/lib/deviation-gradle-plugin.js @@ -0,0 +1,48 @@ +#!/usr/bin/env node +// DEVIATION R-2: the Bugsee Gradle plugin, which Bugsee Android 7.x needs and +// packages/react-native/README.md does not mention ("no Gradle changes are +// required"). Mirrors examples/bare: mavenCentral() in +// settings.gradle pluginManagement, the root `plugins {}` pin from +// native-versions.json, `apply plugin` in app/build.gradle, and an +// android/bugsee.properties with no token (the placeholder never uploads). +'use strict'; +const fs = require('fs'); +const path = require('path'); + +const androidDir = process.argv[2]; +const versions = JSON.parse(fs.readFileSync(process.argv[3], 'utf8')); +const pin = versions.android.gradlePlugin; + +function edit(file, fn) { + const p = path.join(androidDir, file); + const before = fs.readFileSync(p, 'utf8'); + const after = fn(before); + if (after === before) throw new Error(`${file}: edit did not apply`); + fs.writeFileSync(p, after); +} + +const REPOS = ' repositories {\n gradlePluginPortal()\n google()\n mavenCentral()\n }\n'; +edit('settings.gradle', (s) => { + // Templates write it on one line: `pluginManagement { includeBuild("...") }`. + const oneLine = /^pluginManagement \{ (.*) \}$/m; + if (oneLine.test(s)) return s.replace(oneLine, (m, body) => `pluginManagement {\n ${body}\n${REPOS}}`); + if (/^pluginManagement \{\s*$/m.test(s)) return s.replace(/^pluginManagement \{\s*\n/m, (m) => m + REPOS); + return `pluginManagement {\n${REPOS}}\n` + s; +}); + +edit('build.gradle', (s) => + s.replace( + /\napply plugin: "com.facebook.react.rootproject"/, + `\nplugins {\n id 'com.bugsee.android.gradle' version '${pin}' apply false\n}\n\napply plugin: "com.facebook.react.rootproject"`, + ), +); + +edit('app/build.gradle', (s) => + s.replace(/(apply plugin: "com.facebook.react"\n)/, '$1apply plugin: "com.bugsee.android.gradle"\n'), +); + +fs.writeFileSync( + path.join(androidDir, 'bugsee.properties'), + '# campaign app: placeholder token only. No app token configured.\n', +); +console.log(`deviation-gradle-plugin: com.bugsee.android.gradle ${pin}`); diff --git a/scripts/campaign/lib/deviation-ios-bundle-phase.js b/scripts/campaign/lib/deviation-ios-bundle-phase.js new file mode 100644 index 00000000..aea44849 --- /dev/null +++ b/scripts/campaign/lib/deviation-ios-bundle-phase.js @@ -0,0 +1,15 @@ +#!/usr/bin/env node +// DEVIATION R-3: the iOS "Bundle React Native code and images" phase runs +// @bugsee/react-native/scripts/bugsee-xcode.sh (compose, inject the debug id, +// upload) instead of react-native-xcode.sh, as examples/bare does. The +// package README documents only the Android half of the debug-id hooks. +'use strict'; +const fs = require('fs'); + +const pbx = process.argv[2]; +const before = fs.readFileSync(pbx, 'utf8'); +const from = 'REACT_NATIVE_XCODE=\\"$REACT_NATIVE_PATH/scripts/react-native-xcode.sh\\"'; +const to = 'REACT_NATIVE_XCODE=\\"${SRCROOT}/../node_modules/@bugsee/react-native/scripts/bugsee-xcode.sh\\"'; +if (!before.includes(from)) throw new Error(`${pbx}: template bundle phase not found`); +fs.writeFileSync(pbx, before.replace(from, to)); +console.log('deviation-ios-bundle-phase: bundle phase runs bugsee-xcode.sh'); diff --git a/scripts/campaign/lib/ios-log-mirror.js b/scripts/campaign/lib/ios-log-mirror.js new file mode 100644 index 00000000..4553b5c5 --- /dev/null +++ b/scripts/campaign/lib/ios-log-mirror.js @@ -0,0 +1,45 @@ +#!/usr/bin/env node +// Harness support (not integration): React Native writes console.log to +// os_log only, and os_log cannot be streamed off a physical iPhone, so a +// Release build on the XS shows no BUGSEE_E2E marker on the +// `devicectl ... --console` attachment run-ios.sh IOS_LAUNCH=1 reads. Like +// examples/bare's AppDelegate, mirror the BUGSEE_E2E lines (only those) to +// NSLog, which that attachment does show. Template AppDelegate.swift only; +// an AppDelegate of another shape (Expo's) is left alone and said so. +'use strict'; +const fs = require('fs'); +const path = require('path'); + +const iosDir = process.argv[2]; +const candidates = fs + .readdirSync(iosDir, { withFileTypes: true }) + .filter((e) => e.isDirectory() && !/^(Pods|build.*|.*\.xcodeproj|.*\.xcworkspace)$/.test(e.name)) + .map((e) => path.join(iosDir, e.name, 'AppDelegate.swift')) + .filter((p) => fs.existsSync(p)); +if (candidates.length !== 1) { + console.log(`ios-log-mirror: ${candidates.length} AppDelegate.swift found, left alone`); + process.exit(0); +} +const file = candidates[0]; +let s = fs.readFileSync(file, 'utf8'); +if (s.includes('campaignMirrorMarkers')) process.exit(0); +const launch = /(didFinishLaunchingWithOptions launchOptions: \[UIApplication\.LaunchOptionsKey: Any\]\? = nil\n\s*\) -> Bool \{\n)/; +if (!launch.test(s) || !/^import React$/m.test(s)) { + console.log('ios-log-mirror: AppDelegate is not the React Native template shape, left alone'); + process.exit(0); +} +s = s.replace(launch, '$1 campaignMirrorMarkers()\n'); +s += + '\n/// Campaign harness: BUGSEE_E2E markers also go to NSLog, which a\n' + + '/// `devicectl ... --console` attachment shows (os_log it does not).\n' + + 'private func campaignMirrorMarkers() {\n' + + ' let osLog = RCTDefaultLogFunction\n' + + ' RCTSetLogFunction { level, source, fileName, lineNumber, message in\n' + + ' osLog?(level, source, fileName, lineNumber, message)\n' + + ' if let message, message.contains("BUGSEE_E2E") {\n' + + ' NSLog("%@", message)\n' + + ' }\n' + + ' }\n' + + '}\n'; +fs.writeFileSync(file, s); +console.log(`ios-log-mirror: ${path.relative(iosDir, file)}`); diff --git a/scripts/campaign/lib/metro-port.js b/scripts/campaign/lib/metro-port.js new file mode 100644 index 00000000..5861f77b --- /dev/null +++ b/scripts/campaign/lib/metro-port.js @@ -0,0 +1,37 @@ +#!/usr/bin/env node +// Harness support (not integration): each campaign app gets its own Metro +// port, so its Debug build never loads another app's bundle from a Metro some +// other lane started on 8081. Writes /campaign.port and bakes the port +// into the iOS build (RCT_METRO_PORT on React-Core, from the Podfile's +// post_install) where React-Core compiles from source (0.81-0.83); a +// prebuilt core (0.84+) ignores it, so launch-check.sh also passes +// -RCT_jsLocation localhost: at launch. Android takes it at build +// time: -PreactNativeDevServerPort= (build-app.sh). +'use strict'; +const fs = require('fs'); +const path = require('path'); + +const [appDir, port] = process.argv.slice(2); +if (!/^\d{4,5}$/.test(port || '')) throw new Error('usage: metro-port.js '); +fs.writeFileSync(path.join(appDir, 'campaign.port'), `${port}\n`); + +const podfile = path.join(appDir, 'ios', 'Podfile'); +let s = fs.readFileSync(podfile, 'utf8'); +if (!s.includes('campaign metro port')) { + const anchor = /(\n\s*react_native_post_install\([\s\S]*?\n\s*\)\n)/; + if (!anchor.test(s)) throw new Error('Podfile: react_native_post_install(...) not found'); + s = s.replace( + anchor, + `$1 # campaign metro port: this app's Debug build asks Metro on ${port}, not 8081.\n` + + ` installer.pods_project.targets.each do |t|\n` + + ` next unless t.name == 'React-Core'\n` + + ` t.build_configurations.each do |c|\n` + + ` defs = c.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] || ['$(inherited)']\n` + + ` defs = [defs] if defs.is_a?(String)\n` + + ` c.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] = defs + ['RCT_METRO_PORT=${port}']\n` + + ` end\n` + + ` end\n`, + ); + fs.writeFileSync(podfile, s); +} +console.log(`metro-port: ${port}`); diff --git a/scripts/campaign/lib/readme-android.js b/scripts/campaign/lib/readme-android.js new file mode 100644 index 00000000..97b1967b --- /dev/null +++ b/scripts/campaign/lib/readme-android.js @@ -0,0 +1,52 @@ +#!/usr/bin/env node +// README "Android source maps", applied literally to a template's +// android/app/build.gradle: the bugseeDir resolution, react.hermesCommand, +// and `apply from: bugsee-sourcemaps.gradle` at the end of the file. +// The snippet text is read from the packed README, so a README change +// changes what generated apps get. +'use strict'; +const fs = require('fs'); +const path = require('path'); + +const gradleFile = process.argv[2]; +const appRoot = path.resolve(path.dirname(gradleFile), '..', '..'); +const readme = fs.readFileSync( + require.resolve('@bugsee/react-native/README.md', { paths: [appRoot] }), + 'utf8', +); + +const section = readme.slice(readme.indexOf('## Android source maps')); +const fence = /```groovy\n([\s\S]*?)```/.exec(section); +if (!fence) throw new Error('README: no groovy block under "## Android source maps"'); +const snippet = fence[1]; + +const defMatch = /^def bugseeDir[\s\S]*?getParentFile\(\)\n/m.exec(snippet); +const hermesMatch = /^\s*hermesCommand = .*$/m.exec(snippet); +const applyMatch = /^apply from: .*$/m.exec(snippet); +if (!defMatch || !hermesMatch || !applyMatch) { + throw new Error('README snippet changed shape; update scripts/campaign/lib/readme-android.js'); +} +// One line, whole: brackets close on it, and it names both launchers (the +// per-OS choice). A value split over lines would be cut here silently. +const hermesLine = hermesMatch[0]; +const depth = [...hermesLine].reduce((d, ch) => d + ('([{'.includes(ch) ? 1 : ')]}'.includes(ch) ? -1 : 0), 0); +if (depth !== 0 || !hermesLine.includes('hermesc-preserve-js.cmd') || !hermesLine.includes('hermesc-preserve-js.sh')) { + throw new Error(`README hermesCommand is not one whole per-OS line: ${hermesLine.trim()}`); +} + +let gradle = fs.readFileSync(gradleFile, 'utf8'); +const reactBlock = gradle.indexOf('\nreact {'); +if (reactBlock < 0) throw new Error(`${gradleFile}: no top-level react { block`); +gradle = + gradle.slice(0, reactBlock + 1) + + defMatch[0] + + '\n' + + gradle.slice(reactBlock + 1).replace( + /^react \{\n/, + 'react {\n // Copies the JS aside before hermesc compiles it, so the debug id can\n // reach the bytecode.\n ' + + hermesMatch[0].trim() + + '\n', + ); +gradle = gradle.replace(/\s*$/, '\n') + '\n// At the end of the file. Applying it twice is harmless.\n' + applyMatch[0] + '\n'; +fs.writeFileSync(gradleFile, gradle); +console.log(`readme-android: edited ${gradleFile}`); diff --git a/scripts/campaign/lib/use-jsc.js b/scripts/campaign/lib/use-jsc.js new file mode 100644 index 00000000..429446fa --- /dev/null +++ b/scripts/campaign/lib/use-jsc.js @@ -0,0 +1,82 @@ +#!/usr/bin/env node +// N-22: switch a generated app from Hermes to JavaScriptCore the way +// @react-native-community/javascriptcore's README says: +// android/gradle.properties hermesEnabled=false, useThirdPartyJSC=true +// MainApplication.kt the JSC runtime factory on the ReactHost +// AppDelegate.swift createJSRuntimeFactory() -> jsrt_create_jsc_factory() +// pod install USE_THIRD_PARTY_JSC=1 USE_HERMES=0 (gen-rn-app.sh) +// Handles both MainApplication shapes: the ReactNativeHost one (<= 0.8x) and +// the packageList one (0.87). +'use strict'; +const fs = require('fs'); +const path = require('path'); + +const app = process.argv[2]; + +function edit(file, fn) { + const before = fs.readFileSync(file, 'utf8'); + const after = fn(before); + if (after === before) throw new Error(`use-jsc: ${file} did not change`); + fs.writeFileSync(file, after); + console.log(`use-jsc: edited ${path.relative(app, file)}`); +} + +edit(path.join(app, 'android/gradle.properties'), (s) => + s.replace(/^hermesEnabled=true$/m, 'hermesEnabled=false') + + (/useThirdPartyJSC=/.test(s) ? '' : '\n# Enable third-party JSC\nuseThirdPartyJSC=true\n'), +); + +function find(dir, name) { + for (const entry of fs.readdirSync(dir, { withFileTypes: true })) { + const p = path.join(dir, entry.name); + if (entry.isDirectory()) { + const hit = find(p, name); + if (hit) return hit; + } else if (entry.name === name) { + return p; + } + } + return null; +} + +const mainApp = find(path.join(app, 'android/app/src/main'), 'MainApplication.kt'); +edit(mainApp, (s) => { + s = s.replace( + /(import com\.facebook\.react\.ReactHost\n)/, + '$1import io.github.reactnativecommunity.javascriptcore.JSCRuntimeFactory\n', + ); + if (/getDefaultReactHost\(applicationContext, reactNativeHost\)/.test(s)) { + return s.replace( + 'getDefaultReactHost(applicationContext, reactNativeHost)', + 'getDefaultReactHost(applicationContext, reactNativeHost, JSCRuntimeFactory())', + ); + } + return s.replace( + /(getDefaultReactHost\(\s*\n\s*context = applicationContext,\n)/, + '$1 jsRuntimeFactory = JSCRuntimeFactory(),\n', + ); +}); + +// The package README's `-> JSRuntimeFactory` predates RN 0.87, which +// declares `- (JSRuntimeFactoryRef)createJSRuntimeFactory` (Swift: +// JSRuntimeFactoryRef). Use whichever this React Native declares. +const protocolHeader = path.join( + app, + 'node_modules/react-native/Libraries/AppDelegate/RCTJSRuntimeConfiguratorProtocol.h', +); +const factoryType = + fs.existsSync(protocolHeader) && + /\(JSRuntimeFactoryRef\)createJSRuntimeFactory/.test(fs.readFileSync(protocolHeader, 'utf8')) + ? 'JSRuntimeFactoryRef' + : 'JSRuntimeFactory'; +console.log(`use-jsc: createJSRuntimeFactory returns ${factoryType}`); + +const appDelegate = find(path.join(app, 'ios'), 'AppDelegate.swift'); +edit(appDelegate, (s) => + s + .replace(/(import ReactAppDependencyProvider\n)/, '$1import ReactJSC\n') + .replace( + /(class ReactNativeDelegate: RCTDefaultReactNativeFactoryDelegate \{\n)/, + `$1 override func createJSRuntimeFactory() -> ${factoryType} {\n jsrt_create_jsc_factory() // JavaScriptCore, not Hermes (campaign N-22)\n }\n\n`, + ), +); diff --git a/scripts/campaign/lib/workaround-fmt.js b/scripts/campaign/lib/workaround-fmt.js new file mode 100644 index 00000000..7d1f79f2 --- /dev/null +++ b/scripts/campaign/lib/workaround-fmt.js @@ -0,0 +1,32 @@ +#!/usr/bin/env node +// WORKAROUND W-3 (third party: React Native + Xcode 26.4+, not Bugsee): +// fmt 11.0.2, which React Native's Podfile pulls in, no longer compiles as +// C++20 (`call to consteval function ... is not a constant expression` in +// format-inl.h). Build that one pod as C++17 from the Podfile's post_install. +// Applied only when Podfile.lock pins fmt 11.x. +'use strict'; +const fs = require('fs'); +const path = require('path'); + +const iosDir = process.argv[2]; +const lock = fs.readFileSync(path.join(iosDir, 'Podfile.lock'), 'utf8'); +if (!/^ - fmt \(11\./m.test(lock)) { + console.log('workaround-fmt: fmt is not 11.x, nothing to do'); + process.exit(0); +} +const podfile = path.join(iosDir, 'Podfile'); +let s = fs.readFileSync(podfile, 'utf8'); +if (s.includes('campaign W-3')) process.exit(0); +const anchor = /(\n\s*react_native_post_install\([\s\S]*?\n\s*\)\n)/; +if (!anchor.test(s)) throw new Error('Podfile: react_native_post_install(...) not found'); +s = s.replace( + anchor, + `$1 # campaign W-3: fmt 11.x does not compile as C++20 with Xcode 26.4+.\n` + + ` installer.pods_project.targets.each do |t|\n` + + ` next unless t.name == 'fmt'\n` + + ` t.build_configurations.each { |c| c.build_settings['CLANG_CXX_LANGUAGE_STANDARD'] = 'c++17' }\n` + + ` end\n`, +); +fs.writeFileSync(podfile, s); +console.log('workaround-fmt: patched Podfile'); +console.log('NEEDS_POD_INSTALL'); diff --git a/scripts/campaign/pack.sh b/scripts/campaign/pack.sh new file mode 100755 index 00000000..c2a4531b --- /dev/null +++ b/scripts/campaign/pack.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash +# +# Packs @bugsee/react-native and @bugsee/react-native-feedback with `npm pack` +# into /tarballs, the input of gen-rn-app.sh, gen-expo-app.sh +# and pack-install.sh. `prepack` (the releasable-pins guard) runs as it would +# on publish. Prints each tarball's name and SHA-256. +# +# `npm pack`, not `yarn pack`: yarn 4 drops packages/react-native/plugin/build +# (the compiled Expo config plugin that app.plugin.js requires) from the +# tarball, because the repo's .gitignore ignores **/build/**; npm honours the +# package's `files` list. See W-2 in campaign-prep-build.md. +set -euo pipefail + +REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +APPS="${CAMPAIGN_APPS:-$(cd "$REPO/.." && pwd)/campaign-apps}" +OUT="$APPS/tarballs" +mkdir -p "$OUT" +rm -f "$OUT"/*.tgz + +(cd "$REPO" && yarn workspace @bugsee/react-native build:plugin >/dev/null) +for pkg in react-native react-native-feedback; do + (cd "$REPO/packages/$pkg" && npm pack --silent --pack-destination "$OUT" >/dev/null) +done +for tgz in "$OUT"/*.tgz; do + echo "$(basename "$tgz") $(shasum -a 256 "$tgz" | cut -d' ' -f1)" +done +# The compiled plugin must be in the core tarball (app.plugin.js requires it). +tar tzf "$OUT"/bugsee-react-native-[0-9]*.tgz | grep -q '^package/plugin/build/index.js$' \ + || { echo "FAIL: plugin/build/index.js missing from the core tarball"; exit 1; } diff --git a/scripts/campaign/template/App.tsx b/scripts/campaign/template/App.tsx new file mode 100644 index 00000000..a3985bc2 --- /dev/null +++ b/scripts/campaign/template/App.tsx @@ -0,0 +1,81 @@ +/** + * Launch check for a campaign-generated app (scripts/campaign/gen-rn-app.sh). + * + * Launches Bugsee with the placeholder token against the dead endpoint and + * prints the same markers examples/bare prints, so the e2e harness and + * scripts/campaign/launch-check.sh can read them from the device log: + * + * BUGSEE_E2E launching on rn= engine= + * BUGSEE_E2E status= () + * + * Replaced by the N-01 smoke module when gen-rn-app.sh is given --smoke. + */ +import { useEffect, useState } from 'react'; +import { Platform, StyleSheet, Text, View } from 'react-native'; +import Bugsee, { + BugseeLaunchOptions, + Status, + createDefaultLaunchOptions, +} from '@bugsee/react-native'; + +import config from './campaign-config.json'; + +const STATUS_NAMES: Record = { + [Status.Stopped]: 'Stopped', + [Status.Launching]: 'Launching', + [Status.Launched]: 'Launched', + [Status.Stopping]: 'Stopping', +}; + +function engine(): string { + return (globalThis as { HermesInternal?: unknown }).HermesInternal ? 'hermes' : 'jsc'; +} + +export default function App() { + const [status, setStatus] = useState(Status.Stopped); + const [error, setError] = useState(); + + useEffect(() => { + let last = -1; + const poll = setInterval(() => { + Bugsee.getStatus() + .then(next => { + if (next !== last) { + last = next; + setStatus(next); + console.log(`BUGSEE_E2E status=${next} (${STATUS_NAMES[next]})`); + } + }) + .catch(() => undefined); + }, 100); + + const rn = Platform.constants.reactNativeVersion; + console.log( + `BUGSEE_E2E launching on ${Platform.OS} rn=${rn.major}.${rn.minor}.${rn.patch} engine=${engine()}`, + ); + const options = createDefaultLaunchOptions(); + options.endpoint = config.endpoint; + // The package README's `options.serialize()` does not exist: serialize is + // static (README deviation R-1 in campaign-prep-build.md). + const token = Platform.OS === 'ios' ? config.ios : config.android; + Bugsee.launch(token, BugseeLaunchOptions.serialize(options)) + .then(ok => console.log(`BUGSEE_E2E launch() resolved ${ok}`)) + .catch((e: unknown) => { + const message = e instanceof Error ? e.message : String(e); + console.log(`BUGSEE_E2E launch() rejected ${message}`); + setError(message); + }); + return () => clearInterval(poll); + }, []); + + return ( + + Bugsee status: {STATUS_NAMES[status]} + {error === undefined ? null : {error}} + + ); +} + +const styles = StyleSheet.create({ + root: { flex: 1, alignItems: 'center', justifyContent: 'center' }, +}); diff --git a/scripts/campaign/template/campaign-consumer-check.ts b/scripts/campaign/template/campaign-consumer-check.ts new file mode 100644 index 00000000..401fbb4a --- /dev/null +++ b/scripts/campaign/template/campaign-consumer-check.ts @@ -0,0 +1,118 @@ +/** + * API-48 / API-49: the public surface, type-checked by a consumer against the + * installed tarball (`tsc --noEmit` in the generated app). Never executed. + * + * Every named export a consumer may import must resolve with the type it is + * documented to have; every 6.x API removed in 7.x must be a compile error + * (each `@ts-expect-error` fails the check if the line compiles). + */ +import Bugsee, { + AndroidLaunchOptions, + AttributeErrorCode, + BugseeAttributeError, + BugseeLaunchOptions, + BugseeReportError, + BugseeSecure, + BugseeSpanError, + ErrorBoundary, + FrameRate, + IOSLaunchOptions, + IssueSeverity, + LogLevel, + PACKAGE_VERSION, + ReportErrorCode, + SpanErrorCode, + SpanStatus, + type Status, + VideoMode, + VideoQuality, + WRAPPER_TYPE, + createDefaultLaunchOptions, + endpointFor, + type AddedNetworkEvent, + type AttributeValue, + type BreadcrumbSnapshot, + type BreadcrumbFilter, + type BugseeCreatedReport, + type BugseeReport, + type BugseeReportHandler, + type BugseeSecureProps, + type BugseeSpan, + type BugseeTransaction, + type ErrorBoundaryProps, + type EventParams, + type ExceptionOptions, + type LaunchOptions, + type LifecycleEvent, + type LogFilter, + type NetworkFilter, + type SecureRectangle, + type TraceValue, +} from '@bugsee/react-native'; + +export async function consumer(): Promise { + const options: BugseeLaunchOptions = createDefaultLaunchOptions(); + options.captureLogs = true; + options.duration = 60; + const payload: LaunchOptions = BugseeLaunchOptions.serialize(options); + const launched: boolean = await Bugsee.launch('token', payload); + const status: Status = await Bugsee.getStatus(); + const sub = Bugsee.onStatusChange((next: Status) => void next); + sub.remove(); + const life = Bugsee.onLifecycleEvent((event: LifecycleEvent) => void event); + life.remove(); + + const rects: SecureRectangle[] = [{ x: 0, y: 0, width: 1, height: 1 }]; + await Bugsee.setSecureRectangles(rects); + const exceptionOptions: ExceptionOptions = { domain: 'consumer' }; + await Bugsee.logException(new Error('x'), exceptionOptions); + const params: EventParams = { a: 1 }; + await Bugsee.event('e', params); + const trace: TraceValue = 1; + await Bugsee.trace('t', trace); + const value: AttributeValue = 'v'; + await Bugsee.setAttribute('k', value); + await Bugsee.log('line', LogLevel.Info); + + const handler: BugseeReportHandler = { + onBeforeReportCreated: async (report: BugseeReport) => void report, + }; + Bugsee.setReportHandler(handler); + Bugsee.setReportHandler(null); + const logFilter: LogFilter = (line) => line; + Bugsee.setLogFilter(logFilter); + const networkFilter: NetworkFilter = (event) => event; + Bugsee.setNetworkFilter(networkFilter); + const crumbFilter: BreadcrumbFilter = (crumb: BreadcrumbSnapshot) => crumb; + Bugsee.setBreadcrumbFilter(crumbFilter); + const added: AddedNetworkEvent | undefined = undefined; + void added; + + const txn: BugseeTransaction = await Bugsee.startTransaction('n', 'op'); + const span: BugseeSpan = await txn.startChildSpan('child'); + await span.finish(SpanStatus.OK); + const created: BugseeCreatedReport | null = await Bugsee.createReport(); + void created; + + void [launched, status, PACKAGE_VERSION, WRAPPER_TYPE, endpointFor]; + void [AndroidLaunchOptions, IOSLaunchOptions, FrameRate, IssueSeverity, VideoMode, VideoQuality]; + void [AttributeErrorCode, BugseeAttributeError, BugseeReportError, ReportErrorCode]; + void [BugseeSpanError, SpanErrorCode, BugseeSecure, ErrorBoundary]; + const secureProps: Partial = {}; + const boundaryProps: Partial = {}; + void [secureProps, boundaryProps]; + + // Removed in 7.x: each line must NOT compile. + // @ts-expect-error removed in 7.x + Bugsee.pause(); + // @ts-expect-error removed in 7.x + Bugsee.resume(); + // @ts-expect-error removed in 7.x + Bugsee.setEmail('a@b.c'); + // @ts-expect-error removed in 7.x + Bugsee.getDeviceId(); + // @ts-expect-error removed in 7.x + Bugsee.setKeyboardVisibility(true); + // @ts-expect-error upload(summary, description, severity, labels, includeVideo) is gone + Bugsee.upload('s', 'd', IssueSeverity.High, [], true); +} diff --git a/scripts/check-expo-prebuild.sh b/scripts/check-expo-prebuild.sh index 3496e996..3114a814 100755 --- a/scripts/check-expo-prebuild.sh +++ b/scripts/check-expo-prebuild.sh @@ -31,7 +31,7 @@ expect android/settings.gradle 'mavenCentral()' expect android/build.gradle "id 'com.bugsee.android.gradle' version '$plugin_version' apply false" expect android/app/build.gradle 'apply plugin: "com.bugsee.android.gradle"' expect android/app/build.gradle "implementation \"com.bugsee:bugsee-android-ndk:$sdk_version\"" -expect android/app/build.gradle 'scripts/hermesc-preserve-js.sh' +expect android/app/build.gradle '"/scripts/hermesc-preserve-js" + (System.getProperty("os.name").startsWith("Windows") ? ".cmd" : ".sh")' expect android/app/build.gradle 'scripts/bugsee-sourcemaps.gradle")' expect android/app/build.gradle "debugSymbolLevel 'SYMBOL_TABLE'" expect android/bugsee.properties 'No app token configured.' diff --git a/stryker.plugin.json b/stryker.plugin.json index 0cb3ad09..477aed88 100644 --- a/stryker.plugin.json +++ b/stryker.plugin.json @@ -26,6 +26,7 @@ "packages/react-native/plugin/src/**/*.ts", "packages/react-native/scripts/hermes-sourcemaps.js", "packages/react-native/scripts/compose-then-inject.js", + "packages/react-native/scripts/hermesc-preserve-js.js", "!**/__tests__/**" ], "thresholds": { diff --git a/tsconfig.json b/tsconfig.json index 8d518228..b8291d00 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,5 +8,8 @@ // not infer the extension. "allowImportingTsExtensions": true }, - "include": ["scripts/**/*.ts", "packages/*/src/**/*.ts", "packages/*/src/**/*.tsx"] + "include": ["scripts/**/*.ts", "packages/*/src/**/*.ts", "packages/*/src/**/*.tsx"], + // Copied into each campaign app and type-checked there, against the packed + // package (scripts/campaign/gen-rn-app.sh); it cannot resolve the package here. + "exclude": ["scripts/campaign/template/**"] }