fix(ios): harden App Store release automation (#221)
## Summary - replace the unsupported top-level App Store version collection with Apple's app-scoped version endpoint - add tested release-policy and availability readback for exact version/build, `AFTER_APPROVAL`, Denmark only, no preorder, and no automatic future territories - strengthen the stable `App Store Readiness` check and align Fastlane/candidate handoff with the approved 1.0.0 release policy ## Task contract `truckwash-ios-release-20260723` — R4 (`ci-policy`, `release-policy`, `credential-handling`, `branch-protection-or-rules`, `mobile-store-submission`). The user explicitly approved implementation, protected-master delivery, and the App Store release path. ## Changed files - App Store Connect client and dependency-free Node tests - App Store readiness and candidate workflows - Fastlane candidate release configuration - Apple App Store release runbook ## Verification - `node --test tests/node/app-store-connect.test.mjs` — 10 passed - `node scripts/mobile/validate-app-store.mjs --strict` — passed - `node scripts/mobile/check-permissions.mjs` — passed - App Store product-readiness Vitest — 5 passed - ESLint on changed Node files — passed - workflow YAML parsing — passed - `git diff --check` — passed - local Fastlane validation unavailable because Ruby/Bundler is not installed on this host; `App Store Readiness` runs it on GitHub ## Release target - iOS App Store - bundle `io.truckwash.app` - version `1.0.0` - App Store Connect app `6792777794` - Denmark only - automatic release after approval - no preorder or phased release for 1.0.0 The repository App Store automation switch remains disabled until this change is merged and credential health is reverified.
This commit is contained in:
@@ -8,6 +8,8 @@ on:
|
||||
- "fastlane/**"
|
||||
- "ios/**"
|
||||
- "scripts/mobile/**"
|
||||
- "tests/node/app-store-connect.test.mjs"
|
||||
- ".github/workflows/app-store-readiness.yml"
|
||||
- "Gemfile*"
|
||||
workflow_dispatch:
|
||||
|
||||
@@ -20,8 +22,9 @@ concurrency:
|
||||
|
||||
jobs:
|
||||
validate:
|
||||
name: App Store Readiness
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 10
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
@@ -61,12 +64,25 @@ jobs:
|
||||
echo 'Gemfile.lock is missing or stale. Download generated-fastlane-lock and commit it.' >&2
|
||||
exit 1
|
||||
|
||||
- name: Validate App Store metadata and available assets
|
||||
run: node scripts/mobile/validate-app-store.mjs
|
||||
- name: Install the pinned Fastlane dependency graph
|
||||
run: bundle install --jobs 4 --retry 3
|
||||
|
||||
- name: Validate strict App Store metadata and candidate assets
|
||||
run: node scripts/mobile/validate-app-store.mjs --strict
|
||||
|
||||
- name: Validate native mobile permissions
|
||||
run: node scripts/mobile/check-permissions.mjs
|
||||
|
||||
- name: Test App Store Connect automation
|
||||
run: node --test tests/node/app-store-connect.test.mjs
|
||||
|
||||
- name: Validate Fastlane configuration
|
||||
run: bundle exec fastlane lanes
|
||||
|
||||
- name: Validate JavaScript syntax
|
||||
run: |
|
||||
node --check scripts/mobile/validate-app-store.mjs
|
||||
node --check scripts/mobile/app-store-connect.mjs
|
||||
node --check scripts/mobile/create-ios-release-manifest.mjs
|
||||
node --check tests/node/app-store-connect.test.mjs
|
||||
node scripts/mobile/app-store-connect.mjs self-test-jwt
|
||||
|
||||
@@ -100,7 +100,7 @@ jobs:
|
||||
NODE
|
||||
|
||||
promote:
|
||||
name: Sync storefront and prepare manual review
|
||||
name: Sync and verify App Store candidate
|
||||
needs: resolve
|
||||
if: needs.resolve.outputs.enabled == 'true'
|
||||
runs-on: macos-15
|
||||
@@ -153,22 +153,33 @@ jobs:
|
||||
- name: Sync metadata and screenshots without App Review submission
|
||||
run: bundle exec fastlane ios prepare_candidate
|
||||
|
||||
- name: Configure automatic release after approval
|
||||
run: node scripts/mobile/app-store-connect.mjs configure-release-policy
|
||||
|
||||
- name: Read back exact App Store candidate
|
||||
id: readback
|
||||
run: node scripts/mobile/app-store-connect.mjs verify-store-version
|
||||
|
||||
- name: Verify Denmark-only availability and no preorder
|
||||
id: availability
|
||||
run: node scripts/mobile/app-store-connect.mjs verify-availability
|
||||
|
||||
- name: Write candidate handoff
|
||||
env:
|
||||
APP_STORE_STATE: ${{ steps.readback.outputs.app_store_state }}
|
||||
APP_STORE_VERSION_ID: ${{ steps.readback.outputs.app_store_version_id }}
|
||||
RELEASE_TYPE: ${{ steps.readback.outputs.release_type }}
|
||||
AVAILABLE_TERRITORIES: ${{ steps.availability.outputs.available_territories }}
|
||||
run: |
|
||||
echo "### iOS $IOS_MARKETING_VERSION candidate prepared" >> "$GITHUB_STEP_SUMMARY"
|
||||
echo "- Source: \`$IOS_SOURCE_SHA\`" >> "$GITHUB_STEP_SUMMARY"
|
||||
echo "- Exact tested build: \`$IOS_BUILD_NUMBER\` (\`$EXPECTED_APP_STORE_BUILD_ID\`)" >> "$GITHUB_STEP_SUMMARY"
|
||||
echo "- App Store state: \`$APP_STORE_STATE\`" >> "$GITHUB_STEP_SUMMARY"
|
||||
echo "- App Store version ID: \`$APP_STORE_VERSION_ID\`" >> "$GITHUB_STEP_SUMMARY"
|
||||
echo "- Release policy: \`$RELEASE_TYPE\`" >> "$GITHUB_STEP_SUMMARY"
|
||||
echo "- Availability: \`$AVAILABLE_TERRITORIES\` only; preorder disabled" >> "$GITHUB_STEP_SUMMARY"
|
||||
echo "- [Open the app in App Store Connect](https://appstoreconnect.apple.com/apps/$APP_STORE_CONNECT_APP_ID/appstore)" >> "$GITHUB_STEP_SUMMARY"
|
||||
echo "- App Review submission and public release remain manual in App Store Connect." >> "$GITHUB_STEP_SUMMARY"
|
||||
echo "- App Review submission remains manual; Apple will release automatically after approval." >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
disabled:
|
||||
name: Promotion disabled
|
||||
|
||||
Reference in New Issue
Block a user