Skip to content

Commit 65bc9da

Browse files
authored
Merge pull request #24 from Azyyyyyy/ci-quick-ios-check
ci: add quick iOS build+test pass and gate WASM deploy on every test
2 parents f3a0b91 + c7224e4 commit 65bc9da

1 file changed

Lines changed: 159 additions & 7 deletions

File tree

.github/workflows/wasm-docker-build.yml

Lines changed: 159 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -85,9 +85,10 @@ jobs:
8585
8686
# Fast JVM run of every `commonTest` case (~30s after warm cache). Runs in
8787
# parallel with `test` to give PR authors near-immediate feedback if a common
88-
# test breaks, without paying the Android emulator boot. The Android job
89-
# remains the deploy gate (`build-wasm-image needs: test`) — a flaky Skia/AWT
90-
# failure on this job cannot, by itself, block a deploy.
88+
# test breaks, without paying the Android emulator boot. Now part of the
89+
# `build-wasm-image` deploy gate (see the `needs:` list on that job), so a
90+
# Skia/AWT flake here must be treated as a real blocker that needs a rerun
91+
# rather than silently ignored.
9192
desktop-test:
9293
name: shared :desktopTest
9394
runs-on: ubuntu-latest
@@ -125,9 +126,11 @@ jobs:
125126
# to obtain a real token, then exercises ApiInterfaceImpl through the
126127
# Phoenix WebSocket + endpoint-proxy paths (LoadClient + CreateAlter).
127128
#
128-
# Not currently a deploy gate (build-wasm-image still needs only `test`).
129-
# Treat as parity with desktop-test: fast PR feedback, surface failures
130-
# via branch protection in the GH UI rather than via `needs:` here.
129+
# Part of the `build-wasm-image` deploy gate (see the `needs:` list on
130+
# that job). A Testcontainers hiccup or a ghcr.io outage pulling the
131+
# backend image therefore blocks WASM deploy until a rerun succeeds —
132+
# that's the accepted trade-off for guaranteeing every WASM ship has
133+
# green end-to-end signal.
131134
# Approximate cost on a cold runner: ~30 s image pull + ~5 s tests.
132135
#
133136
# Requirements satisfied implicitly on `ubuntu-latest`:
@@ -169,8 +172,157 @@ jobs:
169172
shared/build/reports/tests/desktopIntegrationTest/
170173
shared/build/test-results/desktopIntegrationTest/
171174
175+
# Quick iOS build + test pass. Deliberately mirrors the iOS half of
176+
# mobile-release.yml but stops at `xcodebuild build` — no archive, no
177+
# IPA pack, no attest, no upload. Goal is a ~10-15 min warm signal that
178+
# a future mobile release will still compile, plus running the same
179+
# `commonTest` corpus on iOS that `test` runs on Android.
180+
#
181+
# macos-26 for iOS 26 SDK parity with mobile-release (Compose
182+
# Multiplatform 1.11.x emits `UIViewLayoutRegion` refs that only link
183+
# against the iOS 26 SDK — see the runner comment on mobile-release).
184+
# Cache keys deliberately match mobile-release.yml so PR runs warm the
185+
# ~/.konan and CocoaPods caches that the release workflow will re-use.
186+
ios-test:
187+
name: shared :iosSimulatorArm64Test + xcodebuild build
188+
runs-on: macos-26
189+
timeout-minutes: 40
190+
permissions:
191+
contents: read
192+
193+
steps:
194+
- name: Checkout
195+
uses: actions/checkout@v7
196+
197+
- name: Set up JDK
198+
uses: actions/setup-java@v5
199+
with:
200+
distribution: temurin
201+
java-version: '21'
202+
203+
- name: Set up Gradle
204+
uses: gradle/actions/setup-gradle@v5
205+
206+
# Same key as mobile-release.yml so the two workflows share the
207+
# ~/.konan cache slice (~1-2 GB of Kotlin/Native compiler +
208+
# prebuilts). Adding iosSimulatorArm64 here grows the slice by a
209+
# simulator-arch triple; still well under the 10 GB repo quota.
210+
- name: Cache Kotlin/Native (~/.konan)
211+
uses: actions/cache@v6
212+
with:
213+
path: ~/.konan
214+
key: konan-${{ runner.os }}-${{ hashFiles('gradle.properties') }}-${{ hashFiles('**/*.gradle.kts', 'gradle/libs.versions.toml') }}
215+
restore-keys: |
216+
konan-${{ runner.os }}-${{ hashFiles('gradle.properties') }}-
217+
konan-${{ runner.os }}-
218+
219+
# Same key as mobile-release.yml. The cache-hit output gates
220+
# whether `pod install` needs `--repo-update`.
221+
- name: Cache CocoaPods
222+
id: pods-cache
223+
uses: actions/cache@v6
224+
with:
225+
path: |
226+
~/.cocoapods
227+
~/Library/Caches/CocoaPods
228+
iosApp/Pods
229+
key: cocoapods-${{ runner.os }}-${{ hashFiles('iosApp/Podfile.lock') }}
230+
restore-keys: |
231+
cocoapods-${{ runner.os }}-
232+
233+
# Same prep as mobile-release but without `-PappVersion.*` flags —
234+
# CI doesn't need release-shaped version stamps. writeIosVersionXcconfig
235+
# is still needed so the Podfile's post_install hook can chain
236+
# Version.generated.xcconfig into every Pods xcconfig at pod-install
237+
# time; generateDummyFramework is required or pod install fails with
238+
# "Kotlin framework 'shared' doesn't exist yet".
239+
- name: Prepare KMP iOS artifacts
240+
run: |
241+
set -euo pipefail
242+
./gradlew :shared:writeIosVersionXcconfig :shared:generateDummyFramework --stacktrace
243+
244+
- name: pod install
245+
working-directory: iosApp
246+
run: |
247+
set -euo pipefail
248+
if [ "${{ steps.pods-cache.outputs.cache-hit }}" = "true" ]; then
249+
echo "CocoaPods cache hit — skipping --repo-update"
250+
pod install
251+
else
252+
echo "CocoaPods cache miss — running pod install --repo-update"
253+
pod install --repo-update
254+
fi
255+
256+
# Fast smoke compile of the iOS test source set. Analog to the
257+
# `Compile instrumented tests` step in the Android job — fails fast
258+
# on source-set / cinterop / pod-drift issues before we pay
259+
# simulator boot cost in the test step below.
260+
#
261+
# `-Pkotlin.incremental.native=false` avoids a known Kotlin/Native
262+
# compiler assertion the linker hits on iosSimulatorArm64 Debug/Test
263+
# klibs:
264+
# Failed to build cache for [...]/multiplatform-markdown-renderer[...]
265+
# java.lang.AssertionError: Lowering ReturnsInsertion: phases
266+
# [Enums] are required, but not satisfied
267+
# The Kotlin compiler itself suggests this flag as the workaround.
268+
# Doesn't affect mobile-release.yml which uses linkReleaseFramework
269+
# (a different phase pipeline).
270+
- name: Compile iOS simulator tests
271+
run: ./gradlew :shared:compileTestKotlinIosSimulatorArm64 -Pkotlin.incremental.native=false --stacktrace
272+
273+
# Build the Xcode target for Simulator arch. Skips archive, IPA
274+
# pack, signing, and attestation — the pieces mobile-release adds
275+
# on top. On macos-26 Apple Silicon the Simulator arch is
276+
# `iosSimulatorArm64`, same LLVM target family as the `iosArm64`
277+
# device build mobile-release uses, so Swift + KMP + linker errors
278+
# surface identically. Difference vs mobile-release is limited to
279+
# signing/entitlements (skipped) and thin-binary packaging (n/a
280+
# for `build`).
281+
#
282+
# `ARCHS=arm64 ONLY_ACTIVE_ARCH=YES` pins the build to the
283+
# iosSimulatorArm64 slice. Without them, `generic/platform=iOS
284+
# Simulator` builds a fat slice on Apple Silicon (arm64 + x86_64)
285+
# and Compose Multiplatform's `syncPodComposeResourcesForIos`
286+
# aborts with "Unknown iOS simulator arch: 'x86_64'" — that arch
287+
# slice isn't in the KMP plugin's whitelist because we don't
288+
# declare an iosX64 target in shared/build.gradle.kts.
289+
- name: xcodebuild build (Simulator)
290+
run: |
291+
set -euo pipefail
292+
xcodebuild build \
293+
-workspace iosApp/iosApp.xcworkspace \
294+
-scheme iosApp \
295+
-configuration Debug \
296+
-sdk iphonesimulator \
297+
-destination "generic/platform=iOS Simulator" \
298+
ARCHS=arm64 \
299+
ONLY_ACTIVE_ARCH=YES \
300+
CODE_SIGNING_ALLOWED=NO \
301+
CODE_SIGNING_REQUIRED=NO \
302+
CODE_SIGN_IDENTITY="" \
303+
CODE_SIGN_ENTITLEMENTS="" \
304+
DEVELOPMENT_TEAM=""
305+
306+
# Direct KMP analog of :shared:connectedAndroidDeviceTest — same
307+
# commonTest source set, compiled for iosSimulatorArm64, launched
308+
# on a Kotlin/Native-managed simulator. Iterates the same test
309+
# corpus across both mobile targets. Same -Pkotlin.incremental.native
310+
# workaround as the smoke step; the linker (linkDebugTestIosSimulatorArm64)
311+
# is what actually trips the [Enums] phase assertion.
312+
- name: Run :shared:iosSimulatorArm64Test
313+
run: ./gradlew :shared:iosSimulatorArm64Test -Pkotlin.incremental.native=false --stacktrace
314+
315+
- name: Upload test reports
316+
if: always()
317+
uses: actions/upload-artifact@v7
318+
with:
319+
name: shared-ios-test-report
320+
path: |
321+
shared/build/reports/tests/iosSimulatorArm64Test/
322+
shared/build/test-results/iosSimulatorArm64Test/
323+
172324
build-wasm-image:
173-
needs: test
325+
needs: [test, desktop-test, integration-test, ios-test]
174326
if: github.event_name == 'push'
175327
runs-on: ubuntu-latest
176328
permissions:

0 commit comments

Comments
 (0)