diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml index be5cfa43..e7b39993 100644 --- a/.gitea/workflows/release.yml +++ b/.gitea/workflows/release.yml @@ -2,8 +2,8 @@ name: release # Builds and pushes the minstrel container image to the Gitea registry. # -# push to main → :main and :latest (no APK bundled) -# push tag vYYYY.MM.DD → :vYYYY.MM.DD and :latest (APK bundled) +# push to main → :main and :latest (latest-release APK bundled) +# push tag vYYYY.MM.DD → :vYYYY.MM.DD and :latest (freshly-built APK bundled) # workflow_dispatch → manual trigger (same rules based on the ref) # # Release model: per-day CalVer tags (no trailing patch digit). The day's @@ -20,6 +20,14 @@ name: release # happens in the same android-release job, so the Release-page download # link and the in-image bundled APK are both populated atomically. # +# :latest always carries an APK. Because every main push also moves +# :latest (not just tags), a main build with no APK would silently strip +# the in-app update channel off :latest until the next release. So on +# non-tag builds image-release pulls the MOST RECENT release's signed APK +# and reconstructs its exact versionName (tag + commit-count, the same +# formula android-release bakes in) for the version sidecar — no rebuild, +# just rebundle. Tag builds keep bundling their own freshly-built APK. +# # Android testing (lint + detekt + unit tests, debug APK upload on main) # lives in android.yml and runs independently on every push. @@ -181,6 +189,12 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 + with: + # Full history + tags so non-tag :latest builds can resolve the + # latest release tag's commit count and reconstruct the bundled + # APK's exact versionName (see "Bundle latest release APK" below). + fetch-depth: 0 + fetch-tags: true - name: Detect buildable project id: guard @@ -221,9 +235,8 @@ jobs: | docker login git.fabledsword.com -u "${{ github.actor }}" --password-stdin - name: Download signed APK artifact - # Tag pushes only — android-release just produced this. Main - # pushes skip and the image ships with empty client/ (the - # /api/client/version endpoint then returns 404 by design). + # Tag pushes only — android-release just produced this. Non-tag + # builds take the "Bundle latest release APK" path below instead. if: steps.guard.outputs.ready == 'true' && startsWith(github.ref, 'refs/tags/v') uses: actions/download-artifact@v3 with: @@ -247,6 +260,42 @@ jobs: echo "${APK_VERSION_NAME}" > client/minstrel.apk.version ls -lh client/ + - name: Bundle latest release APK (non-tag :latest builds) + # Main pushes don't build an APK, but they DO move :latest — so + # without this the in-app update channel would vanish from :latest + # until the next tag. Pull the most-recent release's signed APK and + # reconstruct its exact versionName (${TAG#v}.$(git rev-list --count + # TAG) — identical to android-release's formula) so the version + # sidecar the server hands clients matches the installed build. + # Degrades to an empty client/ (404 update channel) — never a wrong + # version — if no release / APK asset / tag-count can be resolved. + if: steps.guard.outputs.ready == 'true' && !startsWith(github.ref, 'refs/tags/v') + shell: bash + env: + CI_TOKEN: ${{ secrets.CI_TOKEN }} + run: | + set -eu + REPO="${GITHUB_REPOSITORY}" + REL_JSON="$(curl -fsSL -H "Authorization: token ${CI_TOKEN}" \ + "https://git.fabledsword.com/api/v1/repos/${REPO}/releases/latest" || true)" + if [ -z "${REL_JSON}" ]; then + echo "::notice::no published release — image ships without bundled APK"; exit 0 + fi + TAG="$(printf '%s' "${REL_JSON}" | grep -oP '"tag_name":\s*"\K[^"]+' | head -1)" + APK_URL="$(printf '%s' "${REL_JSON}" | grep -oP '"browser_download_url":\s*"\K[^"]+' | grep -E '\.apk$' | head -1)" + if [ -z "${TAG}" ] || [ -z "${APK_URL}" ]; then + echo "::notice::latest release '${TAG:-?}' has no APK asset — image ships without bundled APK"; exit 0 + fi + COUNT="$(git rev-list --count "${TAG}" 2>/dev/null || true)" + if [ -z "${COUNT}" ]; then + echo "::notice::could not resolve commit count for ${TAG} (tag not fetched?) — skipping APK bundle"; exit 0 + fi + VERSION_NAME="${TAG#v}.${COUNT}" + curl -fsSL -H "Authorization: token ${CI_TOKEN}" -o client/minstrel.apk "${APK_URL}" + echo "${VERSION_NAME}" > client/minstrel.apk.version + echo "::notice::bundled release APK ${TAG} as version ${VERSION_NAME}" + ls -lh client/ + - name: Build and push if: steps.guard.outputs.ready == 'true' run: |