neiam
/gitgud
Git Gud
public · Issues · Pulls · Labels · Forks · Compare · Actions success · Badges · Packages
⭐
1
Log in to mark this repository.
Version dev builds 0.1.0-dev-<timestamp>, and actually hand it to the build
59b06a9 · Gabriel Morell · 2026-09-14 00:25
Message
{commit_body(@commit)}
Files changed
modified
android/Dockerfile
+18
−1
@@ -72,9 +72,26 @@ RUN ./gradlew --no-daemon :app:dependencies --configuration releaseRuntimeClassp
| 72 | 72 | FROM deps AS build |
| 73 | 73 | |
| 74 | 74 | ARG BUILD_TOOLS=34.0.0 |
| 75 | + | |
| 76 | +# A container build inherits nothing from the runner's environment, so | |
| 77 | +# the version has to be handed in explicitly. Without these, Gradle's | |
| 78 | +# getenv lookups come back empty inside here and every run builds | |
| 79 | +# versionCode 1 — the same filename with different bytes, which the | |
| 80 | +# registry refuses with a 409. | |
| 81 | +# | |
| 82 | +# Declared in this stage rather than at the top so they don't invalidate | |
| 83 | +# the SDK and dependency layers, which is most of the build time. | |
| 84 | +ARG GITHUB_RUN_NUMBER | |
| 85 | +ARG SOURCE_DATE_EPOCH | |
| 86 | +ARG RELEASE_VERSION | |
| 87 | +ENV GITHUB_RUN_NUMBER=${GITHUB_RUN_NUMBER} \ | |
| 88 | + SOURCE_DATE_EPOCH=${SOURCE_DATE_EPOCH} \ | |
| 89 | + RELEASE_VERSION=${RELEASE_VERSION} | |
| 90 | + | |
| 75 | 91 | COPY android/app ./app |
| 76 | 92 | |
| 77 | −RUN ./gradlew --no-daemon :app:assembleRelease | |
| 93 | +RUN echo "building versionCode=${GITHUB_RUN_NUMBER:-1}" \ | |
| 94 | + && ./gradlew --no-daemon :app:assembleRelease | |
| 78 | 95 | |
| 79 | 96 | # Sign as a separate step from Gradle so the keystore never reaches a |
| 80 | 97 | # build script, a Gradle cache, or an image layer. BuildKit secrets are |
modified
android/README.md
+26
−0
@@ -260,6 +260,32 @@ secret is the operational one.
| 260 | 260 | Worth revisiting if more secrets need centralising — one doesn't |
| 261 | 261 | justify the ingress, several might. |
| 262 | 262 | |
| 263 | +## Versioning | |
| 264 | + | |
| 265 | +| | | | |
| 266 | +|---|---| | |
| 267 | +| `versionCode` | the CI run number (`GITHUB_RUN_NUMBER`), `1` locally | | |
| 268 | +| `versionName` | `0.1.0-dev-<unix seconds>`, or `$RELEASE_VERSION` verbatim when set | | |
| 269 | + | |
| 270 | +Both are generated rather than committed. `versionCode` has to increase | |
| 271 | +or Android refuses the update and the registry refuses the upload; | |
| 272 | +`versionName` carries a timestamp so a list of builds sorts and reads at | |
| 273 | +a glance, where bare run numbers don't. | |
| 274 | + | |
| 275 | +The commit isn't in the name because it doesn't need to be: the | |
| 276 | +versionCode *is* the CI run number, and a run knows its own commit. | |
| 277 | + | |
| 278 | +The timestamp comes from `SOURCE_DATE_EPOCH` (or `BUILD_TIMESTAMP`) | |
| 279 | +when set — the workflow computes one stamp per run, so every artifact | |
| 280 | +from a run agrees even if a step is retried. A reproducible-build | |
| 281 | +harness that already sets `SOURCE_DATE_EPOCH` gets a reproducible | |
| 282 | +`versionName` for free. | |
| 283 | + | |
| 284 | +A container build inherits nothing from the runner, so these are passed | |
| 285 | +as `--build-arg` in `android/Dockerfile`. They're declared in the build | |
| 286 | +stage rather than at the top, so changing them doesn't invalidate the | |
| 287 | +SDK and dependency layers — which are most of the build time. | |
| 288 | + | |
| 263 | 289 | ## What's not shipped yet |
| 264 | 290 | |
| 265 | 291 | - **Wiki editing** — the wiki is a git repo of markdown files, and |
modified
android/app/build.gradle.kts
+24
−5
@@ -5,10 +5,27 @@ plugins {
| 5 | 5 | alias(libs.plugins.kotlin.serialization) |
| 6 | 6 | } |
| 7 | 7 | |
| 8 | −// Bumped by hand for a real release; the build metadata after it comes | |
| 9 | −// from CI. | |
| 8 | +// Bumped by hand; everything after it is generated. | |
| 10 | 9 | val baseVersion = "0.1.0" |
| 11 | 10 | |
| 11 | +// Seconds, and taken from the environment when it's there. CI sets it | |
| 12 | +// once per run so every artifact from that run agrees; a local build | |
| 13 | +// computes it, which is fine because local builds are never published. | |
| 14 | +// | |
| 15 | +// SOURCE_DATE_EPOCH first because it's the cross-toolchain convention | |
| 16 | +// for exactly this, and honouring it means a reproducible-build harness | |
| 17 | +// that already sets it gets a reproducible versionName for free. | |
| 18 | +val buildTimestamp: Long = | |
| 19 | + (System.getenv("SOURCE_DATE_EPOCH") ?: System.getenv("BUILD_TIMESTAMP")) | |
| 20 | + ?.toLongOrNull() | |
| 21 | + ?: (System.currentTimeMillis() / 1000) | |
| 22 | + | |
| 23 | +// A real release overrides the whole string; until there is one, every | |
| 24 | +// build says so in its name rather than claiming to be 0.1.0. | |
| 25 | +val appVersionName: String = | |
| 26 | + System.getenv("RELEASE_VERSION")?.takeIf { it.isNotBlank() } | |
| 27 | + ?: "$baseVersion-dev-$buildTimestamp" | |
| 28 | + | |
| 12 | 29 | android { |
| 13 | 30 | namespace = "org.neiam.gitgud.app" |
| 14 | 31 | compileSdk = 34 |
@@ -29,9 +46,11 @@ android {
| 29 | 46 | // never published. |
| 30 | 47 | versionCode = (System.getenv("GITHUB_RUN_NUMBER") ?: "").toIntOrNull() ?: 1 |
| 31 | 48 | |
| 32 | − // The commit is what you want when someone reports a bug | |
| 33 | − // against a build. Local builds just say "dev". | |
| 34 | − versionName = "$baseVersion+" + ((System.getenv("GITHUB_SHA") ?: "dev").take(7)) | |
| 49 | + // Not the commit: versionCode is the CI run number, and a run | |
| 50 | + // knows its own commit, so the sha is one lookup away and the | |
| 51 | + // timestamp orders builds at a glance in a list that versionCode | |
| 52 | + // alone renders as bare integers. | |
| 53 | + versionName = appVersionName | |
| 35 | 54 | } |
| 36 | 55 | |
| 37 | 56 | buildTypes { |
Parents: a2f8319