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
28 changes: 28 additions & 0 deletions .github/actions/select-xcode/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: Select Xcode with Swift 6.2+
description: >
Select the newest Xcode 26+ install on the runner so a swift-tools-version
6.2 Package.swift resolves. macos-latest currently defaults to Xcode 16.4,
which ships Swift 6.1.0 and rejects 6.2 manifests with
"is using Swift tools version 6.2.0 but the installed version is 6.1.0".

# TODO: remove this action and every `- uses: ./.github/actions/select-xcode`
# call site once GitHub's macos-latest image defaults to an Xcode that ships
# Swift 6.2 or later. Track the default at:
# https://github.com/actions/runner-images/blob/main/images/macos/macos-15-Readme.md
# (look for "(default)" in the Xcode table). When the default version meets
# the package's swift-tools-version, the explicit selection becomes redundant.

runs:
using: composite
steps:
- shell: bash
run: |
set -euo pipefail
xcode=$(ls -d /Applications/Xcode_26*.app 2>/dev/null | sort -V | tail -1)
if [[ -z "$xcode" ]]; then
echo "No Xcode 26+ installed on this runner image." >&2
ls /Applications | grep -i xcode >&2 || true
exit 1
fi
sudo xcode-select -s "$xcode/Contents/Developer"
swift --version
32 changes: 32 additions & 0 deletions .github/actions/verify-expected-commit/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: Verify expected commit
description: >
Fail the job if the workflow run's commit does not match the
workflow_dispatch input the maintainer asked for. This is a guard against
accidentally publishing an artifact built from a commit other than the
one cut-release.sh was dispatched against.

inputs:
expected_commit:
description: Full commit SHA the dispatcher expects to run against.
required: true

runs:
using: composite
steps:
- shell: bash
env:
EXPECTED_COMMIT: ${{ inputs.expected_commit }}
run: |
set -euo pipefail

if [[ -z "$EXPECTED_COMMIT" ]]; then
echo "expected_commit input is required." >&2
exit 1
fi

if [[ "$GITHUB_SHA" != "$EXPECTED_COMMIT" ]]; then
echo "Dispatched commit mismatch:" >&2
echo " expected_commit: $EXPECTED_COMMIT" >&2
echo " GITHUB_SHA: $GITHUB_SHA" >&2
exit 1
fi
110 changes: 102 additions & 8 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@ permissions:

jobs:
test-pinned:
name: Test (pinned XCFramework)
name: Test (pinned artifactbundle)
runs-on: macos-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- uses: ./.github/actions/select-xcode
- name: Run tests
run: swift test

Expand All @@ -25,6 +26,8 @@ jobs:
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6

- uses: ./.github/actions/select-xcode

- name: Read Rust toolchain version
id: rust-toolchain
run: |
Expand All @@ -37,16 +40,100 @@ jobs:
toolchain: ${{ steps.rust-toolchain.outputs.version }}
targets: aarch64-apple-darwin,x86_64-apple-darwin,aarch64-apple-ios,aarch64-apple-ios-sim,x86_64-apple-ios

- name: Build Rust XCFramework
run: bash scripts/rust/build/build-rust-xcframework.sh
- name: Build Apple Rust slices
run: bash scripts/rust/build/build-rust-apple-slices.sh

- name: Assemble Apple-only artifactbundle
# The Linux slices are built in a separate release-pipeline job.
# For the from-source PR check on macOS we assemble whatever is
# present (Apple slices only) — SwiftPM picks the host's variant
# at link time, so an Apple-only bundle is sufficient here.
run: bash scripts/rust/build/assemble-artifactbundle.sh ci-from-source

- name: Run tests against locally built XCFramework
- name: Run tests against locally built artifactbundle
env:
TOKENIZERS_RUST_LOCAL_XCFRAMEWORK_PATH: Binaries/TokenizersRust.xcframework
TOKENIZERS_RUST_LOCAL_ARTIFACTBUNDLE_PATH: rust/target/artifactbundle/TokenizersRust.artifactbundle
run: |
swift package clean
swift test

