From 455b68e562ae3d95c98ae30bd9d8539344486aa5 Mon Sep 17 00:00:00 2001 From: Christoph Brandau Date: Wed, 26 Aug 2026 00:54:20 +0200 Subject: [PATCH] feat(android-release): derive Android version code from release tag The release workflow now derives versionName and versionCode from the tag and validates the tag format. It updates multiple project files in the runner workspace without committing changes back to the repository. A verification step ensures the generated Android properties are correct. - Tag parsing validates vMAJOR.MINOR.PATCH and enforces minor/patch <= 999 - VersionCode computed as major*1e6 + minor*1e3 + patch and applied - Added post-build verification of Android versionName and versionCode --- .gitea/workflows/android-release.yml | 76 ++++++++++++++++++++++++++-- README.md | 22 ++++++-- 2 files changed, 89 insertions(+), 9 deletions(-) diff --git a/.gitea/workflows/android-release.yml b/.gitea/workflows/android-release.yml index 8d7bed3..9040de6 100644 --- a/.gitea/workflows/android-release.yml +++ b/.gitea/workflows/android-release.yml @@ -67,18 +67,80 @@ jobs: - name: Install JavaScript dependencies run: npm ci - - name: Check release tag and app version + - name: Apply version from release tag run: | if [ "${GITHUB_REF_TYPE}" != "tag" ]; then echo "Release builds must run on a tag such as v0.1.0." >&2 exit 1 fi - APP_VERSION="$(node -p "require('./src-tauri/tauri.conf.json').version")" - if [ "${GITHUB_REF_NAME}" != "v${APP_VERSION}" ]; then - echo "Tag ${GITHUB_REF_NAME} does not match app version v${APP_VERSION}." >&2 + + RELEASE_VERSION="${GITHUB_REF_NAME#v}" + if [[ ! "${RELEASE_VERSION}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "Tag ${GITHUB_REF_NAME} must use the format vMAJOR.MINOR.PATCH." >&2 exit 1 fi + IFS=. read -r VERSION_MAJOR VERSION_MINOR VERSION_PATCH <<< "${RELEASE_VERSION}" + if (( 10#${VERSION_MINOR} > 999 || 10#${VERSION_PATCH} > 999 )); then + echo "Minor and patch versions must not exceed 999." >&2 + exit 1 + fi + ANDROID_VERSION_CODE=$((10#${VERSION_MAJOR} * 1000000 + 10#${VERSION_MINOR} * 1000 + 10#${VERSION_PATCH})) + if (( ANDROID_VERSION_CODE < 1 || ANDROID_VERSION_CODE > 2100000000 )); then + echo "Derived Android versionCode ${ANDROID_VERSION_CODE} is outside the allowed range." >&2 + exit 1 + fi + + RELEASE_VERSION="${RELEASE_VERSION}" \ + ANDROID_VERSION_CODE="${ANDROID_VERSION_CODE}" \ + GITHUB_REF_NAME="${GITHUB_REF_NAME}" \ + node -e ' + const fs = require("fs"); + const version = process.env.RELEASE_VERSION; + const versionCode = process.env.ANDROID_VERSION_CODE; + + function updateJson(path, update) { + const value = JSON.parse(fs.readFileSync(path, "utf8")); + update(value); + fs.writeFileSync(path, `${JSON.stringify(value, null, 2)}\n`); + } + + updateJson("package.json", value => { value.version = version; }); + updateJson("package-lock.json", value => { + value.version = version; + value.packages[""].version = version; + }); + updateJson("src-tauri/tauri.conf.json", value => { value.version = version; }); + + const cargoTomlPath = "src-tauri/Cargo.toml"; + const cargoToml = fs.readFileSync(cargoTomlPath, "utf8").replace( + /(\[package\][\s\S]*?\nversion = ")[^"]+("\n)/, + `$1${version}$2` + ); + fs.writeFileSync(cargoTomlPath, cargoToml); + + const cargoLockPath = "src-tauri/Cargo.lock"; + const cargoLock = fs.readFileSync(cargoLockPath, "utf8").replace( + /(\[\[package\]\]\nname = "lockscreenwallpaper"\nversion = ")[^"]+("\n)/, + `$1${version}$2` + ); + fs.writeFileSync(cargoLockPath, cargoLock); + + const fdroidPath = ".fdroid.yml"; + let fdroid = fs.readFileSync(fdroidPath, "utf8"); + fdroid = fdroid + .replace(/^ - versionName: .*$/m, ` - versionName: ${version}`) + .replace(/^ versionCode: .*$/m, ` versionCode: ${versionCode}`) + .replace(/^ commit: .*$/m, ` commit: ${process.env.GITHUB_REF_NAME}`) + .replace(/^CurrentVersion: .*$/m, `CurrentVersion: ${version}`) + .replace(/^CurrentVersionCode: .*$/m, `CurrentVersionCode: ${versionCode}`); + fs.writeFileSync(fdroidPath, fdroid); + ' + + echo "RELEASE_VERSION=${RELEASE_VERSION}" >> "${GITEA_ENV}" + echo "ANDROID_VERSION_CODE=${ANDROID_VERSION_CODE}" >> "${GITEA_ENV}" + echo "Building ${GITHUB_REF_NAME} with Android versionCode ${ANDROID_VERSION_CODE}." + - name: Validate signing secrets run: | for SECRET_NAME in \ @@ -105,6 +167,12 @@ jobs: - name: Build unsigned release APK run: npm run tauri -- android build --apk --ci + - name: Verify generated Android version + run: | + TAURI_PROPERTIES="src-tauri/gen/android/app/tauri.properties" + grep -Fx "tauri.android.versionName=${RELEASE_VERSION}" "${TAURI_PROPERTIES}" + grep -Fx "tauri.android.versionCode=${ANDROID_VERSION_CODE}" "${TAURI_PROPERTIES}" + - name: Align and sign APK run: | UNSIGNED_APK="src-tauri/gen/android/app/build/outputs/apk/universal/release/app-universal-release-unsigned.apk" diff --git a/README.md b/README.md index d388204..8bbc372 100644 --- a/README.md +++ b/README.md @@ -49,10 +49,18 @@ Der native Android-Code liegt als lokales Tauri-Plugin in `plugins/android`. Er ## Signierte Android-Releases mit Gitea Actions -Der Workflow `.gitea/workflows/android-release.yml` baut bei Tags wie `v0.1.0` -eine signierte Universal-APK. Der Tag muss der Version in -`src-tauri/tauri.conf.json` mit vorangestelltem `v` entsprechen. Der Workflow -kann außerdem manuell über die Actions-Oberfläche gestartet werden. +Der Workflow `.gitea/workflows/android-release.yml` baut bei Tags wie `v1.2.3` +eine signierte Universal-APK. Der Tag ist dabei die Quelle für alle +Versionsnummern im Build. Die Pipeline setzt vorübergehend die Versionen in +`package.json`, `package-lock.json`, `src-tauri/tauri.conf.json`, +`src-tauri/Cargo.toml`, `src-tauri/Cargo.lock` und `.fdroid.yml`. Diese Änderungen +gelten nur im Arbeitsverzeichnis des Runners und werden nicht zurück ins +Repository geschrieben. + +Aus `v1.2.3` erzeugt Tauri den Android-`versionCode` `1002003` nach dem Schema +`major * 1000000 + minor * 1000 + patch`. Der Workflow kann außerdem manuell +über die Actions-Oberfläche gestartet werden, wenn dabei ein Versions-Tag als +Ref ausgewählt wird. In den Repository-Einstellungen unter **Settings → Actions → Secrets** müssen folgende Secrets angelegt werden: @@ -99,7 +107,11 @@ Weitere Einzelheiten stehen in der [Datenschutzerklärung](PRIVACY.md). ## Veröffentlichungen -Für eine neue Veröffentlichung müssen die Version in `package.json`, `src-tauri/tauri.conf.json` und `src-tauri/Cargo.toml` gemeinsam erhöht und ein passender Git-Tag angelegt werden, beispielsweise `v0.1.0`. Der Android-`versionCode` muss bei jeder Veröffentlichung steigen. +Für ein Gitea-Release genügt ein neuer, höherer Versions-Tag wie `v0.1.1`; die +Pipeline übernimmt daraus alle Versionen für den Build. Für dauerhaft im +Repository gepflegte Versionsstände und F-Droid-Releases sollten die Versionen +in `package.json`, `src-tauri/tauri.conf.json`, `src-tauri/Cargo.toml` und +`.fdroid.yml` zusätzlich im Quellcode aktualisiert werden. Die Metadaten für F-Droid liegen unter `fastlane/metadata/android`. Das Buildrezept für F-Droid befindet sich in `.fdroid.yml`.