Skip to content

Conversation

@philprime
Copy link
Member

Summary

  • Split the 888-line monolithic Fastfile into a modular structure with separate lane files
  • Add custom enable_pr_auto_merge action for GitHub auto-merge via GraphQL API
  • Implement idempotent deploy_beta lane that skips upload if build already exists on TestFlight

Changes

New Structure

fastlane/
├── Fastfile                    # Main entry with imports (46 lines)
├── lanes/
│   ├── build.rb               # build_ci lane
│   ├── release.rb             # beta, prepare_release, deploy_beta, publish
│   ├── utilities.rb           # generate_*, upload_metadata, setup_code_signing, bump_*
│   ├── helpers.rb             # Private helper lanes
│   ├── sentry.rb              # Sentry integration lanes
│   ├── version.rb             # Version management lanes
│   └── git.rb                 # Git and GitHub lanes
└── actions/
    └── enable_pr_auto_merge.rb # Custom action for GitHub auto-merge

Workflow Changes

  • Rename publish-beta-build.ymldeploy-beta.yml
  • Add prepare-release.yml workflow for scheduled/manual releases
  • prepare_release queries TestFlight for next build number upfront
  • deploy_beta checks if build exists before uploading (idempotent, safe to re-run)

Dependencies

  • Add octokit gem for GitHub API interactions

Test Plan

  • bundle exec fastlane lanes shows all 12 public lanes
  • bundle exec fastlane action enable_pr_auto_merge shows custom action docs
  • Test prepare_release workflow manually
  • Test deploy_beta workflow on release branch push

Split the 888-line monolithic Fastfile into a modular structure:

- fastlane/Fastfile: Main entry with imports (46 lines)
- fastlane/lanes/build.rb: build_ci lane
- fastlane/lanes/release.rb: beta, prepare_release, deploy_beta, publish
- fastlane/lanes/utilities.rb: generate_*, upload_metadata, setup_code_signing, bump_*
- fastlane/lanes/helpers.rb: Private helper lanes
- fastlane/lanes/sentry.rb: Sentry integration lanes
- fastlane/lanes/version.rb: Version management lanes
- fastlane/lanes/git.rb: Git and GitHub lanes
- fastlane/actions/enable_pr_auto_merge.rb: Custom action for GitHub auto-merge

Additional changes:
- Add octokit gem for GitHub API interactions
- Rename publish-beta-build.yml to deploy-beta.yml
- Add prepare-release.yml workflow for scheduled releases
- Implement idempotent deploy_beta lane (skips if build already uploaded)
- Query TestFlight for next build number in prepare_release
Copilot AI review requested due to automatic review settings February 2, 2026 10:26
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Refactors Fastlane configuration by splitting a previously monolithic Fastfile into modular lane files, adds a custom Fastlane action to enable GitHub PR auto-merge, and updates CI workflows to support a prepare-release + deploy-beta flow.

