Skip to content

ci: pin every version CI depends on - #17

Draft
dsnsgithub wants to merge 6 commits into
mainfrom
claude/ci-dependency-pinning-tqyhb9
Draft

ci: pin every version CI depends on#17
dsnsgithub wants to merge 6 commits into
mainfrom
claude/ci-dependency-pinning-tqyhb9

Conversation

@dsnsgithub

@dsnsgithub dsnsgithub commented Aug 20, 2026

Copy link
Copy Markdown
Owner

CI was breaking on changes nobody made here: an upstream MMKVCore release, a new Xcode on the macOS image, a floating action tag. Pin everything CI depends on so a green run stays reproducible.

Workflows

  • Pinned runner images instead of the *-latest aliases, which follow new OS releases and swap Xcode, simulator runtimes and CocoaPods underneath an unchanged build: macos-26 for iOS, ubuntu-24.04 elsewhere.
  • Actions pinned to exact patch tags (@v6.1.0) instead of floating majors (@v6). futureware-tech/simulator-action only publishes major tags, so @v4 is the most specific pin available there.
  • Tool versions written inline at their use site: Bun 1.3.14, Temurin 21.0.11, Xcode 26.6, CocoaPods 1.17.0, Maestro 2.8.0.
  • Xcode is selected explicitly rather than inherited from the image default. xcode-select --switch exits non-zero on a missing path, so a retired Xcode fails the job outright instead of silently falling through to whatever the image ships.
  • CocoaPods is installed at the pinned version rather than taken from the image.
  • Maestro is fetched as an immutable release asset instead of piping get.maestro.mobile.dev, which installs whatever is newest from a script that can itself change.
  • The iOS simulator runtime (26.5) is pinned alongside the device model.

JavaScript dependencies

  • Exact versions everywhere, no carets or tildes. bun ci is already frozen, but ranges still let a plain bun install drift, and they silently break patchedDependencies: react-native-mmkv floating to 3.3.4 would drop the app-group path patch with nothing failing until a widget misbehaved.
  • turbo: latest2.7.1, the version the lockfile had already resolved.
  • EAS CLI pinned to 22.0.0 in the mobile scripts and eas.json.
  • bun.lock workspace ranges updated to match; no resolutions changed.

Native dependencies

Audited every podspec and build.gradle in the tree for versions resolved from a remote registry rather than from node_modules.

  • PostHog was the one real gap. posthog-react-native-session-replay's podspec asks for PostHog ~> 3.21, satisfied by 115 published versions and currently resolving to 3.69.8. Now pinned in the app target's Podfile, same mechanism as MMKVCore.
  • Everything else was already deterministic: react-native-mmkv compiles MMKV from vendored source, NotifeeCore builds from a local subspec, and Quick/Nimble/OHHTTPStubs/ExpoModulesTestCore are test_spec-only so they are never installed.
  • On Android, app.notifee:core:+ resolves against notifee's bundled file repo, which ships exactly one version and is published to neither Maven Central nor Google Maven; com.facebook.react:react-native:+ is substituted by the RN Gradle plugin to the exact react-native version; org.webkit:android-jsc:+ has a single Maven Central artifact from 2015; and the Kotlin/Compose/Coil/Fresco interpolations read properties fixed inside their own packages.

Merge with main

