diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 50a6452900b3..32447820af66 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -4,7 +4,7 @@ name: release # the two play bundles to the internal track, writes their listings and records what went # out; foss rides on the github release. Split into jobs so that a half uploaded release # is repairable: "Re-run failed jobs" retries one upload against the bundle already built -# and signed, and a wedged emulator costs the release its pictures and not its binary. +# and signed, and a wedged emulator costs the release its pictures and nothing else. # # No tag triggers anything and none is written before an upload; build/ is # written afterwards, and the v tag only when the drafted release is published, @@ -329,7 +329,7 @@ jobs: # The bundles alone. What the store says about them is the listing job below, so # that a screenshot run that wedged an emulator costs the release its pictures - # and not its binary. + # and neither its binary nor its copy. # # A job per flavor rather than a loop, so one half can be re-run alone. fail-fast # off for the same reason @@ -384,9 +384,22 @@ jobs: # Its own job because the listing is editable for as long as the release is on # the internal track, while a bundle cannot be uploaded twice - and because it # is the half that waits on the emulators. + # + # `screenshot-set` supplies this job, it does not gate it: without the pictures + # the lane writes the listing text and this version's notes and leaves what the + # store has where it is, which is what `stage_screenshots` is written to do. + # Behind a plain `needs:` that never got to run - a wedged emulator skipped the + # job outright and cost the release its copy along with its pictures. + # + # Finishing without them does not spend the release's one chance at them. + # "Re-run failed jobs" re-runs what failed and everything downstream, so a + # repaired capture brings `screenshot-set` and this job with it and the pictures + # go up in a second edit - which is the whole reason the listing is its own job. + # The device that did pass is not run again; its artifact carries over from the + # attempt before, and the two halves merge as they would have. listing: needs: [upload, screenshot-set] - if: ${{ !inputs.dry_run }} + if: ${{ !cancelled() && !inputs.dry_run && needs.upload.result == 'success' }} runs-on: ubuntu-24.04 strategy: fail-fast: false @@ -408,13 +421,23 @@ jobs: given: ${{ inputs.version }} run: .github/scripts/resolve-version.py --input "$given" --dry-run "$dry_run" - # where the lane looks for them, and the same set both apps are given + # where the lane looks for them, and the same set both apps are given. Only + # when there is a set: the artifact is written with if-no-files-found: error, + # so a screenshot-set that passed is the same answer as one that exists - name: fetch the screenshots + if: ${{ needs.screenshot-set.result == 'success' }} uses: actions/download-artifact@v8 with: name: framed path: fastlane/framed + # said out loud, since the release otherwise finishes green over a listing + # still showing the pictures of some earlier version + - name: say there are no pictures this time + if: ${{ needs.screenshot-set.result != 'success' }} + run: | + echo "::warning::no screenshots for this release - the listing goes up with its text alone, and the store keeps the pictures it already has" + - name: play store credentials env: GOOGLE_PLAY_SERVICE_ACCOUNT: ${{ secrets.GOOGLE_PLAY_SERVICE_ACCOUNT }} diff --git a/CLAUDE.md b/CLAUDE.md index bb0c64d7235b..71bc6cc9a609 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -82,9 +82,12 @@ the test can already reach. into the test. The underscore in the name is not a slip either: `frame-screenshots.py` and the generator import it, and a dash cannot be imported. -The release runs the two devices on a runner each, checks the halves together, and only -then writes the listing - which is a job behind the bundle upload, so a wedged emulator -costs the release its pictures and not its binary. +The release runs the two devices on a runner each and checks the halves together, then +writes the listing. That job is behind the bundle upload but **not gated on the pictures**: +without them the lane writes the listing text and the release notes alone and leaves what +the store has, so a wedged emulator costs the release its pictures and nothing else. Do not +put the screenshots back into a plain `needs:` - that is what made a failed capture take the +copy down with it. ## Build diff --git a/README.md b/README.md index 856664b290c2..5c148fb221f1 100644 --- a/README.md +++ b/README.md @@ -125,7 +125,8 @@ icon and the feature graphic are still not uploaded; see `fastlane/metadata/READ Separate from the bundle upload on purpose: a bundle cannot go up twice, while the listing stays editable for as long as the release sits on the internal track - and the listing is the half that waits on the emulators. A screenshot run that wedges costs the release its -pictures, not its binary. +pictures and nothing else: the listing still goes up, with its text and this version's +release notes, over the screenshots the store already has. The run says so as a warning. `fastlane android listingPro` and `listingLite` send the listing without a bundle, which is how a typo is fixed: Play refuses a version code twice, so repairing the words should diff --git a/fastlane/Fastfile b/fastlane/Fastfile index b6f1cc78848c..71c07b9f459c 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -200,7 +200,7 @@ platform :android do # The bundle alone. What the store says about it goes up separately, in # uploadListing, so that a screenshot run that wedged an emulator costs the - # release its pictures and not its binary - and so a rejected word can be + # release its pictures and nothing else - and so a rejected word can be # rewritten and pushed again without touching a bundle play refuses twice. private_lane :uploadBundle do |options| flavor = options[:flavor]