ci(release): prevent Play submission churn and review-clock resets (#6517)

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
James RichandClaude Sonnet 5 authored and GitHub committed 2026-07-29 16:34:07 -05:00
1 parent 094cbadbab
commit dc9f8b9514
3 files changed
+143 -2

No files matched your search

@@ -20,6 +20,11 @@ on:
required: true
type: boolean
default: false
no_review_in_flight:
description: 'Promotions only: I checked Publishing overview > Submission activity and no submission is In review. Every promotion creates a new Play submission, which CANCELS and RESTARTS any review in flight.'
required: false
type: boolean
default: false
permissions:
contents: write
@@ -44,6 +49,15 @@ jobs:
final_tag: ${{ steps.calculate_tags.outputs.final_tag }}
from_channel: ${{ steps.calculate_tags.outputs.from_channel }}
steps:
# Internal releases are exempt: Play internal testing skips full review,
# so only promotions (closed/open/production) can clobber an in-flight
# review. Dry runs never reach Play.
- name: Require review-in-flight confirmation for promotions
if: ${{ !inputs.dry_run && inputs.channel != 'internal' && !inputs.no_review_in_flight }}
run: |
echo "::error::Promotion blocked: confirm no Play review is in flight. Check Play Console > Publishing overview > Submission activity — if a submission shows 'In review', WAIT (a new promotion cancels it and restarts the clock). If clear, re-dispatch with 'no_review_in_flight' checked."
exit 1
- name: Checkout code
uses: actions/checkout@v7.0.1
with:
+41 -2
View File
@@ -55,9 +55,13 @@ on:
HOMEBREW_TAP_TOKEN:
required: false
# Never cancel a promotion mid-flight: being killed between the Play edit
# commit and the GitHub release/tag update leaves the two disagreeing. The
# caller's queue-only concurrency serializes dispatches; this group (keyed by
# channel + tag) is a second line of defense that queues rather than cancels.
concurrency:
group: ${{ github.workflow }}-${{ inputs.tag_name }}
cancel-in-progress: true
group: ${{ github.workflow }}-${{ inputs.channel }}-${{ inputs.tag_name }}
cancel-in-progress: false
permissions:
contents: write
@@ -128,7 +132,42 @@ jobs:
- name: Decode Play Store credentials
run: echo '${{ secrets.GOOGLE_PLAY_JSON_KEY }}' > fastlane/play-store-credentials.json
# A re-dispatched promotion whose versionCode is already live on the
# target track must no-op: every redundant `supply` commit creates a new
# Play submission, and each submission cancels + restarts any review in
# flight (this reset the v2.8.0 review clock repeatedly, Jul 2026).
# Fail-open — a preflight error proceeds to supply, which uses the same
# credentials and will surface any real failure.
# The main checkout above is at the release tag, which predates this
# script — fetch it from the caller's commit instead, so script and
# workflow always move in lockstep. This is a local reusable-workflow
# call (`uses: ./.github/workflows/promote.yml`), so promote.yml is
# loaded from that same commit.
#
# Must NOT be a release input: any tag cut before this landed has no
# scripts/ entry, and a missing script exits 127 at the step level,
# bypassing the script's own fail-open path and hard-failing the
# promotion. workflow_sha is no better — the github context in a called
# reusable workflow is caller-associated, so it resolves to the caller's
# workflow too. A cross-repo caller would need explicit repo+ref inputs.
- name: Checkout preflight script from caller commit
uses: actions/checkout@v7.0.1
with:
ref: ${{ github.sha }}
path: .workflow-ref
sparse-checkout: scripts
- name: Preflight — is this versionCode already on the target track?
id: preflight
env:
VERSION_CODE: ${{ needs.prepare-build-info.outputs.APP_VERSION_CODE }}
run: |
PKG=$(grep '^APPLICATION_ID=' config.properties | cut -d'=' -f2)
bash .workflow-ref/scripts/play-track-preflight.sh \
fastlane/play-store-credentials.json "$PKG" "$TO_TRACK" "$VERSION_CODE"
- name: Promote to next channel
if: ${{ steps.preflight.outputs.already_on_track != 'true' }}
run: |
bundle exec fastlane supply \
--track "$FROM_TRACK" \