GeezerVison is showing github.com — fix: report __runtimeVersion without quotes, and cut CI time with the Xcode 26 compilation cache by edusperoni · Pull Request #546 · NativeScript/ios
Conversation
edusperoni
commented
Nov 5, 2026
•
edited
Loading
Uh oh!
There was an error while loading. Please reload this page.
PR wall-clock time was dominated by waiting for macOS runners, not by the jobs themselves: a run did ~21 min of work (Build ~9.5 min, then Test and Test (ASan) ~11 min each) but often took 70–97 min end to end, because every push and every Dependabot PR queued three full macOS jobs.
This PR:
- queues fewer runs: superseded PR runs and superseded "next" builds on
main are cancelled, and Dependabot opens one PR per ecosystem;
- makes each run faster, mainly through Xcode 26's compilation cache: with a warm cache the Build job takes ~4:20 instead of ~9:30;
- generates
NATIVESCRIPT_VERSION from package.json at build time, so no source file carries a version and the cache can hit across runs;
- fixes
__runtimeVersion, which carried literal quote characters ("9.1.0" with the quotes) and now reports the bare version, matching Android.
Fewer runs
- Cancel superseded PR runs (
pull_request.yml): a concurrency group per PR number with cancel-in-progress: true. A new push cancels that PR's older run; other PRs, stacked ones included, are unaffected.
- Cancel superseded "next" builds on
main (npm_release.yml): job-level concurrency on build and test only. Pushes to main share a group with cancel-in-progress: true, so during a burst of merges only the newest commit's "next" is built and published (it contains the skipped commits). Every other event (tag pushes, workflow_dispatch releases) gets a group with github.run_id, so it is never cancelled or replaced while pending (GitHub keeps one pending job per group). The publishing jobs get no concurrency: cancelling them could leave a half-published release.
- Group Dependabot updates (
dependabot.yml): the 14 npm entries become one multi-directories entry, and both ecosystems get groups:, so each opens one PR per month instead of one per package (it opened 11 PRs on 2026-11-01). Directories, schedule and the major-version ignore rule are unchanged.
Xcode 26.3 + compilation cache
- Both workflows move to Xcode 26.3 (already on the
macos-15 image).
tools/ci-compilation-cache.xcconfig enables COMPILATION_CACHE_ENABLE_CACHING. It is loaded through XCODE_XCCONFIG_FILE, so it reaches every xcodebuild in a job, including the ones the build scripts run.
- Pull requests restore and save a CAS per job (
xcode-cas-<os>-<arch>-{build,test-default,test-asan}-<sha>, restored by prefix so a new commit starts from the newest cache of its kind). Sizes are 76–91 MiB each.
main seeds the PR caches: on pushes to main, the release workflow's iOS build and its test job compile with caching on into an empty CAS and save it (actions/cache/save) under the build and test-default keys. PRs can only restore caches from their own ref or from main, so this makes a PR's first push warm. The release workflow never restores a cache, so shipped binaries are always compiled from source; tag and manual releases don't enable caching at all. Only runs on main can write caches that PRs read; PRs, including forks, can't. Locally, an archive of the iOS device slice built with caching on (empty CAS, 86 compiles through the cache) is byte-identical to one built without it.
-fembed-bitcode is dropped from OTHER_CFLAGS (Debug and Release): with caching on, clang rejects it (-mllvm is not supported with -fembed-bitcode). It had no effect on the output: ENABLE_BITCODE is NO, and none of the four NativeScript slices in a CI-built xcframework has a bitcode section.
Version generated at build time
update_version.sh used to write the package version into NativeScript-Prefix.pch, which every file includes. PR and "next" versions contain the run id, so every run changed the input of every compile and no two runs could share cache results (a cache seeded from main could never hit in a PR).
Now no source file contains a version. A "Generate Version header" build phase on the NativeScript target (next to "Generate RuntimeBuiltins", before Sources) runs tools/generate-version-header.sh, which reads package.json with plutil and writes the gitignored NativeScript/runtime/generated/Version.h, rewriting it only when the version changes. Only NativeScript.mm and Runtime.mm include it. A fresh clone still builds from Xcode with no setup step (checked locally with generated/ deleted: the build succeeds and the binary reports the package.json version), and CI-built frameworks report the bumped PR version (e.g. 9.1.0-pr.546-2026-11-06-42248818974).
update_version.sh is removed: stamping the version was its only real job (no caller passed the argument that set package.json's version), so build_all_ios.sh, build_all_vision.sh, the update-version npm script and the README's release steps drop it.
NATIVESCRIPT_VERSION was never visible outside the runtime (the prefix header isn't shipped), so the only observable outputs are the startup log line and the JS global __runtimeVersion. Both now always match package.json at build time; before, a plain Xcode build reported the stale committed 9.0.4-dev.2.
__runtimeVersion fix
Runtime.mm passed NATIVESCRIPT_VERSION, already a string literal, through STRINGIZE_VALUE_OF, so __runtimeVersion held "9.1.0" including the quote characters. It now passes the define directly, as the Android runtime does with NATIVE_SCRIPT_RUNTIME_VERSION, so both platforms report the bare version, and the unused STRINGIZE macros are gone. A new test in TestRunner/app/tests/ApiTests.js checks the global is a quote-free version string. NativeScript core doesn't read this global.
Smaller fixes
- Duplicate Mac Catalyst archive (
build_nativescript.sh): Catalyst was archived twice to the same path; the second overwrote the first, which cost ~70 s per build. The archive that ships (generic destination) is the one kept.
BUILD_LIBRARIES_FOR_DISTRIBUTION typo: the simulator and device archives passed the plural, which isn't an Xcode setting (xcodebuild accepts any NAME=VALUE silently). The setting is BUILD_LIBRARY_FOR_DISTRIBUTION; the project already sets it to YES, so output is unchanged.
xcodebuild test instead of build test: the test action builds what it needs; the extra build action was a second build pass (~37 s).
xcresulttool instead of xcparse (scripts/export-test-attachments.sh): Homebrew compiled xcparse from source on every test job (~70 s). The script exports the attachments and renames them from manifest.json (junit_0_<uuid>.xml), so the *junit*.xml steps are unchanged.
- Parallel metadata generator build: the
metadata-generator Xcode script phase runs make -j$(sysctl -n hw.ncpu) instead of a single-threaded make (88 s on CI).
- ccache for the metadata generator in the PR Build job (
setup-build-env input ccache, keyed on metadata-generator/** and download_llvm.sh): warm, both generator builds take 14 s instead of ~50 s. Not used in the release workflow.
Results
Against #41361084781 (before this PR):
|
Before this PR |
Xcode 26.3, empty cache |
Warm cache, next commit |
| Build job |
9:33 |
11:01 |
4:17 |
Build step (npm run build-ios) |
7:28 |
7:57 |
2:09 |
| Test-job build |
~3:16 |
~3:30 |
1:40 (ASan 2:18) |
"Empty cache" is #42120629946 attempt 1; "warm cache, next commit" is #42256205154, a new commit restoring the previous run's cache. With a warm cache the NativeScript archives take 16–21 s per platform instead of 65–145 s. The test jobs are now dominated by the ~2.5–3.5 min xcodebuild spends booting the simulator and launching the app. The first run after the base branch moves a lot (e.g. right after a rebase onto main) is mostly cold, as expected.
Tried and reverted
- Booting the simulator before
xcodebuild. Started right after checkout, it slowed the LLVM cache restore from ~11 s to ~3.5 min (unpacking alone ~4 s to 2m19s) along with npm install and the generator build. Started right before xcodebuild, xcodebuild waited ~3.7 min on the booting destination and the freshly booted simulator's background work roughly doubled the build. Letting xcodebuild boot it after building is fastest.
- ccache in the test jobs. It never hit on CI there (0/60, though the same Xcode script phase hits locally), and with the generator compiling alongside the TestRunner build a hit would save about what installing ccache costs.
Verify after merge
The release-workflow changes don't run on PRs:
- The first push to
main should save xcode-cas-*-build-<sha> and xcode-cas-*-test-default-<sha>, and the next new PR's first push should restore them.
- Close-together merges to
main should cancel the older "next" build or test, never a publishing job.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
coderabbitai
Bot
commented
Nov 5, 2026
•
edited
Loading
Uh oh!
There was an error while loading. Please reload this page.
|

Note
Reviews paused
It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.
Use the following commands to manage reviews:
@coderabbitai resume to resume automatic reviews.
@coderabbitai review to trigger a single review.
Use the checkboxes below for quick actions:
- Resume reviews
- Trigger review
No actionable comments were generated in the recent review.
Walkthrough
-
CI workflows add ccache and Xcode compilation-cache settings, change test execution, and replace attachment extraction. NativeScript builds generate the version header from package.json. Archive and project build settings change, and Dependabot consolidates update groups.
Changes: CI Build and Test
| Layer / File(s) |
Summary |
Configure build caches .github/actions/setup-build-env/action.yml, scripts/install-ci-deps.sh, .github/workflows/pull_request.yml, .github/workflows/npm_release.yml, tools/ci-compilation-cache.xcconfig |
The setup action and installer add optional ccache support. CI jobs configure and save Xcode compilation caches, including separate test-matrix cache variants. |
Run tests and export attachments .github/workflows/pull_request.yml, .github/workflows/npm_release.yml, scripts/export-test-attachments.sh |
The workflows pin Xcode 26.3, change concurrency handling, and run test without an explicit build action. Attachment extraction uses a script that exports and renames result-bundle attachments. |
NativeScript Build and Version
| Layer / File(s) |
Summary |
Update archive and project build settings build_nativescript.sh, v8ios.xcodeproj/project.pbxproj |
The archive script removes an earlier Mac Catalyst command and corrects the iOS distribution setting. The project parallelizes metadata-generator builds and removes bitcode flags. |
Generate version metadata during builds tools/generate-version-header.sh, v8ios.xcodeproj/project.pbxproj, NativeScript/NativeScript.mm, NativeScript/runtime/Runtime.mm, NativeScript/NativeScript-Prefix.pch, update_version.sh, package.json, build_all_ios.sh, build_all_vision.sh, README.md |
The Xcode build generates Version.h from the package version, and runtime source files include it. The previous version-update script and command are removed, and the README describes the build-time version source. |
Dependabot Update Grouping
| Layer / File(s) |
Summary |
Group dependency updates .github/dependabot.yml |
GitHub Actions and npm updates are grouped. The npm configuration combines update directories and retains its schedule, pull request limit, and semver-major exclusion. |
Estimated code review effort: 3 (Moderate) | ~25 minutes
Change: Other
Sequence Diagram(s)
sequenceDiagram
participant TestWorkflow
participant ExportScript
participant Xcresulttool
TestWorkflow->>ExportScript: Pass result bundle and output directory
ExportScript->>Xcresulttool: Export result-bundle attachments
Xcresulttool->>ExportScript: Return exported files and manifest
ExportScript->>TestWorkflow: Rename attachments and print test identifiers
Loading
Suggested reviewers: nathanwalker
Merge Risk: Minimal · up to 0e3c1
-
This change speeds up CI and moves version generation to build time. No concrete merge-blocking risk was found. The caching and cancellation behavior should be confirmed after merge, as the PR notes.
Pre-merge checks: 5
-
| Check name |
Status |
Explanation |
| Docstring Coverage |
 |
No eligible functions found in the changed files. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentat… |
| Linked Issues check |
 |
Check skipped because no linked issues were found for this pull request. |
| Out of Scope Changes check |
 |
Check skipped because no linked issues were found for this pull request. |
| Description Check |
 |
Check skipped - CodeRabbit’s high-level summary is enabled. |
| Title check |
 |
The title accurately identifies the runtime version change and the main CI improvement through the Xcode compilation cache. |
Full details: Docstring Coverage
-
Explanation
No eligible functions found in the changed files. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 0 functions across 4 files. (4 skipped: 4 unsupported.)
Finishing Touches
-
Generate docstrings
-
- Commit to this branch
- Create a new PR
- Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.
Share
-
A rabbit checks the cache at dawn Then follows where test bundles spawn New version leaves its header trail The builds proceed along the rail And carrots cheer each passing run
Recent review info
-
Run configuration
-
- Source mode: Auto
- Repository: Unavailable
- Branch: Unavailable
- File: No configuration file from this repository applied
- Commit: Unavailable
- Layers: Repository UI
- Review profile: CHILL
- Plan: Advanced
- Run ID:
cd154187-63b6-441c-b745-26a960100ebe
Commits
-
Reviewing files that changed from the base of the PR and between bc2ab94 and 0e3c1f4.
Files selected for processing: 9
-
NativeScript/NativeScript.mm
NativeScript/runtime/Runtime.mm
README.md
build_all_ios.sh
build_all_vision.sh
package.json
tools/generate-version-header.sh
update_version.sh
v8ios.xcodeproj/project.pbxproj
Files with no reviewable changes: 4
-
- build_all_vision.sh
- update_version.sh
- package.json
- build_all_ios.sh
Included review availability: This review used your included allowance. Your plan provides up to 2 included reviews per hour; 0 remain after this review.
|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Additional comments: 1
-
.github/workflows/pull_request.yml-38-39: 1
-
38-39: Minor · Functional Correctness · Quick win
Unverified finding
Verification ran but could not confirm this finding. It is shown for review, not as a verified issue.
Remove the matrix suffix from the build cache keys.
The build job has no matrix, so matrix.artifact-suffix has no value in either key. Actionlint reports an error on both lines. GitHub evaluates the missing property to an empty string; remove the suffix here and retain it in the test job, where the matrix defines it. (docs.github.com)
Source: Linters/SAST tools
Review info
-
Run configuration
-
- Source mode: Auto
- Repository: Unavailable
- Branch: Unavailable
- File: No configuration file from this repository applied
- Commit: Unavailable
- Layers: Repository UI
- Review profile: CHILL
- Plan: Advanced
- Run ID:
cb3ef5f4-5fb9-455e-b88d-824a4cfda8e7
Commits
-
Reviewing files that changed from the base of the PR and between 418b038 and 688d25c.
Files selected for processing: 4
-
.github/workflows/npm_release.yml
.github/workflows/pull_request.yml
tools/ci-compilation-cache.xcconfig
v8ios.xcodeproj/project.pbxproj
Files with no reviewable changes: 1
-
- v8ios.xcodeproj/project.pbxproj
Included review availability: This review used your included allowance. Your plan provides up to 2 included reviews per hour; 0 remain after this review.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
edusperoni
changed the title
ci: speed up PR pipeline (cancel superseded runs, group Dependabot, drop duplicate Catalyst archive)
ci: cut PR CI time (Xcode 26 compilation cache, cancel superseded runs, group Dependabot)
Nov 6, 2026
…flag name
build_nativescript.sh archived Mac Catalyst twice to the same
xcarchive path; the second archive overwrote the first, so the first
(~70s per build) never reached the xcframework. Keep the later one,
which uses the generic destination that ships today.
The simulator and device archives passed BUILD_LIBRARIES_FOR_DISTRIBUTION,
which is not an Xcode build setting (the setting is
BUILD_LIBRARY_FOR_DISTRIBUTION; "Build Libraries for Distribution" is
only its UI label). xcodebuild accepts any NAME=VALUE silently, so the
flag was a no-op and the project-level YES applied instead. Output is
unchanged; the flag now actually does what it says.
A new push to a PR now cancels the run still in progress for that PR.
Each run holds three macOS runners for ~10 min apiece, and restacking a
PR chain queued full runs for every intermediate push; with the org's
macOS concurrency cap that pushed wall-clock times to 70-97 min.
The 14 npm update entries and the github-actions entry each opened
their own PRs, and every PR runs the full macOS build and both test
lanes (11 PRs on 2026-11-01). Merge the npm entries into a single
multi-directory entry and group updates so each ecosystem gets one PR
per month. Ignore rules and schedule are unchanged.
…hase
The metadata-generator target ran a plain `make`, so the ~25-file
CMake build used one core; on 3-core CI runners it took 88s and every
TestRunner compile waited for it.
- Boot the simulator right after checkout (scripts/boot-ci-simulator.sh)
and target it by UDID, so the boot overlaps dependency setup and the
build instead of running after them (~3 min before the first test).
- Run `xcodebuild test` instead of `build test`: the test action builds
what it needs, and the separate build action cost a second build pass
(~37s).
- Export the junit attachments with xcresulttool via
scripts/export-test-attachments.sh and drop xcparse, which Homebrew
compiled from source on every test job (~70s).
- Route the metadata generator's CMake compiles through ccache in the
pull request workflow, cached per job and keyed on the generator
sources and LLVM version. The release workflow does not enable it:
shipped binaries must not be built from restored caches.
The release workflow's test job gets the same simulator, test-action
and xcresulttool changes so both test jobs stay identical.
Each push to main runs the full release chain (build, test, then
GitHub release, ios-spm tag, npm publish, SPM verification), so a burst
of merges queued one complete run per commit on the shared macOS
runners. Give the build and test jobs per-ref concurrency groups that
cancel an older run when a newer push to main arrives; only the newest
commit's "next" gets published, and it contains the skipped commits.
The publishing jobs get no concurrency: cancelling them could leave a
half-published release (assets and ios-spm tag without the npm
package). Tag pushes and workflow_dispatch releases have their own
groups (event name and ref are part of the key) and never cancel.
Booting right after checkout overlapped the LLVM cache restore and the
dependency installs, and the contention cost more than it saved: the
cache restore went from ~11s to ~3.5 min and the test jobs got 2-3 min
slower overall, though tests started ~2 min sooner after the build.
Start the boot just before xcodebuild so it overlaps only the build.
Also log when the boot starts and finishes, print that log, and print
ccache stats in the pull request jobs, so CI logs show both directly.
Booting the simulator ahead of xcodebuild was slower wherever it ran.
Started right after checkout, it slowed the LLVM cache restore from
~11s to ~3.5 min. Started right before xcodebuild, xcodebuild waited
~3.7 min on the booting destination before building, and the freshly
booted simulator's background work roughly doubled the build time on
the 3-core runners. Booting after the build, as xcodebuild does, keeps
that work off the critical path, so go back to the name destination.
Drop ccache from the test jobs: it never hit there on CI (0/60, though
it hits locally through the same script phase), and with the generator
now compiling alongside the TestRunner build a hit would save about as
much as installing ccache costs. The Build job keeps it: warm, both
generator builds take 14s instead of ~50s.
…oups
A concurrency group keeps one running and one pending job; a newer
arrival replaces the pending one even without cancel-in-progress. With
the group keyed on event and ref, a third overlapping manual release
from main would have cancelled the second while it waited, and that
release would never publish. Only pushes to main share a group now;
every other event's group includes its run id.
Enable COMPILATION_CACHE_ENABLE_CACHING for the pull request workflow
through tools/ci-compilation-cache.xcconfig, loaded with
XCODE_XCCONFIG_FILE so it reaches every xcodebuild in a job, including
the ones the build scripts run. Each job restores and saves its own CAS
(keyed per job and test variant, rolling by commit). The release
workflow stays on Xcode 16.4 without caches: shipped binaries must not
be built from restored caches.
Drop -fembed-bitcode from OTHER_CFLAGS (Debug and Release): with caching
on, clang rejects it ("-mllvm is not supported with -fembed-bitcode").
It had no effect on the output: ENABLE_BITCODE is NO and none of the
shipped NativeScript binaries carries a bitcode section.
…main
PR runs can only restore caches saved by their own PR or by main, so
every PR's first push started with an empty compilation cache. Move the
release workflow to Xcode 26.3 (pull requests already use it, so both
now build with the same toolchain) and, on main, have its iOS build and
its test job compile with caching on into an empty CAS and save it
under the keys the pull request jobs restore. The release workflow
never restores one: shipped binaries are still compiled from source,
and an archive of the device slice built this way is byte-identical to
one built without caching.
Give each pull request test variant its own key segment
(test-default / test-asan): the old "test-" restore prefix of the
default variant also matched the ASan variant's keys.
update_version.sh wrote the package version into NativeScript-Prefix.pch.
Every file includes the prefix header, so a version bump changed the
input of every compile. PR and "next" versions include the run id, so
no two CI runs could share compilation-cache results, and a cache
seeded from main could never hit in a PR.
Move the define into NativeScript/runtime/Version.h, included only by
NativeScript.mm and Runtime.mm, the two files that use it.
No source file carries a version any more. A "Generate Version header"
build phase on the NativeScript target (next to Generate RuntimeBuiltins,
before Sources) runs tools/generate-version-header.sh, which reads
package.json with plutil and writes NativeScript/runtime/generated/Version.h
(gitignored), rewriting it only when the version changes. A fresh clone
builds from Xcode with no setup step, and update_version.sh no longer edits
sources.
Only NativeScript.mm and Runtime.mm include the header, so a version change
recompiles two files instead of every file that includes the prefix header.
Its remaining job was setting package.json's version from
PACKAGE_VERSION when given an argument, and no caller passed one:
build_all_ios.sh, build_all_vision.sh and the update-version npm script
all ran it without arguments. NATIVESCRIPT_VERSION now comes from
package.json at build time, so the README's release steps drop the
"npm run update-version" step too.
DefineNativeScriptVersion passed NATIVESCRIPT_VERSION, already a string
literal, through STRINGIZE_VALUE_OF, so __runtimeVersion held the
version wrapped in quote characters ("\"9.1.0\""). Pass the define
directly, as the Android runtime does with NATIVE_SCRIPT_RUNTIME_VERSION,
and drop the now unused STRINGIZE macros. Add a test that the global is
the bare package version.
edusperoni
changed the title
ci: cut PR CI time (Xcode 26 compilation cache, cancel superseded runs, group Dependabot)
ci: cut CI time with the Xcode 26 compilation cache, and fix __runtimeVersion quoting
Nov 11, 2026
edusperoni
changed the title
ci: cut CI time with the Xcode 26 compilation cache, and fix __runtimeVersion quoting
fix: report __runtimeVersion without quotes, and cut CI time with the Xcode 26 compilation cache
Nov 11, 2026
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Assignees
No one assigned
Milestone
No milestone
Development
Successfully merging this pull request may close these issues.
Uh oh!
There was an error while loading. Please reload this page.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
|