feat: bundle Mieru native binary (no external plugin required)#12
Conversation
Build the Mieru client from source for all 4 Android ABIs and bundle it as app/executableSo/<abi>/libmieru.so, so Mieru profiles work without installing the separate moe.matsuri.exe.mieru plugin. - buildScript/lib/mieru.sh: cross-compiles enfein/mieru v3.34.0 via the NDK for arm64-v8a, armeabi-v7a, x86, x86_64 (./run lib mieru). - PluginManager.initNativeInternal: resolve mieru-plugin -> libmieru.so from nativeLibraryDir, mirroring the bundled-Hysteria mechanism. Falls back to the external APK plugin when the bundled binary is absent. - Executable.EXECUTABLES: add libmieru.so so orphaned processes are cleaned up. - ci.yml: build + cache the Mieru binaries (Go+NDK) and restore before Gradle. Reuses the existing MieruBean / MieruFmt buildMieruConfig / BoxInstance run contract (config via MIERU_CONFIG_JSON_FILE, socket protection via MIERU_PROTECT_PATH) unchanged; upstream mieru v3 honors both.
Address review feedback and ensure shipping APKs bundle Mieru: - Add the Mieru native-build job + cache restore + artifact verification to build.yml, preview.yml, release.yml (previously only ci.yml had it), so release/preview APKs also bundle libmieru.so. - Include buildScript/init/env.sh in the Mieru cache-status hash (mieru.sh sources it). - Pin MIERU_VERSION as a workflow env and include it in the cache key so a version override invalidates stale binaries. - Add a 'Verify Mieru Artifacts' step in each build job that fails fast if any ABI's libmieru.so is missing (guards against a silent cache miss shipping an APK without the bundled binary). - mieru.sh: validate ANDROID_NDK_HOME is set before use.
GitHub Actions cache restore does not reliably preserve the execute bit, so testing with -x could falsely fail on a cache hit. The runtime execute permission is applied by Android when extracting from the APK; here we only need to confirm the binary exists.
Fail fast with a clear message if neither the linux-x86_64 nor darwin-x86_64 NDK LLVM toolchain dir exists, instead of a cryptic CC-not-found later.
Guard the incremental-fetch path: if fetch/checkout fail (e.g. a corrupted .mieru-build from a prior failed run), remove and clone fresh instead of erroring out.
Add a darwin-arm64 fallback for the NDK LLVM toolchain so local Mieru builds work natively on Apple Silicon Macs (newer NDKs ship darwin-arm64).
|
Warning Review limit reached
More reviews will be available in 7 hours and 54 minutes. Learn how PR review limits work. Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file). ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (7)
📝 WalkthroughWalkthroughAdds Mieru as a native plugin: a new ChangesMieru Native Plugin Integration
Sequence Diagram(s)sequenceDiagram
participant mieru_job as mieru job
participant cache as GitHub Actions Cache
participant build_job as build job
participant gradle as Gradle Build
mieru_job->>mieru_job: compute mieru_status hash
mieru_job->>cache: restore app/executableSo (key: MIERU_VERSION+hash)
alt cache miss
mieru_job->>mieru_job: install Go
mieru_job->>mieru_job: ./run lib mieru → libmieru.so ×4 ABIs
mieru_job->>cache: save app/executableSo
end
mieru_job-->>build_job: job complete (needs satisfied)
build_job->>cache: restore mieru-cache
build_job->>build_job: verify libmieru.so for each ABI
build_job->>gradle: run Android Gradle build
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Comment |
|
@coderabbitai full review Please focus on the bundled-binary mechanism, the plugin-resolution fallback (bundled -> external APK), CI cross-compilation wiring, and process lifecycle/cleanup. Two notes on prior local-review findings I assessed and did not apply:
All 4 ABIs verified packaged in both debug and release APKs on the builder. |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
# Get the list of changed files and diffs
gh pr view 12 --repo hawkff/NekoBoxForAndroid --json files,title,body | jq '.files[].path'Length of output: 485 🏁 Script executed: #!/bin/bash
set -euo pipefail
# Get the full diff
gh pr diff 12 --repo hawkff/NekoBoxForAndroidLength of output: 13300 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "=== .gitignore (root) ==="
cat .gitignore 2>/dev/null || echo "not found"
echo ""
echo "=== app/.gitignore ==="
cat app/.gitignore 2>/dev/null || echo "not found"
echo ""
echo "=== soIfExist in PluginManager.kt ==="
grep -n "soIfExist\|initNativeInternal\|nativeLibraryDir\|fallback\|pluginId\|external" \
app/src/main/java/io/nekohasekai/sagernet/plugin/PluginManager.kt
echo ""
echo "=== Executable.kt full EXECUTABLES context ==="
grep -n -A 30 "EXECUTABLES\|killAll\|killProcess" \
app/src/main/java/io/nekohasekai/sagernet/bg/Executable.kt | head -80
echo ""
echo "=== env.sh sources ==="
cat buildScript/init/env.sh 2>/dev/null || echo "not found"
echo ""
echo "=== env_ndk.sh ==="
cat buildScript/init/env_ndk.sh 2>/dev/null || echo "not found"
echo ""
echo "=== check executableSo dir ==="
find app/executableSo -name "*.so" 2>/dev/null | head -20 || echo "no executableSo files in tree"
echo ""
echo "=== existing libcore / hysteria build scripts for comparison ==="
ls buildScript/lib/ 2>/dev/null
echo ""
echo "=== hysteria.sh for pattern reference ==="
cat buildScript/lib/hysteria.sh 2>/dev/null | head -60 || echo "not found"
echo ""
echo "=== check CGO usage in mieru upstream cmd/mieru or main.go ==="
# Can't run go build, but see if we can inspect the mieru source for cgo
# Just note the build_abi params; can't clone here
echo "Cannot access upstream source in sandbox."Length of output: 6325
Review SummaryThe overall architecture is sound and consistent with the existing Hysteria bundled-binary pattern. The plugin fallback chain, process-lifecycle integration, and CI cache design are all correct. A few issues worth addressing: 🔴 Medium1.
# .gitignore
+/.mieru-build2. All four workflows compute: run: cat buildScript/lib/mieru.sh buildScript/init/env.sh | sha1sum > mieru_status
-run: cat buildScript/lib/mieru.sh buildScript/init/env.sh | sha1sum > mieru_status
+run: cat buildScript/lib/mieru.sh buildScript/init/env.sh buildScript/init/env_ndk.sh | sha1sum > mieru_status(Applies to all four workflows in both the 🟡 Minor3. Each of the four workflows defines +env:
+ MIERU_VERSION: v3.34.0
+
jobs:
libcore:
mieru:
- env:
- MIERU_VERSION: v3.34.0
build:
- env:
- MIERU_VERSION: v3.34.04.
-set -e
+set -euo pipefail( 5. The shallow clone stays on disk after a successful build. In CI this is reclaimed at job end, but locally it accumulates. A simple 6. The script uses ✅ What looks good
|
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
.github/workflows/build.yml (1)
41-90:⚠️ Potential issue | 🟠 MajorPin GitHub Actions to commit SHAs and disable checkout credential persistence.
The new Mieru workflow additions use floating action tags and lack checkout credential persistence hardening. This weakens supply-chain guarantees for CI execution.
.github/workflows/build.yml#L42,47,54: pinactions/checkout@v5,actions/cache@v5, andactions/setup-go@v6to commit SHAs; addpersist-credentials: falseto the checkout step..github/workflows/ci.yml#L47,52,59: apply the same pinning andpersist-credentials: falsehardening to checkout, cache, and setup-go..github/workflows/preview.yml#L38,43,50: apply the same pinning and checkout hardening..github/workflows/release.yml#L44,49,56: apply the same pinning and checkout hardening.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/workflows/build.yml around lines 41 - 90, Pin GitHub Actions to specific commit SHAs instead of floating version tags and add credential persistence hardening to improve supply-chain security. In .github/workflows/build.yml (lines 41-90), replace actions/checkout@v5 with its SHA-pinned equivalent, add persist-credentials: false to the checkout step, and pin actions/cache@v5 and actions/setup-go@v6 to their respective commit SHAs. Apply the identical pinning changes for checkout, cache, and setup-go actions in .github/workflows/ci.yml (lines 46-99), .github/workflows/preview.yml (lines 37-85), and .github/workflows/release.yml (lines 43-91), ensuring that each checkout action across all four workflow files includes the persist-credentials: false configuration.Source: Linters/SAST tools
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/build.yml:
- Around line 35-59: Add explicit least-privilege permissions to the mieru job
definitions across multiple workflow files to reduce blast radius. For each of
the following files and their specified line ranges, add a permissions section
at the job level with contents: read permissions: in .github/workflows/build.yml
(lines 35-59) add permissions with contents: read to the mieru job, in
.github/workflows/ci.yml (lines 40-64) add the same permissions configuration to
the corresponding mieru job, in .github/workflows/preview.yml (lines 31-55) add
the same permissions configuration to the mieru job, and in
.github/workflows/release.yml (lines 37-61) add the same permissions
configuration to the mieru job. The permissions should be specified as a
top-level key under each mieru job, not under the steps.
---
Outside diff comments:
In @.github/workflows/build.yml:
- Around line 41-90: Pin GitHub Actions to specific commit SHAs instead of
floating version tags and add credential persistence hardening to improve
supply-chain security. In .github/workflows/build.yml (lines 41-90), replace
actions/checkout@v5 with its SHA-pinned equivalent, add persist-credentials:
false to the checkout step, and pin actions/cache@v5 and actions/setup-go@v6 to
their respective commit SHAs. Apply the identical pinning changes for checkout,
cache, and setup-go actions in .github/workflows/ci.yml (lines 46-99),
.github/workflows/preview.yml (lines 37-85), and .github/workflows/release.yml
(lines 43-91), ensuring that each checkout action across all four workflow files
includes the persist-credentials: false configuration.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 5dfef8df-5bd3-4936-9c3e-08ae34a19a56
📒 Files selected for processing (7)
.github/workflows/build.yml.github/workflows/ci.yml.github/workflows/preview.yml.github/workflows/release.ymlapp/src/main/java/io/nekohasekai/sagernet/bg/Executable.ktapp/src/main/java/io/nekohasekai/sagernet/plugin/PluginManager.ktbuildScript/lib/mieru.sh
…ION, pipefail - Include buildScript/init/env_ndk.sh (NDK version pin) in the Mieru cache-key hash so bumping the NDK invalidates stale binaries. - Hoist MIERU_VERSION to a single workflow-level env block per workflow (was duplicated in the mieru and build jobs). - Add 'set -o pipefail' to mieru.sh as a defensive guard.
|
@coderabbitai review Addressed the env_ndk.sh cache-hash, consolidated MIERU_VERSION to workflow-level env, and added set -o pipefail. On the latest local-review findings (all assessed, none applied):
Builds verified on the Linux builder (all 4 ABIs packaged, debug + release). |
|
Triggering a full re-review on the updated branch now. ✏️ Learnings added
✅ Action performedFull review finished. |
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
buildScript/lib/mieru.sh (1)
20-53:⚠️ Potential issue | 🟠 MajorPin Mieru to an immutable commit SHA (not only a tag).
Building from
MIERU_VERSIONalone allows silent source drift if the upstream tag is moved, which can ship unreviewed native binaries. Add a pinnedMIERU_COMMIT, verify the tag resolves to it, and include that commit value in CI cache keys.Proposed hardening diff
-MIERU_VERSION="${MIERU_VERSION:-v3.34.0}" +MIERU_VERSION="${MIERU_VERSION:-v3.34.0}" +# Immutable source pin for reproducible/safe builds. +MIERU_COMMIT="${MIERU_COMMIT:-1532c85cc8ca08dff469326f35a3f027697c6950}" @@ -if [ -d "$WORK/.git" ]; then - if git -C "$WORK" fetch --depth 1 origin "refs/tags/$MIERU_VERSION" \ - && git -C "$WORK" checkout -q FETCH_HEAD; then +if [ -d "$WORK/.git" ]; then + if git -C "$WORK" fetch --depth 1 origin "$MIERU_COMMIT" "refs/tags/$MIERU_VERSION" \ + && [ "$(git -C "$WORK" rev-parse "$MIERU_VERSION^{commit}")" = "$MIERU_COMMIT" ] \ + && git -C "$WORK" checkout -q --detach "$MIERU_COMMIT"; then need_clone=0 else echo ">> existing $WORK is unusable; re-cloning" fi fi if [ "$need_clone" -eq 1 ]; then rm -rf "$WORK" - git clone --depth 1 --branch "$MIERU_VERSION" https://github.com/enfein/mieru.git "$WORK" + git clone --no-checkout https://github.com/enfein/mieru.git "$WORK" + git -C "$WORK" fetch --depth 1 origin "$MIERU_COMMIT" "refs/tags/$MIERU_VERSION" + [ "$(git -C "$WORK" rev-parse "$MIERU_VERSION^{commit}")" = "$MIERU_COMMIT" ] + git -C "$WORK" checkout -q --detach "$MIERU_COMMIT" fi🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@buildScript/lib/mieru.sh` around lines 20 - 53, The current implementation pins only the Mieru tag via MIERU_VERSION variable, which allows source drift if the upstream tag is moved, potentially shipping unreviewed native binaries. Add a new MIERU_COMMIT variable to pin the build to a specific immutable commit SHA. After fetching the tag in the git fetch operation (in the section where git -C "$WORK" fetch --depth 1 origin "refs/tags/$MIERU_VERSION" is called), verify that the fetched tag resolves to the expected commit by comparing it against the pinned MIERU_COMMIT value before proceeding with the git checkout. This ensures that builds are reproducible and cannot silently drift if upstream tags are modified.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@buildScript/lib/mieru.sh`:
- Around line 20-53: The current implementation pins only the Mieru tag via
MIERU_VERSION variable, which allows source drift if the upstream tag is moved,
potentially shipping unreviewed native binaries. Add a new MIERU_COMMIT variable
to pin the build to a specific immutable commit SHA. After fetching the tag in
the git fetch operation (in the section where git -C "$WORK" fetch --depth 1
origin "refs/tags/$MIERU_VERSION" is called), verify that the fetched tag
resolves to the expected commit by comparing it against the pinned MIERU_COMMIT
value before proceeding with the git checkout. This ensures that builds are
reproducible and cannot silently drift if upstream tags are modified.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 0664743e-9f6d-4fc3-b3b9-1f191d28f17a
📒 Files selected for processing (5)
.github/workflows/build.yml.github/workflows/ci.yml.github/workflows/preview.yml.github/workflows/release.ymlbuildScript/lib/mieru.sh
🚧 Files skipped from review as they are similar to previous changes (3)
- .github/workflows/build.yml
- .github/workflows/preview.yml
- .github/workflows/release.yml
Summary
Makes Mieru work without the separate
moe.matsuri.exe.mieruplugin by building theMieru client from source for all Android ABIs and bundling it as
app/executableSo/<abi>/libmieru.so.How it works
Reuses the existing bundled-binary mechanism (the same one Hysteria uses):
app/executableSo/<abi>/libmieru.sois packaged into the APK (via thejniLibs.srcDir("executableSo")hook) and extracted tonativeLibraryDirat install.PluginManager.initNativeInternalresolvesmieru-plugin -> libmieru.sofromnativeLibraryDir. If the bundled binary is absent it still falls back to the externalAPK plugin, so nothing breaks for existing plugin users.
MieruBean/MieruFmt.buildMieruConfig/BoxInstancerun-contract isunchanged: invoked as
<path> run, config viaMIERU_CONFIG_JSON_FILE, VPN socketprotection via
MIERU_PROTECT_PATH. Verified upstream mieru v3.34.0 still honors both.Changes
buildScript/lib/mieru.sh(./run lib mieru): cross-compilesenfein/mieruv3.34.0via the NDK for arm64-v8a, armeabi-v7a, x86, x86_64. (Upstream only ships an arm64
prebuilt and the old matsuri plugin shipped arm64+x86_64; this covers all four.)
PluginManager.initNativeInternal: add"mieru-plugin" -> soIfExist("libmieru.so").Executable.EXECUTABLES: addlibmieru.sofor orphan-process cleanup.ci.yml,build.yml,preview.yml,release.yml: aNative Build (Mieru)job buildsand caches the binaries (Go+NDK), the build job restores the cache, and a
Verify Mieru Artifactsstep fails fast if any ABI'slibmieru.sois missing — sorelease/preview APKs always bundle Mieru.
Verification (AWS Linux builder)
./run lib mieruproduces all 4 ABI binaries (proper Android PIE executables)../gradlew app:assembleOssDebugandassembleOssReleasesucceed; verifiedlib/<abi>/libmieru.sois packaged in the APK for all 4 ABIs (debug and release).ubuntu-latestdefault), so the CI job resolves the runner's NDK viaenv_ndk.sh'sANDROID_NDK_HOMEfallback — the same mechanism the existing libcore job uses.ANDROID_NDK_HOME, supports linux-x86_64 /darwin-x86_64 / darwin-arm64 NDK hosts, and re-clones if the source checkout is corrupted.
Notes on rejected review suggestions
go.mod'stoolchain go1.24.9(set in the merged Go-toolchain-align PR); a Go patch bump is a separatemaintenance concern.
works with the runner's default NDK (27) via the existing
env_ndk.shfallback,consistent with the libcore job which uses the same pattern.
Runtime testing note
Build/packaging verified (binary present + executable contract intact). Live proxy
runtime test pending the batched emulator QA (EC2 metal/vCPU quota increase pending).
Scope
no Android UI changes (the existing plugin-resolution fallback handles UX), no DB/
serialization changes. Binaries are gitignored and built in CI (not committed).