Skip to content

ci(native): build and test the native Swift packages on macOS - #7984

Merged
cixzhang merged 3 commits into
mainfrom
native/swift-ci
Oct 23, 2026
Merged

cixzhang merged 3 commits into
mainfrom
native/swift-ci

Conversation

@cixzhang

@cixzhang cixzhang commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

Maintainer impact

The Swift package in @astryxdesign/native (generated contracts under apple/) and the versioned swift-ios-<n>/ packages have no CI. Nothing compiles their Swift or runs their Swift tests, so a generator change can ship Swift that does not build, and an iOS-only branch (#if os(iOS)) is never compiled at all.

Intended invariant

AST-082 FR5 and FR16: the Swift binding is generated from the same contracts as the TypeScript types, and a core contract change reaches every native platform on its next build. The Node drift test already proves the committed Swift matches the generator; this lane proves that Swift builds and its tests pass.

Change

Two jobs in ci.yml, with test joining the second (AST-030 FR11: new coverage inside the shared surface joins its owner, no separately routed lane):

  • native-swift-scope (ubuntu-slim) decides with pr-scope-touches.sh, loaded from the base, whether the change touches Swift or a Package.swift under packages/native. Docsite-only and tooling-only changes skip; an undecidable scope runs the work.
  • test-native-swift asks for macos-15 only when that scope applies and otherwise passes on ubuntu-slim without work, so no other pull request waits for a macOS runner. On macOS, for every directory holding a Package.swift: swift test when the package supports macOS and has a test target (else swift build), an iOS Simulator build, and the iOS Simulator tests of an iOS-only package. The scheme is read from xcodebuild -list.

ci-test-join.mjs requires test-native-swift to succeed; ci-native-swift.test.mjs pins the routing (Linux scope, conditional runner, fail-closed scope, specialized-lane skip).

Evidence

  • Failure before the change: no workflow compiles packages/native/**/*.swift.
  • Success after the change: this PR's own run (it triggers on the workflow file).
  • Product/runtime behavior verified unchanged: only CI files change (ci.yml, the test-join script and its tests).

Scope

  • No intended public API, product behavior, visual direction, or policy change.
  • Product changes discovered during the work were removed or split.
  • Public text and artifacts contain no internal Meta context.

Testing

  • pnpm vitest run --project node .github/scripts: 66 files, 1182 tests pass (join matrix, historical-join contract, new routing test).
  • The Swift steps ran green on macos-15 at two earlier heads (55 s as a separate workflow; 2 min 10 s as test-native-swift). This head changes only CI files, so its own run skips the Swift work; the stacked PRs above exercise it.
  • test-node (4/4) (official package list, MobilePowerSearch) and test-ui (2/4) (PowerSearch filter flow) fail at the base commit too; they are a main breakage, not this change.

Adds a Native Swift workflow that builds and tests every Swift package under
packages/native on a macOS runner: `swift test` for packages that support
macOS, and an iOS Simulator build (or test, for an iOS-only package). Until
now the generated Swift binding was only typechecked by hand.
@vercel

vercel Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

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

Project Deployment Actions Updated
astryx Ready Ready Preview Oct 23, 2026 7:43pm UTC

Request Review

@astracat-bot

astracat-bot Bot commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

AI review status for this pull request.

Review status Updated
🟢 Reviewed (for maintainers only) Oct 23, 2026, 7:57 PM UTC

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Meta Open Source bot. label Oct 23, 2026
@github-actions

github-actions Bot commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

PR Analysis Report

📚 Storybook Preview

View Storybook for this PR

🧪 Sandbox Preview

View Sandbox for this PR

No new or modified components detected.

Bundle Size Summary

No component packages changed.

Accessibility Audit

Status: No accessibility violations detected.

Visual Regression

46 of 496 shot(s) changed.

A change here is a question, not a failure: check whether the after is the
picture you intended. Record that review on the PR. Baseline maintenance is an
explicit dispatch of CI; this report never rewrites the baseline or adds a release gate.

component story theme mode pixels
Selector Indicator Space Bottom Sheet Narrow End RTL neutral light 753,933
Selector Indicator Space Bottom Sheet Wide Start neutral light 740,072
Chat Full AI Chat probe light 230,823
Chat Full AI Chat probe dark 229,805
RichTextEditor Markdown parity: side by side probe dark 94,866
RichTextEditor Markdown parity: side by side probe light 89,762
Collapsible Dividers probe dark 66,445
Collapsible Dividers probe light 66,445
Chat Default neutral dark 36,304
Chat Default neutral light 35,767
Chat Empty State probe light 23,531
Chat Empty State probe dark 22,622
ChatAutoScroll Scroll Behavior Comparison neutral dark 15,378
ChatAutoScroll Scroll Behavior Comparison neutral light 14,107
Chat With Attachments probe light 13,406
Chat With Attachments probe dark 11,232
ProgressBar With Target Mark probe dark 7,895
ProgressBar With Target Mark probe light 7,895
Markdown Default neutral dark 7,573
Markdown Default neutral light 7,566

and 26 more.


Generated by PR Enrichment workflow | Storybook | Sandbox | View full report

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requesting changes: compiling and testing the native Swift in CI closes a real gap, but this workflow routes its own pull-request lane with a paths filter. docs/specs/AST-030/spec.md FR11 instead requires new coverage for paths the CI classifier sends to shared-or-unknown, as it does for packages/native/**, to join that broad owner. Please either run the Swift job from the broad lane in ci.yml and add it to the test join, or first land an owner-approved FR10 amendment to AST-030 that admits a native surface and route the job through it. While there, run swift test only when a package has a test target (.github/workflows/native-swift.yml:66-67), since SwiftPM fails swift test on a package without one.

[Automated review]

Comment thread .github/workflows/native-swift.yml Outdated
Comment on lines +11 to +16
pull_request:
branches: ['main']
paths:
- 'packages/native/**/*.swift'
- 'packages/native/**/Package.swift'
- '.github/workflows/native-swift.yml'

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This pull_request path filter makes the Swift build a separately routed lane. The CI classifier already routes packages/native/** to shared-or-unknown, and AST-030 FR11 says new coverage inside an existing surface must join that owner instead of creating a separately routed lane. Consider running this job from the broad lane in ci.yml, added to the test join, or admitting a native surface through an FR10 amendment first. The push: main trigger below is outside AST-030's scope.

Comment thread .github/workflows/native-swift.yml Outdated
xcodebuild -list -json > "$RUNNER_TEMP/schemes.json"
scheme=$(python3 -c 'import json,sys; s=json.load(open(sys.argv[1]))["workspace"]["schemes"]; print(next((x for x in s if x.endswith("-Package")), s[0]))' "$RUNNER_TEMP/schemes.json")
if [ "$macos" = True ]; then
swift test

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

swift test runs here even when $tests is False. For a package without a test target, SwiftPM stops with error: no tests found, and set -euo pipefail then skips the iOS Simulator build and every later package. Guarding it with [ "$tests" = True ], and running swift build otherwise, avoids that once such a package exists.

Moves the Swift build out of its own path-filtered workflow into ci.yml as
test-native-swift, joined by test (AST-030 FR11). The job does its macOS work
only when a pull request changes Swift under packages/native or ci.yml, and
builds a package without a test target instead of running swift test.

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requesting changes: joining the Swift build to test in ci.yml is the owner AST-030 FR11 asks for, and the job compiled and tested the native Swift on this head, but it now holds every non-spec pull request's required test check for a macos-15 runner, usually followed by a full-history clone, even when nothing Swift changed (this pull request's runs queued 12 and 31 minutes for that runner). Please decide applicability on a Linux runner first and request macos-15 only when Swift work applies, for example with a conditional runs-on or an expected-skip rule in ci-test-join.mjs like the one test-build has, and skip the job's work on tooling-only changes as test-ui does. While there, replace the inline git diff ... || true check with .github/scripts/pr-scope-touches.sh loaded from the base SHA, which fails closed when it cannot compute the diff, counts renames, and does not need fetch-depth: 0.

[Automated review]

Comment thread .github/workflows/ci.yml Outdated
test-native-swift:
needs: [check-scope]
if: ${{ github.event_name != 'workflow_dispatch' && always() && !cancelled() && needs.check-scope.outputs.spec_only != 'true' }}
runs-on: macos-15

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This puts a macos-15 runner in front of test on every non-spec pull request, including docsite-only and tooling-only ones that never build Swift, and outside docsite-only changes the full-history checkout below as well; this pull request's runs queued 12 and 31 minutes for that runner. Deciding changed on a Linux runner first and using something like runs-on: ${{ needs.<scope>.outputs.changed == 'true' && 'macos-15' || 'ubuntu-slim' }} (or an expected-skip rule in ci-test-join.mjs like test-build's) keeps the coverage without the wait.

Comment thread .github/workflows/ci.yml Outdated
exit 0
fi
changed=$(git diff --name-only "$(git merge-base "$BASE_SHA" HEAD)" HEAD \
| grep -E '^packages/native/.*(\.swift|Package\.swift)$|^\.github/workflows/ci\.yml$' || true)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

|| true also hides a failed git merge-base or git diff, so a missing base SHA reads as No native Swift change and the build is skipped with success; a .swift file renamed out of the pattern is missed too. .github/scripts/pr-scope-touches.sh, loaded from the base SHA as at line 1642, exits 2 when it cannot tell and uses --no-renames; treating any exit other than 1 as changed=true fails closed and lets the checkout drop fetch-depth: 0.

…anged

A native-swift-scope job on ubuntu-slim decides, with pr-scope-touches.sh
loaded from the base, whether the change touches Swift under packages/native.
test-native-swift asks for macos-15 only then, and otherwise passes on Linux,
so other pull requests no longer wait for a macOS runner. Docsite-only and
tooling-only changes skip the Swift work; an undecidable scope runs it.

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Recommending approval: the Swift build now runs inside test from ci.yml, a Linux job decides whether native Swift changed and requests macos-15 only then, and a scope that cannot be decided still runs the build, which resolves the earlier automated change requests. As an optional follow-up, ci-native-swift.test.mjs stays green if the default scope=2 (ci.yml:769) becomes scope=1 or the pattern at ci.yml:775 stops matching Swift, so pinning both, or running the step against a few fixture paths, would keep the lane from breaking silently. Separately, with ci.yml out of the pattern an edit to the Swift step itself, like this pull request, never runs it; moving the script into its own file under .github/scripts/ and adding that path to the pattern would cover such edits without putting macOS on every workflow change. The description's Evidence line still says this pull request's own run exercises the Swift job, which no longer holds at this head.

[Automated review]

'git show "$BASE_SHA:.github/scripts/pr-scope-touches.sh"',
);
// Only "out of scope" (exit 1) skips; unknown (2) runs the Swift work.
expect(decide).toContain('if [ "$scope" = 1 ]; then');

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This pins the comparison, but not the default scope=2 (ci.yml:769) or the pattern (ci.yml:775): changing the default to scope=1, or the pattern to one that cannot match a Swift path, leaves the whole .github/scripts suite green while native Swift changes would skip the build. Asserting both, or running the step against a few fixture paths (Swift source, Package.swift, a new swift-ios-<n>/ package, a moved-out .swift), would make the fail-closed promise mutation-sensitive.

Comment thread .github/workflows/ci.yml
&& git fetch --quiet --no-tags --depth=1 origin "$BASE_SHA" \
&& git show "$BASE_SHA:.github/scripts/pr-scope-touches.sh" > "$helper"; then
scope=0
bash "$helper" "$BASE_SHA" '^packages/native/.*(\.swift|/Package\.swift)$' || scope=$?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With ci.yml out of this pattern (the earlier head had it), an edit to the build step below never runs it, so a break first fails the next native Swift pull request. Moving the script into its own file under .github/scripts/ and adding that path here would cover lane edits without putting macos-15 in front of every workflow change.

@cixzhang
cixzhang merged commit 0b78509 into main Oct 23, 2026
70 checks passed
@cixzhang
cixzhang deleted the native/swift-ci branch October 23, 2026 20:00

This branch was successfully deployed

1 active deployment
Preview — a6c25e89 Deployed Oct 23, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Meta Open Source bot.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant