Rename every shit.zen.* / asm.patchify.* class to a fresh random 16-char name in one random 16-char package on each build, via an ASM ClassRemapper pass (build.gradle ext.obfuscateJar, run after ForgeGradle reobfJar). Class names only; methods/fields preserved. Manifest Premain/Agent-Class, the DllBootstrap Class.forName string, and the native bridge name (generated_names.h OZ_BRIDGE_FQCN) are wired to the generated names. Residual original-name strings (loggers, log text, the asm.patchify.* property keys) scrubbed. Emits build/rename-mapping.txt.
Pin Qt to 6.10.2 (vcpkg builtin-baseline + CI tag 2026.04.27): MSVC 14.44 crashes building Qt 6.11.0. Build Qt single-threaded locally (VCPKG_MAX_CONCURRENCY=1, loader --parallel 1, gated off GitHub Actions) to dodge parallel-compilation compiler crashes on that toolset.
CI uploads rename-mapping.txt as an artifact and attaches it to the Release; the Release body warns that prebuilt artifacts share one fixed (blacklist-able) mapping and users should self-compile. README documents the obfuscation + the self-compile warning.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The vcpkg install was cloned at a floating HEAD while native/vcpkg.json
carried no builtin-baseline, so every run resolved the qtbase port afresh.
Its package ABI drifted whenever upstream vcpkg moved, the restored
actions/cache entry no longer matched, and qtbase was rebuilt from source
(~31 min) on every push. Because the cache key is static (hash of
vcpkg.json, unchanged since 05-23) the rebuilt tree was never saved back
either, so the miss repeated forever.
Pin both sides to vcpkg tag 2026.05.25
(d015e31e90838a4c9dfa3eed45979bc70d9357fc, qtbase 6.11.0):
- native/vcpkg.json: add "builtin-baseline"
- workflow: clone --branch 2026.05.25
Adding the baseline also changes the vcpkg.json hash, so this run gets a
fresh cache key, cold-builds Qt once, and saves it; subsequent runs match
the now-deterministic ABI and skip the qtbase build entirely.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Three workflow ergonomics tweaks:
- Path filter now also includes build.gradle / settings.gradle /
gradle.properties / gradle/wrapper/** / gradlew(.bat) and the
workflow file itself, so a wrapper bump or build-script change
still triggers CI even though only src/ and native/ files compile
into the final artifact.
- Job-level if: skips the entire build when the push commit message
contains [SKIP CI] (Actions' contains() is case-insensitive for
string operands, so [skip ci] and [SKIP CI] both qualify).
workflow_dispatch is unaffected because head_commit is null there.
- [Release] marker detection in the PowerShell publish step now uses
StringComparison.OrdinalIgnoreCase so [release], [Release] and
[RELEASE] all cut a release; .NET's default .Contains is case-
sensitive which made the existing rule too brittle.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add an opt-in publish step at the tail of the build job:
- Bump job permissions to contents: write so the workflow token can
create releases.
- Read the HEAD commit message via `git log -1` and set an
is_release output when it contains the literal substring [Release].
- When set, `gh release create build-<sha>` and attach the staged
OpenZenLoader-<sha>.exe and OpenZen-<sha>.jar. Notes are piped
through a file (--notes-file) so brackets / newlines in the commit
message can't corrupt the CLI invocation.
Pushes without the marker keep producing only the existing per-build
artifacts.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
GitHub deprecated Node 20 for JavaScript actions on 2025-09-19; v4 of
checkout / setup-java / cache / upload-artifact and ilammy/msvc-dev-cmd
still ship a Node 20 binary in action.yml. Set
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24=true at job scope so the runner
executes them under Node 24 now - silences the deprecation warning and
removes a 2026-06-02 surprise when the default flips.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Stage build/dist/OpenZenLoader.exe and build/libs/hey-1.0.jar into
build/release as OpenZenLoader-<sha>.exe and OpenZen-<sha>.jar, then
publish each as its own upload-artifact entry whose name matches the
file. actions/upload-artifact always wraps a zip around the payload, so
this is as close as we can get to "no wrapper" without cutting a Release
- documented inline.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The previous run still rebuilt Qt from source on the second push.
vcpkg's x-gha binary cache backend relies on the v1 GitHub Actions
cache API and the ACTIONS_CACHE_URL / ACTIONS_RUNTIME_TOKEN
secrets exported via actions/github-script. On the current
windows-2022 image the v1 API is being phased out (the new
ACTIONS_RESULTS_URL is what's reachable), so x-gha quietly missed
on every run.
Switch to a path-based actions/cache@v4 entry:
- path: vcpkg\installed + vcpkg-archives
- key: vcpkg-qt-${runner.os}-${hash(native/vcpkg.json)}
- restore-keys: vcpkg-qt-${runner.os}- (warm start on a vcpkg.json bump)
vcpkg.installed is the deployed Qt tree CMake's manifest mode
inspects; if it already contains qtbase[widgets] the first vcpkg
install call is a no-op and CMake configure finishes in seconds.
vcpkg-archives gets fed via the new
VCPKG_BINARY_SOURCES=clear;files,...\vcpkg-archives,readwrite
binary source so transitive package binaries are also persisted
across runs - useful when vcpkg.json changes and only some libs
need rebuilding.
Also drop the now-unused "Export GitHub Actions cache tokens"
step (no x-gha backend, no tokens needed).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
windows-2022 runners with VS Enterprise installed pre-set a
system-wide VCPKG_ROOT pointing at
"C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\vcpkg".
That value wins over the job-level `env: VCPKG_ROOT:` in PowerShell
expansions, so the previous run tried to git-clone into that
read-only path and failed with "destination path ... already exists
and is not an empty directory".
Drop the job-level VCPKG_ROOT and instead resolve the path to
"$GITHUB_WORKSPACE\vcpkg" inside the clone step, then write it
back via GITHUB_ENV so every later step (including Gradle's
findVcpkg / configureNative) picks up our copy. The build step
also dumps VCPKG_ROOT / JAVA_HOME / OPENZEN_BUILD_REVISION up
front for quick diagnosis if the override ever drifts again.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
GitHub Actions (.github/workflows/build-loader.yml)
- Runs on every push to master and on workflow_dispatch.
- windows-2022 runner: setup-java 17 (Temurin), ilammy/msvc-dev-cmd
for MSVC x64, clones + bootstraps microsoft/vcpkg into the
workspace, installs UPX via choco, caches Gradle, then runs
`gradlew --no-daemon clean dll upxCompress` with VCPKG_ROOT and
OPENZEN_BUILD_REVISION exported.
- vcpkg's x-gha binary cache is wired in via VCPKG_BINARY_SOURCES +
ACTIONS_CACHE_URL / ACTIONS_RUNTIME_TOKEN, so the first run pays
the Qt static-build cost (~30 min - 2 h) and every push after
that pulls cached qtbase artifacts.
- Publishes build/dist/OpenZenLoader.exe as the
OpenZenLoader-<sha> artifact (30 day retention).
Git revision in window title
- build.gradle: gitShortRevision() prefers OPENZEN_BUILD_REVISION
from the environment (set by CI) and falls back to
`git rev-parse --short=7 HEAD` for local builds.
- configureNative passes -DOPENZEN_BUILD_REVISION=<sha> to CMake
and logs the resolved value.
- native/loader/CMakeLists.txt forwards it as a compile definition.
- MainWindow.buildUi sets the window title to
"OpenZen Loader (build abc1234)" when the macro is defined, plain
"OpenZen Loader" otherwise.
UPX compression
- New upxCompress Gradle task: locates upx on PATH, runs
`upx --best --lzma` on build/dist/OpenZenLoader.exe and logs the
before/after size. Skips with a warning (not an error) when upx
is missing so local builds keep working without it. CI's
`gradlew clean dll upxCompress` always shrinks the artifact.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>