Files
oxc/.github/workflows/ci.yml
T
Yuji Sugiura 296623d868 feat(oxfmt)!: format parser:html files by oxc_formatter_htmlish (#28273)
Fixes #28263 

- Before this PR: `.html` and also html-in-{js,md,etc} are formatted by
Prettier.
  - Because of that, we had to run JS, which became an overhead.
- After this PR: All of them are formatted natively by
`oxc_formatter_htmlish`.
  - No overhead.

At the same time, since we are fixing Prettier bugs and inconsistent
behavior, please refer to DIVERGENCES.md for the differences.
2026-10-30 16:29:13 +09:00

1010 lines
44 KiB
YAML

name: CI
permissions: {}
on:
workflow_dispatch:
pull_request:
types: [opened, synchronize]
merge_group:
push:
branches:
- main
concurrency:
group: ${{ github.workflow }}-${{ github.ref_name }}-${{ github.event.pull_request.number || github.sha }}
cancel-in-progress: ${{ github.ref_name != 'main' }}
env:
INSTA_REQUIRE_FULL_MATCH: 1
jobs:
test-ubuntu:
name: Test Linux
runs-on: namespace-profile-linux-x64-default
steps:
- uses: taiki-e/checkout-action@7d1e50e93dc4fb3bba58f85018fadf77898aee8b # v1.4.2
- uses: oxc-project/setup-node@f46a72f95efdc55273fcd042d61c84e723b2892c # v1.4.1
- uses: namespacelabs/nscloud-cache-action@1124a6f3ce44e5cf84cc22111530961f4d2a15f9 # v1.7.0
with:
path: |
/home/runner/.rustup
./crates/oxc_formatter_tests/prettier
cache: |
rust
pnpm
- run: cargo ck
- run: cargo test --all-features
- run: git diff --exit-code # Must commit everything
test-ubuntu-aarch64:
if: ${{ github.ref_name == 'main' }}
name: Test Linux ARM64
runs-on: ubuntu-24.04-arm
steps:
- uses: taiki-e/checkout-action@7d1e50e93dc4fb3bba58f85018fadf77898aee8b # v1.4.2
- parallel:
- uses: oxc-project/setup-node@f46a72f95efdc55273fcd042d61c84e723b2892c # v1.4.1
- uses: oxc-project/setup-rust@3d6fb132fbe7cdcb66bf8ec193911c2945369d12 # v1.0.17
with:
save-cache: ${{ github.ref_name == 'main' }}
cache-key: warm-aarch64
- run: cargo ck
- run: cargo test --all-features
- run: git diff --exit-code # Must commit everything
test-mac: # Separate job to save a job on PRs
if: ${{ github.ref_name == 'main' }}
name: Test Mac
runs-on: namespace-profile-mac-default
steps:
- uses: taiki-e/checkout-action@7d1e50e93dc4fb3bba58f85018fadf77898aee8b # v1.4.2
# `cache: pnpm` is intentionally omitted. On Mac OS, `cache: pnpm` replaces the pnpm store directory
# (`~/setup-pnpm/node_modules/.bin/store/v11`) with a symlink into the Namespace cache volume.
# But `oxc-project/setup-node` also caches that same path via `actions/cache`, and at job end
# `tar` archives the symlink record itself rather than the store contents - producing a faulty cache.
# `actions/cache` already handles pnpm correctly here, only `rust` needs the NS volume.
# Note: On Linux this action uses bind-mounts, which don't have this problem - it's a Mac OS problem only.
- parallel:
- uses: oxc-project/setup-node@f46a72f95efdc55273fcd042d61c84e723b2892c # v1.4.1
- uses: namespacelabs/nscloud-cache-action@1124a6f3ce44e5cf84cc22111530961f4d2a15f9 # v1.7.0
with:
cache: rust
- run: cargo ck
- run: cargo test --all-features
- run: git diff --exit-code # Must commit everything
test-windows:
if: ${{ github.ref_name == 'main' || github.event_name == 'workflow_dispatch' }}
name: Test Windows
runs-on: windows-latest
steps:
- run: git config --system core.longpaths true
- uses: taiki-e/checkout-action@7d1e50e93dc4fb3bba58f85018fadf77898aee8b # v1.4.2
# Unsung heroes of the internet, who led me here to speed up Windows' slowness:
# https://github.com/actions/cache/issues/752#issuecomment-1847036770
# https://github.com/astral-sh/uv/blob/502e04200d52de30d3159894833b3db4f0d6644d/.github/workflows/ci.yml#L158
- uses: samypr100/setup-dev-drive@562171fe8df7401fdf582d766cd3b6ab80d5b1a0 # v4.1.0
with:
workspace-copy: true
drive-size: 12GB
drive-format: ReFS
env-mapping: |
CARGO_HOME,{{ DEV_DRIVE }}/.cargo
RUSTUP_HOME,{{ DEV_DRIVE }}/.rustup
# Copy before installing: the Dev Drive action does not preserve pnpm 12 directory symlinks.
- parallel:
- uses: oxc-project/setup-node@f46a72f95efdc55273fcd042d61c84e723b2892c # v1.4.1
- name: Install Rust
working-directory: ${{ env.DEV_DRIVE_WORKSPACE }}
shell: bash
run: |
# This `awk` command will find the value of our Minimum Supported Rust Version and store it as `MSRV`.
# NOTE: this will fail if there are any other items named `rust-version`. We assume there is only one in our `Cargo.toml`.
MSRV=$(awk -F'=' '/rust-version/ {gsub(/[" ]/, "", $2); printf "%s", ($2 + "")}' Cargo.toml)
# Set profile to minimal and channel to our Minimum Supported Rust Version.
# Running our tests on this channel ensures that our code uses APIs that are supported in our `MSRV`.
sed -i -e 's/profile = "default"/profile = "minimal"/g' -e "s/channel = .*/channel = \"$MSRV\"/g" rust-toolchain.toml
rustup set profile minimal
rustup show
git restore .
- name: Install dependencies on Dev Drive
id: dependencies
working-directory: ${{ env.DEV_DRIVE_WORKSPACE }}
run: pnpm install --frozen-lockfile --ignore-scripts
background: true
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
with:
workspaces: ${{ env.DEV_DRIVE_WORKSPACE }}
save-if: ${{ github.ref_name == 'main' }}
shared-key: windows-latest
- name: Compile tests
run: cargo test --all-features --no-run
working-directory: ${{ env.DEV_DRIVE_WORKSPACE }}
shell: bash
- wait: dependencies
- name: Run tests
# No need for `cargo ck` because it's already checked in linux
# Run `website` tests separately to avoid feature unification problems with `oxlint`
run: cargo test --all-features
working-directory: ${{ env.DEV_DRIVE_WORKSPACE }}
shell: bash
test-big-endian:
if: ${{ github.ref_name == 'main' }}
name: Test big-endian # s390x-unknown-linux-gnu is a big-endian
runs-on: namespace-profile-linux-x64-default
steps:
- uses: taiki-e/checkout-action@7d1e50e93dc4fb3bba58f85018fadf77898aee8b # v1.4.2
- uses: namespacelabs/nscloud-cache-action@1124a6f3ce44e5cf84cc22111530961f4d2a15f9 # v1.7.0
with:
path: /home/runner/.rustup
cache: rust
- uses: taiki-e/install-action@f7e5d7c961414b23f5b25b2da9294395d08513ad # v2.87.26
with:
tool: cross
# `--lib --bins --tests` to skip doctests which are very slow to run via `cross`.
# https://github.com/cross-rs/cross/issues/1703
# `--skip prettier_conformance`: suite provisioning needs curl/tar, absent in the cross container
- run: cross test --lib --bins --tests --all-features --target s390x-unknown-linux-gnu -- --skip prettier_conformance
test-32bit:
if: ${{ github.ref_name == 'main' }}
name: Test 32-bit # armv7-unknown-linux-gnueabihf is a 32-bit target
runs-on: namespace-profile-linux-x64-default
steps:
- uses: taiki-e/checkout-action@7d1e50e93dc4fb3bba58f85018fadf77898aee8b # v1.4.2
- uses: namespacelabs/nscloud-cache-action@1124a6f3ce44e5cf84cc22111530961f4d2a15f9 # v1.7.0
with:
path: /home/runner/.rustup
cache: rust
- uses: taiki-e/install-action@f7e5d7c961414b23f5b25b2da9294395d08513ad # v2.87.26
with:
tool: cross
# `--lib --bins --tests` to skip doctests which are very slow to run via `cross`.
# https://github.com/cross-rs/cross/issues/1703
# `--skip prettier_conformance`: suite provisioning needs curl/tar, absent in the cross container
- run: cross test --workspace --lib --bins --tests --all-features --exclude oxc_language_server --exclude oxc_linter --exclude oxlint --target armv7-unknown-linux-gnueabihf -- --skip prettier_conformance
test-wasm32-wasip1-threads:
name: Test wasm32-wasip1-threads
runs-on: namespace-profile-linux-x64-default
steps:
- uses: taiki-e/checkout-action@7d1e50e93dc4fb3bba58f85018fadf77898aee8b # v1.4.2
- uses: $/.github/actions/check-changes
id: filter
with:
exclude: oxc_linter
paths: napi/,npm/,apps/,pnpm-lock.yaml,package.json
token: ${{ secrets.GITHUB_TOKEN }}
pr-number: ${{ github.event.pull_request.number }}
- uses: oxc-project/setup-node@f46a72f95efdc55273fcd042d61c84e723b2892c # v1.4.1
if: steps.filter.outputs.changed == 'true'
- uses: namespacelabs/nscloud-cache-action@1124a6f3ce44e5cf84cc22111530961f4d2a15f9 # v1.7.0
if: steps.filter.outputs.changed == 'true'
with:
path: /home/runner/.rustup
cache: |
rust
pnpm
- name: Install Playwright Chromium
id: playwright
if: steps.filter.outputs.changed == 'true'
working-directory: napi/parser
run: pnpm exec playwright install chromium
background: true
- name: Build
if: steps.filter.outputs.changed == 'true'
run: |
rustup target add wasm32-wasip1
rustup target add wasm32-wasip1-threads
pnpm run build-wasm-dev
pnpm run build-wasip1-dev
git diff --exit-code # Must commit everything
- wait: playwright
- name: Test wasi in browser
if: steps.filter.outputs.changed == 'true'
working-directory: napi/parser
run: |
rm -rf *.wasm
pnpm run build-wasi
pnpm run build-wasm-dev
pnpm run build-wasip1-dev
pnpm run build-npm-dir
pnpm run test-browser
pnpm build-browser-bundle --npmDir npm-dir
test-napi-compiler:
name: Test NAPI Compiler
runs-on: namespace-profile-linux-x64-default
steps:
- uses: taiki-e/checkout-action@7d1e50e93dc4fb3bba58f85018fadf77898aee8b # v1.4.2
- uses: $/.github/actions/check-changes
id: filter
with:
exclude: oxc_linter,oxc_language_server
paths: napi/parser/,napi/minify/,napi/transform/,napi/transform-react/,napi/transform-relay/,npm/oxc-types/,npm/runtime/,tasks/e2e/,pnpm-lock.yaml,package.json
token: ${{ secrets.GITHUB_TOKEN }}
pr-number: ${{ github.event.pull_request.number }}
- uses: $/.github/actions/clone-submodules
id: napi-fixtures
if: steps.filter.outputs.changed == 'true'
with:
babel: false
node-compat-table: false
background: true
- uses: oxc-project/setup-node@f46a72f95efdc55273fcd042d61c84e723b2892c # v1.4.1
if: steps.filter.outputs.changed == 'true'
- uses: namespacelabs/nscloud-cache-action@1124a6f3ce44e5cf84cc22111530961f4d2a15f9 # v1.7.0
if: steps.filter.outputs.changed == 'true'
with:
path: /home/runner/.rustup
cache: |
rust
pnpm
- wait: napi-fixtures
- if: steps.filter.outputs.changed == 'true'
name: Build packages
run: pnpm --workspace-concurrency=1 --filter "./napi/parser" --filter "./napi/minify" --filter "./napi/transform" --filter "./napi/transform-react" --filter "./napi/transform-relay" build-test
- parallel:
- if: steps.filter.outputs.changed == 'true'
name: Run tests
env:
RUN_RAW_RANGE_TESTS: "true"
RUN_RAW_TOKENS_TESTS: "true"
run: pnpm --workspace-concurrency=1 --filter "./napi/parser" --filter "./napi/minify" --filter "./napi/transform" --filter "./napi/transform-react" --filter "./napi/transform-relay" test
- if: steps.filter.outputs.changed == 'true'
name: Run e2e tests
working-directory: tasks/e2e
run: pnpm run test
- if: steps.filter.outputs.changed == 'true'
run: |
git diff --exit-code # Must commit everything
test-napi-shared:
name: Test NAPI Shared
runs-on: namespace-profile-linux-x64-default
steps:
- uses: taiki-e/checkout-action@7d1e50e93dc4fb3bba58f85018fadf77898aee8b # v1.4.2
- uses: $/.github/actions/check-changes
id: filter
with:
paths: apps/shared/,pnpm-lock.yaml,package.json
token: ${{ secrets.GITHUB_TOKEN }}
pr-number: ${{ github.event.pull_request.number }}
- uses: oxc-project/setup-node@f46a72f95efdc55273fcd042d61c84e723b2892c # v1.4.1
if: steps.filter.outputs.changed == 'true'
- uses: namespacelabs/nscloud-cache-action@1124a6f3ce44e5cf84cc22111530961f4d2a15f9 # v1.7.0
if: steps.filter.outputs.changed == 'true'
with:
path: /home/runner/.rustup
cache: |
pnpm
- if: steps.filter.outputs.changed == 'true'
name: Run tests
run: pnpm --filter "@oxapps/shared" test
test-napi-oxlint:
name: Test NAPI Oxlint
runs-on: namespace-profile-linux-x64-default
steps:
- uses: taiki-e/checkout-action@7d1e50e93dc4fb3bba58f85018fadf77898aee8b # v1.4.2
- uses: $/.github/actions/check-changes
id: filter
with:
paths: apps/oxlint/,apps/shared/,npm/oxlint/,npm/oxlint-plugin-eslint/,npm/oxlint-plugins/,npm/oxc-types/,crates/oxc_config/,pnpm-lock.yaml,package.json
token: ${{ secrets.GITHUB_TOKEN }}
pr-number: ${{ github.event.pull_request.number }}
- uses: oxc-project/setup-node@f46a72f95efdc55273fcd042d61c84e723b2892c # v1.4.1
if: steps.filter.outputs.changed == 'true'
- uses: namespacelabs/nscloud-cache-action@1124a6f3ce44e5cf84cc22111530961f4d2a15f9 # v1.7.0
if: steps.filter.outputs.changed == 'true'
with:
path: /home/runner/.rustup
cache: |
rust
pnpm
- if: steps.filter.outputs.changed == 'true'
name: Run tests
run: |
pnpm --filter "./apps/oxlint" build-test
pnpm --filter "./apps/oxlint" test
- if: steps.filter.outputs.changed == 'true'
run: |
git diff --exit-code # Must commit everything
test-napi-oxfmt:
name: Test NAPI Oxfmt
runs-on: namespace-profile-linux-x64-default
steps:
- uses: taiki-e/checkout-action@7d1e50e93dc4fb3bba58f85018fadf77898aee8b # v1.4.2
- uses: $/.github/actions/check-changes
id: filter
with:
paths: apps/oxfmt/,apps/shared/,npm/oxfmt/,crates/oxc_formatter/,crates/oxc_formatter_core/,crates/oxc_formatter_css/,crates/oxc_formatter_graphql/,crates/oxc_formatter_json/,crates/oxc_formatter_yaml/,crates/oxc_formatter_markdown/,crates/oxc_formatter_htmlish/,crates/oxc_formatter_toml/,crates/oxc_config/,pnpm-lock.yaml,package.json
token: ${{ secrets.GITHUB_TOKEN }}
pr-number: ${{ github.event.pull_request.number }}
- uses: $/.github/actions/check-changes
id: filter-conformance
with:
paths: apps/oxfmt/,npm/oxfmt/,crates/oxc_formatter/,crates/oxc_formatter_core/,crates/oxc_formatter_css/,crates/oxc_formatter_graphql/,crates/oxc_formatter_json/,crates/oxc_formatter_yaml/,crates/oxc_formatter_markdown/,crates/oxc_formatter_htmlish/,crates/oxc_formatter_toml/,pnpm-lock.yaml,package.json
token: ${{ secrets.GITHUB_TOKEN }}
pr-number: ${{ github.event.pull_request.number }}
- uses: oxc-project/setup-node@f46a72f95efdc55273fcd042d61c84e723b2892c # v1.4.1
if: steps.filter.outputs.changed == 'true' || steps.filter-conformance.outputs.changed == 'true'
- uses: namespacelabs/nscloud-cache-action@1124a6f3ce44e5cf84cc22111530961f4d2a15f9 # v1.7.0
if: steps.filter.outputs.changed == 'true'
with:
path: /home/runner/.rustup
cache: |
rust
pnpm
# Conformance ---
- uses: namespacelabs/nscloud-cache-action@1124a6f3ce44e5cf84cc22111530961f4d2a15f9 # v1.7.0
id: conformance-fixtures
if: steps.filter-conformance.outputs.changed == 'true'
with:
# Warm fixtures skip download entirely (`.version` stamps in download-fixtures.js)
path: ./apps/oxfmt/conformance/fixtures/externals
background: true
- if: steps.filter.outputs.changed == 'true' || steps.filter-conformance.outputs.changed == 'true'
name: Build tests
run: pnpm --filter "./apps/oxfmt" build-test
- wait: conformance-fixtures
- if: steps.filter-conformance.outputs.changed == 'true'
name: Download conformance fixtures
run: pnpm --filter "./apps/oxfmt" download-fixtures
- parallel:
- if: steps.filter.outputs.changed == 'true'
name: Run tests
run: pnpm --filter "./apps/oxfmt" test
- if: steps.filter-conformance.outputs.changed == 'true'
name: Run conformance tests
run: pnpm --filter "./apps/oxfmt" conformance
# --- Conformance
- if: steps.filter.outputs.changed == 'true' || steps.filter-conformance.outputs.changed == 'true'
run: |
git diff --exit-code # Must commit everything
test-napi-compiler-windows:
name: Test NAPI Compiler (windows)
runs-on: windows-latest
if: ${{ github.ref_name == 'main' || github.event_name == 'workflow_dispatch' }}
# Keep steps sequential to avoid event.json locking: https://github.com/actions/runner/issues/4641
steps:
- run: git config --system core.longpaths true
- uses: taiki-e/checkout-action@7d1e50e93dc4fb3bba58f85018fadf77898aee8b # v1.4.2
- uses: $/.github/actions/check-changes
id: filter
with:
exclude: oxc_linter,oxc_language_server
paths: napi/parser/,napi/minify/,napi/transform/,napi/transform-react/,napi/transform-relay/,npm/oxc-types/,npm/runtime/,tasks/e2e/,pnpm-lock.yaml,package.json
token: ${{ secrets.GITHUB_TOKEN }}
pr-number: ${{ github.event.pull_request.number }}
- uses: samypr100/setup-dev-drive@562171fe8df7401fdf582d766cd3b6ab80d5b1a0 # v4.1.0
if: steps.filter.outputs.changed == 'true'
with:
workspace-copy: true
drive-size: 12GB
drive-format: ReFS
env-mapping: |
CARGO_HOME,{{ DEV_DRIVE }}/.cargo
RUSTUP_HOME,{{ DEV_DRIVE }}/.rustup
# Copy before installing: the Dev Drive action does not preserve pnpm 12 directory symlinks.
- uses: oxc-project/setup-node@f46a72f95efdc55273fcd042d61c84e723b2892c # v1.4.1
if: steps.filter.outputs.changed == 'true'
- uses: oxc-project/setup-rust@3d6fb132fbe7cdcb66bf8ec193911c2945369d12 # v1.0.17
if: steps.filter.outputs.changed == 'true'
with:
cache-key: napi
save-cache: ${{ github.ref_name == 'main' }}
- uses: $/.github/actions/clone-submodules
if: steps.filter.outputs.changed == 'true'
with:
babel: false
node-compat-table: false
- name: Install dependencies on Dev Drive
if: steps.filter.outputs.changed == 'true'
working-directory: ${{ env.DEV_DRIVE_WORKSPACE }}
run: pnpm install --frozen-lockfile --ignore-scripts
- if: steps.filter.outputs.changed == 'true'
name: Build packages
working-directory: ${{ env.DEV_DRIVE_WORKSPACE }}
shell: bash
run: |
cargo build --profile coverage \
-p oxc_parser_napi \
-p oxc_minify_napi \
-p oxc_transform_napi \
-p oxc_transform_react_napi \
-p oxc_transform_relay_napi \
--features oxc_parser_napi/tokens
artifacts=(
"oxc_parser_napi.dll:napi/parser/src-js/parser.win32-x64-msvc.node"
"oxc_minify_napi.dll:napi/minify/minify.win32-x64-msvc.node"
"oxc_transform_napi.dll:napi/transform/transform.win32-x64-msvc.node"
"oxc_transform_react_napi.dll:napi/transform-react/transform-react.win32-x64-msvc.node"
"oxc_transform_relay_napi.dll:napi/transform-relay/transform-relay.win32-x64-msvc.node"
)
for artifact in "${artifacts[@]}"; do
IFS=: read -r source destination <<< "$artifact"
source="target/coverage/$source"
test -f "$source"
cp "$source" "$destination"
done
- if: steps.filter.outputs.changed == 'true'
name: Run tests
working-directory: ${{ env.DEV_DRIVE_WORKSPACE }}
env:
RUN_RAW_RANGE_TESTS: "true"
run: pnpm --workspace-concurrency=1 --filter "./napi/parser" --filter "./napi/minify" --filter "./napi/transform" --filter "./napi/transform-react" --filter "./napi/transform-relay" test
- if: steps.filter.outputs.changed == 'true'
name: Run e2e tests
working-directory: ${{ env.DEV_DRIVE_WORKSPACE }}/tasks/e2e
run: |
pnpm install --frozen-lockfile
pnpm run test
- if: steps.filter.outputs.changed == 'true'
working-directory: ${{ env.DEV_DRIVE_WORKSPACE }}
run: |
git diff --exit-code # Must commit everything
test-napi-oxlint-windows:
name: Test NAPI Oxlint (windows)
runs-on: windows-latest
if: ${{ github.ref_name == 'main' }}
steps:
- run: git config --system core.longpaths true
- uses: taiki-e/checkout-action@7d1e50e93dc4fb3bba58f85018fadf77898aee8b # v1.4.2
- uses: $/.github/actions/check-changes
id: filter
with:
paths: apps/oxlint/,apps/shared/,npm/oxlint/,npm/oxlint-plugin-eslint/,npm/oxlint-plugins/,npm/oxc-types/,crates/oxc_config/,pnpm-lock.yaml,package.json
token: ${{ secrets.GITHUB_TOKEN }}
pr-number: ${{ github.event.pull_request.number }}
- uses: samypr100/setup-dev-drive@562171fe8df7401fdf582d766cd3b6ab80d5b1a0 # v4.1.0
if: steps.filter.outputs.changed == 'true'
with:
workspace-copy: true
drive-size: 12GB
drive-format: ReFS
env-mapping: |
CARGO_HOME,{{ DEV_DRIVE }}/.cargo
RUSTUP_HOME,{{ DEV_DRIVE }}/.rustup
# Copy before installing: the Dev Drive action does not preserve pnpm 12 directory symlinks.
- parallel:
- uses: oxc-project/setup-node@f46a72f95efdc55273fcd042d61c84e723b2892c # v1.4.1
if: steps.filter.outputs.changed == 'true'
- uses: oxc-project/setup-rust@3d6fb132fbe7cdcb66bf8ec193911c2945369d12 # v1.0.17
if: steps.filter.outputs.changed == 'true'
with:
cache-key: napi
save-cache: ${{ github.ref_name == 'main' }}
- name: Install dependencies on Dev Drive
if: steps.filter.outputs.changed == 'true'
working-directory: ${{ env.DEV_DRIVE_WORKSPACE }}
run: pnpm install --frozen-lockfile --ignore-scripts
- if: steps.filter.outputs.changed == 'true'
name: Run tests
working-directory: ${{ env.DEV_DRIVE_WORKSPACE }}
run: |
pnpm --filter "./apps/oxlint" build-test
pnpm --filter "./apps/oxlint" test
- if: steps.filter.outputs.changed == 'true'
working-directory: ${{ env.DEV_DRIVE_WORKSPACE }}
run: |
git diff --exit-code # Must commit everything
test-napi-oxfmt-windows:
name: Test NAPI Oxfmt (windows)
runs-on: windows-latest
if: ${{ github.ref_name == 'main' }}
steps:
- run: git config --system core.longpaths true
- uses: taiki-e/checkout-action@7d1e50e93dc4fb3bba58f85018fadf77898aee8b # v1.4.2
- uses: $/.github/actions/check-changes
id: filter
with:
paths: apps/oxfmt/,apps/shared/,npm/oxfmt/,crates/oxc_formatter/,crates/oxc_formatter_core/,crates/oxc_formatter_css/,crates/oxc_formatter_graphql/,crates/oxc_formatter_json/,crates/oxc_formatter_yaml/,crates/oxc_formatter_markdown/,crates/oxc_formatter_htmlish/,crates/oxc_formatter_toml/,crates/oxc_config/,pnpm-lock.yaml,package.json
token: ${{ secrets.GITHUB_TOKEN }}
pr-number: ${{ github.event.pull_request.number }}
- uses: samypr100/setup-dev-drive@562171fe8df7401fdf582d766cd3b6ab80d5b1a0 # v4.1.0
if: steps.filter.outputs.changed == 'true'
with:
workspace-copy: true
drive-size: 12GB
drive-format: ReFS
env-mapping: |
CARGO_HOME,{{ DEV_DRIVE }}/.cargo
RUSTUP_HOME,{{ DEV_DRIVE }}/.rustup
# Copy before installing: the Dev Drive action does not preserve pnpm 12 directory symlinks.
- parallel:
- uses: oxc-project/setup-node@f46a72f95efdc55273fcd042d61c84e723b2892c # v1.4.1
if: steps.filter.outputs.changed == 'true'
- uses: oxc-project/setup-rust@3d6fb132fbe7cdcb66bf8ec193911c2945369d12 # v1.0.17
if: steps.filter.outputs.changed == 'true'
with:
cache-key: napi
save-cache: ${{ github.ref_name == 'main' }}
- name: Install dependencies on Dev Drive
if: steps.filter.outputs.changed == 'true'
working-directory: ${{ env.DEV_DRIVE_WORKSPACE }}
run: pnpm install --frozen-lockfile --ignore-scripts
- if: steps.filter.outputs.changed == 'true'
name: Run tests
working-directory: ${{ env.DEV_DRIVE_WORKSPACE }}
run: |
pnpm --filter "./apps/oxfmt" build-test
pnpm --filter "./apps/oxfmt" test
- if: steps.filter.outputs.changed == 'true'
working-directory: ${{ env.DEV_DRIVE_WORKSPACE }}
run: |
git diff --exit-code # Must commit everything
typos:
name: Spell Check
runs-on: ubuntu-slim
steps:
- uses: taiki-e/checkout-action@7d1e50e93dc4fb3bba58f85018fadf77898aee8b # v1.4.2
- uses: crate-ci/typos@7f9d0819ca1c008ade73f4b41fa9d258d48f605b # v1.51.1
with:
files: .
lint:
name: Lint
runs-on: namespace-profile-linux-x64-default
env:
CARGO_BUILD_WARNINGS: deny
steps:
- uses: taiki-e/checkout-action@7d1e50e93dc4fb3bba58f85018fadf77898aee8b # v1.4.2
- uses: oxc-project/setup-node@f46a72f95efdc55273fcd042d61c84e723b2892c # v1.4.1
- uses: namespacelabs/nscloud-cache-action@1124a6f3ce44e5cf84cc22111530961f4d2a15f9 # v1.7.0
with:
path: /home/runner/.rustup
cache: |
rust
pnpm
- name: JavaScript lint
id: javascript-lint
run: |
pnpm --filter oxlint-app build-js
pnpm --filter oxfmt-app build-js
pnpm --filter oxc-codegen build
node --run lint
background: true
- run: rustup component add clippy
- run: cargo lint
# Catch lints which only fire with `debug_assertions` off (i.e. release mode),
# e.g. code becoming unused when a `#[cfg(debug_assertions)]` block vanishes.
# Main-only and without `--all-targets`: these failures are rare and a post-merge
# failure pinpoints the culprit commit, so not worth doubling clippy time on every PR.
# `--all-targets` is omitted because test/bench code is never built in release mode
# with warnings denied.
- if: ${{ github.ref_name == 'main' }}
run: cargo clippy --all-features --profile dev-no-debug-assertions
- wait: javascript-lint
doc:
name: Doc
runs-on: namespace-profile-linux-x64-default
env:
TOOLCHAIN: nightly-2026-09-30
steps:
- uses: taiki-e/checkout-action@7d1e50e93dc4fb3bba58f85018fadf77898aee8b # v1.4.2
- uses: namespacelabs/nscloud-cache-action@1124a6f3ce44e5cf84cc22111530961f4d2a15f9 # v1.7.0
with:
path: /home/runner/.rustup
cache: rust
- run: rustup toolchain install "${TOOLCHAIN}" --profile minimal --component rust-docs
# https://github.com/rust-lang/rust/issues/146895
- run: RUSTDOCFLAGS='-D warnings' cargo +"${TOOLCHAIN}" doc --no-deps --document-private-items --all-features -Zrustdoc-mergeable-info
conformance:
name: Conformance
runs-on: namespace-profile-linux-x64-default
steps:
- uses: taiki-e/checkout-action@7d1e50e93dc4fb3bba58f85018fadf77898aee8b # v1.4.2
- name: Check if conformance should run
id: filter
uses: $/.github/actions/check-changes
with:
packages: oxc_coverage,oxc_transform_conformance
paths: tasks/coverage/,tasks/common/,tasks/transform_conformance/,pnpm-lock.yaml
token: ${{ secrets.GITHUB_TOKEN }}
pr-number: ${{ github.event.pull_request.number }}
- uses: $/.github/actions/clone-submodules
id: conformance-fixtures
if: steps.filter.outputs.changed == 'true'
with:
node-compat-table: false
background: true
- uses: oxc-project/setup-node@f46a72f95efdc55273fcd042d61c84e723b2892c # v1.4.1
if: steps.filter.outputs.changed == 'true'
- uses: namespacelabs/nscloud-cache-action@1124a6f3ce44e5cf84cc22111530961f4d2a15f9 # v1.7.0
if: steps.filter.outputs.changed == 'true'
with:
path: /home/runner/.rustup
cache: |
rust
pnpm
- uses: taiki-e/install-action@f7e5d7c961414b23f5b25b2da9294395d08513ad # v2.87.26
if: steps.filter.outputs.changed == 'true'
with:
tool: just
- wait: conformance-fixtures
- name: Check Conformance
if: steps.filter.outputs.changed == 'true'
run: |
just update-transformer-fixtures
just coverage
git diff --exit-code
lexer:
# `oxc_lexer` compiles to one of 3 implementations, chosen at build time:
# 1. AVX2 on x86_64 with `avx2` + `bmi2` + `popcnt`.
# 2. Neon on aarch64.
# 3. Generic everywhere else.
#
# Each leg of this job builds one of them. The AVX2 and generic legs run on x86_64, the Neon leg on aarch64.
#
# Coverage by other jobs:
# - None of `avx2`, `bmi2` and `popcnt` is in the x86_64 baseline on any platform, so the AVX2 implementation
# is never built unless asked for explicitly - which means no other job here lints, tests or runs it.
# - No other job builds Neon on PRs (the aarch64 test jobs only run on `main`), and no other job lints it.
# - Generic is already linted by the "Lint" job and tested by the "Test Linux" job,
# so this job only runs conformance for it.
#
# All legs regenerate the committed conformance snapshots and fail on any diff,
# which ensures the 3 implementations behave identically to each other.
#
# See the LEXER section of `justfile` for the recipes.
name: Lexer (${{ matrix.variant }})
runs-on: ${{ matrix.runner }}
env:
CARGO_BUILD_WARNINGS: deny
strategy:
fail-fast: false
matrix:
include:
- variant: generic
runner: namespace-profile-linux-x64-default
suffix: ""
- variant: AVX2
runner: namespace-profile-linux-x64-default
suffix: "-avx2"
- variant: Neon
runner: ubuntu-24.04-arm
suffix: ""
steps:
- uses: taiki-e/checkout-action@7d1e50e93dc4fb3bba58f85018fadf77898aee8b # v1.4.2
- name: Check if lexer checks should run
id: filter
uses: $/.github/actions/check-changes
with:
packages: oxc_lexer,oxc_coverage
paths: crates/oxc_lexer/,tasks/coverage/,.cargo/lexer-avx2.toml,justfile,.github/workflows/ci.yml
token: ${{ secrets.GITHUB_TOKEN }}
pr-number: ${{ github.event.pull_request.number }}
- uses: $/.github/actions/clone-submodules
id: lexer-fixtures
if: steps.filter.outputs.changed == 'true'
with:
node-compat-table: false
background: true
# The x86_64 legs run on Namespace runners, which provide the cache
- uses: namespacelabs/nscloud-cache-action@1124a6f3ce44e5cf84cc22111530961f4d2a15f9 # v1.7.0
if: ${{ steps.filter.outputs.changed == 'true' && matrix.variant != 'Neon' }}
with:
path: /home/runner/.rustup
cache: rust
- uses: taiki-e/install-action@f7e5d7c961414b23f5b25b2da9294395d08513ad # v2.87.26
if: ${{ steps.filter.outputs.changed == 'true' && matrix.variant != 'Neon' }}
with:
tool: just
- run: rustup component add clippy
# No need to run on generic - linting already covered by "Lint" job
if: ${{ steps.filter.outputs.changed == 'true' && matrix.variant == 'AVX2' }}
# The Neon leg runs on a GitHub-hosted runner, where Namespace's cache isn't available.
# Set up the same way as the "Test Linux ARM64" job, but with its own cache.
- uses: oxc-project/setup-rust@3d6fb132fbe7cdcb66bf8ec193911c2945369d12 # v1.0.17
if: ${{ steps.filter.outputs.changed == 'true' && matrix.variant == 'Neon' }}
with:
save-cache: ${{ github.ref_name == 'main' }}
cache-key: lexer-aarch64
tools: just
components: clippy
- name: Lint
# No need to run on generic - already covered by "Lint" job
if: ${{ steps.filter.outputs.changed == 'true' && matrix.variant != 'generic' }}
run: just lint-lexer${{ matrix.suffix }}
- name: Test
# No need to run on generic - already covered by "Test Linux" job
if: ${{ steps.filter.outputs.changed == 'true' && matrix.variant != 'generic' }}
run: just test-lexer${{ matrix.suffix }}
- wait: lexer-fixtures
- name: Conformance
# Run on all 3 implementations - none of them is run anywhere else.
# The harness prints which implementation it was built with (`oxc_lexer::IMPLEMENTATION`).
# Checking it here is what guarantees this leg exercised the build it asked for.
if: steps.filter.outputs.changed == 'true'
run: |
set -o pipefail
just conformance-lexer${{ matrix.suffix }} | tee "$RUNNER_TEMP/lexer.log"
grep -qxF "Lexer implementation: ${{ matrix.variant }}" "$RUNNER_TEMP/lexer.log" ||
{ echo "::error::Expected ${{ matrix.variant }}, but the harness reported otherwise"; exit 1; }
git diff --exit-code
lexer-benchmark:
# Benchmark `oxc_lexer`'s AVX2 implementation against the lexer in `oxc_parser`, built without the AVX2 flags
# (see `lexer` job above).
#
# Measures wallclock time, so doesn't use CodSpeed. `oxc_lexer` is designed to exploit out-of-order execution
# and branch prediction, which CodSpeed's CPU simulation does not model.
# Results are only printed in the log. The "change" reported for `oxc_lexer` is relative to `oxc_parser`.
#
# Only runs when `oxc_lexer` or the benchmark itself changes.
# See the LEXER section of `justfile` for the recipe.
name: Lexer Benchmark
runs-on: namespace-profile-linux-x64-default
steps:
- uses: taiki-e/checkout-action@7d1e50e93dc4fb3bba58f85018fadf77898aee8b # v1.4.2
- name: Check if lexer benchmark should run
id: filter
uses: $/.github/actions/check-changes
with:
paths: crates/oxc_lexer/,tasks/benchmark/benches/lexer_wallclock_old.rs,tasks/benchmark/benches/lexer_wallclock_new.rs,tasks/benchmark/benches/lexer_common.rs,.cargo/lexer-avx2.toml,justfile,.github/workflows/ci.yml
token: ${{ secrets.GITHUB_TOKEN }}
pr-number: ${{ github.event.pull_request.number }}
- uses: namespacelabs/nscloud-cache-action@1124a6f3ce44e5cf84cc22111530961f4d2a15f9 # v1.7.0
if: steps.filter.outputs.changed == 'true'
with:
path: /home/runner/.rustup
cache: rust
- uses: taiki-e/install-action@f7e5d7c961414b23f5b25b2da9294395d08513ad # v2.87.26
if: steps.filter.outputs.changed == 'true'
with:
tool: just
- name: Benchmark
# The `oxc_lexer` benchmark prints which implementation it was built with (`oxc_lexer::IMPLEMENTATION`).
# Checking it here is what guarantees this job benchmarked the AVX2 implementation.
# `just bench-lexer-avx2` builds the `oxc_parser` benchmark without the AVX2 flags (not checked here).
if: steps.filter.outputs.changed == 'true'
env:
# Keep criterion's saved results out of `target`, which may be cached.
# Otherwise criterion would also compare `oxc_parser` against an earlier CI run.
CRITERION_HOME: ${{ runner.temp }}/criterion
run: |
set -o pipefail
just bench-lexer-avx2 | tee "$RUNNER_TEMP/bench.log"
grep -qxF "Lexer implementation: AVX2" "$RUNNER_TEMP/bench.log" ||
{ echo "::error::Expected AVX2, but the benchmark reported otherwise"; exit 1; }
test-codegen:
name: Test Codegen
runs-on: namespace-profile-linux-x64-default
steps:
- uses: taiki-e/checkout-action@7d1e50e93dc4fb3bba58f85018fadf77898aee8b # v1.4.2
- name: Check if codegen tests should run
id: filter
uses: $/.github/actions/check-changes
with:
packages: oxc_codegen_conformance,oxc_parser_napi
paths: .github/workflows/ci.yml,packages/codegen/,tasks/codegen_conformance/,napi/parser/,npm/oxc-types/,tasks/coverage/,pnpm-lock.yaml,pnpm-workspace.yaml,package.json
token: ${{ secrets.GITHUB_TOKEN }}
pr-number: ${{ github.event.pull_request.number }}
- uses: $/.github/actions/clone-submodules
id: codegen-fixtures
if: steps.filter.outputs.changed == 'true'
with:
node-compat-table: false
background: true
- uses: oxc-project/setup-node@f46a72f95efdc55273fcd042d61c84e723b2892c # v1.4.1
if: steps.filter.outputs.changed == 'true'
- uses: namespacelabs/nscloud-cache-action@1124a6f3ce44e5cf84cc22111530961f4d2a15f9 # v1.7.0
if: steps.filter.outputs.changed == 'true'
with:
path: /home/runner/.rustup
cache: |
rust
pnpm
- wait: codegen-fixtures
- name: Run codegen tests
if: steps.filter.outputs.changed == 'true'
run: |
pnpm --filter oxc-codegen build-test
pnpm --filter oxc-codegen test
git diff --exit-code
minification:
name: Minification Size
runs-on: namespace-profile-linux-x64-default
steps:
- uses: taiki-e/checkout-action@7d1e50e93dc4fb3bba58f85018fadf77898aee8b # v1.4.2
- uses: $/.github/actions/check-changes
id: filter
with:
packages: oxc_minsize
token: ${{ secrets.GITHUB_TOKEN }}
pr-number: ${{ github.event.pull_request.number }}
- uses: namespacelabs/nscloud-cache-action@1124a6f3ce44e5cf84cc22111530961f4d2a15f9 # v1.7.0
if: steps.filter.outputs.changed == 'true'
with:
path: /home/runner/.rustup
cache: rust
- name: Check minification size
if: steps.filter.outputs.changed == 'true'
run: |
cargo minsize
git diff --exit-code
# Write a human-readable diff of the minified output vs the base branch
# to the job summary for review (same flow as `just minifier-diff`).
- name: Diff minify output
if: ${{ !cancelled() && steps.filter.outputs.changed == 'true' && github.event_name == 'pull_request' }}
env:
BASE_REF: ${{ github.event.pull_request.base.ref }}
run: |
cargo minsize --compress-only pr
if fetch_output=$(git fetch --depth 1 origin "$BASE_REF" 2>&1); then
printf '%s\n' "$fetch_output" >&2
else
fetch_status=$?
printf '%s\n' "$fetch_output" >&2
if [[ "$BASE_REF" == graphite-base/* ]] &&
grep -Fxq "fatal: couldn't find remote ref $BASE_REF" <<< "$fetch_output"; then
printf 'Skipped minify output diff: base ref `%s` is no longer available on origin.\n' "$BASE_REF" >> "$GITHUB_STEP_SUMMARY"
exit 0
fi
exit "$fetch_status"
fi
git checkout -f FETCH_HEAD
cargo minsize --compress-only main
echo '# Minify Output Diff' >> "$GITHUB_STEP_SUMMARY"
changed=false
for pr_file in target/minifier/pr/*.js; do
name=$(basename "$pr_file")
if ! diff -uN "target/minifier/main/$name" "$pr_file" > /tmp/minify.diff; then
changed=true
{
echo "## $name"
echo '```diff'
head -n 200 /tmp/minify.diff
if [ "$(wc -l < /tmp/minify.diff)" -gt 200 ]; then echo '... (truncated)'; fi
echo '```'
} >> "$GITHUB_STEP_SUMMARY"
fi
done
if [ "$changed" = false ]; then
echo 'No difference in compress-only output compared to the base branch.' >> "$GITHUB_STEP_SUMMARY"
fi
allocs:
name: Allocations
runs-on: namespace-profile-linux-x64-default
steps:
- uses: taiki-e/checkout-action@7d1e50e93dc4fb3bba58f85018fadf77898aee8b # v1.4.2
- uses: $/.github/actions/check-changes
id: filter
with:
packages: oxc_track_memory_allocations
paths: tasks/track_memory_allocations/
token: ${{ secrets.GITHUB_TOKEN }}
pr-number: ${{ github.event.pull_request.number }}
- uses: namespacelabs/nscloud-cache-action@1124a6f3ce44e5cf84cc22111530961f4d2a15f9 # v1.7.0
if: steps.filter.outputs.changed == 'true'
with:
path: /home/runner/.rustup
cache: rust
- name: Check allocations
if: steps.filter.outputs.changed == 'true'
run: |
cargo allocs
git diff --exit-code ||
(echo 'Allocations have changed. Run the `cargo allocs` command to update the allocation snapshot, otherwise please fix the regression.' && exit 1)
ast_changes:
name: AST Changes
runs-on: namespace-profile-linux-x64-default
steps:
- uses: taiki-e/checkout-action@7d1e50e93dc4fb3bba58f85018fadf77898aee8b # v1.4.2
- uses: dorny/paths-filter@ceb8a2b8f2d89434be7ff52d3de7ec3738c5cc9d # v4.0.3
id: filter
with:
filters: ".github/generated/ast_changes_watch_list.yml"
- uses: oxc-project/setup-node@f46a72f95efdc55273fcd042d61c84e723b2892c # v1.4.1
if: steps.filter.outputs.src == 'true'
- uses: namespacelabs/nscloud-cache-action@1124a6f3ce44e5cf84cc22111530961f4d2a15f9 # v1.7.0
if: steps.filter.outputs.src == 'true'
with:
path: /home/runner/.rustup
cache: |
rust
pnpm
- run: rustup component add rustfmt
if: steps.filter.outputs.src == 'true'
- name: Check AST Changes
if: steps.filter.outputs.src == 'true'
run: |
cargo run -p oxc_ast_tools
git diff --exit-code ||
(echo 'AST changes caused the "generated" code to get outdated. Have you forgotten to run the `just ast` command and/or commit generated codes?' && exit 1)
lintgen:
name: Linter changes
runs-on: namespace-profile-linux-x64-default
steps:
- uses: taiki-e/checkout-action@7d1e50e93dc4fb3bba58f85018fadf77898aee8b # v1.4.2
- uses: $/.github/actions/check-changes
id: filter
with:
packages: oxc_linter,oxc_track_linter_timings
paths: tasks/linter_codegen/,tasks/track_linter_timings/,npm/oxlint/configuration_schema.json,apps/oxlint/src-js/package/config.generated.ts,apps/oxlint/scripts/generate-config-types.ts,apps/oxlint/package.json,pnpm-lock.yaml,package.json
token: ${{ secrets.GITHUB_TOKEN }}
pr-number: ${{ github.event.pull_request.number }}
- uses: oxc-project/setup-node@f46a72f95efdc55273fcd042d61c84e723b2892c # v1.4.1
if: steps.filter.outputs.changed == 'true'
- uses: namespacelabs/nscloud-cache-action@1124a6f3ce44e5cf84cc22111530961f4d2a15f9 # v1.7.0
if: steps.filter.outputs.changed == 'true'
with:
path: /home/runner/.rustup
cache: |
rust
pnpm
- run: rustup component add rustfmt
if: steps.filter.outputs.changed == 'true'
- name: Check linter changes
if: steps.filter.outputs.changed == 'true'
run: |
cargo lintgen
pnpm --filter oxlint-app generate-config-types
cargo lint-timings
git diff --exit-code ||
(echo 'Linter generated files or timing snapshots have changed. Run `cargo lintgen`, `just linter-schema-json`, `just linter-config-ts`, and `cargo lint-timings`, then commit the generated files.' && exit 1)