Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .changeset/ios-ci-disabled.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
---

Comment out the iOS native CI job. Four `pod lib lint` invocations ran
sequentially on a macOS runner and took 81 minutes a run — against 2m17s for the
Android equivalent — because each lint builds the Lynx engine from scratch in
its own sandbox to check roughly 2,600 lines of Objective-C.

Nothing compiles the Objective-C now. `native-contract.test.ts` still pins the
native method surfaces against the TypeScript, so a renamed or dropped method is
still caught, but a syntax error or a missing header will reach a tarball. Run
`pod lib lint` by hand on a Mac before releasing a change under
`packages/lynx-*/ios/`. The job is left commented out in the workflow with the
two fixes worth making before turning it back on.
6 changes: 5 additions & 1 deletion .claude/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,11 @@ came from:
with an explanation locally, `--require-sdk` in CI. The Gradle harness lives in
`android-check/` rather than `android/` so the shipped directory stays clean.
- **`pod lib lint`** compiles the Objective-C against the real Lynx pod and iOS
SDK. macOS only, so CI is the only place it can ever run.
SDK. macOS only. It ran in CI until the job's cost — 81 minutes a run, almost
all of it recompiling the Lynx engine once per pod — bought it out; the job is
commented out in `.github/workflows/ci.yml` with the two fixes worth making
before re-enabling it. Nothing compiles the Objective-C now unless someone
runs this by hand on a Mac.

**None of that is a device.** Permission flows, `AlarmManager` under Doze, APNs
registration and FCM delivery are unverified, as is whether Lynx's annotation
Expand Down
98 changes: 64 additions & 34 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,9 @@ jobs:
- name: Smoke and e2e test built artifacts
run: bun run test:dist

# The two jobs below are the only things anywhere that compile the native
# halves of the `@amritk/lynx-*` packages — notifications, location, dialogs
# and deep linking.
# `android` below — and `ios`, while it was enabled — are the only things
# anywhere that compile the native halves of the `@amritk/lynx-*` packages —
# notifications, location, dialogs and deep linking.
# Everything in `Check` above runs in JavaScript and would pass just as
# happily against Kotlin and Objective-C that do not build —
# `src/native-contract.test.ts` narrows that gap by comparing the two native
Expand Down Expand Up @@ -97,34 +97,64 @@ jobs:
- name: Compile the Android libraries
run: bun run check:android --require-sdk

ios:
name: iOS native
runs-on: macos-15
steps:
- uses: actions/checkout@v4

# `pod lib lint` builds the podspec's sources against the real iOS SDK,
# resolving `Lynx` from CocoaPods so the Lynx headers the module imports
# are the actual ones. It validates from the files on disk and never
# fetches the podspec's `source`.
#
# `--allow-warnings` because CocoaPods warns about things a library
# published from a monorepo subdirectory cannot avoid; it does NOT
# suppress compiler errors, which is the point of running this.
- name: Lint and compile the notifications pod
working-directory: packages/lynx-notifications/ios
run: pod lib lint MiniLynxNotifications.podspec --allow-warnings --platforms=ios

# A separate step rather than a second podspec in the same command, so a
# failure names which pod broke without reading the log.
- name: Lint and compile the location pod
working-directory: packages/lynx-location/ios
run: pod lib lint MiniLynxLocation.podspec --allow-warnings --platforms=ios

- name: Lint and compile the dialogs pod
working-directory: packages/lynx-dialogs/ios
run: pod lib lint MiniLynxDialogs.podspec --allow-warnings --platforms=ios

