Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,24 @@ jobs:
- name: Build release APK
run: ./gradlew :app:assembleRelease --no-daemon --stacktrace

# The APK's versionName is git-derived (app/build.gradle.kts), so a
# mismatch here means the checkout wasn't at the tag (workflow_dispatch
# on a moving branch, or describe picked an older tag). Fail before the
# release is published rather than ship a mislabelled APK.
- name: Verify APK version matches the tag
if: startsWith(github.ref, 'refs/tags/v')
run: |
set -e
tag="${GITHUB_REF_NAME#v}"
aapt="$(ls $ANDROID_HOME/build-tools/*/aapt2 | sort -V | tail -1)"
apk="$(ls app/build/outputs/apk/release/*.apk | head -1)"
apk_version="$("$aapt" dump badging "$apk" | sed -n "s/^package:.*versionName='\([^']*\)'.*/\1/p" | head -1)"
echo "tag: $tag apk: $apk_version"
if [ "$apk_version" != "$tag" ]; then
echo "::error::APK versionName ($apk_version) does not match the tag being released (v$tag)"
exit 1
fi

- name: Rename APK with tag
run: |
tag="${GITHUB_REF_NAME:-untagged}"
Expand Down
31 changes: 29 additions & 2 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,5 +1,32 @@
import org.gradle.api.tasks.Copy

// ── Version from the v* tag (single source of truth) ────────────────────────
// versionName = the most recent v* tag (via git describe); versionCode = the
// number of commits since the tag began, plus a base, so it is deterministic
// and strictly monotonic across builds without manual bumping — versionName
// sat at "0.1.0" for eight releases because nothing forced the bump.
// Tarball/no-git builds fall back to the constants below; keep them at the
// last released values.
fun gitVersionName(fallback: String): String {
val describe = try {
val p = ProcessBuilder("git", "describe", "--tags", "--match", "v*")
.directory(rootDir).redirectErrorStream(true).start()
p.inputStream.bufferedReader().readText().trim().also { p.waitFor() }
} catch (_: Exception) { "" }
return if (describe.startsWith("v")) describe.removePrefix("v") else fallback
}

fun gitVersionCode(base: Int, fallback: Int): Int {
// Count commits since the first commit reachable from HEAD; the base keeps
// historical codes (0.1.8 shipped as 8) below any git-derived value.
val count = try {
val p = ProcessBuilder("git", "rev-list", "--count", "HEAD")
.directory(rootDir).redirectErrorStream(true).start()
p.inputStream.bufferedReader().readText().trim().toIntOrNull().also { p.waitFor() }
} catch (_: Exception) { null }
return base + (count ?: (fallback - base))
}

plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
Expand All @@ -15,8 +42,8 @@ android {
applicationId = "at.dasher.android"
minSdk = 24
targetSdk = 35
versionCode = 8
versionName = "0.1.8"
versionCode = gitVersionCode(base = 1000, fallback = 8)
versionName = gitVersionName(fallback = "0.1.8")

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"

Expand Down
Loading