diff --git a/.gitea/workflows/android.yml b/.gitea/workflows/android.yml index ea1f7726..ad54d2f0 100644 --- a/.gitea/workflows/android.yml +++ b/.gitea/workflows/android.yml @@ -56,7 +56,21 @@ on: - 'rust-toolchain.toml' - 'scripts/ci/**' - '.gitea/workflows/android.yml' + # Manual runs are BUILD-ONLY by default. The escape hatch below exists because a push run can + # go missing entirely: merge two PRs seconds apart and Gitea attributes the window's runs to the + # newer head, so the older merge sha gets no run at all — its android change then sits on main + # having never been built, let alone published (2026-08-14: `1e5dca4c`, PR #235, lost its run to + # `b5cace3a` 12 s later). Re-running the PR run does NOT recover it: a re-run replays the original + # `pull_request` event, so every gate below stays false. Only a dispatch with publish=true can + # ship that commit without inventing a filler push. workflow_dispatch: + inputs: + publish: + # String, not a boolean: matches apple.yml's `testflight` input, which is the form proven + # to evaluate correctly on this Gitea. Compared as `inputs.publish == 'true'` below. + description: "Also publish this build (registry + Google Play). main -> beta+alpha, vX.Y.Z tag -> production at 100%. Default false: a stray click must not reach testers." + required: false + default: "false" # Shared compile cache: sccache -> RustFS S3 (storage.unom.io, LAN-pinned via ci-core's # unbound). The NDK clang targets get their own key universes automatically (keys embed @@ -228,7 +242,9 @@ jobs: # Single source of the version name + the Play track for the release steps below. versionCode # stays github.run_number (monotonic across both tracks; Play rejects a regressed code). - name: Version + channel - if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')) + if: >- + (github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && inputs.publish == 'true')) + && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')) run: | eval "$(bash scripts/ci/pf-version.sh)" # -> PF_BASE (one minor ahead of the latest stable tag) case "$GITHUB_REF" in @@ -250,7 +266,9 @@ jobs: echo "android version $VN -> Play track '$TRACK'${ALSO:+ (+ '$ALSO')}" - name: Build Release (signed AAB + universal APK) - if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')) + if: >- + (github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && inputs.publish == 'true')) + && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')) working-directory: clients/android env: VERSION_CODE: ${{ github.run_number }} # VERSION_NAME comes from the Version+channel step (GITHUB_ENV) @@ -285,7 +303,9 @@ jobs: # main = canary store + `canary/` sideload alias; a `vX.Y.Z` tag = `latest/` alias + attached # to the unified Gitea Release. - name: Publish to generic registry + attach to Gitea release - if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')) + if: >- + (github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && inputs.publish == 'true')) + && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')) env: REGISTRY: git.unom.io OWNER: unom @@ -328,7 +348,9 @@ jobs: # `--status inProgress --user-fraction 0.2`; to undo a bad one, halt or roll back from the # Console (or `android-promote.yml`, which can re-point production at an older versionCode). - name: Upload to Google Play - if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')) + if: >- + (github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && inputs.publish == 'true')) + && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')) env: SERVICE_ACCOUNT_JSON: ${{ secrets.SERVICE_ACCOUNT_JSON }} run: | diff --git a/docs/releases/README.md b/docs/releases/README.md index 4f7878cc..a5a39bb3 100644 --- a/docs/releases/README.md +++ b/docs/releases/README.md @@ -24,6 +24,16 @@ release is born complete and the announcement always has something to say. Discord `#releases`. Pressing "go" is the quality gate — a half-built release is never announced. Stable-only; a `-rc` tag is refused unless `allow_prerelease=true`. +**If a platform's run never appears, do not re-run the PR run — it cannot publish.** A re-run +replays the original event (`pull_request`), and android's publish steps are gated on a `push`, so +they stay skipped no matter how often you press it. Merging two PRs seconds apart can leave the +older merge sha with **no run at all** — Gitea attributes the window's runs to the newer head +(2026-08-14: `1e5dca4c` lost its run to `b5cace3a`, 12 s later), which is how an android change +reaches main having never been built. Recover it by dispatching `android.yml` on that ref with +**`publish=true`**; that is the only manual path reaching the registry and Play, and a plain +dispatch stays build-only so a stray click can't ship to testers. Check for the gap by matching +your own merge sha in the run list — "CI ran" is not the same as "your commit ran". + Editing the notes after the tag is fine: update this file, then re-run step 4 (or PATCH the body via the API) — the announce step always re-syncs from the file, so the file stays authoritative even across a tag re-point.