- name: Lint and compile the deep-linking pod
working-directory: packages/lynx-deep-linking/ios
run: pod lib lint MiniLynxDeepLinking.podspec --allow-warnings --platforms=ios
# DISABLED — the iOS job below was the entire wall clock of this workflow:
# 81 minutes, against 2m17s for `Android native` and 50s for `Check`.
#
# Not because the code is large. The four pods are ~2,600 lines of
# Objective-C between them. Each `pod lib lint` builds an eight-target graph
# — `Lynx`, `LynxBase`, `LynxServiceAPI`, `Lynx-LynxResources`, `PrimJS` and
# a generated host `App` — and compiling the Lynx engine is essentially the
# whole twenty minutes per pod. Because every lint gets its own throwaway
# sandbox, that compile happened four times per run, sequentially, to check
# four small files. On a macOS runner's 10x multiplier that is ~810 billed
# minutes for a check whose Android counterpart costs two.
#
# What is lost while this is off: nothing anywhere compiles the Objective-C.
# `src/native-contract.test.ts` still compares the native method surfaces
# against the TypeScript one, so a renamed or dropped method is still caught
# — but a syntax error, a bad selector, or a header that no longer exists
# will reach a published tarball. Treat a release touching
# `packages/lynx-*/ios/**` as unverified and lint those pods by hand:
#
# cd packages/lynx-notifications/ios
# pod lib lint MiniLynxNotifications.podspec --allow-warnings --platforms=ios
#
# Re-enabling as-is brings the 81 minutes back with it. The two fixes worth
# making first, in order of payoff: gate the job on `packages/lynx-*/ios/**`
# and the podspecs changing (use a job-level `if:`, not a workflow-level
# `paths:` — a workflow that never runs never reports, and a required status
# check that never reports blocks the PR forever), and collapse the four
# lints into a single Podfile carrying all four as development pods so Lynx
# compiles once instead of four times.
#
# ios:
# name: iOS native
# runs-on: macos-15
# steps:
# - uses: actions/checkout@v4
#
# # `pod lib lint` builds the podspec's sources against the real iOS SDK,
# # resolving `Lynx` from CocoaPods so the Lynx headers the module imports
# # are the actual ones. It validates from the files on disk and never
# # fetches the podspec's `source`.
# #
# # `--allow-warnings` because CocoaPods warns about things a library
# # published from a monorepo subdirectory cannot avoid; it does NOT
# # suppress compiler errors, which is the point of running this.
# - name: Lint and compile the notifications pod
# working-directory: packages/lynx-notifications/ios
# run: pod lib lint MiniLynxNotifications.podspec --allow-warnings --platforms=ios
#
# # A separate step rather than a second podspec in the same command, so a
# # failure names which pod broke without reading the log.
# - name: Lint and compile the location pod
# working-directory: packages/lynx-location/ios
# run: pod lib lint MiniLynxLocation.podspec --allow-warnings --platforms=ios
#
# - name: Lint and compile the dialogs pod
# working-directory: packages/lynx-dialogs/ios
# run: pod lib lint MiniLynxDialogs.podspec --allow-warnings --platforms=ios
#
# - name: Lint and compile the deep-linking pod
# working-directory: packages/lynx-deep-linking/ios
# run: pod lib lint MiniLynxDeepLinking.podspec --allow-warnings --platforms=ios
12 changes: 7 additions & 5 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,13 @@ sources of their own:
[`packages/lynx-dialogs`](./packages/lynx-dialogs) — `@amritk/lynx-dialogs`,
the platform's own date picker, action sheet and alert — and
[`packages/lynx-deep-linking`](./packages/lynx-deep-linking) —
`@amritk/lynx-deep-linking`, deep links in and out. Those compile in CI —
`bun run check:android` for the Kotlin, `pod lib lint` on a macOS runner for the
Objective-C — and a parity suite pins their method surfaces against the
TypeScript. **None of it has run on a device.** See each package's `AGENTS.md`
for what that does and does not cover.
`@amritk/lynx-deep-linking`, deep links in and out. The Kotlin compiles in CI —
`bun run check:android` — and a parity suite pins their method surfaces against
the TypeScript. The Objective-C compiles nowhere automatic: `pod lib lint` on a
macOS runner cost 81 minutes a run and is commented out in
`.github/workflows/ci.yml`, so run it by hand on a Mac when you touch `ios/`.
**None of it has run on a device.** See each package's `AGENTS.md` for what that
does and does not cover.

The four are deliberately alike: each was built from the last one's shape, so a
structural change to one is usually owed to the others. Where one diverges it
Expand Down
8 changes: 5 additions & 3 deletions packages/lynx-deep-linking/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,10 @@ bun run --filter='@amritk/lynx-deep-linking' build
bun run check:android
```

The iOS half is compiled by `pod lib lint` on a macOS CI runner — there is no
way to build it on Linux at all. See "What is verified, and what is not" below,
The iOS half is not compiled anywhere automatic: the macOS CI job that ran
`pod lib lint` is commented out in `.github/workflows/ci.yml` because it cost 81
minutes a run, and there is no way to build it on Linux at all. Run it by hand
on a Mac when you touch `ios/`. See "What is verified, and what is not" below,
which is the most important thing on this page.

## Layout
Expand Down Expand Up @@ -140,7 +142,7 @@ one you are relying on before trusting a green run.
| Facade behaviour | `bun run test` | real code, fake platform |
| Cross-language signatures | `src/native-contract.test.ts` | parses Kotlin + Objective-C, compares to TypeScript |
| Kotlin compiles + packages | `bun run check:android` | real `org.lynxsdk.lynx:lynx` AAR, real Android SDK |
| Objective-C compiles | `pod lib lint` (CI, macOS) | real Lynx pod, real iOS SDK |
| Objective-C compiles | `pod lib lint` (manual, macOS — not in CI) | real Lynx pod, real iOS SDK |

`native-contract.test.ts` is the cheapest of these and catches the failure a
compiler cannot on either side: a method renamed in one language, an argument
Expand Down
2 changes: 1 addition & 1 deletion packages/lynx-deep-linking/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@ reach stand behind it, and it is worth knowing which one a green run came from:
| Facade behaviour | `bun run test` | real code, fake platform |
| Cross-language signatures | `src/native-contract.test.ts` | parses Kotlin + Objective-C, compares to TypeScript |
| Kotlin compiles + packages | `bun run check:android` | real Lynx AAR, real Android SDK |
| Objective-C compiles | `pod lib lint` (CI, macOS) | real Lynx pod, real iOS SDK |
| Objective-C compiles | `pod lib lint` (manual, macOS — not in CI) | real Lynx pod, real iOS SDK |

None of that is a device. Whether the `ContentProvider` initialiser wins the
race against the first activity, whether `+load` fires before the launch
Expand Down
8 changes: 5 additions & 3 deletions packages/lynx-dialogs/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,10 @@ bun run --filter='@amritk/lynx-dialogs' build
bun run check:android
```

The iOS half is compiled by `pod lib lint` on a macOS CI runner — there is no
way to build it on Linux at all. See "What is verified, and what is not" below,
The iOS half is not compiled anywhere automatic: the macOS CI job that ran
`pod lib lint` is commented out in `.github/workflows/ci.yml` because it cost 81
minutes a run, and there is no way to build it on Linux at all. Run it by hand
on a Mac when you touch `ios/`. See "What is verified, and what is not" below,
which is the most important thing on this page.

## Layout
Expand Down Expand Up @@ -207,7 +209,7 @@ one you are relying on before trusting a green run.
| Facade behaviour | `bun run test` | real code, fake platform |
| Cross-language signatures | `src/native-contract.test.ts` | parses Kotlin + Objective-C, compares to TypeScript |
| Kotlin compiles + packages | `bun run check:android` | real `org.lynxsdk.lynx:lynx` AAR, real Android SDK |
| Objective-C compiles | `pod lib lint` (CI, macOS) | real Lynx pod, real iOS SDK |
| Objective-C compiles | `pod lib lint` (manual, macOS — not in CI) | real Lynx pod, real iOS SDK |

`native-contract.test.ts` is the cheapest of these and catches the failure a
compiler cannot on either side: a method renamed in one language, an argument
Expand Down
6 changes: 3 additions & 3 deletions packages/lynx-dialogs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -211,9 +211,9 @@ background-thread code.
## What is verified, and what is not

The facade and the fake run here. `bun run check:android` compiles the Kotlin
against the real Lynx AAR, `pod lib lint` compiles the Objective-C against the
real iOS SDK, and `src/native-contract.test.ts` pins the method surfaces against
each other.
against the real Lynx AAR and `src/native-contract.test.ts` pins the method
surfaces against each other. `pod lib lint` compiles the Objective-C against the
real iOS SDK, but it is no longer part of CI — run it by hand on a Mac.

