1- # Prod TestFlight Release
1+ # Prod TestFlight Release — thin caller. The job lives in convos-releases:
2+ # https://github.com/xmtplabs/convos-releases/blob/main/.github/workflows/ios-testflight-prod.yml
23#
3- # Builds the Convos (Prod) scheme for the App Store on every push to `dev`
4- # and uploads the IPA to the prod app's internal TestFlight tester groups.
5- # These builds are signed with the prod bundle ID (org.convos.ios) and talk
6- # to the prod backend (config.prod.json) - testers are exercising prod.
7- #
8- # PR ad-hoc builds (firebase-pr.yml) are unaffected: they keep the dev bundle
9- # ID, the dev network, and Firebase App Distribution.
10- #
11- # Concurrency: only the latest commit's build completes. If a new commit
12- # lands while a previous build is running, the in-flight build is cancelled.
13- #
14- # Required repository secrets:
15- # CONVOS_CERTIFICATES_TOKEN - GitHub PAT with read access to
16- # xmtplabs/convos-certificates
17- # MATCH_PASSWORD - Passphrase for the match repo
18- # encryption
19- # APP_STORE_CONNECT_API_KEY_ID - Key ID for the ASC API key
20- # APP_STORE_CONNECT_ISSUER_ID - Issuer ID for the ASC API key
21- # APP_STORE_CONNECT_API_PRIVATE_KEY - Raw .p8 contents of the ASC API key
22- # CACHIX_AUTH_TOKEN - Auth token for the xmtp Cachix cache
23- #
24- # Optional repository secrets (Sentry crash reporting; prod builds ship
25- # without Sentry when these are unset):
26- # SENTRY_DSN_PROD - DSN baked into prod builds
27- # SENTRY_AUTH_TOKEN - Token for dSYM upload (symbolication)
28- # SENTRY_ORG - Sentry org slug for dSYM upload
29- # SENTRY_PROJECT - Sentry project slug for dSYM upload
30-
4+ # Required repository secrets (passed via `secrets: inherit`):
5+ # CONVOS_CERTIFICATES_TOKEN, MATCH_PASSWORD, APP_STORE_CONNECT_API_KEY_ID,
6+ # APP_STORE_CONNECT_ISSUER_ID, APP_STORE_CONNECT_API_PRIVATE_KEY,
7+ # CACHIX_AUTH_TOKEN; optional POSTHOG_API_KEY_PROD, SENTRY_DSN_PROD,
8+ # SENTRY_AUTH_TOKEN, SENTRY_ORG, SENTRY_PROJECT.
319name : Prod TestFlight Release
3210
3311on :
@@ -43,134 +21,14 @@ concurrency:
4321
4422jobs :
4523 testflight_prod :
46- name : Build & upload to TestFlight (Prod)
47- runs-on : warp-macos-latest-arm64-12x
48- timeout-minutes : 45
24+ # Callers must grant what the reusable workflow's job declares — a called
25+ # workflow can't elevate beyond the caller's grant.
4926 permissions :
5027 id-token : write
5128 contents : read
52- env :
53- # Bump both together when Xcode upgrades; the hash is from
54- # `nix store add /Applications/Xcode_NN.N.app`.
55- XCODE_APP_PATH : /Applications/Xcode_26.3.app
56- XCODE_NIX_PATH : /nix/store/x9hdz5mfp44i9b05sswp271jdv68r8vx-Xcode.app
57- steps :
58- - uses : actions/checkout@v6
59- with :
60- # latest_testflight_build_number doesn't need history, but
61- # git rev-list --count HEAD does; the deeper of the two wins.
62- fetch-depth : 0
63-
64- - name : Checkout convos-certificates (match repo)
65- uses : actions/checkout@v6
66- with :
67- repository : xmtplabs/convos-certificates
68- token : ${{ secrets.CONVOS_CERTIFICATES_TOKEN }}
69- path : convos-certificates
70- persist-credentials : false
71-
72- - name : Install Nix
73- uses : nixbuild/nix-quick-install-action@v33
74- with :
75- nix_conf : |
76- experimental-features = nix-command flakes
77-
78- - name : Pin Xcode into /nix/store
79- uses : xmtplabs/xmtp-cache-apple@v1
80- with :
81- xcode-path : ${{ env.XCODE_APP_PATH }}
82- xcode-nix-path : ${{ env.XCODE_NIX_PATH }}
83-
84- # Action installs cachix + substituter; push happens manually after
85- # the build so we can exclude Xcode's closure (EULA + 11 GiB).
86- - name : Set up xmtp Cachix cache
87- uses : cachix/cachix-action@v15
88- with :
89- name : xmtp
90- authToken : ${{ secrets.CACHIX_AUTH_TOKEN }}
91- skipPush : true
92-
93- - name : Snapshot pre-build store paths
94- run : nix path-info --all | sort > /tmp/store-paths-before
95-
96- - name : Enter dev shell
97- uses : nicknovitski/nix-develop@v1
98- with :
99- arguments : " --accept-flake-config --impure"
100-
101- - name : Verify Xcode
102- run : xcodebuild -version
103-
104- - name : Generate Secrets.swift
105- env :
106- BUILD_CONFIGURATION : Prod
107- POSTHOG_API_KEY : ${{ secrets.POSTHOG_API_KEY_PROD }}
108- SENTRY_DSN_PROD : ${{ secrets.SENTRY_DSN_PROD }}
109- run : ./Scripts/generate-secrets-secure.sh
110-
111- - name : Run fastlane testflight_prod
112- env :
113- MATCH_PASSWORD : ${{ secrets.MATCH_PASSWORD }}
114- # CI uses the local match checkout; local dev uses SSH (Matchfile default).
115- MATCH_GIT_URL : ${{ github.workspace }}/convos-certificates
116- APP_STORE_CONNECT_API_KEY_ID : ${{ secrets.APP_STORE_CONNECT_API_KEY_ID }}
117- APP_STORE_CONNECT_ISSUER_ID : ${{ secrets.APP_STORE_CONNECT_ISSUER_ID }}
118- # Passed directly to fastlane's app_store_connect_api_key via key_content;
119- # stays in memory, never written to disk, redacted from logs.
120- APP_STORE_CONNECT_API_PRIVATE_KEY : ${{ secrets.APP_STORE_CONNECT_API_PRIVATE_KEY }}
121- GITHUB_SHA : ${{ github.sha }}
122- GITHUB_REF_NAME : ${{ github.ref_name }}
123- run : fastlane testflight_prod
124-
125- # Sentry symbolicates prod crash reports server-side using the dSYMs
126- # from the archive (gym zips them next to the IPA). Skips cleanly when
127- # the Sentry secrets are not configured.
128- - name : Upload dSYMs to Sentry
129- env :
130- SENTRY_AUTH_TOKEN : ${{ secrets.SENTRY_AUTH_TOKEN }}
131- SENTRY_ORG : ${{ secrets.SENTRY_ORG }}
132- SENTRY_PROJECT : ${{ secrets.SENTRY_PROJECT }}
133- run : |
134- if [ -z "$SENTRY_AUTH_TOKEN" ]; then
135- echo "SENTRY_AUTH_TOKEN not set - skipping dSYM upload."
136- exit 0
137- fi
138- shopt -s nullglob
139- dsym_zips=(build/*.dSYM.zip)
140- if [ ${#dsym_zips[@]} -eq 0 ]; then
141- echo "No dSYM zips found in build/ - nothing to upload."
142- exit 0
143- fi
144- sentry-cli debug-files upload "${dsym_zips[@]}"
145-
146- - name : Upload IPA artifact
147- if : always()
148- uses : actions/upload-artifact@v7
149- with :
150- name : Convos-Prod-TestFlight-${{ github.run_id }}
151- path : build/Convos-Prod-TestFlight.ipa
152- if-no-files-found : warn
153- retention-days : 14
154-
155- # Push paths produced this job, minus Xcode and anything whose
156- # closure references it. Skipped on failure to avoid caching
157- # half-built derivations that would poison subsequent runs.
158- - name : Push new paths to xmtp Cachix
159- if : success()
160- run : |
161- set -e
162- new_paths=$(comm -13 \
163- /tmp/store-paths-before \
164- <(nix path-info --all | sort))
165- if [ -z "$new_paths" ]; then
166- echo "No new paths to push."
167- exit 0
168- fi
169- tainted=$(nix-store --query --referrers-closure "$XCODE_NIX_PATH" | sort -u)
170- to_push=$(comm -23 <(echo "$new_paths" | sort -u) <(echo "$tainted"))
171- if [ -z "$to_push" ]; then
172- echo "Nothing to push after filtering."
173- exit 0
174- fi
175- echo "Pushing $(echo "$to_push" | wc -l | tr -d ' ') paths to xmtp cache"
176- echo "$to_push" | xargs cachix push xmtp
29+ uses : xmtplabs/convos-releases/.github/workflows/ios-testflight-prod.yml@main
30+ with :
31+ runner : warp-macos-latest-arm64-12x
32+ xcode-app-path : /Applications/Xcode_26.3.app
33+ xcode-nix-path : /nix/store/x9hdz5mfp44i9b05sswp271jdv68r8vx-Xcode.app
34+ secrets : inherit
0 commit comments