Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 27 additions & 4 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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/<version> is
# written afterwards, and the v<version> tag only when the drafted release is published,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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' }}
Comment thread
andiwand marked this conversation as resolved.
runs-on: ubuntu-24.04
strategy:
fail-fast: false
Expand All @@ -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 }}
Expand Down
9 changes: 6 additions & 3 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion fastlane/Fastfile
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
Loading