test-linux:
# Build the Rust crate for the host Linux target, assemble a
# Linux-only artifactbundle, and run the offline test subset
# against it. Catches PRs that touch `Sources/Tokenizers/`,
# the UniFFI wrapper, or the Rust crate before merge rather
# than at release time.
#
# The job filters to AlgorithmTests + UniffiSpikeTests because
# the other test files go through `HubClient` from `swift-hf-api`,
# which triggers a deterministic `libcurl.Easy Code=43`
# (`CURLE_BAD_FUNCTION_ARGUMENT`) crash in
# `_HTTPURLProtocol.configureEasyHandle` on Swift 6.2.x / 6.3.x +
# Linux when curl reuses a handle across an HTTP redirect. The
# crash is upstream FoundationNetworking, not anything in this
# repo (which does not use URLSession at all).
#
# Issue: https://github.com/swiftlang/swift-corelibs-foundation/issues/5445
# Fix: https://github.com/swiftlang/swift-corelibs-foundation/pull/5448
# (merged 2026-04-08; drops the `preferredReceiveBufferSize`
# call that libcurl 8.5.0 rejects on a reused handle)
#
# The fix is on `main` and `release/6.4.x` only — not backported
# to 6.2.x or 6.3.x — so the first user-visible Swift release
# containing it is 6.4. When the `container:` below is bumped
# to `swift:6.4` or later, drop the `--filter` flags in the
# test step to run the full suite.
name: Test (Linux, offline subset)
runs-on: ubuntu-latest
container: swift:6.2.3
defaults:
run:
# Inside the swift container the runner falls back to /bin/sh,
# which rejects `set -o pipefail`. Force bash so inline scripts
# behave the same as on a regular Linux runner.
shell: bash
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6

- name: Read Rust toolchain version
id: rust-toolchain
run: |
version=$(awk -F'"' '/^channel/ {print $2; exit}' rust-toolchain.toml)
echo "version=$version" >> "$GITHUB_OUTPUT"

- name: Install rustup and the host Linux target
# The container is a Swift toolchain image without rustup.
# `dtolnay/rust-toolchain` does not work inside containers, so
# bootstrap rustup directly.
env:
RUST_VERSION: ${{ steps.rust-toolchain.outputs.version }}
run: |
set -euo pipefail
apt-get update -qq
apt-get install -y --no-install-recommends \
curl ca-certificates build-essential pkg-config python3 zip
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \
| sh -s -- -y --default-toolchain "${RUST_VERSION}" --profile minimal
echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"

- name: Build host Linux Rust slice
env:
TOKENIZERS_LINUX_TARGETS: x86_64-unknown-linux-gnu
run: bash scripts/rust/build/build-rust-linux-archives.sh

- name: Assemble Linux-only artifactbundle
run: bash scripts/rust/build/assemble-artifactbundle.sh ci-linux-from-source

- name: Run offline-only swift test against the locally assembled bundle
env:
TOKENIZERS_RUST_LOCAL_ARTIFACTBUNDLE_PATH: rust/target/artifactbundle/TokenizersRust.artifactbundle
run: |
swift package clean
# TODO(swift-6.4): drop --filter flags once the
# `container:` above is bumped to swift:6.4 or later;
# the job-level comment has the upstream issue links.
swift test --filter Algorithm --filter UniffiSpike

wrapper-drift-changes:
# Detects whether the PR touched any input that influences the generated
# UniFFI wrapper, so the (expensive) `wrapper-drift` job below can be
Expand Down Expand Up @@ -74,9 +161,10 @@ jobs:
# `Sources/TokenizersFFI/Generated/TokenizersFFI.swift` still matches what
# the bindgen would emit for the current Rust source. The check rebuilds
# only the host static library and the bindgen binary (no per-target
# matrix, no xcframework), but is still gated by `wrapper-drift-changes`
# on PRs that don't touch the relevant inputs to skip the cargo build
# entirely. `push` and `workflow_dispatch` always run the check.
# matrix, no artifactbundle assembly), but is still gated by
# `wrapper-drift-changes` on PRs that don't touch the relevant inputs to
# skip the cargo build entirely. `push` and `workflow_dispatch` always
# run the check.
name: UniFFI wrapper drift
runs-on: macos-latest
needs: wrapper-drift-changes
Expand All @@ -86,6 +174,8 @@ jobs:
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6

- uses: ./.github/actions/select-xcode

- name: Read Rust toolchain version
id: rust-toolchain
run: |
Expand All @@ -107,6 +197,8 @@ jobs:
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6

- uses: ./.github/actions/select-xcode

- name: Verify documentation
run: bash scripts/verify-docs.sh

Expand All @@ -116,6 +208,8 @@ jobs:
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6

- uses: ./.github/actions/select-xcode

- run: scripts/swift-format.sh

- name: Suggest fixes (if check fails)
Expand Down
Loading
Loading