Skip to content

ci: run linting without building native code - #435

Merged
kraenhansen merged 3 commits into
nextfrom
claude/issue-414-lint-without-native-build
Aug 13, 2026
Merged

ci: run linting without building native code#435
kraenhansen merged 3 commits into
nextfrom
claude/issue-414-lint-without-native-build

Conversation

@kraenhansen

Copy link
Copy Markdown
Collaborator

What

Resolves both TODOs in the lint job of .github/workflows/check.yml, so the job no longer needs a native (JDK/Android SDK/NDK/Rust-Android) toolchain just to type-check TypeScript.

  1. weak-node-api — the lint job now runs the existing pnpm --filter weak-node-api run prebuild:prepare script instead of bootstrap. prebuild:prepare (header copy + C++/TS declaration codegen) already didn't touch anything native — it only needs clang-format, which the job already installs via aminya/setup-cpp. bootstrap additionally ran prebuild:build (the actual CMake/NDK/Xcode native compile), which was never needed for typing.

  2. ferric-example — added a new ferric build --dts-only flag. It runs a plain host cargo build (napi-rs's typedef codegen) to emit the crate's .d.ts and JS entrypoint, without cross-compiling any Android/Apple binaries. The output library basename is now derived from cargo metadata's cdylib target name instead of from the paths of already-built platform artifacts (previously determineLibraryBasename would assert()-fail on an empty array whenever ANDROID_HOME/darwin weren't present, since ferric build's target auto-detection produced zero targets). Wired up as ferric-example's new build:types script.

With both native builds no longer needed, the JDK 17, Android SDK, and rustup target add x86_64-linux-android steps are removed from the lint job entirely.

What CI will actually verify

This is a CI-infrastructure change — the important verification (that the new lint job steps work correctly on GitHub's runners without the JDK/Android SDK/NDK installed) can only be confirmed by this PR's own lint job run, not by me locally. What I did verify locally on a Linux worker with Node 24 and cargo present, but no Android SDK, NDK, or JDK, reproducing the new job's steps end-to-end:

pnpm install
pnpm run build
pnpm --filter weak-node-api run prebuild:prepare
pnpm --filter @react-native-node-api/ferric-example run build:types
pnpm run lint
pnpm run prettier:check
pnpm run depcheck
pnpm run publint

All of these passed, and the generated ferric_example.d.ts/.js matched what a full ferric build would produce (export declare function sum(a: number, b: number): number). I also confirmed the previous behavior actually failed the way I expected: ferric build with no explicit targets on this same toolchain-less box throws AssertionError: Expected at least one library path to determine its basename (from determineLibraryBasename), which is exactly the bug the JDK/Android SDK setup was masking by making ANDROID_HOME available.

I could not exercise the actual GitHub Actions runner environment, self-hosted caching, or the interaction with ccache/aminya/setup-cpp in situ — that's what this PR's CI run itself needs to confirm.

Addresses #414. Confident enough in both halves that this should let the whole issue close once CI is green — flagging as "addresses" rather than "closes" until the actual CI run confirms it holds up on GitHub's infrastructure.


🤖 Generated with Claude Code

https://claude.ai/code/session_01DaK9eAAF5G8wj6UT8VekAm


Generated by Claude Code

The lint job set up a full native toolchain (JDK 17, Android SDK + NDK,
x86_64-linux-android Rust target) and ran two native bootstraps purely to
get generated TypeScript types for type-checking. Resolve both TODOs:

- Add `ferric build --dts-only`, which generates a crate's `.d.ts` and JS
  entrypoint via a plain host `cargo build` (napi-rs typedef codegen),
  without cross-compiling any Android/Apple binaries. The library basename
  is derived from `cargo metadata`'s cdylib target instead of from built
  artifact paths, so no platform build is needed to compute it. Wire this
  up as `ferric-example`'s new `build:types` script.
- Use `weak-node-api`'s existing `prebuild:prepare` script (header copy +
  C++/TS declaration codegen) instead of `bootstrap` (which also runs the
  native CMake build). It already required nothing beyond clang-format.

With both native builds no longer needed for typing, the lint job drops
the JDK 17, Android SDK, and `rustup target add` steps entirely.

Verified locally (Node 24, cargo present, no Android/Apple SDK): fresh
`pnpm install && pnpm run build`, then `pnpm --filter weak-node-api run
prebuild:prepare`, `pnpm --filter @react-native-node-api/ferric-example
run build:types`, `pnpm run lint`, `pnpm run prettier:check`, `pnpm run
depcheck` and `pnpm run publint` all pass end-to-end with no native
toolchain present, reproducing the new lint job's steps.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DaK9eAAF5G8wj6UT8VekAm
@kraenhansen

Copy link
Copy Markdown
Collaborator Author

I suspect we cannot trust that generateTypeScriptDeclarations doesn't actually drive a build of native code.

…ding them

kraenhansen suspected generateTypeScriptDeclarations doesn't actually skip a
native build. Confirmed: napi-rs's `napi build` has no typegen-only mode — it
always runs a real `cargo build`, and --dts-only leaves a fully populated
~123MB target/ directory (including a compiled libferric_example.so) behind.
"Skipping the native build entirely" was wrong; only Android/Apple
cross-compilation was actually skipped, and the lint job stayed coupled to
the host Rust toolchain's health exactly as #414 wanted to avoid.

Switch to the issue's other suggested option: commit ferric_example.d.ts and
ferric_example.js as a checked-in fixture (no longer gitignored), and drop
the ferric-example build:types step from the lint job entirely — it no
longer needs to regenerate anything. --dts-only stays, now documented
accurately, as the way to regenerate the fixture by hand after changing
packages/ferric-example/src/lib.rs.

To catch drift, the two CI jobs that already do a real `ferric build`
(Android and Apple triplets) now `git diff --exit-code` the two committed
files right after building. Both are label-gated rather than running on
every PR, so this doesn't fully close the gap — flagged in the PR thread.

Also excludes the two fixture files from Prettier: they're left in napi-rs's
own output formatting so regenerating them reproduces the committed bytes
exactly, and the new drift check doesn't false-positive on formatting alone.

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

Copy link
Copy Markdown
Collaborator Author

Confirmed the suspicion — napi-rs's napi build has no typegen-only mode. Running --dts-only locally leaves a fully populated ~123MB target/ directory behind, including a compiled libferric_example.so, so it's a real host cargo build, not a no-op. "Skipping the native build entirely" in the flag's original description was wrong; only Android/Apple cross-compilation was actually skipped, so the lint job stayed coupled to the host Rust toolchain's health — exactly what #414 was trying to get away from.

Pushed a fix (63f12e8) that switches to the issue's other suggested option: ferric_example.d.ts/.js are now committed as a fixture (no longer gitignored), and the lint job no longer runs any ferric command at all. --dts-only stays as the way to regenerate the fixture by hand, with its description corrected to say what it actually does.

To guard against the fixture drifting from the Rust source, the two CI jobs that already do a real ferric build (Android and Apple triplets) now git diff --exit-code the two committed files right after building. Caveat: both of those jobs are label-gated (Android 🤖 / Ferric 🦀) rather than running on every PR, so this doesn't fully close the gap for an unlabeled PR that changes packages/ferric-example/src/lib.rs — flagging that rather than papering over it.


🤖 Generated with Claude Code

https://claude.ai/code/session_01DaK9eAAF5G8wj6UT8VekAm


Generated by Claude Code

@kraenhansen kraenhansen self-assigned this Aug 13, 2026
Per review feedback on #435.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DaK9eAAF5G8wj6UT8VekAm
@kraenhansen
kraenhansen merged commit 679e0d4 into next Aug 13, 2026
16 checks passed
@kraenhansen
kraenhansen deleted the claude/issue-414-lint-without-native-build branch August 13, 2026 08:25
@kraenhansen kraenhansen added Android 🤖 Anything related to the Android platform (Gradle, NDK, Android SDK) Ferric 🦀 labels Aug 13, 2026 — with Claude
kraenhansen added a commit that referenced this pull request Aug 13, 2026
* ci: run linting without building native code (#414)

The lint job set up a full native toolchain (JDK 17, Android SDK + NDK,
x86_64-linux-android Rust target) and ran two native bootstraps purely to
get generated TypeScript types for type-checking. Resolve both TODOs:

- Add `ferric build --dts-only`, which generates a crate's `.d.ts` and JS
  entrypoint via a plain host `cargo build` (napi-rs typedef codegen),
  without cross-compiling any Android/Apple binaries. The library basename
  is derived from `cargo metadata`'s cdylib target instead of from built
  artifact paths, so no platform build is needed to compute it. Wire this
  up as `ferric-example`'s new `build:types` script.
- Use `weak-node-api`'s existing `prebuild:prepare` script (header copy +
  C++/TS declaration codegen) instead of `bootstrap` (which also runs the
  native CMake build). It already required nothing beyond clang-format.

With both native builds no longer needed for typing, the lint job drops
the JDK 17, Android SDK, and `rustup target add` steps entirely.

Verified locally (Node 24, cargo present, no Android/Apple SDK): fresh
`pnpm install && pnpm run build`, then `pnpm --filter weak-node-api run
prebuild:prepare`, `pnpm --filter @react-native-node-api/ferric-example
run build:types`, `pnpm run lint`, `pnpm run prettier:check`, `pnpm run
depcheck` and `pnpm run publint` all pass end-to-end with no native
toolchain present, reproducing the new lint job's steps.

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

* ci: commit ferric-example's declarations as a fixture instead of building them

kraenhansen suspected generateTypeScriptDeclarations doesn't actually skip a
native build. Confirmed: napi-rs's `napi build` has no typegen-only mode — it
always runs a real `cargo build`, and --dts-only leaves a fully populated
~123MB target/ directory (including a compiled libferric_example.so) behind.
"Skipping the native build entirely" was wrong; only Android/Apple
cross-compilation was actually skipped, and the lint job stayed coupled to
the host Rust toolchain's health exactly as #414 wanted to avoid.

Switch to the issue's other suggested option: commit ferric_example.d.ts and
ferric_example.js as a checked-in fixture (no longer gitignored), and drop
the ferric-example build:types step from the lint job entirely — it no
longer needs to regenerate anything. --dts-only stays, now documented
accurately, as the way to regenerate the fixture by hand after changing
packages/ferric-example/src/lib.rs.

To catch drift, the two CI jobs that already do a real `ferric build`
(Android and Apple triplets) now `git diff --exit-code` the two committed
files right after building. Both are label-gated rather than running on
every PR, so this doesn't fully close the gap — flagged in the PR thread.

Also excludes the two fixture files from Prettier: they're left in napi-rs's
own output formatting so regenerating them reproduces the committed bytes
exactly, and the new drift check doesn't false-positive on formatting alone.

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

* ci: drop the explanatory comment from ferric-example/.gitignore

Per review feedback on #435.

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Android 🤖 Anything related to the Android platform (Gradle, NDK, Android SDK) Ferric 🦀

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants