# Wolfskin TV container builds Run from a full Git checkout with the upstream `v*` and `p*` tags available. The host needs Docker, GNU Make, Bash, Git, and `flock` (util-linux), not Java or the Android SDK. ```sh git fetch origin --tags make apk # clean, signed default release: three ABI splits + universal make test # upstream testDefaultDebugUnitTest in the same image, no secrets make apk-debug # default debug, no secrets; MPV stub and no extra codec decoders ``` APKs remain under `app/build/outputs/apk/default/{release,debug}/`. Debug builds are not playback-complete and must not be distributed as releases. Upstream's `.github/workflows` and dependency version catalog are unchanged; release builds use the catalog's exact Wholphin extension version (currently `0.2.4`). Do not place local extension AARs in `app/libs`: upstream prefers them over the pinned GitHub Packages dependencies. Upload release assets renamed to `Wolfskin.apk` / `Wolfskin-.apk` for the in-app updater. Gradle outputs `Wholphin-default-release--[-].apk`; strip the flavor, version, and code as well as replacing the prefix. Upstream enables JVM unit tests only for debug variants; there is no `testDefaultReleaseUnitTest` task. `make test` uses the closest default-flavor task without changing upstream's test configuration. ## Toolchain and disk use `Dockerfile` pins Temurin JDK `21.0.11+10` by image digest, command-line tools `23.0` and platform-tools `37.0.1` by archive and checksum, and build-tools `36.0.0` by SDK package version. The SDK manager registers platform `37.0` and its licenses; the build rejects any platform revision other than 2. An SDK revision upgrade therefore requires a deliberate Dockerfile update. Gradle comes from the committed wrapper. The image is built for `linux/amd64`; the default local image tag is `wolfskin-build:sdk37-jdk21`. Upstream's build timestamp means APKs are not byte-for-byte reproducible. Gradle downloads and caches live in the named volume `wolfskin-gradle`, not `/tmp`. Module outputs stay in the checkout. Builds run with the host UID/GID, `--no-daemon`, and at most four workers. The repository's Git metadata is mounted read-only, including when the checkout is a linked worktree. Override `WOLFSKIN_BUILD_IMAGE` to change the locally built image tag and `WOLFSKIN_GRADLE_VOLUME` to isolate a cache. `make build-image` builds only the image. Do not delete shared caches during another build. Every Gradle container holds a host-side exclusive lock for its entire run. Concurrent targets sharing a cache queue rather than timing out on Gradle's cross-container cache journal lock; a waiting invocation prints one line. The default lock is `/var/tmp/${WOLFSKIN_GRADLE_VOLUME}.gradle.lock` (using `wolfskin-gradle` when unset). Override `WOLFSKIN_GRADLE_LOCK_FILE` with a path whose parent directory already exists. All callers sharing a volume must use the same lock path on the Docker host. Do not delete the file while builds are running or waiting. The lock releases automatically when the invocation exits. ## Release credentials The default escrow is `/opt/stacks/ventoy/machine-parity/manifest-tank/secrets/wolfskin/`. Check `mountpoint -q /mnt/ventoy` before using that path. Set `WOLFSKIN_SECRETS_DIR` to use another existing directory. Its required files are: | File | Contents | | --- | --- | | `release.jks` | Separate Wolfskin PKCS12 keystore, RSA 4096, alias `wolfskin` | | `release.properties` | Java properties `storePassword` and `keyPassword` | | `github-packages-token` | GitHub token with `read:packages`, username `JustinZeus` | The directory must be mode `0700`, each file mode `0600`, owned by the building user. Never commit them or put their contents in logs. Back up the keystore and passwords together: losing the release key forces a reinstall. Do not reuse wolfskin-android's key or the gh CLI login token. Release builds mount escrow read-only. The token is loaded inside the container into upstream's `ORG_GRADLE_PROJECT_WholphinExtensionsUsername` and `ORG_GRADLE_PROJECT_WholphinExtensionsPassword` environment hooks, never a Docker command argument. The signing init script registers `wolfskin`; the nonsecret `release-signing.properties` is mounted as `local.properties` so upstream's `release.signing.config` hook selects it. Any host `local.properties` is hidden only for the duration of this container. Keystore and passwords stay outside the checkout and image. Missing credentials fail the release rather than silently shipping the MPV stub. ## Versioning and local release markers Upstream computes `versionName` from `git describe --tags --long --match='v*'`, dropping the leading `v`, and computes `versionCode` from the count of all local `v*` and `p*` tags. Preserve and fetch those tags; a shallow or incomplete checkout can give an incorrect version. Use the full `git describe` output as the human release name. After committing the intended release, with a clean checkout and fetched tags: ```sh make release-tag make apk ``` The helper creates one lightweight `pwolfskin-YYYY_MM_DD` tag in UTC, appending `_1`, `_2`, etc. on collision. Each marker adds one to `versionCode` without changing upstream's `v*`-based version name. It never pushes tags, publishes a release, or installs an APK. Do not delete published release markers. Tag creation and publication must be serialized between release operators. ## Inspecting release artifacts Use `apksigner verify --verbose --print-certs APK` and `aapt2 dump badging APK` inside the build image. Compare the signer SHA-256 with `keytool -list -v -alias wolfskin -keystore /path/to/release.jks` (enter its password privately). Inspect APK ZIP entries for real native MPV and ffmpeg libraries for each shipped ABI; a successful Java compilation alone does not prove that codec extensions are included.