Skip to content

Migrate Android fastlane to play_publisher and add release workflows - #2

Merged
lohanidamodar merged 6 commits into
mainfrom
feat/fastlane
Aug 20, 2026
Merged

Migrate Android fastlane to play_publisher and add release workflows#2
lohanidamodar merged 6 commits into
mainfrom
feat/fastlane

Conversation

@lohanidamodar

@lohanidamodar lohanidamodar commented Aug 19, 2026

Copy link
Copy Markdown
Collaborator

Brings the Android fastlane setup in line with the other popupbits Flutter apps, and adds the GitHub Actions release pipelines for both stores.

iOS fastlane was already identical to that standard (match signing, App Store Connect API key, metadata under ios/fastlane/metadata/) — the only change there is setup_ci, see below.

Android fastlane → play_publisher

The Android Fastfile had grown to ~600 lines — mostly Supply::Client plumbing to work around two upstream fastlane bugs (#21530, #21529) and Google Play's draft-app restriction. That code now lives in fastlane-plugin-play_publisher, so the Fastfile here is just lane names.

All nine lanes are unchanged — build, internal, beta, release, upload_metadata, upload_screenshots, upload_listing, download_metadata, screenshots — verified with bundle exec fastlane lanes before and after.

One behaviour change: play_build now verifies release signing and fails if android/key.properties is missing, instead of silently producing a debug-signed AAB that Play would reject on upload. Set PLAY_PUBLISHER_VERIFY_SIGNING=false to opt out.

Release workflows

Workflow Trigger Does
Android Release manual, pick internal / beta / release (+ release status) decodes the keystore, flutter test, runs the matching fastlane lane
iOS Release manual, pick beta (TestFlight) / release (App Store) / build match signing, flutter build ios --no-codesign, then the lane; uploads the IPA as an artifact
Refresh Gemfile.lock manual regenerates both locks on a runner and commits them

The other popupbits apps call a shared reusable workflow (popupbits/.github) for the Android one. That repo is private, and GitHub does not let a public repo in a different org call a private reusable workflow, so the steps are inlined here instead.

Both Gemfile.locks are now committed — Bundler runs frozen in CI, so the release jobs fail without them. They were resolved in a ruby:3.2 container and carry the ruby, x86_64-linux and aarch64-linux platforms; a frozen bundle install was verified on Linux.

sync_signing in the iOS Fastfile now calls setup_ci, since match cannot install a certificate on a runner with no unlocked keychain. It is a no-op outside CI.

Also included

  • android/fastlane/metadata/android/en-US/changelogs/1.txt — the changelogs directory only had a .gitkeep, so a versionCode 1 release would have shipped with no "what's new" text. Mirrors the existing iOS release_notes.txt.
  • A ## Releasing section in the README covering both platforms' lanes, required env vars and the workflows.
  • Fastlane's generated artifacts (fastlane/README.md, report.xml, .bundle/, vendor/) added to .gitignore.

Before the first run

Set the repository secrets listed at the top of each workflow file. Neither workflow has ever run, so treat the first internal / beta build as the smoke test.

Release enablement (later commits)

  • google-services.json in CI. android/app/build.gradle.kts only applies the FCM Gradle plugin when the file is present, so a CI build without it succeeds and ships with push silently disabled. The workflow now decodes it from ANDROID_GOOGLE_SERVICES_JSON and warns loudly when the secret is unset.
  • iOS build-number override. TestFlight rejects a re-upload at the same build number; the workflow takes an optional build_number at dispatch time instead of requiring a pubspec edit per retry.
  • Play listing images. metadata/android/en-US/images/ did not exist, so upload_screenshots had nothing to upload and the listing could not be published. Added the required 512×512 icon.png and 1024×500 featureGraphic.png, generated from the mark and wordmark the web app uses. Screenshots still have to be captured by hand — Play needs at least two phone screenshots.

The Android Fastfile had grown to ~600 lines of Supply::Client
workarounds for draft apps and the crashing supply Uploader/Setup paths.
That code now lives in fastlane-plugin-play_publisher, so the Fastfile is
just lane names. Lane names and behaviour are unchanged, except
play_build now fails fast when android/key.properties is missing instead
of silently producing a debug-signed AAB.

Also adds the missing changelog for versionCode 1, a Releasing section to
the README, and fastlane's generated artifacts to .gitignore.
Android Release and iOS Release both run on workflow_dispatch and drive
the existing fastlane lanes. The popupbits apps call a shared reusable
workflow for this, but that lives in a private repo and GitHub will not
let a public repo in another org call it, so the steps are inlined here.

Commits both Gemfile.locks — Bundler runs frozen in CI, so the release
jobs need them — plus a Refresh Gemfile.lock workflow to regenerate them
on a runner after a Gemfile change.

match cannot install a certificate on a CI runner without an unlocked
keychain, so sync_signing now calls setup_ci.
@lohanidamodar lohanidamodar changed the title Move Android fastlane logic into the play_publisher plugin Migrate Android fastlane to play_publisher and add release workflows Aug 19, 2026
Without ANDROID_GOOGLE_SERVICES_JSON the Gradle FCM plugin is skipped and
the release ships with push silently disabled, so decode it from a secret
when present and warn when it is not.

TestFlight rejects a re-upload at the same build number, so allow one to
be passed at dispatch time instead of editing pubspec for every retry.
Google Play requires a 512x512 icon and a 1024x500 feature graphic before
a listing can be published, and upload_screenshots had no images/
directory to read. Both are generated from the refetch mark and wordmark
used by the web app. Screenshots still have to be captured by hand.
The publishing service account JSON is a private key; fastlane reads it
through PLAY_STORE_JSON_KEY_PATH or PLAY_STORE_JSON_KEY_DATA, so it never
needs to be in the tree.
@lohanidamodar
lohanidamodar merged commit 63b6d77 into main Aug 20, 2026
@lohanidamodar
lohanidamodar deleted the feat/fastlane branch August 20, 2026 01:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant