1- name : Sentry iOS Upload (Snapshots)
1+ name : Sentry Snapshots Upload
22
33on :
44 push :
@@ -15,9 +15,12 @@ defaults:
1515env :
1616 DERIVED_DATA_PATH : ${{ github.workspace }}/DerivedData-snapshot-upload
1717 SNAPSHOT_UPLOAD_BASE_DIR : ${{ github.workspace }}/ios/snapshot-images
18+ BUILD_PRODUCTS_ARCHIVE : ${{ github.workspace }}/snapshot-build-products.tar.gz
1819
1920jobs :
20- upload_sentry_snapshots :
21+ # Build the snapshot test bundle once on a single runner, then share the
22+ # products with downstream simulator jobs via an artifact.
23+ build_for_testing :
2124 runs-on : macos-26
2225
2326 env :
4144 - name : Setup gems
4245 run : exec ../.github/scripts/ios/setup.sh
4346
47+ - name : Prepare DerivedData directory
48+ run : mkdir -p "${DERIVED_DATA_PATH}"
49+
4450 - name : Cache Swift Package Manager
4551 uses : actions/cache@v4
4652 with :
5056 key : ${{ runner.os }}-spm-${{ hashFiles('**/Package.resolved') }}
5157 restore-keys : ${{ runner.os }}-spm-
5258
53- - name : Prepare snapshot directories
54- run : |
55- mkdir -p "${SNAPSHOT_UPLOAD_BASE_DIR}/iphone-17-pro-max"
56- mkdir -p "${SNAPSHOT_UPLOAD_BASE_DIR}/iphone-17e"
57- mkdir -p "${SNAPSHOT_UPLOAD_BASE_DIR}/ipad-air-11-inch-m4"
58- mkdir -p "${DERIVED_DATA_PATH}"
59-
60- - name : Boot simulators
61- run : |
62- xcrun simctl boot "iPhone 17 Pro Max" || true
63- xcrun simctl boot "iPhone 17e" || true
64- xcrun simctl boot "iPad Air 11-inch (M4)" || true
65-
66- - name : Build snapshot tests
59+ - name : Build snapshot tests for distribution
6760 run : |
6861 set -o pipefail && xcodebuild build-for-testing \
6962 -scheme HackerNews \
8275 FUSE_BUILD_SCRIPT_PHASES=YES \
8376 | xcpretty
8477
78+ - name : Archive test products
79+ run : tar -C "${DERIVED_DATA_PATH}/Build" -czf "${BUILD_PRODUCTS_ARCHIVE}" Products
80+
81+ - name : Upload build products artifact
82+ uses : actions/upload-artifact@v7
83+ with :
84+ name : snapshot-build-products
85+ path : ${{ env.BUILD_PRODUCTS_ARCHIVE }}
86+ if-no-files-found : error
87+
88+ # One simulator per runner. Each job extracts the shared build products,
89+ # runs the snapshot tests, and uploads its PNGs as a per-sim artifact so
90+ # the final job can aggregate them.
91+ generate_snapshots :
92+ runs-on : macos-26
93+ needs : build_for_testing
94+
95+ strategy :
96+ fail-fast : false
97+ matrix :
98+ include :
99+ - simulator_name : iPhone 17 Pro Max
100+ slug : iphone-17-pro-max
101+ - simulator_name : iPhone 17e
102+ slug : iphone-17e
103+ - simulator_name : iPad Air 11-inch (M4)
104+ slug : ipad-air-11-inch-m4
105+
106+ env :
107+ XCODE_RUNNING_FOR_PREVIEWS : 1
108+
109+ steps :
110+ - name : Checkout
111+ uses : actions/checkout@v6
112+ with :
113+ fetch-depth : 0
114+
115+ - name : Select Xcode 26.4
116+ run : sudo xcode-select -s /Applications/Xcode_26.4.app
117+
118+ - name : Set up Ruby env
119+ uses : ruby/setup-ruby@v1
120+ with :
121+ ruby-version : 3.3.10
122+ bundler-cache : true
123+
124+ - name : Setup gems
125+ run : exec ../.github/scripts/ios/setup.sh
126+
127+ - name : Download build products artifact
128+ uses : actions/download-artifact@v5
129+ with :
130+ name : snapshot-build-products
131+ path : ${{ github.workspace }}
132+
133+ - name : Extract test products
134+ # Restore the build products back under the same DerivedData path used
135+ # during build-for-testing. The generated .xctestrun file references
136+ # those product locations directly, so test-without-building does not
137+ # need an extra -derivedDataPath flag as long as we recreate the same
138+ # layout on each runner.
139+ run : |
140+ mkdir -p "${DERIVED_DATA_PATH}/Build"
141+ tar -C "${DERIVED_DATA_PATH}/Build" -xzf "${BUILD_PRODUCTS_ARCHIVE}"
142+
143+ - name : Set snapshot export directory
144+ # Xcode strips the TEST_RUNNER_ prefix and forwards the rest into the
145+ # test runner process, which is how the snapshot tests discover where
146+ # to write PNGs.
147+ run : echo "TEST_RUNNER_SNAPSHOTS_EXPORT_DIR=${SNAPSHOT_UPLOAD_BASE_DIR}/${{ matrix.slug }}" >> "$GITHUB_ENV"
148+
85149 - name : Resolve xctestrun file
86150 run : |
87151 XCTESTRUN_PATH="$(find "${DERIVED_DATA_PATH}/Build/Products" -name '*.xctestrun' -print -quit)"
@@ -91,40 +155,63 @@ jobs:
91155 fi
92156 echo "XCTESTRUN_PATH=${XCTESTRUN_PATH}" >> "$GITHUB_ENV"
93157
94- - name : Generate snapshot images (iPhone 17 Pro Max)
95- env :
96- TEST_RUNNER_SNAPSHOTS_EXPORT_DIR : ${{ env.SNAPSHOT_UPLOAD_BASE_DIR }}/iphone-17-pro-max
97- run : |
98- set -o pipefail && xcodebuild test-without-building \
99- -xctestrun "${XCTESTRUN_PATH}" \
100- -destination 'platform=iOS Simulator,name=iPhone 17 Pro Max,arch=arm64' \
101- -only-testing:HackerNewsTests/HackerNewsSnapshotTest \
102- -resultBundlePath ../SnapshotResults-iphone-17-pro-max.xcresult \
103- | xcpretty
158+ - name : Boot simulator
159+ run : xcrun simctl boot "${{ matrix.simulator_name }}" || true
104160
105- - name : Generate snapshot images (iPhone 17e)
106- env :
107- TEST_RUNNER_SNAPSHOTS_EXPORT_DIR : ${{ env.SNAPSHOT_UPLOAD_BASE_DIR }}/iphone-17e
108- run : |
109- set -o pipefail && xcodebuild test-without-building \
110- -xctestrun "${XCTESTRUN_PATH}" \
111- -destination 'platform=iOS Simulator,name=iPhone 17e,arch=arm64' \
112- -only-testing:HackerNewsTests/HackerNewsSnapshotTest \
113- -resultBundlePath ../SnapshotResults-iphone-17e.xcresult \
114- | xcpretty
161+ - name : Prepare snapshot export directory
162+ run : mkdir -p "${TEST_RUNNER_SNAPSHOTS_EXPORT_DIR}"
115163
116- - name : Generate snapshot images (iPad)
117- env :
118- TEST_RUNNER_SNAPSHOTS_EXPORT_DIR : ${{ env.SNAPSHOT_UPLOAD_BASE_DIR }}/ipad-air-11-inch-m4
164+ - name : Generate snapshot images
119165 run : |
120166 set -o pipefail && xcodebuild test-without-building \
121167 -xctestrun "${XCTESTRUN_PATH}" \
122- -destination 'platform=iOS Simulator,name=iPad Air 11-inch (M4) ,arch=arm64' \
168+ -destination 'platform=iOS Simulator,name=${{ matrix.simulator_name }} ,arch=arm64' \
123169 -only-testing:HackerNewsTests/HackerNewsSnapshotTest \
124- -resultBundlePath ../SnapshotResults-ipad-air-11-inch-m4. xcresult \
170+ -resultBundlePath " ../SnapshotResults-${{ matrix.slug }}. xcresult" \
125171 | xcpretty
126172
127173 - name : List generated snapshot files
174+ run : |
175+ echo "Generated snapshot files for ${{ matrix.simulator_name }}:"
176+ find "${TEST_RUNNER_SNAPSHOTS_EXPORT_DIR}" -type f | sort
177+ echo
178+ echo "Total PNG images: $(find "${TEST_RUNNER_SNAPSHOTS_EXPORT_DIR}" -type f -name '*.png' | wc -l | tr -d ' ')"
179+
180+ - name : Upload generated snapshots
181+ uses : actions/upload-artifact@v7
182+ with :
183+ name : ${{ matrix.slug }}
184+ path : ${{ env.TEST_RUNNER_SNAPSHOTS_EXPORT_DIR }}
185+ if-no-files-found : error
186+
187+ # Aggregate every per-sim artifact and upload the combined set to Sentry
188+ # in a single fastlane invocation.
189+ upload_snapshots :
190+ runs-on : macos-26
191+ needs : generate_snapshots
192+
193+ steps :
194+ - name : Checkout
195+ uses : actions/checkout@v6
196+ with :
197+ fetch-depth : 0
198+
199+ - name : Set up Ruby env
200+ uses : ruby/setup-ruby@v1
201+ with :
202+ ruby-version : 3.3.10
203+ bundler-cache : true
204+
205+ - name : Setup gems
206+ run : exec ../.github/scripts/ios/setup.sh
207+
208+ - name : Download generated snapshots
209+ uses : actions/download-artifact@v5
210+ with :
211+ path : ${{ env.SNAPSHOT_UPLOAD_BASE_DIR }}
212+ pattern : " *"
213+
214+ - name : List aggregated snapshot files
128215 run : |
129216 echo "Generated snapshot files:"
130217 find "${SNAPSHOT_UPLOAD_BASE_DIR}" -type f | sort
0 commit comments