Commit graph

12 commits

Author SHA1 Message Date
Danila Gornushko
b3e5ef3aa2
fix(release): keep Cargo.lock in sync on version bump; build --locked (#239)
Some checks are pending
CI / kmod-activator (push) Blocked by required conditions
CI / kmod (android12-5.10) (push) Blocked by required conditions
CI / kmod (android13-5.10) (push) Blocked by required conditions
CI / kmod (android13-5.15) (push) Blocked by required conditions
CI / kmod (android14-5.15) (push) Blocked by required conditions
CI / kmod (android14-6.1) (push) Blocked by required conditions
CI / kmod (android15-6.6) (push) Blocked by required conditions
CI / kmod (android16-6.12) (push) Blocked by required conditions
CI / kmod-qemu (android12-5.10) (push) Blocked by required conditions
CI / kmod-qemu (android13-5.10) (push) Blocked by required conditions
CI / kmod-qemu (android13-5.15) (push) Blocked by required conditions
CI / kmod-qemu (android14-5.15) (push) Blocked by required conditions
CI / kmod-qemu (android14-6.1) (push) Blocked by required conditions
CI / kmod-qemu (android15-6.6) (push) Blocked by required conditions
CI / kmod-qemu (android16-6.12) (push) Blocked by required conditions
CI / kpm-qemu (android12-5.10) (push) Blocked by required conditions
CI / kpm-qemu (android13-5.10) (push) Blocked by required conditions
CI / kpm-qemu (android13-5.15) (push) Blocked by required conditions
CI / kpm-qemu (android14-5.15) (push) Blocked by required conditions
CI / kpm-qemu (android14-6.1) (push) Blocked by required conditions
CI / kpm-qemu (android15-6.6) (push) Blocked by required conditions
CI / kpm-qemu (android16-6.12) (push) Blocked by required conditions
CI / kpm-qemu-legacy (4.14) (push) Blocked by required conditions
CI / kpm-qemu-legacy (4.19) (push) Blocked by required conditions
CI / kpm-qemu-legacy (5.4) (push) Blocked by required conditions
CI / kpm (push) Blocked by required conditions
CI / zygisk (push) Blocked by required conditions
CI / lsposed (push) Blocked by required conditions
CI / portshide (push) Blocked by required conditions
CI / release (push) Blocked by required conditions
release.py rewrites each crate's `version = "..."` in Cargo.toml but never
touched Cargo.lock, so the lock kept the previous version. The first
`cargo build` in CI then rewrote the lock to match, dirtying the working tree —
and get_build_version()'s `git describe --dirty` stamped every artifact
"X.Y.Z-dirty" (that's why the v1.1.0 KPM/Ports/etc. modules report 1.1.0-dirty).

Two layers so it cannot recur:
- release.py now runs `cargo update --offline --workspace` for both lockfiles
  after the Cargo.toml bump, so the release commit carries a matching lock.
- The Rust build steps (activator, zygisk cdylib, lsposed probe) pass --locked,
  so a drifted lock fails the build loudly instead of silently rewriting it.

Also commits the now-synced 1.1.0 lockfiles.
2026-07-05 00:10:14 +03:00
okhsunrog
60bccf53a6 style: ruff format release.py 2026-07-01 23:15:25 +03:00
okhsunrog
3c7853d2d5 chore: bump internal crates (activator, protocol) to 1.0.0
Don't leave the shared vpnhide_activator / vpnhide_protocol crates on the
old version while the release is 1.0.0. release.py now bumps them too.
2026-07-01 22:48:01 +03:00
okhsunrog
7dfc506ce9 fix(release): bump the KPM module.prop version too
release.py patched kmod/zygisk/ports module.prop but not the KPM one
(kmod/kpm/module/module.prop), which was added after the script was
written — so the KPM module shipped with a stale versionCode=701. Add it
to the patched files and set it to v1.0.0/10000 for this release.
2026-07-01 22:42:28 +03:00
okhsunrog
07a1d167d4 chore(release): patch only versionCode in build.gradle.kts
build.gradle.kts sets versionName = buildVersion (computed from
build-version.py / the git tag), not a literal, so release.py's
versionName-literal patch never matched and aborted the release.
versionName is derived from the vX.Y.Z tag, so only versionCode needs
patching there.
2026-07-01 22:29:38 +03:00
Danila Gornushko
1c3e0a88d1
Close audit findings in clean-device, release, changelog, update-json, codegen (#209)
* fix(scripts): close audit findings in clean-device, release, changelog, update-json

Five tooling fixes from the codebase audit.

- clean-device.sh wiped only three /data/adb dirs and the legacy uids file, so a
  "clean" test left the canonical config, the root-owned APatch superkey under
  /data/adb/vpnhide, and the KPM/ports state behind. Mirror the full
  FULL_RESET_FILES / FULL_RESET_DIRS list from FullReset.kt.
- release.py ran the irreversible changelog rotation (save_json + delete the
  fragment files) BEFORE patching the version-bearing source files, so a drifted
  module.prop / Cargo.toml / gradle format failed mid-release with the changelog
  already mutated and the duplicate-version guard then refusing a retry. Dry-run-
  validate every version-patch regex before the changelog step.
- changelog.py printed the new fragment path relative to cwd, raising ValueError
  when cwd is not an ancestor of changelog.d (e.g. run from scripts/), a spurious
  traceback after the fragment was written fine. Make it relative to REPO_ROOT.
- update-json.sh computed versionCode with bare $(( )), so a zero-padded version
  component (1.08.0) was read as octal and aborted on digit 8/9. Force base-10.
- codegen-hooks.py now uses write_if_changed and reports only what changed, like
  codegen-interfaces.py, instead of rewriting every file and bumping mtimes.

* refactor(scripts): extract scripts/codegen_lib.py shared by both generators

codegen-interfaces.py and codegen-hooks.py each re-derived the same scaffolding:
REPO_ROOT, the "AUTO-GENERATED … Regenerate with …" banner, the verbatim
11-segment path to the LSPosed app's generated Kotlin package, and the
write-if-changed + change-reporting tail of main(). That duplication had already
drifted. Move it into scripts/codegen_lib.py (REPO_ROOT, generated_header,
lsposed_generated_kt, write_if_changed, emit_outputs), imported by both — the
shared-lib pattern scripts/ already uses for changelog_lib.py.

Generated output is byte-identical (verified: re-running both codegen scripts
leaves no diff in any generated file). The escaping helpers stay per-script —
they legitimately differ (C/Rust/Kotlin) and consolidating them risks output
drift for no gain.

* style: ruff format codegen_lib + generators
2026-06-30 11:31:45 +03:00
okhsunrog
0d4cf09866 chore: CI + scripts cleanup (review items #12 #13 #24 #31 #32 #37)
Six small review-list items rolled together — all CI/dev-tooling, no
runtime behaviour change.

  #12  Dockerfile: pin Rust 1.95.0 and cargo-ndk 4.1.2 (was floating
       `stable` + latest cargo-ndk on monthly rebuild). Versions live
       in ENV vars to make the next bump a one-line edit.

  #13  Add shellcheck to lint job. SC2034/SC3043 excluded — Magisk
       reads SKIPUNZIP externally; Android's /system/bin/sh (mksh on
       Pixel) does support `local` despite POSIX. Verified locally
       that the 11 .sh files (module-side + dev tooling) pass.
       shellcheck baked into the CI image via apt; inline apt-get
       fallback covers the window before image rebuild.

  #24  ci.yml keystore.properties: replace heredoc with `printf '%s\n'`.
       Heredoc without single-quoted EOF re-expands $, backticks and
       backslashes in the password — printf takes the value verbatim.

  #31  scripts/release.py::patch_file now hard-fails when a regex
       pattern doesn't match (was silently leaving stale versions).

  #32  Split rotate_fragments_into_history into rotate + delete steps
       so release.py can save_json + write_md *before* unlinking the
       fragment files. If anything in between fails, fragments are
       still on disk and the run is retryable.

  #37  codegen-interfaces.py: emit `assert!(matches_vpn(…), msg)` /
       `assert!(!matches_vpn(…), msg)` instead of
       `assert_eq!(matches_vpn(…), true/false, msg)` —
       clippy::bool_assert_comparison was firing on every generated
       row under `cargo clippy --tests`. Both generated test modules
       regenerated. CI's clippy steps now also pass `--tests` so this
       class of regression is caught.
2026-04-27 01:14:03 +03:00
okhsunrog
91013acb54 ci+chore: add ruff (format + lint) for python scripts
Repo had ~1800 lines of Python (kmod/build.py, scripts/*, zygisk/build.py,
portshide/build-zip.py) with no formatter or linter. Long-lived scripts
like scripts/release.py and scripts/codegen-interfaces.py benefit from
catching unused-import / undefined-name / outdated-syntax issues early.

  pyproject.toml — ruff config, target-py312, line-length 100,
                   rules E F W I B UP SIM. Excludes zygisk/third_party,
                   target/, .claude/.
  ci.yml — astral-sh/ruff-action@v4 for `format --check` and `check`,
           ahead of the slow Rust/Gradle steps so it fails fast.
  docs/development.md — add `uvx ruff …` to the local-lint snippet.

Cleanup applied (`ruff format` + `ruff check --fix`):
  - reformat: kmod/build.py, scripts/{changelog_lib,codegen-interfaces,
    release,stats}.py, zygisk/build.py
  - I001: split multi-name imports onto separate lines after the
    sys.path.insert prelude (kmod/build.py, zygisk/build.py)
  - E501 manual: wrap one console.print line in scripts/release.py

Stdlib-only invariant from scripts/build_lib.py is preserved — ruff is
a dev/CI tool, not imported at runtime.
2026-04-26 23:48:37 +03:00
okhsunrog
85da8f85de fix(scripts): CHANGELOG.md at release time + Markdown fragments
Two changes that together eliminate changelog merge conflicts from
concurrent PRs:

1. **CHANGELOG.md is regenerated only by release.py.** The previous cut
   still had every changelog.py invocation rewrite CHANGELOG.md with a
   different [Unreleased] block, so two PRs producing different
   unreleased content collided on the MD file. Checked-in CHANGELOG.md
   now contains released versions only. Unreleased is rendered on
   demand from changelog.d/ via scripts/preview-changelog.py — prints
   to stdout, writes nothing.

2. **Fragment format: Markdown instead of TOML.** Filenames now look
   like `<type>-<slug>-<hex4>.md` (e.g.
   `fixed-dev-version-mismatch-a1b2.md`). Type is readable at-a-glance
   in the directory listing; 4-char random hex prevents collision when
   two PRs pick the same slug. Body is plain Markdown with `## English`
   / `## Русский` sections — renders directly on GitHub, no YAML/TOML
   parser dependency.

- scripts/changelog_lib.py: MD parser replaces tomllib. render_full_md
  drops the [Unreleased] block; write_md(data) signature simplified;
  render_unreleased_md(fragments) for on-demand preview.
- scripts/changelog.py: writes <type>-<slug>-<hex4>.md, no MD regen.
- scripts/release.py: updated to the new write_md signature.
- scripts/preview-changelog.py: new.
- changelog.d/*.md: 10 existing TOML fragments migrated to MD. One
  fragment (changelog-entries-now-live-as-per) updated to say Markdown
  instead of TOML since that's the final state by the time this ships.
- CHANGELOG.md: regenerated — Unreleased block gone.
- .gitattributes: merge=union moved from *.toml to *.md.
- docs/changelog.md, docs/releasing.md, CONTRIBUTING.md,
  changelog.d/README.md, CLAUDE.md: describe the new format + flow.
2026-04-19 21:57:34 +03:00
okhsunrog
40b15dd063 feat(scripts): migrate changelog to per-PR fragments under changelog.d/
The JSON `unreleased` section was a conflict magnet — every PR touching
it collided with every other PR touching it. Fragments sidestep that:
each entry is its own TOML file, two PRs get two files, merges don't
touch the same bytes.

- changelog_lib.py: add FRAGMENTS_DIR, load_fragments(),
  fragments_as_sections(), rotate_fragments_into_history();
  render_full_md() now takes fragments explicitly.
- changelog.py: write a TOML fragment to changelog.d/<ts>-<slug>.toml
  instead of appending to JSON. Auto-derive slug from EN text; accept
  --slug override.
- release.py: load fragments, rotate into history[0], delete them.
- lsposed/app/src/main/assets/changelog.json: drop `unreleased` key;
  only `history` remains (the 8 unreleased entries migrated to
  changelog.d/*.toml). CHANGELOG.md regenerates byte-identical.
- UpdateChecker.kt: fix long-standing loadChangelog bug — was calling
  parseChangelogEntry on the root object, which has no `version` field,
  so the parse always threw and the function silently returned null.
  Simplify ChangelogData to {history} and parse history[]; drop the
  unused `current` field that DashboardScreen just concatenated back in.
- .gitattributes: mark changelog.d/*.toml as merge=union, belt-and-
  suspenders fallback for same-filename collisions (rare — filenames
  carry a timestamp).
- docs/changelog.md, docs/releasing.md, CONTRIBUTING.md,
  changelog.d/README.md: describe the new flow.
2026-04-19 20:05:39 +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
ad2590a453 refactor(scripts): unreleased section + rename scripts
Rework the changelog and release flow to remove the aspirational
top-level version that made it unclear whether new entries were
landing in an already-released section.

Schema change: `changelog.json` now has an explicit `unreleased`
object instead of hoisting the upcoming version to the top level. The
old `{version, sections, history}` layout becomes
`{unreleased, history}`, with the previously-released version moved
into `history[0]`.

New entries always go into `unreleased` via `changelog.py`. Releasing
is a single atomic operation (`release.py X.Y.Z`) that promotes
`unreleased` into `history[0]` with the target version number,
propagates the version to every source file, and regenerates the
markdown artifacts.

Script renames:
- `_changelog.py` → `changelog_lib.py` (no more underscore-prefixed
  module that's imported by two siblings)
- `changelog-add.py` → `changelog.py`
- `update-version.py` → `release.py` (does more than just version
  propagation — the name now reflects the full release action)

CHANGELOG.md rendering follows Keep a Changelog: a `## [Unreleased]`
block appears on top only when there are unreleased entries; the
update-json/changelog.md shown in Magisk/KSU popups still skips
Unreleased (only released versions make sense there).

Docs (docs/changelog.md, docs/releasing.md, CONTRIBUTING.md, CLAUDE.md)
updated with the new commands and the clarified model.

CLAUDE.md additionally gains a "read these before doing any work"
section that lists the contributor docs — so future sessions load the
workflow rules into context instead of skipping them as optional.
2026-04-17 14:34:47 +03:00
Renamed from scripts/update-version.py (Browse further)