Skip to content

Add a KMP runner, and let it gate the merge it is checking - #57

Draft
vaishnav-0 wants to merge 1 commit into
mainfrom
ci/kmp-pipeline
Draft

Add a KMP runner, and let it gate the merge it is checking#57
vaishnav-0 wants to merge 1 commit into
mainfrom
ci/kmp-pipeline

Conversation

@vaishnav-0

Copy link
Copy Markdown
Collaborator

Adds a Kotlin Multiplatform runner, for Lascade-Co/geoanimator. Designed in that repository's
ADR 0028,
which carries the full reasoning; this describes what lands here.

What's here

File What it is
.github/workflows/kmp-build-debug.yml The gate and the internal build. 5 jobs.
.github/workflows/kmp-device-tests.yml Emulator run of the device suites, on demand.
triggers/kmp-pr.yml, triggers/kmp-dev-push.yml, triggers/kmp-device-tests.yml Copy-paste triggers, per the convention in CLAUDE.md.

Event types are kmp-debug and kmp-device-tests — distinct from build-debug-apk and
debug-ios, so nothing collides with the existing handlers.

Three deliberate departures from the Android and iOS runners

1. It posts a commit status back, so it can gate a merge. travel-animator-android/CLAUDE.md
already records the problem: the dispatcher in the app repo goes green in under fifteen seconds
while the real build runs here, where GitHub can't surface it as a required check — so a broken
change can be merged past a red pipeline. This runner posts ci/kmp-gate (pending → success/failure)
onto the head SHA using the installation token it already mints to check the source out. That status
is requirable in branch protection.

This is why the triggers send sha, which the existing triggers don't.

2. Its tests block. The iOS runner runs none; geoanimator has 184 test files. The Apple job is
not optional either — RenG publishes no jvm target, so :render has none, and
FramePlanAdapterTest's bake-key assertions are reachable from no JVM suite. It's also unrationed,
because this repository is public and macOS minutes are free. (The disabled workflow it replaces
gated its iOS job behind a label for exactly the cost reason that doesn't apply here.)

3. No local.properties from Infisical. Runners preset ANDROID_HOME, and RenG resolves
anonymously from maven.rohittp.com, so the build needs no repository credentials. Infisical
/Build (staging) carries four keystore values plus TELEGRAM_CHAT_ID, and their names match the
environment variables the Gradle build reads — so there's no remapping step. The Android runner
needs one only because its Infisical names disagree with its build's.

Verification

The workflows can't be tested from a PR — repository_dispatch fires only on the default branch —
so both jobs were rehearsed locally against the source repo instead. All 8 gate steps pass:
qualityCheck, -p build-logic spotlessCheck, the desktop and Android host suites,
assembleDebug, the iOS framework link, the Apple-native tests, and an unsigned iosApp Xcode
build. actionlint is clean apart from two known false positives (its bundled metadata still thinks
create-github-app-token's client-id is invalid — it's actually the current input and app-id is
the deprecated one; and SC2129 style notes on $GITHUB_OUTPUT redirects).

The rehearsal caught two things the first real run would otherwise have: the Android host-test task
is testAndroidHostTest, not androidHostTest; and :ui:iosSimulatorArm64Test fails to compile
because PictureRateTest calls the JVM-only String.format. The latter is excluded with that
measured reason stated inline, and recorded in geoanimator's known gaps rather than worked around.

Before this does anything

  • CENTRAL_DISPATCH_TOKEN in Lascade-Co/geoanimator, and the triggers copied there (done on that
    side already).
  • Infisical project geoanimator, env staging, path /Build.
  • The CI GitHub App needs statuses: write on Lascade-Co/geoanimator, or the post-back 403s
    and the whole point of item 1 is lost. This is the one thing I couldn't verify from outside.

Merging makes it live for anything dispatching kmp-debug; today that's geoanimator alone. Worth
rehearsing once via workflow_dispatch on main after merge, since that's the only rehearsal the
dispatch model allows.

🤖 Generated with Claude Code

geoanimator is Kotlin Multiplatform, so neither existing Android nor iOS runner
fits: one build graph produces the Android APK, the Apple framework and the JVM
test binaries, and bending two single-platform pipelines together would mean
configuring Gradle twice.

Three things here differ from the Android and iOS runners deliberately.

It posts a commit status back to the source repository. travel-animator-android's
own CLAUDE.md records why that matters: the dispatcher in the app repo goes green
in under fifteen seconds while the real build runs here, where GitHub cannot make
it a required check, so a broken change can be merged past a red pipeline. The
runner already mints a repo-scoped installation token to check the source out, so
it can post ci/kmp-gate with the same token. The trigger sends the head SHA for
this reason.

Its tests block. The iOS runner runs none; geoanimator has 184 test files, and the
Apple job is not optional because RenG publishes no jvm target — FramePlanAdapter's
bake-key assertions live in :render's commonTest and are reachable from no JVM
suite. It is also unrationed: this repository is public, so macOS minutes are free.

It reads no local.properties from Infisical. Runners preset ANDROID_HOME, and RenG
resolves anonymously, so the only secrets are the debug keystore and a chat id.
Their Infisical names match the environment variables the Gradle build reads, so
there is no remapping step — the Android runner needs one only because its names
disagree with its build's.

The gate excludes :ui:iosSimulatorArm64Test for a measured reason, stated inline:
that task fails to compile, not to pass.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant