iOS App Store Fastlane harness and tag-split release CI#331
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds a strict App Store release GitHub Actions workflow triggered by semver tags, narrows TestFlight tag triggers, introduces Fastlane configuration and metadata, implements CI preflight/signing and dry-run tooling for both workflows, adds export options and docs, and ignores bundled iOS gems. ChangesiOS App Store release + TestFlight CI
Sequence Diagram(s)sequenceDiagram
actor Developer
participant GHA as GitHub Actions<br/>(ios-app-store-release.yml)
participant Pre as Preflight Job<br/>(xcodegen, smoke tests)
participant Runner as macOS Runner
participant Key as Temp Keychain
participant FL as Fastlane<br/>(bundle exec fastlane)
participant ASC as App Store Connect<br/>API
Developer->>GHA: Push ios-vX.Y.Z tag
rect rgba(100, 149, 237, 0.5)
Note over GHA,Pre: Pre-flight
GHA->>Runner: checkout, setup Node 22, npm ci
GHA->>Pre: install xcodegen (retry), generate Xcode project
GHA->>Pre: run iOS smoke e2e (Release)
GHA->>GHA: upload diagnostics & xcresult artifacts
end
rect rgba(144, 238, 144, 0.5)
Note over GHA,Key: Release (after preflight)
GHA->>GHA: validate tag vs MARKETING_VERSION, checklists, dry-run evidence
GHA->>Runner: ensure Xcode SDK >= 26
GHA->>Key: create temp keychain, import cert & provisioning profile
GHA->>Runner: setup Ruby 3.3, bundler cache
GHA->>FL: run `bundle exec fastlane release` with ASC env
end
rect rgba(255, 182, 193, 0.5)
Note over FL,ASC: Fastlane interactions
FL->>Runner: build archive/export (gym/xcodebuild)
FL->>ASC: upload IPA / sync metadata (deliver/pilot)
FL->>ASC: optionally submit for review
end
GHA->>Key: delete temp keychain & cleanup secrets
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related issues
Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Tip 💬 Introducing Slack Agent: The best way for teams to turn conversations into code.Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.
Built for teams:
One agent for your entire SDLC. Right inside Slack. Comment |
|
@CodeAnt-AI review |
|
Your free trial PR review limit of 100 PRs has been reached. Please upgrade your plan to continue using CodeAnt AI. |
|
@coderabbitai full review |
✅ Actions performedFull review triggered. |
|
User cursoragent@cursor.com does not have a PR Review subscription. Go to Team management and add this email to the PR Review subscription. |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@ios/docs/app-store-metadata.md`:
- Line 3: The opening paragraph in app-store-metadata.md currently implies the
markdown table and the ios/fastlane/metadata deliver files are both
authoritative; update that paragraph to state explicitly that the
machine-deliverable strings (name, subtitle, description, keywords, release
notes, URLs, review notes) in fastlane/metadata are the single source of truth
for App Store Connect uploads and that this markdown is narrative/reference
only; also change the sentence about reconciling ASC to instruct contributors to
update fastlane/metadata (or ASC) and record the change in the PR or release
notes so the two stay in sync.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 9b1356a0-78f5-4829-a4c4-aff0426924e6
⛔ Files ignored due to path filters (1)
ios/Gemfile.lockis excluded by!**/*.lock
📒 Files selected for processing (22)
.github/workflows/ios-app-store-release.yml.github/workflows/ios-testflight.yml.gitignoreREADME.mddocs/operations/automation-evidence.mddocs/operations/ios-app-store-submission.mdios/Gemfileios/RELEASING.mdios/docs/app-store-metadata.mdios/fastlane/Appfileios/fastlane/Deliverfileios/fastlane/Fastfileios/fastlane/metadata/en-US/description.txtios/fastlane/metadata/en-US/keywords.txtios/fastlane/metadata/en-US/marketing_url.txtios/fastlane/metadata/en-US/name.txtios/fastlane/metadata/en-US/privacy_url.txtios/fastlane/metadata/en-US/release_notes.txtios/fastlane/metadata/en-US/subtitle.txtios/fastlane/metadata/en-US/support_url.txtios/fastlane/metadata/review_information/notes.txtscripts/ios-app-store-dry-run.mjs
70736ac to
fa501f1
Compare
|
Please address the open review thread from CodeRabbit:
Open thread to address:
|
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
scripts/ios-app-store-dry-run.mjs (1)
554-559:⚠️ Potential issue | 🟠 Major | ⚡ Quick winPopulate
intendedTagfrom the actual release tag.Now that the report distinguishes TestFlight vs. App Store workflows, the default
intendedTagfallback is misleading for App Store runs: unless callers override it, the generatedsummary.json/summary.mdstill reportsios-v<marketing>-build<build>, even when the workflow was triggered by a strict semver tag likeios-v1.0.3. That makes the release evidence wrong on the path this PR is adding.💡 Suggested fix
+ const ciTag = + process.env.GITHUB_REF_TYPE === "tag" && process.env.GITHUB_REF_NAME?.startsWith("ios-v") + ? process.env.GITHUB_REF_NAME + : undefined; - const intendedTag = args.intendedTag ?? `ios-v${project.marketingVersion}-build${project.buildNumber}`; + const intendedTag = + args.intendedTag ?? ciTag ?? `ios-v${project.marketingVersion}-build${project.buildNumber}`;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@scripts/ios-app-store-dry-run.mjs` around lines 554 - 559, The summary currently sets intendedTag to a generated fallback (“ios-v<marketing>-build<build>”) which is misleading for App Store runs; update report.preflightSummary so intendedTag is populated from the actual release tag (e.g., the tag variable used to detect the workflow trigger such as releaseTag / tagName / the parsed git ref) when present, and only fall back to the generated ios-v... value if that actual tag is absent; change the assignment near report.preflightSummary (alongside intendedTag, testflightWorkflowUrl, appStoreReleaseWorkflowUrl) to prefer the real release tag value and preserve existing behavior when callers explicitly override intendedTag.
🧹 Nitpick comments (1)
ios/fastlane/Fastfile (1)
29-33: ⚡ Quick winRequire
shellwordsbefore callingshellescape.
String#shellescapeis provided via Ruby’sshellwordsstdlib, so this lane is relying on Fastlane to load that extension transitively. If it does not,preflightdies withNoMethodErrorbefore the dry run starts. (ruby-doc.org)💡 Suggested fix
# frozen_string_literal: true +require "shellwords" default_platform(:ios)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@ios/fastlane/Fastfile` around lines 29 - 33, The lane preflight calls String#shellescape (via repo_root.shellescape and release_owner.shellescape) but never requires Ruby's shellwords stdlib; add a require 'shellwords' near the top of the Fastfile (before lane :preflight or at file top) so the Shellwords extension that provides shellescape is loaded and avoids a NoMethodError when running preflight.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.github/workflows/ios-app-store-release.yml:
- Around line 195-201: The workflow uses the wrong `security` subcommand name:
replace the incorrect `security list-keychain -d user -s $KEYCHAIN_PATH
$(security list-keychains -d user | tr -d '"')` invocation with the correct
plural `list-keychains` usage so the keychain search list is set properly;
update the line containing `security list-keychain` to call `security
list-keychains` (and keep the existing flags and subshell) so certificate import
and Fastlane steps can proceed.
---
Outside diff comments:
In `@scripts/ios-app-store-dry-run.mjs`:
- Around line 554-559: The summary currently sets intendedTag to a generated
fallback (“ios-v<marketing>-build<build>”) which is misleading for App Store
runs; update report.preflightSummary so intendedTag is populated from the actual
release tag (e.g., the tag variable used to detect the workflow trigger such as
releaseTag / tagName / the parsed git ref) when present, and only fall back to
the generated ios-v... value if that actual tag is absent; change the assignment
near report.preflightSummary (alongside intendedTag, testflightWorkflowUrl,
appStoreReleaseWorkflowUrl) to prefer the real release tag value and preserve
existing behavior when callers explicitly override intendedTag.
---
Nitpick comments:
In `@ios/fastlane/Fastfile`:
- Around line 29-33: The lane preflight calls String#shellescape (via
repo_root.shellescape and release_owner.shellescape) but never requires Ruby's
shellwords stdlib; add a require 'shellwords' near the top of the Fastfile
(before lane :preflight or at file top) so the Shellwords extension that
provides shellescape is loaded and avoids a NoMethodError when running
preflight.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: f80d9f7c-4561-416d-8313-c1a6c1362315
⛔ Files ignored due to path filters (1)
ios/Gemfile.lockis excluded by!**/*.lock
📒 Files selected for processing (22)
.github/workflows/ios-app-store-release.yml.github/workflows/ios-testflight.yml.gitignoreREADME.mddocs/operations/automation-evidence.mddocs/operations/ios-app-store-submission.mdios/Gemfileios/RELEASING.mdios/docs/app-store-metadata.mdios/fastlane/Appfileios/fastlane/Deliverfileios/fastlane/Fastfileios/fastlane/metadata/en-US/description.txtios/fastlane/metadata/en-US/keywords.txtios/fastlane/metadata/en-US/marketing_url.txtios/fastlane/metadata/en-US/name.txtios/fastlane/metadata/en-US/privacy_url.txtios/fastlane/metadata/en-US/release_notes.txtios/fastlane/metadata/en-US/subtitle.txtios/fastlane/metadata/en-US/support_url.txtios/fastlane/metadata/review_information/notes.txtscripts/ios-app-store-dry-run.mjs
✅ Files skipped from review due to trivial changes (14)
- ios/fastlane/metadata/en-US/subtitle.txt
- ios/fastlane/metadata/en-US/marketing_url.txt
- .gitignore
- ios/fastlane/metadata/en-US/name.txt
- ios/fastlane/metadata/en-US/keywords.txt
- ios/fastlane/metadata/en-US/support_url.txt
- ios/fastlane/metadata/en-US/release_notes.txt
- ios/Gemfile
- ios/fastlane/metadata/en-US/privacy_url.txt
- .github/workflows/ios-testflight.yml
- ios/fastlane/Deliverfile
- ios/fastlane/Appfile
- ios/docs/app-store-metadata.md
- ios/fastlane/metadata/en-US/description.txt
|
Fixed in |
|
@cursor review |
|
@coderabbitai full review |
✅ Actions performedFull review triggered. |
|
Fixed in |
|
@coderabbitai full review |
|
@graphite-app re-review |
|
@cursor review |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (1)
ios/Gemfile (1)
5-5: Tighten the Fastlane version range for more predictable patch updates.On Line 5,
~> 2.227allows any2.*release. Although a committedGemfile.lockpins to2.233.1today, tightening to~> 2.227.0constrains future updates to patch releases within the2.227.xseries for added safety.Proposed change
-gem "fastlane", "~> 2.227" +gem "fastlane", "~> 2.227.0"🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@ios/Gemfile` at line 5, Update the Fastlane gem declaration in the Gemfile: replace the loose version specifier gem "fastlane", "~> 2.227" with a tighter patch-only constraint (e.g., "~> 2.227.0") so future Bundler updates stay within the 2.227.x series; edit the line containing gem "fastlane" to use the new version specifier and run bundle install to update Gemfile.lock.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.github/workflows/ios-app-store-release.yml:
- Around line 192-193: The macOS runner's base64 is BSD and doesn't accept
--decode; update the two decode commands that use BUILD_CERTIFICATE_BASE64 and
BUILD_PROVISION_PROFILE_BASE64 to use the BSD flag -D and quote the output path
variables CERTIFICATE_PATH and PP_PATH (e.g., use -D and "$CERTIFICATE_PATH",
"$PP_PATH") to ensure compatibility and safe handling of spaces/special chars.
In `@ios/fastlane/Deliverfile`:
- Around line 6-7: The comment above the submit_for_review call is out of sync
with the actual default; update the comment to match the setting used by
submit_for_review(false) (or if CI should auto-submit, change the call to
submit_for_review(true)). Specifically, either revise the comment to state
“Default: do not auto-submit; CI sets submit_for_review(true) when releasing” to
match submit_for_review(false), or change the submit_for_review(...) invocation
to true so the code matches the existing comment.
In `@ios/RELEASING.md`:
- Around line 84-97: The docs currently state that "bundle exec fastlane
release" is screenshots-only but Fastfile defines the release lane as preflight
+ gym + deliver (which uploads the IPA); remove or update the stale
screenshots-only reference and explicitly note that fastlane release runs
preflight, builds (gym) and runs deliver (which will upload binaries), and add
the correct command/lane for screenshots-only (e.g., metadata_only or a
dedicated screenshots lane) so readers use metadata_only (or the named
screenshots lane) when they only intend to push screenshots; refer to the lane
names release, preflight, build/gym, deliver, and metadata_only in the Fastfile
to locate and align the documentation.
---
Nitpick comments:
In `@ios/Gemfile`:
- Line 5: Update the Fastlane gem declaration in the Gemfile: replace the loose
version specifier gem "fastlane", "~> 2.227" with a tighter patch-only
constraint (e.g., "~> 2.227.0") so future Bundler updates stay within the
2.227.x series; edit the line containing gem "fastlane" to use the new version
specifier and run bundle install to update Gemfile.lock.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: b49c57b2-a95b-4aee-84cb-616d3c57bdeb
⛔ Files ignored due to path filters (1)
ios/Gemfile.lockis excluded by!**/*.lock
📒 Files selected for processing (23)
.github/workflows/ios-app-store-release.yml.github/workflows/ios-testflight.yml.gitignoreREADME.mddocs/operations/automation-evidence.mddocs/operations/ios-app-store-submission.mdios/ExportOptionsFastlane.plistios/Gemfileios/RELEASING.mdios/docs/app-store-metadata.mdios/fastlane/Appfileios/fastlane/Deliverfileios/fastlane/Fastfileios/fastlane/metadata/en-US/description.txtios/fastlane/metadata/en-US/keywords.txtios/fastlane/metadata/en-US/marketing_url.txtios/fastlane/metadata/en-US/name.txtios/fastlane/metadata/en-US/privacy_url.txtios/fastlane/metadata/en-US/release_notes.txtios/fastlane/metadata/en-US/subtitle.txtios/fastlane/metadata/en-US/support_url.txtios/fastlane/metadata/review_information/notes.txtscripts/ios-app-store-dry-run.mjs
- Add '!ios-v*-build*' exclusion so TestFlight build tags (ios-v*-build*) no longer trigger the App Store release workflow - Fix base64 --decode → -D for macOS BSD base64 compatibility; quote CERTIFICATE_PATH and PP_PATH variables - Update dry-run A4 check to match new glob pattern 'ios-v*.*.*' - Fix Deliverfile comment: CI does not submit unless SUBMIT_FOR_REVIEW=1 - Fix RELEASING.md stale alias: fastlane release now runs preflight + gym + deliver, not screenshots-only Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Fixed in |
|
@cursor review |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.github/workflows/ios-app-store-release.yml:
- Around line 135-136: The workflow currently runs the "Generate App Store
submission dry-run evidence" step without APPSTORE_APP_ID/ASC credentials and
also sets SKIP_PREFLIGHT="1", so queryAsc() returns { live: false } and no live
preflight validation runs; to fix, either remove SKIP_PREFLIGHT or ensure the
dry-run step is executed with the ASC env vars (APPSTORE_APP_ID and API
credentials) so queryAsc() can detect live=true and Fastlane's preflight/dry-run
actually validates the build before deliver; update the step that sets
SKIP_PREFLIGHT (env SKIP_PREFLIGHT) or the step named "Generate App Store
submission dry-run evidence" to include the necessary APPSTORE_*/ASC env
variables accordingly.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 7def8293-ea6c-4e2e-82e1-e3f786c1c513
📒 Files selected for processing (4)
.github/workflows/ios-app-store-release.ymlios/RELEASING.mdios/fastlane/Deliverfilescripts/ios-app-store-dry-run.mjs
✅ Files skipped from review due to trivial changes (1)
- ios/fastlane/Deliverfile
🚧 Files skipped from review as they are similar to previous changes (1)
- scripts/ios-app-store-dry-run.mjs
- Add actions/setup-node@v4 (node-version: 22) to fastlane-release job before the dry-run step, matching the version pinned in pre-flight-tests - Pipe MARKETING_VERSION sed extraction through xargs to trim trailing whitespace — unquoted YAML values with trailing spaces would cause a false version mismatch Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
@cursor review |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 6ceb4c2. Configure here.
| const workflowSecretRefs = requiredWorkflowSecrets.filter((secret) => workflow.includes(`secrets.${secret}`)); | ||
| const workflowSecretRefs = requiredWorkflowSecrets.filter( | ||
| (secret) => workflow.includes(`secrets.${secret}`) && releaseWorkflow.includes(`secrets.${secret}`), | ||
| ); |
There was a problem hiding this comment.
Secret check requires both workflows causing fragile validation
Low Severity
The workflowSecretRefs filter uses && requiring every secret to appear in both workflow files. If a future refactor removes a secret reference from one workflow (e.g., TestFlight stops needing APPSTORE_API_* directly), the dry-run would report all those secrets as "missing" even though the App Store release workflow still references them. An || (either workflow references the secret) would correctly validate that at least one workflow uses each required secret.
Reviewed by Cursor Bugbot for commit 6ceb4c2. Configure here.
The dry-run treated a secret as present only when both TestFlight and App Store release workflows referenced it. Use OR so a secret referenced by either workflow satisfies B2, matching how secrets are actually consumed when workflows diverge. Co-authored-by: Bretton Auerbach <auerbachb@users.noreply.github.com>
|
User cursoragent@cursor.com does not have a PR Review subscription. Go to Team management and add this email to the PR Review subscription. |
|
CodeAnt AI is running the review. |
Sequence DiagramThis PR introduces a separate tag-triggered GitHub workflow for App Store releases using Fastlane and refines the dry-run evidence flow, while splitting tags so TestFlight uploads and App Store metadata plus binary uploads are triggered independently. sequenceDiagram
participant Dev as Developer
participant GH as GitHub
participant TF as TestFlight workflow
participant AS as App Store release workflow
participant DryRun as Dry run script
participant FL as Fastlane
participant ASC as App Store Connect
Dev->>GH: Push ios-v*-build* tag
GH->>TF: Trigger TestFlight build upload
Dev->>GH: Push ios-vMAJOR.MINOR.PATCH tag
GH->>AS: Trigger App Store release workflow
AS->>DryRun: Run ios app store dry run
AS->>FL: Run fastlane release lane
FL->>ASC: Upload metadata and binary without submission
ASC-->>FL: Accept upload
FL-->>AS: Report release upload status
Generated by CodeAnt AI |
| def asc_api_key_id | ||
| ENV["APP_STORE_CONNECT_API_KEY_ID"] || ENV.fetch("APPSTORE_API_KEY_ID") | ||
| end | ||
|
|
||
| def asc_api_key_issuer_id | ||
| ENV["APP_STORE_CONNECT_API_ISSUER_ID"] || ENV.fetch("APPSTORE_API_ISSUER_ID") | ||
| end | ||
|
|
||
| def asc_api_key_content | ||
| raw = ENV["APP_STORE_CONNECT_API_KEY_CONTENT"] || ENV.fetch("APPSTORE_API_PRIVATE_KEY") | ||
| raw.gsub("\\n", "\n") |
There was a problem hiding this comment.
Suggestion: These environment fallbacks use || with ENV[...], but in Ruby an empty string is truthy, so a missing/misconfigured secret that resolves to "" will not fall back to APPSTORE_* and will pass empty credentials into Fastlane. This causes authentication to fail later with less clear errors; treat empty strings as unset before choosing the fallback. [incorrect condition logic]
Severity Level: Major ⚠️
- ❌ TestFlight uploads fail when new env secrets misconfigured.
- ❌ App Store release lane `:release` breaks with empty API key.
- ⚠️ Metadata-only uploads via `:metadata_only` can also fail.
- ⚠️ Debugging failures harder due to deferred auth errors.Steps of Reproduction ✅
1. Configure environment so `APP_STORE_CONNECT_API_KEY_ID` is defined but empty (e.g.,
GitHub secret set to `\"\"`), while legacy `APPSTORE_API_KEY_ID` remains correctly
populated; this is consumed by `asc_api_key_id` at `ios/fastlane/Fastfile:7-9`.
2. From `ios/`, run `bundle exec fastlane beta` which executes lane `:beta` at
`ios/fastlane/Fastfile:138-147`.
3. During `:beta`, `stillpoint_api_key` at `ios/fastlane/Fastfile:20-27` calls
`asc_api_key_id`, which returns the empty string from
`ENV["APP_STORE_CONNECT_API_KEY_ID"]` and never falls back to
`ENV.fetch("APPSTORE_API_KEY_ID")`.
4. `pilot` at `ios/fastlane/Fastfile:142-146` receives an `api_key` constructed with an
empty `key_id`, causing App Store Connect authentication to fail at upload time with a
less-direct error instead of failing fast on missing configuration.Fix in Cursor | Fix in VSCode Claude
(Use Cmd/Ctrl + Click for best experience)
Prompt for AI Agent 🤖
This is a comment left during a code review.
**Path:** ios/fastlane/Fastfile
**Line:** 7:17
**Comment:**
*Incorrect Condition Logic: These environment fallbacks use `||` with `ENV[...]`, but in Ruby an empty string is truthy, so a missing/misconfigured secret that resolves to `""` will not fall back to `APPSTORE_*` and will pass empty credentials into Fastlane. This causes authentication to fail later with less clear errors; treat empty strings as unset before choosing the fallback.
Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix| export_options: "ExportOptionsFastlane.plist", | ||
| output_directory: ENV["GYM_OUTPUT_DIRECTORY"] || "build", | ||
| output_name: "StillPoint.ipa", | ||
| clean: !ENV["GYM_NO_CLEAN"] |
There was a problem hiding this comment.
Suggestion: clean: !ENV["GYM_NO_CLEAN"] treats any non-nil string (including "0" or "false") as truthy, so setting GYM_NO_CLEAN=false still disables cleaning. Parse this env var explicitly to avoid inverted behavior and accidental dirty builds. [incorrect condition logic]
Severity Level: Major ⚠️
- ⚠️ Build lane `:build` may skip cleaning unexpectedly.
- ⚠️ TestFlight uploads via `:beta` risk using dirty builds.
- ⚠️ App Store release lane `:release` may reuse stale artifacts.
- ⚠️ Env flag semantics confusing for CI configuration owners.Steps of Reproduction ✅
1. Set the environment variable `GYM_NO_CLEAN=false` (or `GYM_NO_CLEAN=0`) in the shell or
CI environment used to run Fastlane; this value is read at `ios/fastlane/Fastfile:134`.
2. From `ios/`, run `bundle exec fastlane build` to execute lane `:build` defined at
`ios/fastlane/Fastfile:125-136`.
3. Inside `:build`, `gym` is invoked with `clean: !ENV["GYM_NO_CLEAN"]` at
`ios/fastlane/Fastfile:134`; because any non-nil string is truthy in Ruby,
`ENV["GYM_NO_CLEAN"]` is `"false"`, so `!ENV["GYM_NO_CLEAN"]` evaluates to `false` and
cleaning is disabled despite the variable being set to `"false"`.
4. The same behavior occurs when running `bundle exec fastlane beta` (lane `:beta` at
`ios/fastlane/Fastfile:138-147`) or `bundle exec fastlane release` (lane `:release` at
`ios/fastlane/Fastfile:161-175`), both of which call `build`, leading to unintentionally
dirty builds when operators expect cleaning to occur.Fix in Cursor | Fix in VSCode Claude
(Use Cmd/Ctrl + Click for best experience)
Prompt for AI Agent 🤖
This is a comment left during a code review.
**Path:** ios/fastlane/Fastfile
**Line:** 134:134
**Comment:**
*Incorrect Condition Logic: `clean: !ENV["GYM_NO_CLEAN"]` treats any non-nil string (including `"0"` or `"false"`) as truthy, so setting `GYM_NO_CLEAN=false` still disables cleaning. Parse this env var explicitly to avoid inverted behavior and accidental dirty builds.
Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix| const releaseTagOk = | ||
| releaseWorkflow.includes("tags:") && releaseWorkflow.includes("'ios-v*.*.*'"); |
There was a problem hiding this comment.
Suggestion: The tag-split validation for the App Store workflow only checks for the positive glob and does not verify the required exclusion pattern for build tags. This can incorrectly mark A4 as passing even when ios-v*-build* would also trigger the App Store workflow, causing both workflows to run on the same tag. Include an explicit check for the negative pattern ('!ios-v*-build*') as part of the A4 validation. [logic error]
Severity Level: Major ⚠️
- ❌ Dry-run A4 passes despite overlapping tag triggers.
- ⚠️ Both TestFlight and App Store workflows can run together.
- ⚠️ Tag-split contract in docs is not reliably enforced.Steps of Reproduction ✅
1. In the App Store release workflow at `.github/workflows/ios-app-store-release.yml:3-8`,
observe the tag configuration:
- `tags:` with patterns `'ios-v*.*.*'` and ` '!ios-v*-build*'` (lines 6-8) intended to
run only on semver tags, excluding `ios-v*-build*` build tags.
2. In the dry-run script `scripts/ios-app-store-dry-run.mjs`, `main()` (lines 366-383)
reads both workflows into `workflow` and `releaseWorkflow` and then computes
`testflightTagOk` and `releaseTagOk` at lines 397-400:
- `const testflightTagOk = workflow.includes("tags:") &&
workflow.includes("'ios-v*-build*'");`
- `const releaseTagOk = releaseWorkflow.includes("tags:") &&
releaseWorkflow.includes("'ios-v*.*.*'");`
3. Note that `releaseTagOk` (line 399-400) only asserts that the App Store workflow
contains `tags:` and `'ios-v*.*.*'`; it does not require the exclusion pattern
`'!ios-v*-build*'`, even though the workflow itself currently has that line.
4. If a future edit removes the negative pattern from
`.github/workflows/ios-app-store-release.yml` but leaves `'ios-v*.*.*'`, then running `npm
run ios:app-store:dry-run` (script defined in `package.json:22` to invoke `node
scripts/ios-app-store-dry-run.mjs`) will still set `releaseTagOk = true`, causing
`testflightTagOk && releaseTagOk` to pass at lines 2-7 (file view 400+), and `pass("A4",
...)` is recorded, even though the App Store workflow would now also trigger on
`ios-v*-build*` tags and run concurrently with `.github/workflows/ios-testflight.yml`.Fix in Cursor | Fix in VSCode Claude
(Use Cmd/Ctrl + Click for best experience)
Prompt for AI Agent 🤖
This is a comment left during a code review.
**Path:** scripts/ios-app-store-dry-run.mjs
**Line:** 399:400
**Comment:**
*Logic Error: The tag-split validation for the App Store workflow only checks for the positive glob and does not verify the required exclusion pattern for build tags. This can incorrectly mark A4 as passing even when `ios-v*-build*` would also trigger the App Store workflow, causing both workflows to run on the same tag. Include an explicit check for the negative pattern (`'!ios-v*-build*'`) as part of the A4 validation.
Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix| const workflowSecretRefs = requiredWorkflowSecrets.filter( | ||
| (secret) => workflow.includes(`secrets.${secret}`) || releaseWorkflow.includes(`secrets.${secret}`), | ||
| ); |
There was a problem hiding this comment.
Suggestion: Secret-reference validation now treats a secret as present if it appears in either workflow, which allows missing TestFlight references to be masked by App Store workflow references. This can produce a false B2 pass even though the TestFlight workflow is misconfigured and will fail when it needs that secret. Validate required TestFlight secrets against the TestFlight workflow itself (or check both workflows independently and fail the one that is missing refs). [logic error]
Severity Level: Major ⚠️
- ❌ TestFlight workflow can lack required secrets while B2 passes.
- ⚠️ Dry-run checklist misrepresents per-workflow secret wiring.
- ⚠️ Secret regressions in one workflow are harder to detect.Steps of Reproduction ✅
1. In the TestFlight workflow `.github/workflows/ios-testflight.yml`, note that the upload
step at lines 222-242 sets:
- `APPSTORE_API_KEY_ID`, `APPSTORE_API_ISSUER_ID`, `APPSTORE_API_PRIVATE_KEY` from `${{
secrets.* }}` and uses them to create `AuthKey_${APPSTORE_API_KEY_ID}.p8` for
`xcodebuild` upload.
2. In the App Store release workflow `.github/workflows/ios-app-store-release.yml`,
observe that the `Fastlane release` job (lines 213-223) also references the same
`APPSTORE_API_*` secrets in its environment.
3. In `scripts/ios-app-store-dry-run.mjs`, `main()` (lines 366-383) reads both workflows
into `workflow` and `releaseWorkflow`, then computes `workflowSecretRefs` at lines 21-23
(view 400+), using:
- `const workflowSecretRefs = requiredWorkflowSecrets.filter((secret) =>
workflow.includes(\`secrets.${secret}\`) ||
releaseWorkflow.includes(\`secrets.${secret}\`));`
This OR condition treats a secret as "covered" if it appears in either workflow file.
4. If `.github/workflows/ios-testflight.yml` is later edited so that, for example,
`APPSTORE_API_PRIVATE_KEY` is no longer referenced there (breaking the upload step) but
`.github/workflows/ios-app-store-release.yml` still references
`secrets.APPSTORE_API_PRIVATE_KEY`, then running `npm run ios:app-store:dry-run`
(entrypoint at `package.json:22`) will:
- Still include `APPSTORE_API_PRIVATE_KEY` in `workflowSecretRefs` because it appears
in `releaseWorkflow`.
- Produce `missingWorkflowRefs.length === 0` and therefore call `pass("B2", ...)` or
`warn("B2", ...)` at lines 39-56 (view 400+), setting `reportSecretStatus.status`
accordingly.
- Classify checklist item B2 via `classifyIssueChecklist()` (lines 266-311) using
`reportSecretStatus`, so the dry-run report in
`artifacts/ios-app-store-dry-run/summary.md` shows B2 as satisfied even though the
TestFlight workflow is misconfigured and will fail when it reaches the `Upload to App
Store Connect` step that now lacks a valid secret binding.Fix in Cursor | Fix in VSCode Claude
(Use Cmd/Ctrl + Click for best experience)
Prompt for AI Agent 🤖
This is a comment left during a code review.
**Path:** scripts/ios-app-store-dry-run.mjs
**Line:** 420:422
**Comment:**
*Logic Error: Secret-reference validation now treats a secret as present if it appears in either workflow, which allows missing TestFlight references to be masked by App Store workflow references. This can produce a false B2 pass even though the TestFlight workflow is misconfigured and will fail when it needs that secret. Validate required TestFlight secrets against the TestFlight workflow itself (or check both workflows independently and fail the one that is missing refs).
Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix|
CodeAnt AI finished running the review. |
|
CodeAnt AI is running the review. |
Sequence DiagramThis PR introduces a new tag-split model where strict ios-vMAJOR.MINOR.PATCH tags trigger an App Store release workflow that runs preflight tests, generates an evidence log, and then uses Fastlane to build and upload the binary and metadata without auto-submitting for review. sequenceDiagram
participant Developer
participant GitHubActions as iOS App Store release workflow
participant Fastlane
participant AppStoreConnect
Developer->>GitHubActions: Push ios-vMAJOR.MINOR.PATCH tag
GitHubActions->>GitHubActions: Run release-config smoke tests
GitHubActions->>GitHubActions: Validate tag and checklists; generate dry-run evidence
GitHubActions->>Fastlane: Invoke release lane with App Store API key
Fastlane->>Fastlane: Build IPA with gym
Fastlane->>AppStoreConnect: Upload metadata and binary with deliver (no submit for review)
AppStoreConnect-->>Developer: Binary and metadata available for manual submission
Generated by CodeAnt AI |
|
CodeAnt AI finished running the review. |


