Version dev builds 0.1.0-dev-<timestamp>, and hand it to the container

e44a7c9 · Gabriel Morell · 2026-09-14 00:42

7 files +54 -6
Message
{commit_body(@commit)}

Files changed

modified .forgejo/workflows/android.yml
+12 −0
@@ -74,9 +74,21 @@ jobs:
74 74
75 75 - name: Build and sign the APK
76 76 if: steps.keystore.outputs.skip != 'true'
77 + env:
78 + RUN_NUMBER: ${{ github.run_number }}
77 79 run: |
80 + set -euo pipefail
81 +
82 + # One stamp for the whole run, computed here rather than
83 + # inside Gradle, so every artifact a run produces agrees on
84 + # its version even if the build is retried a step at a time.
85 + stamp=$(date -u +%s)
86 + echo "version: 0.1.0-dev-$stamp (versionCode $RUN_NUMBER)"
87 +
78 88 docker buildx build \
79 89 --target android-export \
90 + --build-arg GITHUB_RUN_NUMBER="$RUN_NUMBER" \
91 + --build-arg SOURCE_DATE_EPOCH="$stamp" \
80 92 --secret id=keystore,src=./release.jks \
81 93 --secret id=keystore_password,src=./keystore.pass \
82 94 --output type=local,dest=./apk \
modified android/Dockerfile
+18 −1
@@ -78,9 +78,26 @@ RUN ./gradlew --no-daemon :app:dependencies --configuration releaseRuntimeClassp
78 78 FROM deps AS build
79 79
80 80 ARG BUILD_TOOLS=34.0.0
81 +
82 +# A container build inherits nothing from the runner's environment, so
83 +# the version has to be handed in explicitly. Without these, Gradle's
84 +# getenv lookups come back empty inside here and every run builds
85 +# versionCode 1 — the same filename with different bytes, which the
86 +# registry refuses with a 409.
87 +#
88 +# Declared in this stage rather than at the top so they don't invalidate
89 +# the SDK and dependency layers, which is most of the build time.
90 +ARG GITHUB_RUN_NUMBER
91 +ARG SOURCE_DATE_EPOCH
92 +ARG RELEASE_VERSION
93 +ENV GITHUB_RUN_NUMBER=${GITHUB_RUN_NUMBER} \
94 + SOURCE_DATE_EPOCH=${SOURCE_DATE_EPOCH} \
95 + RELEASE_VERSION=${RELEASE_VERSION}
96 +
81 97 COPY app ./app
82 98
83 −RUN ./gradlew --no-daemon :app:assembleRelease
99 +RUN echo "building versionCode=${GITHUB_RUN_NUMBER:-1}" \
100 + && ./gradlew --no-daemon :app:assembleRelease
84 101
85 102 # Sign as a separate step from Gradle so the keystore never reaches a
86 103 # build script, a Gradle cache, or an image layer. BuildKit secrets are
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.feedpug.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: 53dbc26