diff --git a/.gitea/workflows/android-release.yml b/.gitea/workflows/android-release.yml index b43d269..8d7bed3 100644 --- a/.gitea/workflows/android-release.yml +++ b/.gitea/workflows/android-release.yml @@ -11,6 +11,9 @@ jobs: name: Build signed release APK runs-on: ubuntu-latest timeout-minutes: 60 + permissions: + code: read + releases: write env: ANDROID_COMPILE_SDK: "36" ANDROID_BUILD_TOOLS: "36.0.0" @@ -19,6 +22,7 @@ jobs: ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }} ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }} ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }} + GITEA_RELEASE_TOKEN: ${{ secrets.GITEA_TOKEN }} steps: - name: Check out repository @@ -64,8 +68,11 @@ jobs: run: npm ci - name: Check release tag and app version - if: gitea.ref_type == '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 @@ -78,7 +85,8 @@ jobs: ANDROID_KEYSTORE_BASE64 \ ANDROID_KEYSTORE_PASSWORD \ ANDROID_KEY_ALIAS \ - ANDROID_KEY_PASSWORD + ANDROID_KEY_PASSWORD \ + GITEA_RELEASE_TOKEN do if [ -z "${!SECRET_NAME:-}" ]; then echo "Missing Gitea Actions secret: ${SECRET_NAME}" >&2 @@ -118,11 +126,73 @@ jobs: rm "${ALIGNED_APK}" (cd "${RELEASE_DIRECTORY}" && sha256sum WallpaperFlow-release.apk > WallpaperFlow-release.apk.sha256) - - name: Upload release APK - uses: actions/upload-artifact@v3 - with: - name: WallpaperFlow-${{ gitea.ref_name }}-android - path: | - release/WallpaperFlow-release.apk + - name: Attach APK to Gitea release + run: | + API_ROOT="${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}" + RELEASE_JSON="${RUNNER_TEMP}/wallpaperflow-release.json" + CREATE_JSON="${RUNNER_TEMP}/wallpaperflow-create-release.json" + ASSETS_JSON="${RUNNER_TEMP}/wallpaperflow-release-assets.json" + + STATUS="$(curl --silent --show-error \ + --output "${RELEASE_JSON}" \ + --write-out '%{http_code}' \ + --header "Authorization: token ${GITEA_RELEASE_TOKEN}" \ + "${API_ROOT}/releases/tags/${GITHUB_REF_NAME}")" + + if [ "${STATUS}" = "404" ]; then + node -e ' + process.stdout.write(JSON.stringify({ + tag_name: process.env.GITHUB_REF_NAME, + name: `WallpaperFlow ${process.env.GITHUB_REF_NAME}`, + body: "Automatisch erstelltes Android-Release.", + draft: false, + prerelease: false + })) + ' > "${CREATE_JSON}" + curl --fail --silent --show-error \ + --request POST \ + --header "Authorization: token ${GITEA_RELEASE_TOKEN}" \ + --header "Content-Type: application/json" \ + --data-binary "@${CREATE_JSON}" \ + --output "${RELEASE_JSON}" \ + "${API_ROOT}/releases" + elif [ "${STATUS}" != "200" ]; then + echo "Could not read Gitea release for ${GITHUB_REF_NAME} (HTTP ${STATUS})." >&2 + exit 1 + fi + + RELEASE_ID="$(node -e ' + const release = JSON.parse(require("fs").readFileSync(process.argv[1], "utf8")); + if (!release.id) process.exit(1); + process.stdout.write(String(release.id)); + ' "${RELEASE_JSON}")" + + curl --fail --silent --show-error \ + --header "Authorization: token ${GITEA_RELEASE_TOKEN}" \ + --output "${ASSETS_JSON}" \ + "${API_ROOT}/releases/${RELEASE_ID}/assets" + + for FILE in \ + release/WallpaperFlow-release.apk \ release/WallpaperFlow-release.apk.sha256 - if-no-files-found: error + do + ASSET_NAME="$(basename "${FILE}")" + ASSET_ID="$(ASSET_NAME="${ASSET_NAME}" node -e ' + const assets = JSON.parse(require("fs").readFileSync(process.argv[1], "utf8")); + const asset = assets.find(item => item.name === process.env.ASSET_NAME); + if (asset) process.stdout.write(String(asset.id)); + ' "${ASSETS_JSON}")" + + if [ -n "${ASSET_ID}" ]; then + curl --fail --silent --show-error \ + --request DELETE \ + --header "Authorization: token ${GITEA_RELEASE_TOKEN}" \ + "${API_ROOT}/releases/${RELEASE_ID}/assets/${ASSET_ID}" + fi + + curl --fail --silent --show-error \ + --request POST \ + --header "Authorization: token ${GITEA_RELEASE_TOKEN}" \ + --form "attachment=@${FILE}" \ + "${API_ROOT}/releases/${RELEASE_ID}/assets" >/dev/null + done diff --git a/README.md b/README.md index 7110cfa..d388204 100644 --- a/README.md +++ b/README.md @@ -68,10 +68,17 @@ Den Keystore-Inhalt für das Secret erzeugt man unter Linux mit: base64 -w 0 /sicherer/pfad/wallpaperflow-release.jks ``` -Nach erfolgreichem Lauf stehen `WallpaperFlow-release.apk` und die zugehörige -SHA-256-Prüfsumme als Artefakt des Workflows bereit. Der Keystore darf nicht in -das Repository eingecheckt werden und muss dauerhaft gesichert bleiben, da -spätere Updates mit demselben Schlüssel signiert werden müssen. +Nach erfolgreichem Lauf erstellt die Pipeline ein Gitea-Release für den Tag +beziehungsweise verwendet ein bereits vorhandenes Release. Die Dateien +`WallpaperFlow-release.apk` und `WallpaperFlow-release.apk.sha256` werden direkt +an dieses Release angehängt. Bei einem erneuten Lauf für denselben Tag werden +vorhandene Anhänge mit diesen Namen ersetzt. Dafür muss der integrierte +`GITEA_TOKEN` Schreibzugriff auf Releases besitzen; die Pipeline fordert die +Berechtigung `releases: write` selbst an. + +Der Keystore darf nicht in das Repository eingecheckt werden und muss dauerhaft +gesichert bleiben, da spätere Updates mit demselben Schlüssel signiert werden +müssen. Voraussetzung ist ein aktiver Gitea-Actions-Runner mit dem Label `ubuntu-latest`, Netzwerkzugriff auf npm, Rust und die Android-SDK-Server sowie