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`.