User description
Summary
Implements the issue #242 automation harness: Bundler-managed Fastlane under
ios/(gym,pilot,deliver), canonicalfastlane/metadata/en-US/source of truth (folds PR #302 /app-store-metadata.mdcopy into deliver files), a new tag-triggered App Store release workflow, and an evidence log for automated vs manual steps.Tag model (important)
To avoid two workflows firing on the same tag, tags are split:
ios-v*-build*(example:ios-v1.0.3-build10) →.github/workflows/ios-testflight.yml(unchanged behavior:xcodebuildarchive + export upload).ios-vMAJOR.MINOR.PATCHthat must equalMARKETING_VERSIONinios/project.yml(example:ios-v1.0.3) →.github/workflows/ios-app-store-release.yml→bundle exec fastlane release.CI behavior
fastlane releaseruns preflight (npm run ios:app-store:dry-run), thengym+deliver(metadata + IPA).DELIVER_SKIP_SUBMISSION=1sodeliveruploads binary + metadata but does not submit for review (harness / Checklist: AI-assisted iOS App Store submission automation dry run #242 default; Release V1.1 to App Store #296 can remove this guard when a release owner approves live submission).APPSTORE_API_*are mapped to Fastlane’s standardAPP_STORE_CONNECT_API_KEY_ID,APP_STORE_CONNECT_API_ISSUER_ID,APP_STORE_CONNECT_API_KEY_CONTENTin the workflow (same secret values; no renames).Fastfilereads those names first, with fallback toAPPSTORE_*for local runs.Docs
ios/RELEASING.md— TestFlight vs App Store tag flow + Fastlane lane table; secrets documented as already wired.docs/operations/automation-evidence.md— automated vs human gates + submission behavior.ios/docs/app-store-metadata.md— points canonical deliverable strings atfastlane/metadata/.Verification
node scripts/ios-app-store-dry-run.mjspasses after workflow/script updates.Closes #242
Summary by CodeRabbit
New Features
Documentation
Chores
CodeAnt-AI Description
Split iOS release tags between TestFlight uploads and App Store releases
What Changed
ios-v*-build*tags, while strictios-vMAJOR.MINOR.PATCHtags trigger the App Store release workflow.ios/fastlane/metadata/so release text comes from one source.Impact
✅ Fewer accidental release triggers✅ Clearer App Store release steps✅ More reliable TestFlight vs App Store tagging🔄 Retrigger CodeAnt AI Review
Details
💡 Usage Guide
Checking Your Pull Request
Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.
Talking to CodeAnt AI
Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:
This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.
Example
Preserve Org Learnings with CodeAnt
You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:
This helps CodeAnt AI learn and adapt to your team's coding style and standards.
Example
Retrigger review
Ask CodeAnt AI to review the PR again, by typing:
Check Your Repository Health
To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.