**None of that is a device.** See [`AGENTS.md`](./AGENTS.md) for exactly what
each check does and does not cover.
Expand Down
8 changes: 5 additions & 3 deletions packages/lynx-location/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,10 @@ bun run --filter='@amritk/lynx-location' build
bun run check:android
```

The iOS half is compiled by `pod lib lint` on a macOS CI runner — there is no
way to build it on Linux at all. See "What is verified, and what is not" below,
The iOS half is not compiled anywhere automatic: the macOS CI job that ran
`pod lib lint` is commented out in `.github/workflows/ci.yml` because it cost 81
minutes a run, and there is no way to build it on Linux at all. Run it by hand
on a Mac when you touch `ios/`. See "What is verified, and what is not" below,
which is the most important thing on this page.

## Layout
Expand Down Expand Up @@ -166,7 +168,7 @@ one you are relying on before trusting a green run.
| Facade behaviour | `bun run test` | real code, fake platform |
| Cross-language signatures | `src/native-contract.test.ts` | parses Kotlin + Objective-C, compares to TypeScript |
| Kotlin compiles + packages | `bun run check:android` | real `org.lynxsdk.lynx:lynx` AAR, real Android SDK |
| Objective-C compiles | `pod lib lint` (CI, macOS) | real Lynx pod, real iOS SDK |
| Objective-C compiles | `pod lib lint` (manual, macOS — not in CI) | real Lynx pod, real iOS SDK |

`native-contract.test.ts` is the cheapest of these and catches the failure a
compiler cannot on either side: a method renamed in one language, an argument
Expand Down
2 changes: 1 addition & 1 deletion packages/lynx-location/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ reach stand behind it, and it is worth knowing which one a green run came from:
| Facade behaviour | `bun run test` | real code, fake platform |
| Cross-language signatures | `src/native-contract.test.ts` | parses Kotlin + Objective-C, compares to TypeScript |
| Kotlin compiles + packages | `bun run check:android` | real Lynx AAR, real Android SDK |
| Objective-C compiles | `pod lib lint` (CI, macOS) | real Lynx pod, real iOS SDK |
| Objective-C compiles | `pod lib lint` (manual, macOS — not in CI) | real Lynx pod, real iOS SDK |

None of that is a device. Permission flows, provider selection, what a fix
actually contains outdoors, whether a watch survives a backgrounding, and
Expand Down
8 changes: 5 additions & 3 deletions packages/lynx-notifications/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@ bun run --filter='@amritk/lynx-notifications' build
bun run check:android
```

The iOS half is compiled by `pod lib lint` on a macOS CI runner — there is no
way to build it on Linux at all. See "What is verified, and what is not" below,
The iOS half is not compiled anywhere automatic: the macOS CI job that ran
`pod lib lint` is commented out in `.github/workflows/ci.yml` because it cost 81
minutes a run, and there is no way to build it on Linux at all. Run it by hand
on a Mac when you touch `ios/`. See "What is verified, and what is not" below,
which is the most important thing on this page.

## Layout
Expand Down Expand Up @@ -126,7 +128,7 @@ one you are relying on before trusting a green run.
| Facade behaviour | `bun run test` | real code, fake platform |
| Cross-language signatures | `src/native-contract.test.ts` | parses Kotlin + Objective-C, compares to TypeScript |
| Kotlin compiles + packages | `bun run check:android` | real `com.lynx:lynx` AAR, real Android SDK |
| Objective-C compiles | `pod lib lint` (CI, macOS) | real Lynx pod, real iOS SDK |
| Objective-C compiles | `pod lib lint` (manual, macOS — not in CI) | real Lynx pod, real iOS SDK |

`native-contract.test.ts` is the cheapest of these and catches the failure a
compiler cannot on either side: a method renamed in one language, an argument
Expand Down
2 changes: 1 addition & 1 deletion packages/lynx-notifications/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ notifications.deliver({ title: 'Order shipped' })
| TypeScript facade | `bun run test` | everywhere |
| JS ⇄ native contract (names, arities, event names) | `src/native-contract.test.ts` | everywhere |
| Kotlin compiles and packages to an AAR | `bun run check:android` | needs an Android SDK; CI |
| Objective-C compiles against the iOS SDK | `pod lib lint` | macOS only; CI |
| Objective-C compiles against the iOS SDK | `pod lib lint` | macOS only; not in CI, run by hand |
| Any of it working on a device | — | **nothing** |

The last row is the one to keep in mind. Both native halves compile against the
Expand Down
Loading