Skip to content

Commit 9cf7428

Browse files
committed
ci(ios): Add Sentry snapshot upload workflows
Add dedicated GitHub Actions workflows for uploading SnapshotPreviews and Swift Snapshot Testing outputs to Sentry. Remove the old Sentry upload step from the Emerge snapshots workflow so each pipeline has a single responsibility.
1 parent 9f535cd commit 9cf7428

4 files changed

Lines changed: 305 additions & 5 deletions

File tree

.github/scripts/ios/setup.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@
33
# Install Ruby Bundler
44
gem install bundler
55

6-
# Install Ruby Gems
6+
# Install Ruby Gems for Fastlane and XCPretty
77
bundle install

.github/workflows/ios_emerge_upload_snapshots.yml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,3 @@ jobs:
8888
--client-library swift-snapshot-testing \
8989
--project-root . \
9090
--debug
91-
- name: Upload snapshots to Sentry
92-
env:
93-
SENTRY_SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_SENTRY_AUTH_TOKEN }}
94-
run: bundle exec fastlane ios upload_sentry_snapshots
Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
name: Sentry Snapshots Upload
2+
3+
on:
4+
push:
5+
branches: [main, cameroncooke/snapshots-ci]
6+
paths: [ios/**, .github/workflows/ios*]
7+
pull_request:
8+
branches: [main, cameroncooke/snapshots-ci]
9+
paths: [ios/**, .github/workflows/ios*]
10+
11+
defaults:
12+
run:
13+
working-directory: ./ios
14+
15+
env:
16+
DERIVED_DATA_PATH: ${{ github.workspace }}/DerivedData-snapshot-upload
17+
SNAPSHOT_UPLOAD_BASE_DIR: ${{ github.workspace }}/ios/snapshot-images
18+
BUILD_PRODUCTS_ARCHIVE: ${{ github.workspace }}/snapshot-build-products.tar.gz
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+
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: 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+
149+
- name: Resolve xctestrun file
150+
run: |
151+
XCTESTRUN_PATH="$(find "${DERIVED_DATA_PATH}/Build/Products" -name '*.xctestrun' -print -quit)"
152+
if [ -z "${XCTESTRUN_PATH}" ]; then
153+
echo "No .xctestrun file found under ${DERIVED_DATA_PATH}/Build/Products" >&2
154+
exit 1
155+
fi
156+
echo "XCTESTRUN_PATH=${XCTESTRUN_PATH}" >> "$GITHUB_ENV"
157+
158+
- name: Boot simulator
159+
run: xcrun simctl boot "${{ matrix.simulator_name }}" || true
160+
161+
- name: Prepare snapshot export directory
162+
run: mkdir -p "${TEST_RUNNER_SNAPSHOTS_EXPORT_DIR}"
163+
164+
- name: Generate snapshot images
165+
run: |
166+
set -o pipefail && xcodebuild test-without-building \
167+
-xctestrun "${XCTESTRUN_PATH}" \
168+
-destination 'platform=iOS Simulator,name=${{ matrix.simulator_name }},arch=arm64' \
169+
-only-testing:HackerNewsTests/HackerNewsSnapshotTest \
170+
-resultBundlePath "../SnapshotResults-${{ matrix.slug }}.xcresult" \
171+
| xcpretty
172+
173+
- 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
215+
run: |
216+
echo "Generated snapshot files:"
217+
find "${SNAPSHOT_UPLOAD_BASE_DIR}" -type f | sort
218+
echo
219+
echo "Total PNG images: $(find "${SNAPSHOT_UPLOAD_BASE_DIR}" -type f -name '*.png' | wc -l | tr -d ' ')"
220+
221+
- name: Upload snapshots to Sentry
222+
env:
223+
SENTRY_SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_SENTRY_AUTH_TOKEN }}
224+
run: bundle exec fastlane ios upload_sentry_snapshots path:"${SNAPSHOT_UPLOAD_BASE_DIR}"
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
name: Sentry Snapshots Upload (Swift Snapshot Testing)
2+
3+
on:
4+
push:
5+
branches: [main]
6+
paths: [ios/**, .github/workflows/ios*]
7+
pull_request:
8+
branches: [main]
9+
paths: [ios/**, .github/workflows/ios*]
10+
11+
jobs:
12+
upload_sentry_swift_snapshots:
13+
runs-on: macos-26
14+
15+
defaults:
16+
run:
17+
working-directory: ./ios
18+
19+
env:
20+
XCODE_RUNNING_FOR_PREVIEWS: 1
21+
# Enables recording mode when running Swift Snapshot Testing tests
22+
TEST_RUNNER_SNAPSHOT_TESTING_RECORD: all
23+
24+
steps:
25+
- name: Checkout
26+
uses: actions/checkout@v6
27+
with:
28+
fetch-depth: 0
29+
30+
- name: Set up Ruby env
31+
uses: ruby/setup-ruby@v1
32+
with:
33+
ruby-version: 3.3.10
34+
bundler-cache: true
35+
36+
- name: Setup gems
37+
run: exec ../.github/scripts/ios/setup.sh
38+
39+
- name: Boot iPhone simulator
40+
run: xcrun simctl boot "iPhone 17 Pro Max" || true
41+
42+
- name: Cache Swift Package Manager
43+
uses: actions/cache@v4
44+
with:
45+
path: |
46+
~/Library/Caches/org.swift.swiftpm
47+
~/Library/Developer/Xcode/DerivedData/HackerNews-*/SourcePackages
48+
key: ${{ runner.os }}-spm-${{ hashFiles('**/Package.resolved') }}
49+
restore-keys: ${{ runner.os }}-spm-
50+
51+
- name: Generate snapshot images
52+
# continue-on-error is needed because Swift Snapshot Testing treats snapshot recording as test failures
53+
continue-on-error: true
54+
run: |
55+
set -o pipefail && xcodebuild test \
56+
-scheme HackerNews \
57+
-sdk iphonesimulator \
58+
-destination 'platform=iOS Simulator,name=iPhone 17 Pro Max,arch=arm64' \
59+
-only-testing:HackerNewsTests/SwiftSnapshotTest \
60+
-resultBundlePath ../SnapshotResults-swift-snapshots.xcresult \
61+
-skipPackagePluginValidation \
62+
ONLY_ACTIVE_ARCH=YES \
63+
TARGETED_DEVICE_FAMILY=1 \
64+
SUPPORTS_MACCATALYST=NO \
65+
CODE_SIGNING_ALLOWED=NO \
66+
COMPILATION_CACHING=YES \
67+
EAGER_LINKING=YES \
68+
FUSE_BUILD_SCRIPT_PHASES=YES \
69+
| xcpretty
70+
71+
- name: List generated images
72+
run: |
73+
echo "Generated snapshot images:"
74+
ls -1 HackerNewsTests/__Snapshots__/SwiftSnapshotTest/ || echo "No snapshots found"
75+
echo "Total: $(ls -1 HackerNewsTests/__Snapshots__/SwiftSnapshotTest/ 2>/dev/null | wc -l | tr -d ' ') images"
76+
77+
- name: Upload snapshots to Sentry
78+
env:
79+
SENTRY_SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_SENTRY_AUTH_TOKEN }}
80+
run: bundle exec fastlane ios upload_sentry_snapshots_swift_snapshot_testing

0 commit comments

Comments
 (0)