Build and Release APK / Build APK and publish release (push) Canceled after 0s
Import of ProLibertateGames (upstream main, GPL-3.0) rebranded for Wallacenet and repointed at git.wallacenet.ca. - Identity: applicationId ca.wallacenet.pilgrimsofkatan (+ .dev for the dev channel), launcher names "Pilgrims of Katan" / "Pilgrims of Katan DEV", neutral hexagon branding replacing the Wallace tartan. Uncial Antiqua keeps its OFL licence, and the GPL-3.0 LICENSE plus upstream attribution stay. - OTA: Updater.kt now reads this repo's own Gitea releases (/api/v1/repos/devadmin/PilgrimsOfKatan/releases...) and BuildConfig. OTA_ENABLED is on; Gitea answers the same JSON shape the code already parsed. - Build numbers: versionCode = CI run number + plgVersionRunOffset (1000), with the tag and versionName derived from the same figure. The offset is permanent, because Gitea Actions restarts run numbers at 1 and a reset would otherwise make Android reject the next build as a downgrade. - CI: ci.yml publishes a dev prerelease from dev, release.yml a production release from main, both through scripts/publish-release.sh (Gitea REST API, runnable by hand until an Actions runner is registered). - Docs: docs/CI.md records the channel, numbering and runner decisions; docs/README.md seeds the per-game RULES-* conventions; CLAUDE.md carries the agent ground rules. Verified: ./gradlew assembleDebug and testDebugUnitTest green on JDK 17 / compileSdk 34, both variants stamped, installing side by side under the new identity. Co-Authored-By: Paperclip <[email protected]>
137 lines
6.4 KiB
Markdown
137 lines
6.4 KiB
Markdown
# CI, releases, and OTA — decisions and how to build
|
|
|
|
Status as of the Pilgrims of Katan bootstrap (WALA-118).
|
|
|
|
## Decisions
|
|
|
|
1. **The release channel is this instance's own releases.** The app ships from
|
|
<https://git.wallacenet.ca/devadmin/PilgrimsOfKatan>, and the in-app updater
|
|
reads that repo's releases over Gitea's REST API. GitHub is not a channel for
|
|
this project; the only GitHub URLs left are the `upstream` remote and the
|
|
attribution link in the README.
|
|
|
|
The repo is **public on purpose**: the updater downloads anonymously, with no
|
|
token embedded in the APK. A private repo would make every check fail
|
|
(the API answers 404 to anonymous callers), not leak anything — but it would
|
|
also stop updates dead.
|
|
|
|
2. **CI lives in Actions-compatible workflows in `.github/workflows/`.**
|
|
Gitea Actions runs workflows from `.github/workflows/` (or `.gitea/workflows/`),
|
|
so these files serve both a GitHub-style runner and the Gitea instance.
|
|
Publishing goes through `scripts/publish-release.sh` — Gitea's REST API, the
|
|
same routes the app's updater reads — rather than the `gh` CLI, which a runner
|
|
image need not carry.
|
|
|
|
| Workflow | Trigger | Publishes |
|
|
| --- | --- | --- |
|
|
| `ci.yml` | push to `dev`, PRs into `dev`/`main`, manual | dev prerelease, from `dev` only |
|
|
| `release.yml` | push to `main`, manual | production release, from `main` only |
|
|
|
|
Pull requests build and check but never publish. Prereleases are marked so
|
|
the "latest release" endpoint — which skips them — can never hand a dev build
|
|
to a production install.
|
|
|
|
3. **A runner has to be registered before any of that executes.** As of the
|
|
bootstrap the instance has **no Actions runner**, so the workflows are
|
|
correct but idle: a Gitea runner (Docker or host) must be registered against
|
|
the instance and able to resolve `actions/checkout`,
|
|
`actions/setup-java`, `android-actions/setup-android` and
|
|
`actions/upload-artifact`. Until then the manual build below and the manual
|
|
publish script are the working paths.
|
|
|
|
4. **The local build is the guaranteed path.** Workflows need a runner that may
|
|
not exist yet; `./gradlew assembleDebug` + `./gradlew testDebugUnitTest`
|
|
work on any machine with JDK 17 and the Android SDK (compileSdk 34), and CI's
|
|
green means exactly these two commands plus lint.
|
|
|
|
## Build numbers, tags, and why updates never downgrade
|
|
|
|
An APK is **`<series>.<build number>`**, the release tag is **`v<series>.<build
|
|
number>`**, and the build number is what Android sees as `versionCode`:
|
|
|
|
```
|
|
build number = CI run number + plgVersionRunOffset (gradle.properties)
|
|
```
|
|
|
|
- `plgVersionSeries` (currently `1.4`) is the human-facing series, set by hand
|
|
in `gradle.properties`.
|
|
- `plgVersionRunOffset` (currently `1000`) is a **permanent floor**, also set by
|
|
hand in `gradle.properties`, set once at bootstrap and never changed.
|
|
|
|
Why the offset exists: Gitea Actions restarts run numbers at 1 for each
|
|
workflow, so without it the first release of a fresh instance would be build
|
|
`1` — the number an unstamped local build already carries — and any reset of
|
|
the counter would hand Android a *lower* `versionCode`, which it refuses as a
|
|
downgrade. With the offset, every stamped build starts at 1001 and only ever
|
|
climbs, because the run number is monotonic and the offset never moves. The
|
|
gradle build and both workflows read the same two properties, so the tag and
|
|
the APK inside it cannot name different versions.
|
|
|
|
The series is a name for people: bumping it changes nothing about ordering,
|
|
because `versionCode` never includes the series.
|
|
|
|
Unstamped builds (local, or a PR build) are `versionCode 1`, below every
|
|
release — the honest answer for a build nobody stamped.
|
|
|
|
## Manual build (works today, no runner needed)
|
|
|
|
```sh
|
|
# JDK 17, Android SDK with platform 34 + build-tools 34
|
|
export JAVA_HOME=... # a JDK 17 install
|
|
export ANDROID_HOME=... # SDK root (or set sdk.dir in local.properties)
|
|
|
|
./gradlew assembleDebug # dev-channel app (default: ca.wallacenet.pilgrimsofkatan.dev)
|
|
PLG_CHANNEL=production ./gradlew assembleDebug # production app (ca.wallacenet.pilgrimsofkatan)
|
|
./gradlew testDebugUnitTest # JVM unit tests
|
|
./gradlew lintDebug # Android lint (CI runs this too)
|
|
```
|
|
|
|
The two variants install side by side: different applicationId, different
|
|
launcher name (`Pilgrims of Katan DEV` vs `Pilgrims of Katan`), different brand
|
|
colour.
|
|
|
|
## Manual release (works today, no runner needed)
|
|
|
|
```sh
|
|
GITEA_TOKEN=… ./scripts/publish-release.sh v1.4.1001 app/build/outputs/apk/debug/app-debug.apk release "$(git rev-parse HEAD)"
|
|
```
|
|
|
|
The script takes `<tag> <apk> <prerelease|release> [target-sha]`, creates the
|
|
release through `POST /api/v1/repos/{owner}/{repo}/releases`, and attaches the
|
|
APK through `POST …/releases/{id}/assets` — which is the upload route Gitea's
|
|
Swagger declares for this instance (1.27.3). If the tag already exists it
|
|
publishes into the existing release instead of failing, so a retry is safe.
|
|
|
|
## What the in-app updater reads
|
|
|
|
`app/src/main/java/org/prolibertate/games/update/Updater.kt`:
|
|
|
|
| Channel | Endpoint |
|
|
| --- | --- |
|
|
| production | `GET /api/v1/repos/devadmin/PilgrimsOfKatan/releases/latest` |
|
|
| dev | `GET /api/v1/repos/devadmin/PilgrimsOfKatan/releases?limit=30` |
|
|
|
|
- `/releases/latest` is documented by Gitea as *the most recent non-prerelease,
|
|
non-draft release* — the same rule GitHub implements, so the production
|
|
channel keeps skipping dev builds.
|
|
- The dev path takes the highest `versionCode` among the prereleases rather
|
|
than whatever came first on the page, so it does not depend on server
|
|
ordering.
|
|
- The JSON shape is the one the code already parsed from GitHub: `tag_name`,
|
|
`prerelease`, `draft`, and assets carrying `browser_download_url`. Gitea says
|
|
`limit` where GitHub says `per_page`.
|
|
- Each asset must be named so it contains its tag —
|
|
`PilgrimsOfKatan-v1.4.1001.apk` for tag `v1.4.1001` — because the updater
|
|
picks the versioned APK out of the release's assets by that rule.
|
|
|
|
`BuildConfig.OTA_ENABLED` is a kill switch, not a channel decision: it is
|
|
`true`, and setting it `false` makes every check report that and fetch nothing.
|
|
`UpdatePolicy.kt` — channel rules, tag parsing, verdicts — is pure and stays
|
|
under test either way.
|
|
|
|
## Keystore
|
|
|
|
`app/debug.keystore` is committed deliberately and must never be rotated:
|
|
every build — local, CI, release — signs with it, so an OTA update installs
|
|
over the previous build instead of being rejected for a signature mismatch.
|