1+ name : Sentry Snapshots Upload
2+
3+ on :
4+ push :
5+ branches : [main]
6+ pull_request :
7+ branches : [main]
8+ paths : [ios/**, .github/workflows/ios*, .github/scripts/ios/**]
9+
10+ defaults :
11+ run :
12+ working-directory : ./ios
13+
14+ env :
15+ DERIVED_DATA_PATH : ${{ github.workspace }}/DerivedData-snapshot-upload
16+ SNAPSHOT_UPLOAD_BASE_DIR : ${{ github.workspace }}/ios/snapshot-images
17+ BUILD_PRODUCTS_ARCHIVE : ${{ github.workspace }}/snapshot-build-products.tar.gz
18+ XCTESTRUN_FILENAME : HackerNewsSnapshotTests.xctestrun
19+
20+ jobs :
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 :
24+ runs-on : macos-26
25+
26+ env :
27+ TEST_RUNNER_XCODE_RUNNING_FOR_PREVIEWS : 1
28+
29+ steps :
30+ - name : Checkout
31+ uses : actions/checkout@v6
32+ with :
33+ fetch-depth : 0
34+
35+ - name : Select Xcode 26.4
36+ run : sudo xcode-select -s /Applications/Xcode_26.4.app
37+
38+ - name : Set up Ruby env
39+ uses : ruby/setup-ruby@v1
40+ with :
41+ ruby-version : 3.3.10
42+ bundler-cache : true
43+
44+ - name : Setup gems
45+ run : exec ../.github/scripts/ios/setup.sh
46+
47+ - name : Prepare DerivedData directory
48+ run : mkdir -p "${DERIVED_DATA_PATH}"
49+
50+ - name : Cache Swift Package Manager
51+ uses : actions/cache@v4
52+ with :
53+ path : |
54+ ~/Library/Caches/org.swift.swiftpm
55+ ${{ env.DERIVED_DATA_PATH }}/SourcePackages
56+ key : ${{ runner.os }}-spm-${{ hashFiles('**/Package.resolved') }}
57+ restore-keys : ${{ runner.os }}-spm-
58+
59+ - name : Build snapshot tests for distribution
60+ run : |
61+ set -o pipefail && xcodebuild build-for-testing \
62+ -scheme HackerNews \
63+ -sdk iphonesimulator \
64+ -destination 'platform=iOS Simulator,name=iPhone 17 Pro Max,arch=arm64' \
65+ -only-testing:HackerNewsTests/HackerNewsSnapshotTest \
66+ -resultBundlePath ../SnapshotBuildForTesting.xcresult \
67+ -derivedDataPath "${DERIVED_DATA_PATH}" \
68+ -skipPackagePluginValidation \
69+ ONLY_ACTIVE_ARCH=YES \
70+ TARGETED_DEVICE_FAMILY="1,2" \
71+ SUPPORTS_MACCATALYST=NO \
72+ CODE_SIGNING_ALLOWED=NO \
73+ COMPILATION_CACHING=YES \
74+ EAGER_LINKING=YES \
75+ FUSE_BUILD_SCRIPT_PHASES=YES \
76+ | xcpretty
77+
78+ - name : Normalize xctestrun path
79+ run : |
80+ XCTESTRUN_SOURCE="$(find "${DERIVED_DATA_PATH}/Build/Products" -name '*.xctestrun' -print -quit)"
81+ if [ -z "${XCTESTRUN_SOURCE}" ]; then
82+ echo "No .xctestrun file found under ${DERIVED_DATA_PATH}/Build/Products" >&2
83+ exit 1
84+ fi
85+
86+ cp "${XCTESTRUN_SOURCE}" "${DERIVED_DATA_PATH}/Build/Products/${XCTESTRUN_FILENAME}"
87+
88+ - name : Archive test products
89+ run : tar -C "${DERIVED_DATA_PATH}/Build" -czf "${BUILD_PRODUCTS_ARCHIVE}" Products
90+
91+ - name : Upload build products artifact
92+ uses : actions/upload-artifact@v7
93+ with :
94+ name : snapshot-build-products
95+ path : ${{ env.BUILD_PRODUCTS_ARCHIVE }}
96+ if-no-files-found : error
97+
98+ # One simulator per runner. Each job extracts the shared build products,
99+ # runs the snapshot tests, and uploads its PNGs as a per-sim artifact so
100+ # the final job can aggregate them.
101+ generate_snapshots :
102+ runs-on : macos-26
103+ needs : build_for_testing
104+
105+ strategy :
106+ fail-fast : false
107+ matrix :
108+ include :
109+ - simulator_name : iPhone 17 Pro Max
110+ slug : iphone-17-pro-max
111+ - simulator_name : iPhone 17e
112+ slug : iphone-17e
113+ - simulator_name : iPad Air 11-inch (M4)
114+ slug : ipad-air-11-inch-m4
115+
116+ env :
117+ TEST_RUNNER_XCODE_RUNNING_FOR_PREVIEWS : 1
118+ # Keep this base path in sync with the workflow-level SNAPSHOT_UPLOAD_BASE_DIR.
119+ TEST_RUNNER_SNAPSHOTS_EXPORT_DIR : ${{ github.workspace }}/ios/snapshot-images/${{ matrix.slug }}
120+
121+ steps :
122+ - name : Checkout
123+ uses : actions/checkout@v6
124+ with :
125+ fetch-depth : 0
126+
127+ - name : Select Xcode 26.4
128+ run : sudo xcode-select -s /Applications/Xcode_26.4.app
129+
130+ - name : Set up Ruby env
131+ uses : ruby/setup-ruby@v1
132+ with :
133+ ruby-version : 3.3.10
134+ bundler-cache : true
135+
136+ - name : Setup gems
137+ run : exec ../.github/scripts/ios/setup.sh
138+
139+ - name : Download build products artifact
140+ uses : actions/download-artifact@v5
141+ with :
142+ name : snapshot-build-products
143+ path : ${{ github.workspace }}
144+
145+ - name : Extract test products
146+ run : |
147+ mkdir -p "${DERIVED_DATA_PATH}/Build"
148+ tar -C "${DERIVED_DATA_PATH}/Build" -xzf "${BUILD_PRODUCTS_ARCHIVE}"
149+
150+ - name : Boot simulator
151+ run : xcrun simctl boot "${{ matrix.simulator_name }}" || true
152+
153+ - name : Prepare snapshot export directory
154+ run : mkdir -p "${TEST_RUNNER_SNAPSHOTS_EXPORT_DIR}"
155+
156+ - name : Generate snapshot images
157+ run : |
158+ set -o pipefail && xcodebuild test-without-building \
159+ -xctestrun "${DERIVED_DATA_PATH}/Build/Products/${XCTESTRUN_FILENAME}" \
160+ -destination 'platform=iOS Simulator,name=${{ matrix.simulator_name }},arch=arm64' \
161+ -only-testing:HackerNewsTests/HackerNewsSnapshotTest \
162+ -resultBundlePath "../SnapshotResults-${{ matrix.slug }}.xcresult" \
163+ | xcpretty
164+
165+ - name : Upload snapshots artifacts
166+ uses : actions/upload-artifact@v7
167+ with :
168+ name : snapshots-${{ matrix.slug }}
169+ path : ${{ env.TEST_RUNNER_SNAPSHOTS_EXPORT_DIR }}
170+ if-no-files-found : error
171+
172+ # Aggregate every per-sim artifact and upload the combined set to Sentry
173+ # in a single fastlane invocation.
174+ upload_snapshots :
175+ runs-on : macos-26
176+ needs : generate_snapshots
177+
178+ steps :
179+ - name : Checkout
180+ uses : actions/checkout@v6
181+ with :
182+ fetch-depth : 0
183+
184+ - name : Set up Ruby env
185+ uses : ruby/setup-ruby@v1
186+ with :
187+ ruby-version : 3.3.10
188+ bundler-cache : true
189+
190+ - name : Setup gems
191+ run : exec ../.github/scripts/ios/setup.sh
192+
193+ - name : Download generated snapshots
194+ uses : actions/download-artifact@v5
195+ with :
196+ path : ${{ env.SNAPSHOT_UPLOAD_BASE_DIR }}
197+ pattern : " snapshots-*"
198+
199+ - name : List aggregated snapshot files
200+ run : |
201+ echo "Generated snapshot files:"
202+ find "${SNAPSHOT_UPLOAD_BASE_DIR}" -type f | sort
203+ echo
204+ echo "Total PNG images: $(find "${SNAPSHOT_UPLOAD_BASE_DIR}" -type f -name '*.png' | wc -l | tr -d ' ')"
205+
206+ - name : Upload snapshots to Sentry
207+ env :
208+ SENTRY_SENTRY_AUTH_TOKEN : ${{ secrets.SENTRY_SENTRY_AUTH_TOKEN }}
209+ run : bundle exec fastlane ios upload_sentry_snapshots path:"${SNAPSHOT_UPLOAD_BASE_DIR}"
0 commit comments