main moved android builds to ubuntu (#14) and setup-java to v5 (#15) while this was open. Both decisions are kept, with the pinning re-applied on top: android runs on ubuntu-24.04 rather than ubuntu-latest, and setup-java is @v5.7.0 rather than the floating @v5.

Verification

Every job is green on this branch: lint, typecheck, test-web, test-android on ubuntu-24.04, and the full iOS run on macos-26 including the Maestro suite. Between them they exercise the Xcode select, the CocoaPods pin, the Maestro download, the simulator runtime pin, and the PostHog pod resolving during pod install.

What still isn't pinned

  • CocoaPods binds only while the image ships nothing newer. gem install cocoapods -v 1.17.0 guarantees that version is present, but pod resolves to the newest installed, and EAS calls pod from PATH. Today macos-26 ships exactly 1.17.0 so the pin holds; a future image shipping newer CocoaPods would win.
  • Node.js comes from the runner image. Pinning the image label pins it in practice, but patch bumps land.
  • Ruby likewise; only the CocoaPods gem on top of it is pinned.
  • Runner images are eventually retired. When macos-26 or ubuntu-24.04 goes, the job fails immediately with an unknown-label error rather than drifting — that is the intended failure mode, but it does mean re-pinning Xcode and CocoaPods to whatever the next image ships.
  • Android SDK components Gradle downloads come from what the generated android/ project requests, which is set by the pinned Expo/React Native versions.

Claude-Session: https://claude.ai/code/session_01QRXxEuKNzgWvK2RxJjQ8TC

CI was breaking on changes nobody made here: an upstream MMKVCore release,
a new Xcode on the macOS image, a floating action tag. Pin the whole
toolchain so a green run stays reproducible.

Workflows:
- runs-on macos-26 / ubuntu-24.04 instead of the *-latest aliases, which
  follow new OS releases and swap Xcode, simulator runtimes and CocoaPods
  underneath an unchanged build
- third-party actions pinned to full commit SHAs instead of floating major
  tags, with the version in a trailing comment
- move shared toolchain setup into .github/actions/setup so all pinned
  versions live in one file: Bun 1.3.14, Node 24.18.0 (macOS) / 22.23.2
  (Ubuntu), Temurin 21.0.11+10, Xcode 26.6, CocoaPods 1.17.0, Maestro 2.8.0
- select Xcode explicitly rather than inheriting the image default, and fail
  with the list of installed versions if the pinned one is gone
- force the pinned CocoaPods through a PATH shim; EAS resolves `pod` from
  PATH, so pinning the gem alone loses to a newer preinstalled version
- fetch Maestro as an immutable release asset instead of piping
  get.maestro.mobile.dev, which installs whatever is newest
- pin the iOS simulator runtime (26.5) as well as the device model

Dependencies:
- exact versions everywhere, no carets or tildes. bun ci is already frozen,
  but ranges still let a plain `bun install` drift, and they silently break
  patchedDependencies: react-native-mmkv floating to 3.3.4 would drop the
  app-group path patch with nothing failing until a widget misbehaved
- turbo: latest -> 2.7.1 (the version the lockfile already resolved)
- pin EAS CLI to 22.0.0 in the mobile scripts and eas.json
- bun.lock workspace ranges updated to match; no resolutions changed

Adds .github/README.md documenting where each pin lives, how to bump one,
and what still can't be pinned.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QRXxEuKNzgWvK2RxJjQ8TC
@vercel

vercel Bot commented Aug 20, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
scheduli Ready Ready Preview Aug 20, 2026 11:36pm

@dsnsgithub
dsnsgithub marked this pull request as draft August 20, 2026 12:04
Use exact patch tags (@v6.1.0) instead of commit SHAs. A tag can be
force-moved by the maintainer where a SHA cannot, but exact tags are far
easier to read and bump and still close the floating-major hole that broke
CI in the first place. futureware-tech/simulator-action only publishes
major tags, so @v4 stays as the most specific pin available.

Drop the .github/actions/setup composite and inline its steps, keeping the
action list to the five already in use. Each workflow now declares its own
tool versions in a top-level env: block.

This also drops actions/setup-node, so Node is no longer pinned explicitly.
Pinning the runner image label pins it in practice — the major version
can't change within a given image — but patch bumps land. Recorded in
.github/README.md alongside the other things that can't be fully pinned.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QRXxEuKNzgWvK2RxJjQ8TC
Audited every native dependency on both platforms.

iOS, resolved from the CocoaPods CDN rather than node_modules:
- PostHog was the one real gap. posthog-react-native-session-replay's
  podspec asks for `~> 3.21`, which today resolves to 3.69.8 — 115
  published versions satisfy that range and the newest landed yesterday.
  Pin it in the app target's Podfile, same mechanism as MMKVCore.
- Everything else is local: react-native-mmkv compiles MMKV from vendored
  source, NotifeeCore builds from a local subspec, and Quick/Nimble/
  OHHTTPStubs/ExpoModulesTestCore are test_spec-only so they are never
  installed.

Android, verified already deterministic:
- app.notifee:core:+ resolves against notifee's bundled file repo, which
  ships exactly one version (202108261754) and is published to no remote.
- com.facebook.react:react-native:+ is substituted by the RN Gradle plugin
  to the exact react-native version.
- org.webkit:android-jsc:+ has a single Maven Central artifact (r174650,
  2015) and jsc-android is not installed.
- Kotlin/Compose/Coil/Fresco versions interpolate from properties fixed
  inside their own packages.

Verified the Podfile change by running `expo prebuild --platform ios`:
the pin lands inside `target 'Scheduli'` and the MMKV block is intact.

Also drops the comments from the workflows and the .github/README.md.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QRXxEuKNzgWvK2RxJjQ8TC
`21.0.11+10` came from the Adoptium API release name, but setup-java
matches against its own list where the same build reads
`21.0.11+10.0.LTS`. When the requested version carries build metadata
setup-java requires an exact compareBuild match, so `+10` never matched
`+10.0.LTS` and every macOS job failed at Install Java.

Use `21.0.11`. With no build metadata setup-java falls through to a
normal semver range, which ignores build metadata and resolves to the
single available 21.0.11 build. Still an exact JDK patch pin, and it does
not depend on how Adoptium renders the build suffix — which is what broke.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QRXxEuKNzgWvK2RxJjQ8TC
main moved android builds to ubuntu (#14) and setup-java to v5 (#15),
both touching the two android workflows this branch rewrote.

Kept main's decisions and re-applied the pinning on top:
- android runners are ubuntu, pinned to ubuntu-24.04 rather than
  ubuntu-latest
- setup-java is v5, pinned to v5.7.0 rather than the floating v5
- test-ios.yml carries setup-java too, so it moves to v5.7.0 as well for
  consistency, though it did not conflict

Verified before resolving that java-version 21.0.11 still holds: v5.7.0's
isVersionSatisfies keeps the same semantics for a range without build
metadata, and Temurin jdk-21.0.11+10 is published for linux x64, which
the move to ubuntu now requires.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QRXxEuKNzgWvK2RxJjQ8TC
Drop the per-workflow env: blocks. Every version now sits at its use
site, like the action tags already did.

Select Xcode is one line: xcode-select --switch already exits non-zero on
a path that is not there, so the existence check and the error message
were restating the failure it produces anyway.

Pin CocoaPods is one line. The PATH shim it replaces only mattered if a
future runner image ships CocoaPods newer than the pin, since `pod`
resolves to the newest installed version.

Install Maestro is three lines: fetch the pinned release asset, unzip,
add to PATH. The launcher is a Gradle start script that resolves its own
location, so extracting outside ~/.maestro is fine, and curl -f already
fails on a version that does not exist.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QRXxEuKNzgWvK2RxJjQ8TC
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.

2 participants