Changes:

  • Modularized Fastlane lanes into fastlane/lanes/*.rb and simplified fastlane/Fastfile imports
  • Added enable_pr_auto_merge custom action using Octokit + GitHub GraphQL API
  • Added prepare-release.yml and updated deploy-beta.yml to support release-branch-driven beta deployments

Reviewed changes

Copilot reviewed 12 out of 13 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
fastlane/Fastfile Replaced monolithic lane definitions with imports for modular lane files.
fastlane/lanes/build.rb Defines build_ci lane for CI archives and optional Sentry upload.
fastlane/lanes/release.rb Adds/defines release lanes: beta, prepare_release, deploy_beta, publish.
fastlane/lanes/utilities.rb Adds utility lanes for icons, screenshots, metadata upload, code signing, and version bump lanes.
fastlane/lanes/helpers.rb Extracts private helper lanes used across build/release workflows.
fastlane/lanes/version.rb Adds private lanes for version/build number coordination with App Store Connect/TestFlight.
fastlane/lanes/sentry.rb Adds private lanes for Sentry release creation/finalization and uploads.
fastlane/lanes/git.rb Adds private lanes for PR creation, tagging, and invoking auto-merge action.
fastlane/actions/enable_pr_auto_merge.rb New custom action to enable PR auto-merge via GraphQL.
Gemfile Adds octokit dependency for GitHub API operations.
Gemfile.lock Locks octokit and its transitive dependencies.
.github/workflows/prepare-release.yml New scheduled/manual workflow to bump version/build and open a release PR.
.github/workflows/deploy-beta.yml Renames/retools workflow to deploy beta on release/** pushes.
Comments suppressed due to low confidence (1)

.github/workflows/deploy-beta.yml:10

  • github.job.name is not a valid GitHub Actions context property; the job id is available as github.job. As written, this expression may evaluate unexpectedly (or to an empty string), which can cause unrelated runs to share the same concurrency group. Consider switching to ${{ github.job }} (or remove the job component if you intentionally want a workflow-wide lock).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +72 to +73
# Generate version files
_make(target: "generate")
Copy link

Copilot AI Feb 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_check_and_bump_version_if_needed already runs _make(target: "generate") when it bumps the version, but prepare_release unconditionally runs _make(target: "generate") again. This makes the workflow do duplicate work and can add time/flakiness. Consider running generate only once (e.g., in prepare_release only when version_check_result[:bumped] is false, or remove the generate step from _check_and_bump_version_if_needed and keep it centralized in prepare_release).

Suggested change
# Generate version files
_make(target: "generate")
# Generate version files (only if not already generated by _check_and_bump_version_if_needed)
_make(target: "generate") unless version_check_result[:bumped]

Copilot uses AI. Check for mistakes.
Comment on lines +10 to +13
desc "Generate app icon sizes from source image using fastlane-plugin-appicon"
desc "Generates all required iOS app icon sizes from Resources/AppIcon.png"
desc "Strips metadata to ensure git-reproducible output"
lane :generate_app_icons do
Copy link

Copilot AI Feb 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fastlane only keeps a single desc for a lane (the last desc call before the lane definition). With multiple desc lines here, earlier descriptions will be overwritten and won't show up in fastlane lanes. Consider combining these into one multi-line description (or keep only one desc).

Copilot uses AI. Check for mistakes.
Comment on lines +59 to +63
desc "Prepare release: bump version and create release branch with PR"
desc "Checks App Store Connect for published version and bumps patch if needed"
desc "Queries TestFlight for latest build number and sets next build number"
desc "Used by prepare-release.yml workflow (scheduled/manual trigger)"
desc "The PR will trigger deploy-beta.yml workflow when the release branch is pushed"
Copy link

Copilot AI Feb 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same issue as elsewhere: multiple desc calls will overwrite each other, so only the last one will be shown for prepare_release. Consider collapsing these into a single multi-line desc (or keeping one).

Suggested change
desc "Prepare release: bump version and create release branch with PR"
desc "Checks App Store Connect for published version and bumps patch if needed"
desc "Queries TestFlight for latest build number and sets next build number"
desc "Used by prepare-release.yml workflow (scheduled/manual trigger)"
desc "The PR will trigger deploy-beta.yml workflow when the release branch is pushed"
desc <<~DESC
Prepare release: bump version and create release branch with PR
Checks App Store Connect for published version and bumps patch if needed
Queries TestFlight for latest build number and sets next build number
Used by prepare-release.yml workflow (scheduled/manual trigger)
The PR will trigger deploy-beta.yml workflow when the release branch is pushed
DESC

Copilot uses AI. Check for mistakes.
Comment on lines +30 to +36
# Enable auto-merge using GraphQL mutation
mutation = <<~GRAPHQL
mutation {
enablePullRequestAutoMerge(input: {
pullRequestId: "#{node_id}"
mergeMethod: #{merge_method}
}) {
Copy link

Copilot AI Feb 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

merge_method is interpolated directly into the GraphQL mutation. Since GraphQL expects an enum here, any unexpected value will cause the mutation to fail, and (because this is raw string interpolation) it also leaves room for query injection if the value ever becomes user-controlled. Consider validating/normalizing merge_method against an allowlist (MERGE/SQUASH/REBASE) and substituting the sanitized value into the query.

Copilot uses AI. Check for mistakes.
workflow_dispatch:

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.job.name}}
Copy link

Copilot AI Feb 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

github.job.name is not a valid GitHub Actions context property; the job id is available as github.job. As written, this expression may evaluate unexpectedly (or to an empty string), which can cause unrelated runs to share the same concurrency group. Consider switching to ${{ github.job }} (or remove the job component if you intentionally want a workflow-wide lock).

Suggested change
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.job.name}}
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.job }}

Copilot uses AI. Check for mistakes.
Comment on lines +95 to +100
# Check if this build is already uploaded to TestFlight
if _build_already_uploaded?(version: version_number, build: build_number)
UI.success "✅ Build #{version_number} (#{build_number}) already exists on TestFlight"
UI.success "Skipping upload - previous run succeeded. Nothing to do."
next
end
Copy link

Copilot AI Feb 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

deploy_beta treats “build exists on TestFlight” as equivalent to “previous run succeeded” and exits early. If a previous run uploaded the build but failed afterward (e.g., during _finalize_sentry_release or any post-upload step you may add later), re-running will skip all remaining steps and never retry them. To make the lane truly safe to re-run after partial failures, consider skipping only the upload_to_testflight step when the build exists, while still running (or re-running) the other side-effect steps that should be idempotent (e.g., Sentry finalize).

Copilot uses AI. Check for mistakes.
Comment on lines +35 to +40
# Compare versions semantically
if _version_already_published?(current_version, published_version)
UI.important "Version #{current_version} is already published. Bumping patch version..."
_bump_version(bump_type: "patch")
_make(target: "generate")

Copy link

Copilot AI Feb 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The version check only bumps when current_version is exactly equal to the published version. If the local current_version is lower than the published App Store version (e.g., local 1.2.0, published 1.3.0), _version_already_published? returns false and this lane will keep the lower version, which will later fail App Store/TestFlight upload. Consider changing the logic to bump when current_version <= published_version (or compute next_version as published_version + patch) using a proper semantic version compare (e.g., Gem::Version).

Copilot uses AI. Check for mistakes.
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.

2 participants