Commit graph

42 commits

Author SHA1 Message Date
okhsunrog
ecf8f5cd98 ci: export ANDROID_NDK_ROOT for Gobley plugin
Real cause of the lsposed/lint NPE on CI: Gobley's
RustAndroidTarget.ndkToolchainDir resolves the NDK by checking, in
order, the explicit `ndkRoot` parameter, `<sdkRoot>/ndk/<latestVersion>`,
then `$ANDROID_NDK_ROOT`. The CI image installs the NDK as a separate
tree at /opt/android-ndk and exports `ANDROID_NDK_HOME`, not
`ANDROID_NDK_ROOT` — so all three lookups return null and Gobley's `!!`
produces a bare `NullPointerException` during `:app` configuration.

Locally my shell exports `ANDROID_NDK_ROOT` (Android Studio convention),
which is why the issue only surfaces in CI.

Bake `ANDROID_NDK_ROOT` into the CI Dockerfile and export it inline in
the lint / lsposed gradle steps so this PR's CI passes before the image
rebuilds. Revert the prior `rustup target add x86_64-unknown-linux-gnu`
and `--stacktrace` debug additions — that was a wrong-hypothesis
workaround (the host target is already installed by `rustup-init`).
2026-04-26 04:41:24 +03:00
okhsunrog
c24aeccb4b ci: add --stacktrace to lsposed gradle invocations (debug) 2026-04-26 04:41:24 +03:00
okhsunrog
33fa5574c3 ci: install x86_64-unknown-linux-gnu rust target for lsposed jobs
Gobley's cargo plugin enumerates Kotlin targets at gradle configure
time and queries rustup for each one — including the JVM host target,
even though we never build for it (`androidUnitTest = false` skips
wiring the JVM cargo build into Android unit tests, but the build
entry is still created at configure time).

Without `x86_64-unknown-linux-gnu` installed, that lookup returns
null and `:app:lint` / `assembleRelease` die with a bare
`NullPointerException` during project configuration.

Add the target as a workflow step in the lint and lsposed jobs so
this PR's CI passes immediately, and bake it into the CI Dockerfile
so subsequent image rebuilds carry it.
2026-04-26 04:41:24 +03:00
okhsunrog
1ff35d3347 ci(lsposed/native): make cargo test build on host
`libc::ioctl`'s second arg is `Ioctl`, which is `c_int` on android-arm64
but `c_ulong` on linux x86_64. Hardcoding `as i32` made the host build
of the lsposed/native test harness fail with a type mismatch, so
`cargo test --lib` couldn't compile and the generated `iface_lists`
unit tests in this crate were silently dead.

Use `as _` so the cast picks the right width per target. Then add the
matching `cargo test (lsposed native)` step in CI for symmetry with the
zygisk crate, so the codegen tests actually run.
2026-04-26 04:06:40 +03:00
okhsunrog
38f9b56f5b feat(codegen): grammar forms digits_optional / any + generated tests for all 4 langs
Some checks are pending
CI / kmod (android13-5.15) (push) Waiting to run
CI / kmod (android14-5.15) (push) Waiting to run
CI / kmod (android14-6.1) (push) Waiting to run
CI / kmod (android15-6.6) (push) Waiting to run
CI / kmod (android16-6.12) (push) Waiting to run
CI / kmod (android12-5.10) (push) Waiting to run
CI / kmod (android13-5.10) (push) Waiting to run
CI / setup (push) Waiting to run
CI / lint (push) Blocked by required conditions
CI / zygisk (push) Blocked by required conditions
CI / lsposed (push) Blocked by required conditions
CI / portshide (push) Waiting to run
CI / release (push) Blocked by required conditions
Two follow-ups to #90 in one PR:

1. Two new match forms in data/interfaces.toml grammar:
     suffix = "digits_optional"   prefix + 0+ ASCII digits
     suffix = "any"                prefix + 1+ any chars
   Needed by the upcoming whitelist (PR-B) for patterns like
   `seth_lte\d*` and `v4-.+`. Not used by any current [[vpn]] rule, but
   the helper functions are exercised by direct unit tests in the
   generated test modules so a bug would surface before whitelist lands.

2. [[test]] vectors in data/interfaces.toml that the codegen renders
   into per-language unit tests:
     - zygisk + lsposed/native: #[cfg(test)] mod tests inside the
       generated iface_lists.rs (run via `cargo test`)
     - lsposed/app: a separate IfaceListsGeneratedTest under
       src/test/kotlin (run via `:app:testDebugUnitTest`)
     - kmod: a userspace test driver test_iface_lists.c — the
       generated header now has __KERNEL__-guarded includes so the
       same matcher compiles against libc, and a new lint step builds
       and runs it via gcc.
   36 fixed vectors today; trivial to grow as new rules / corner cases
   come up. CI catches drift on the next push: any single matcher that
   disagrees with the toml fails its job.

