feat(gitea): attach build artifacts to Gitea releases
Android Release / Build signed release APK (push) Successful in 37m54s

The Android release workflow now creates or updates a Gitea
release and attaches the APK and its SHA256 to it. A
GITEA_TOKEN with releases: write is required for uploads.
The README now documents the Gitea release flow and the
required permissions.

- Enforces that releases run only on a tag and matches the app version.
- Replaces or updates release assets to match tag names.
- Requires GITEA_TOKEN with releases: write for uploads.
This commit is contained in:
2026-08-26 00:14:39 +02:00
parent 7c15f51355
commit aca5562f81
2 changed files with 90 additions and 13 deletions
+79 -9
View File
@@ -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
+11 -4
View File
@@ -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