No production behavior change — generated matches_vpn / vpnhide_iface_is_vpn
/ IfaceLists.isVpnIface bodies are byte-identical to before; only the
helper functions and test modules grew.
2026-04-25 21:18:29 +03:00
Danila Gornushko
daa98f3138
refactor: drive VPN-iface matching from a single TOML source of truth (#90)
The kernel module, zygisk, lsposed-native, and the LSPosed Kotlin module
each had their own hand-written list of VPN interface name prefixes,
and the four had drifted: kmod/zygisk/HookEntry knew utun/l2tp/gre
while lsposed-native and DiagnosticsScreen only knew tun/wg/ppp/tap/
ipsec/xfrm. So the self-test could PASS while the hooks were actually
hiding more interfaces.

Move the rules to data/interfaces.toml and render four matchers from it
via scripts/codegen-interfaces.py — one per language target. A new lint
job re-runs the codegen and fails if anything drifts.

The match grammar is intentionally tiny so each codegen target
implements it without depending on regex (kernel C can't):
  exact / prefix / prefix+digits / contains.

Side effect: native diagnostics now agree with the hooks, so the
self-test in DiagnosticsScreen will recognize utun*, l2tp*, gre* and
*vpn* substrings as VPN tunnels (previously it would silently PASS on
those). The /proc/net/route check also moved from raw substring to
whitespace-tokenized matching, which avoids matching VPN-prefix
substrings that show up by chance inside hex-encoded IP addresses.

Existing zygisk filter unit tests still pass unchanged — public API of
is_vpn_iface_bytes / is_vpn_iface_cstr is preserved, only the body now
delegates to the generated matches_vpn().

Cargo.lock files updated incidentally (synced with Cargo.toml versions
that were already 0.7.1 in the manifests).
2026-04-25 20:53:11 +03:00
Horizon
cf4e72fa01
fix(build): port build scripts to Python to allow Windows contributors to build subprojects (#83)
* Rewrite build-version and all build-zip bash scripts to python

* Add executable permissions to python build scripts

* Use python build script for kmod in CI

* Fix

* Enhance kmod build script, add/fix docs, CI edits

* Delete remaining build-zip bash scripts

* Delete remaining build-zip bash scripts
2026-04-25 19:53:15 +03:00
okhsunrog
4ad2ba8c2d ci: fall back to ephemeral keystore when secrets are unavailable
GitHub Actions does not expose secrets to workflows triggered by PRs from
forks, so the lsposed job's `assembleRelease` was failing with a corrupt
release.jks for every external contributor. Generate a throwaway keystore
on the fly in that case so fork PRs get a green CI; signed-for-release
artifacts (push/tag runs) keep using the real secrets unchanged.
2026-04-25 19:42:44 +03:00
BlueGradientHorizon
e170a6e9df Simpler approach to lowercasing
Revert adding docker/metadata-action
2026-04-21 18:32:28 +03:00
BlueGradientHorizon
1f662b0489 Proper fix instead of dirty hack
Use docker/metadata-action to perform username lowercasing
2026-04-20 21:05:03 +03:00
BlueGradientHorizon
0830cd82fc Try to fix CI 2026-04-20 20:42:44 +03:00
okhsunrog
b09f05ce60 feat: diagnose wrong-variant kmod installs end-to-end
Users routinely installed the wrong GKI variant of the kmod zip and
saw no signal beyond "installed, inactive". This adds a full chain
from build to diagnostics so the wrong-variant case is both obvious
to the user and fully captured in bug reports.

Why each piece exists:

- CI stamps `gkiVariant=<kmi>` into each variant's `module.prop`
  so the app can identify what was installed without guessing.
- `post-fs-data.sh` records `/data/adb/vpnhide_kmod/load_status`
  (boot_id, uname -r, gki_variant, insmod exit+stderr, kprobes,
  root manager) and `load_dmesg` at every boot — this survives
  reboots and is the only record of insmod failures by the time
  the user opens the app.
- Dashboard reads both, always computes the kernel-based
  recommendation, and emits targeted issues: wrong-variant,
  unknown-variant (pre-stamp zip that also failed to load),
  kmod-on-unsupported-kernel, kprobes-missing, or generic
  load-failed with the captured stderr.
- Diagnostics screen adds a "Kmod load trace" card so bug
  reports can come in as a screenshot, and the debug zip
  includes load_status + load_dmesg for deeper analysis.

Also aligns `lsposed/native/Cargo.lock` with Cargo.toml (0.6.1
→ 0.6.2) — a real stale-lock fix surfaced by the gradle build.
2026-04-19 20:41:30 +03:00
okhsunrog
98b05c256f fix(lsposed): skip version-mismatch warning for dev builds
Dashboard compared module.prop versions (e.g. 0.6.2) directly against
BuildConfig.VERSION_NAME (which carries the git-describe suffix
0.6.2-14-g1f2205e on dev builds), always flagging mismatch. Now uses
baseVersion() to strip -N-gSHA before comparison; pre-release tags
(-rc1, -beta) are preserved.

Adds unit tests for normalizeVersion, compareSemver, baseVersion and
versionsMismatch, and wires :app:testDebugUnitTest into CI so they
actually run.
2026-04-19 18:51:32 +03:00
okhsunrog
0a9fcef3c0 ci: rename vpnhide APK artifact + publish as draft release
Two tweaks driven by the same goal — make the artifact list on the CI
run page less ambiguous and give the release step a review gate.

- The APK artifact was named `vpnhide`, which blends in with the other
  module-zip artifacts (`vpnhide-kmod-*`, `vpnhide-zygisk`,
  `vpnhide-ports`). Rename to `vpnhide-apk` so every entry in the
  Artifacts list names the thing you actually get when you download it.
- Release-on-tag job now creates a DRAFT GitHub release instead of
  publishing directly. Gives a chance to eyeball the release notes and
  attached binaries before they go public, and avoids racing
  update-json.sh against the assets becoming reachable.

docs/releasing.md and the release.py post-run hints updated to reflect
the manual Publish step and the fact that update-json still has to
wait for the release to be *published*, not just drafted (draft
release assets sit behind auth).
2026-04-17 15:54:32 +03:00
okhsunrog
637761e678 fix(ci): don't dirty committed module.prop when injecting updateJson
Yesterday's Phase 2 commit left the zygisk and portshide CI artifacts
carrying a "-dirty" suffix in their module.prop version: CI appended
`updateJson=...` to the committed module.prop *before* calling
build-zip.sh, so when build-version.sh ran inside the script it saw
the dirtied working tree and `git describe --dirty` appended "-dirty".

Move the updateJson injection into build-zip.sh itself, gated on an
UPDATE_JSON_URL env var. CI sets the env var via the job step `env:`
block; committed module.prop files are no longer touched. Local dev
builds leave the var unset and ship without updateJson, matching the
previous behaviour.

kmod CI already did things in the right order (version computed
before any module.prop edits); left that step as-is.
2026-04-17 14:54:33 +03:00
okhsunrog
3fc735572a build: stamp git-describe build version into every artifact
Add scripts/build-version.sh — a single source of truth for the
effective version string:

  * HEAD on tag vX.Y.Z          -> "X.Y.Z"
  * N commits past tag          -> "X.Y.Z-N-gSHA"
  * working tree dirty          -> additional "-dirty" suffix
  * no git / no matching tag    -> VERSION file fallback

Wired into every packaging path:

  * zygisk/build-zip.sh and portshide/build-zip.sh now stage a copy of
    module/ and sed-patch `version=` in the staging copy, so committed
    module.prop files stay at the last-released version.
  * kmod/build-zip.sh now builds into a staging copy too.
  * The kmod CI step runs build-version.sh and sed-patches module.prop
    before zipping (git installed in the DDK container).
  * lsposed/app/build.gradle.kts exec's build-version.sh at configure
    time and assigns the result to `versionName` (versionCode stays
    static, still bumped by release.py).

All actions/checkout@v6 gained `fetch-depth: 0` so git describe sees
the full tag history inside CI containers.

Result: a locally built or CI-from-main APK shows up in Android
Settings as e.g. `0.6.1-16-gf86e5e5`, and the zip inside carries the
same string in module.prop; the Magisk/KSU manager displays it in the
update list. Release tag builds are indistinguishable from before —
clean `X.Y.Z`. Diagnostic bug reports now carry the exact commit in
the App version line of device_info.txt.
2026-04-17 14:42:40 +03:00
okhsunrog
a4426ec655 docs(changelog): add full CHANGELOG.md at repo root
Split the generated markdown into two files:

  - CHANGELOG.md at repo root — full history with the Keep a Changelog
    header. Human-facing, discoverable from the GitHub repo page.
  - update-json/changelog.md — still truncated to the last 5 versions,
    for the Magisk/KSU update popup.

Both are regenerated from changelog.json on every changelog-add.py
and update-version.py run.

Also switch the CI release-notes extraction to read CHANGELOG.md so
the body is future-proof once a tag ages out of the short popup file.
2026-04-16 00:42:59 +03:00
okhsunrog
9c13c761a3 ci(release): use changelog.md section as release notes body
Extract the current tag's section from update-json/changelog.md with
awk and pass it as body_path to softprops/action-gh-release. Keeping
generate_release_notes=true so GitHub still appends the auto PR list
and "Full Changelog" link below our handwritten summary.
2026-04-16 00:34:25 +03:00
okhsunrog
d99485a9fb ci: bump GitHub actions to Node.js 24 runtime
Bumps actions/checkout v4→v6, actions/cache v4→v5,
actions/upload-artifact v4→v7, actions/download-artifact v4→v8
to silence the Node.js 20 deprecation warnings GitHub is emitting
ahead of the June 2026 cutoff.

softprops/action-gh-release stays on v2 (third-party, wasn't in the
deprecation list and v3 would need a separate compatibility review).
2026-04-16 00:31:39 +03:00
okhsunrog
5341c07781 Wire portshide into release + update-json pipeline
Mirror the kmod/zygisk plumbing so KernelSU-Next / Magisk pick up
portshide updates automatically:

- scripts/update-version.sh bumps portshide/module/module.prop along
  with the other modules when VERSION changes
- scripts/update-json.sh writes update-json/update-ports.json pointing
  at the current release zip
- CI appends updateJson=.../update-ports.json to the portshide
  module.prop before zipping, matching kmod/zygisk
- Dashboard reports portshide version mismatches as issues, with the
  same up/down/different wording the other modules use
2026-04-15 16:45:58 +03:00
okhsunrog
7faeb5ce9a CI: build vpnhide-ports.zip
Adds a portshide job mirroring the simple kmod zip packaging (no build
needed, just zip the module directory). Artifact lands next to the
other release zips so the gh-release step picks it up for tagged
builds.
2026-04-15 16:41:38 +03:00
okhsunrog
5aadb0165b Rename APK artifact from vpnhide-lsposed to vpnhide 2026-04-14 03:00:10 +03:00
okhsunrog
6f80892f24 Move update JSON files to update-json/ directory 2026-04-13 23:05:01 +03:00
okhsunrog
8f522166ee Add Magisk/KSU auto-update support via updateJson
- update-version.sh generates per-KMI update JSON files for kmod and one
  for zygisk, pointing to GitHub Release artifacts
- CI injects updateJson URL into module.prop before packaging zips
- module.prop in repo stays clean (no updateJson), CI appends it per-variant
- Update version mismatch issue texts to direct users to KernelSU/Magisk
  Modules for updating
- Fix versionName/versionCode back to 0.4.2 (was accidentally 0.4.3 from
  test bump)
2026-04-13 21:15:35 +03:00
okhsunrog
14af7b7ec4 Rework Apps tab UI and fix lint issues
- Replace top bar filter icon with inline "Show system apps" checkbox
- Add hint card explaining L/K/Z layer toggles and Zygisk caveat
- Move showSystem state inside AppPickerScreen
- Fix RELEASE_OR_CODENAME lint error (requires API 30, min is 29)
- Mark technical check strings as translatable="false"
- Add Android lint step to CI
- Apply ktlint formatting
2026-04-13 20:48:26 +03:00
okhsunrog
69d2aa3903 ci: add Android SDK to CI image, revert lsposed job to container 2026-04-13 15:35:28 +03:00
okhsunrog
16f4746c9a fix: lsposed CI back to bare ubuntu with Rust+NDK setup (needs Android SDK) 2026-04-13 15:34:05 +03:00
okhsunrog
d1a547984a fix: use GITHUB_WORKSPACE env var instead of github.workspace in container 2026-04-13 15:30:53 +03:00
okhsunrog
12c082911e fix: use absolute paths in lsposed CI job (container workdir) 2026-04-13 15:27:37 +03:00
okhsunrog
892e3bb910 refactor: remove separate process, delete test-app, simplify diagnostics
- Remove CheckRunnerService and :checks process — the ECONNREFUSED
  issue was caused by Android per-app network restriction, not Vector
  runtime. Checks run directly in the main process now.
- Delete test-app entirely — all diagnostics are now in the VPN Hide app
- Remove test-app from CI, lint, .gitignore, and update-version.sh
- Add lsposed/native to version update script and CI lint
2026-04-13 14:58:52 +03:00
okhsunrog
567b377d47 feat: merge diagnostics into VPN Hide app
- Add native Rust checks library (lsposed/native/) with all 26 detection
  checks, ported from test-app with updated JNI package path
- Add NativeChecks.kt JNI bridge
- Add DiagnosticsScreen.kt with full test UI (sections, banners, cards)
- Add bottom navigation: Apps tab (target picker) + Diagnostics tab
- Update CI: lsposed job now uses CI container image (has Rust + NDK)
- Merge all diagnostic strings into lsposed strings.xml (EN + RU)
- Add cargo-ndk build task to lsposed gradle
2026-04-13 11:35:53 +03:00
okhsunrog
540d7cbfcc feat: add release signing for lsposed and test-app APKs
- Add signingConfigs to both build.gradle.kts files, reading keystore
  credentials from keystore.properties (gitignored, local absolute path)
- Both debug and release builds use the same signing key for consistent
  signatures between local and CI builds
- CI decodes keystore from ANDROID_KEYSTORE_BASE64 secret, builds
  assembleRelease instead of assembleDebug
- Add keystore-related files to .gitignore for test-app
2026-04-13 00:13:44 +03:00
okhsunrog
b4677cdb25 ci: add clang-format, ktlint, and cargo test to lint job
- Rename lint-rust → lint, add clang-format and ktlint checks
- Add cargo test step for zygisk unit tests
- Install clang-format and ktlint 1.8.0 in CI Docker image
2026-04-12 23:29:36 +03:00
okhsunrog
5107412d04 ci: rename APK artifacts to vpnhide-lsposed.apk and vpnhide-test.apk 2026-04-11 23:07:03 +03:00
okhsunrog
5d5bfaa482 ci: auto-detect clang version in DDK image 2026-04-11 22:58:41 +03:00
okhsunrog
f071e7877b ci: use DDK images for kmod, build all 7 GKI generations
Replace custom kernel source cloning + prepare with pre-built DDK
Docker images (ghcr.io/ylarod/ddk-min). These include kernel headers,
Module.symvers, and AOSP clang for each GKI generation.

Now builds for: android12-5.10, android13-5.10, android13-5.15,
android14-5.15, android14-6.1, android15-6.6, android16-6.12.

Drop sha256 files from releases.
2026-04-11 22:55:49 +03:00
okhsunrog
efed29f7d5 ci: add Rust lint job (fmt + clippy for both crates) 2026-04-11 22:29:40 +03:00
okhsunrog
e413debe45 refactor: replace C++ native checks with Rust
Port all 15 native VPN detection checks from C++ to Rust using
jni + libc crates. Gradle triggers cargo-ndk automatically via
a preBuild dependency — single `./gradlew installDebug` builds
everything.

- Remove CMakeLists.txt and native-lib.cpp
- Add test-app/native/ Rust crate with Cargo.toml and src/lib.rs
- Gradle buildRustNative task runs cargo-ndk, copies .so to jniLibs
- Update CI test-app job with Rust + NDK setup
- 23/23 checks pass on device
2026-04-11 22:19:59 +03:00
okhsunrog
fe2a104572 ci: unify into single workflow with parallel builds
Replace separate kmod/zygisk/lsposed workflows with one ci.yml.
Four jobs run in parallel: kmod, zygisk, lsposed, test-app.
A release job depends on all four and runs only on tag pushes,
creating a GitHub release with all artifacts and checksums.
2026-04-11 21:46:49 +03:00
okhsunrog
50176322d3 ci: add AOSP clang to PATH for host tool compilation 2026-04-11 21:07:57 +03:00
okhsunrog
5ee50935c4 ci: use AOSP clang in Docker image, fix kmod build
- Add Google's AOSP clang (clang-r487747c, same as Pixel kernel build)
  to the CI Docker image via sparse checkout. Distro clang caused ABI
  mismatches leading to bootloops on device.
- Update kmod workflow to use the Docker image + AOSP clang instead of
  system clang from apt.
- Replace symvers with real vmlinux.symvers from Pixel kernel build
  (8050 symbols vs 4060 from device .ko extraction).
- Add kmod build deps (bc, kmod, cpio, binutils-aarch64) to Docker image.
2026-04-11 20:53:20 +03:00
okhsunrog
12daca5c1a monorepo: combine vpnhide-zygisk, vpnhide (lsposed), and vpnhide-kmod
Unified repository for the complete Android VPN-hiding stack:
- zygisk/ — Rust Zygisk module (inline libc hooks via shadowhook)
- lsposed/ — Kotlin LSPosed module (Java API + system_server hooks)
- kmod/ — C kernel module (kretprobe hooks, invisible to anti-tamper)

CI workflows use path filters to build only the changed component.
2026-04-11 15:01:49 +03:00