Skip to content
Open
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
64 changes: 49 additions & 15 deletions .github/actions/move-artifacts/move-artifacts.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,28 +33,42 @@ async function moveArtifacts() {
console.log('Starting artifact move process...');

try {
// Find and move rslint binaries
// Move Go CLI binaries into the @rslint/native-{tuple} subpackages (flat,
// alongside the .node). Artifacts are named `{platform}-rslint`. Go
// binaries are statically linked, so one linux build serves both glibc and
// musl — it's copied into both the -gnu and -musl tuple dirs.
const platformToTuples = {
'darwin-arm64': ['darwin-arm64'],
'darwin-x64': ['darwin-x64'],
'linux-arm64': ['linux-arm64-gnu', 'linux-arm64-musl'],
'linux-x64': ['linux-x64-gnu', 'linux-x64-musl'],
'win32-arm64': ['win32-arm64-msvc'],
'win32-x64': ['win32-x64-msvc'],
};

const rslintFiles = findBinaries('binaries', '-rslint');
console.log(`Found ${rslintFiles.length} rslint binary files`);

for (const file of rslintFiles) {
console.log(`Processing ${file}`);
const isWindows = file.includes('win32');
const filename = path.basename(file);
const dirname = filename.replace(/-rslint$/, '');
const targetDir = path.join('npm', 'rslint', dirname);

const targetFile = path.join(
targetDir,
isWindows ? 'rslint.exe' : 'rslint',
);

// Create target directory and copy file
fs.mkdirSync(targetDir, { recursive: true });
fs.copyFileSync(file, targetFile);
fs.chmodSync(targetFile, 0o755); // Make executable

console.log(`Copied ${file} to ${targetFile}`);
const platform = filename.replace(/-rslint$/, ''); // e.g. linux-x64
const tuples = platformToTuples[platform];
if (!tuples) {
console.log(`Warning: no tuple mapping for ${platform}, skipping`);
continue;
}
const binName = isWindows ? 'rslint.exe' : 'rslint';

for (const tuple of tuples) {
const targetDir = path.join('npm', 'rslint', tuple);
const targetFile = path.join(targetDir, binName);
fs.mkdirSync(targetDir, { recursive: true });
fs.copyFileSync(file, targetFile);
fs.chmodSync(targetFile, 0o755); // Make executable
console.log(`Copied ${file} to ${targetFile}`);
}
}

// Find and move tsgo binaries to lib directory
Expand Down Expand Up @@ -129,6 +143,26 @@ async function moveArtifacts() {
console.log(`Copied built files to ${targetLibDir}`);
}

// Move napi `.node` parser binaries into the @rslint/native-{tuple}
// subpackages (flat, alongside the Go binary). Artifacts are named
// `rslint.{tuple}.node`; target is `npm/rslint/{tuple}/` (libc suffix kept
// so gnu/musl stay separate). Not chmod'd — a `.node` is dlopen'd.
const nodeFiles = findBinaries('binaries', 'rslint.');
console.log(`Found ${nodeFiles.length} napi .node files`);

for (const file of nodeFiles) {
console.log(`Processing ${file}`);
const filename = path.basename(file); // rslint.linux-x64-gnu.node
const tuple = filename.replace(/^rslint\./, '').replace(/\.node$/, ''); // linux-x64-gnu
const targetDir = path.join('npm', 'rslint', tuple);
const targetFile = path.join(targetDir, filename);

fs.mkdirSync(targetDir, { recursive: true });
fs.copyFileSync(file, targetFile);

console.log(`Copied ${file} to ${targetFile}`);
}

console.log('Artifact move process completed successfully!');
} catch (error) {
console.error('Error:', error.message);
Expand Down
48 changes: 48 additions & 0 deletions .github/actions/setup-rust/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
name: Setup Rust
description: Install the pinned Rust toolchain and optionally enable build caching.

inputs:
targets:
description: Comma-separated target triples to install (e.g. aarch64-unknown-linux-gnu).
required: false
default: ''
components:
description: Comma-separated rustup components to install (e.g. rustfmt, clippy).
required: false
default: ''
cache:
description: Whether to enable Swatinem/rust-cache.
required: false
default: 'true'
cache-key:
description: Extra key segment for the cache (e.g. the target triple on the napi matrix).
required: false
default: ''
cache-workspaces:
description: Workspaces passed to Swatinem/rust-cache (defaults to the repo root).
required: false
default: ''
cache-bin:
description: >
Whether rust-cache caches ~/.cargo/bin. Set 'false' on runner images where
caching the cargo shim breaks `cargo fmt`/`clippy` (Swatinem/rust-cache#341).
required: false
default: 'true'

runs:
using: composite
steps:
- name: Install Rust
uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable
with:
targets: ${{ inputs.targets }}
components: ${{ inputs.components }}

- name: Rust cache
if: ${{ inputs.cache == 'true' }}
uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
with:
key: ${{ inputs.cache-key }}
workspaces: ${{ inputs.cache-workspaces }}
cache-bin: ${{ inputs.cache-bin }}
cache-on-failure: true
13 changes: 12 additions & 1 deletion .github/workflows/benchmark-cli.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,19 @@ jobs:
- name: Setup Node.js
uses: ./.github/actions/setup-node

- name: Setup Rust
uses: ./.github/actions/setup-rust
with:
cache: 'false'

# build:js is now one rslib build covering the library surface AND the
# eslint-plugin worker; the worker imports @rslint/native, whose
# index.d.ts comes from `napi build`. So build napi first even though the
# CLI benchmarks only exercise the Go binary.
- name: Build napi parser
run: pnpm --filter @rslint/native run build
- name: Build JS package
run: pnpm --filter @rslint/core run build
run: pnpm --filter @rslint/core run build:bin && pnpm --filter @rslint/core run build:js

- name: Run CLI benchmarks
uses: CodSpeedHQ/action@d872884a306dd4853acf0f584f4b706cf0cc72a2
Expand Down
32 changes: 24 additions & 8 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,13 @@ jobs:
- name: Setup Node.js
uses: ./.github/actions/setup-node

# napi build produces @rslint/native's index.d.ts (needed by core's
# build:js dts) + the win32 .node (needed at test runtime).
- name: Setup Rust
uses: ./.github/actions/setup-rust
- name: Build napi parser (native)
run: pnpm --filter @rslint/native run build

- name: Download rslint.exe
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
Expand All @@ -155,7 +162,6 @@ jobs:
pnpm --filter @rslint/core run build:js
pnpm --filter @typescript-eslint/rule-tester build
pnpm --filter rslint build
pnpm --filter @rslint/eslint-plugin-runner build

- name: VSCode Test Cache
uses: lynx-infra/cache@5c6160a6a4c7fca80a2f3057bb9dfc9513fcb732
Expand Down Expand Up @@ -224,6 +230,13 @@ jobs:
- name: Setup Node.js
uses: ./.github/actions/setup-node

# napi build produces @rslint/native's index.d.ts (needed by core's
# build:js dts) + the host .node (needed at test runtime).
- name: Setup Rust
uses: ./.github/actions/setup-rust
- name: Build napi parser (native)
run: pnpm --filter @rslint/native run build

- name: Format
if: runner.os == 'Linux'
run: pnpm format:check
Expand Down Expand Up @@ -279,8 +292,16 @@ jobs:
- name: Setup Node.js
uses: ./.github/actions/setup-node

- name: Setup Rust
uses: ./.github/actions/setup-rust
with:
cache: 'false'

# build:js is one rslib build (library surface + eslint-plugin worker);
# the worker needs @rslint/native's index.d.ts from `napi build`.
- name: Build
run: |
pnpm --filter @rslint/native run build
pnpm --filter '@rslint/core' build:js
pnpm --filter '@rslint/wasm' build
test-rust:
Expand All @@ -297,15 +318,10 @@ jobs:
submodules: true

- name: Setup Rust
uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable
uses: ./.github/actions/setup-rust
with:
components: rustfmt, clippy

- name: Rust Cache
uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
with:
workspaces: '.'
cache-on-failure: true
cache-workspaces: '.'
# Workaround for Swatinem/rust-cache#341: on the new macos runner image
# caching ~/.cargo/bin restores a broken `cargo` shim that resolves to
# rustup-init, causing `cargo fmt`/`clippy` to fail with
Expand Down
113 changes: 106 additions & 7 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ jobs:
publish-npm:
if: ${{ inputs.to_release == 'all' || inputs.to_release == 'npm' }}
name: ${{ inputs.dry_run == true && 'Dry Run - NPM Packages' || 'Publish NPM Packages' }}
needs: [build]
needs: [build, napi-build]
runs-on: ubuntu-22.04
environment: release
permissions:
Expand Down Expand Up @@ -96,6 +96,17 @@ jobs:

- name: Move binaries
uses: ./.github/actions/move-artifacts
# @rslint/native ships index.js (the napi loader) + index.d.ts, both
# generated by `napi build` (gitignored). core's build:js dts bundle
# also needs index.d.ts. The per-platform .node binaries ship in the
# @rslint/native-{tuple} subpackages, populated from the napi-build
# artifacts by move-artifacts above.
- name: Setup Rust
uses: ./.github/actions/setup-rust
with:
cache: 'false'
- name: Build napi parser (index.js + index.d.ts)
run: pnpm --filter @rslint/native build
- name: Build @rslint/core dist
run: pnpm --filter @rslint/core build:js
- name: Publish npm packages
Expand Down Expand Up @@ -244,6 +255,15 @@ jobs:
- name: Format code
run: pnpm format:check

# napi build produces index.d.ts (core build:js dts) + host .node
# (runner tests at the Test step run the worker, which loads it).
- name: Setup Rust
uses: ./.github/actions/setup-rust
with:
cache: 'false'
- name: Build napi parser (native)
run: pnpm --filter @rslint/native build

- name: Build
run: pnpm run build

Expand Down Expand Up @@ -315,12 +335,7 @@ jobs:
cache-name: ${{ matrix.node_os }}-${{ matrix.node_arch }}

- name: Setup Rust
uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable

- name: Cache Rust
uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
with:
cache-on-failure: true
uses: ./.github/actions/setup-rust

- name: Install pnpm
run: corepack enable
Expand Down Expand Up @@ -361,3 +376,87 @@ jobs:
with:
name: ${{ matrix.node_os }}-${{ matrix.node_arch }}-tsgo-built
path: typescript-go/built

# napi `.node` parser binaries, one per target → the @rslint/native-{tuple}
# subpackages. Separate matrix from the Go build job (which is ubuntu-only):
# napi win32-msvc/darwin need native runners since zig and
# @napi-rs/cross-toolchain can't target windows-msvc. Each leg uploads a
# uniquely-named artifact (`native-{tuple}` — upload-artifact@v4 rejects
# dupes); move-artifacts matches the `rslint.` filename prefix. gnu cross
# legs use --use-napi-cross (@napi-rs/cross-toolchain, gnu-only); musl uses
# -x (cargo-zigbuild) since cross-toolchain has no musl sysroot.
napi-build:
name: Build napi (${{ matrix.settings.target }})
strategy:
fail-fast: false
matrix:
settings:
- host: macos-latest
target: aarch64-apple-darwin
artifact: native-darwin-arm64
- host: macos-latest
target: x86_64-apple-darwin
artifact: native-darwin-x64
- host: windows-latest
target: x86_64-pc-windows-msvc
artifact: native-win32-x64-msvc
- host: windows-latest
target: aarch64-pc-windows-msvc
artifact: native-win32-arm64-msvc
- host: rspack-ubuntu-22.04-large
target: x86_64-unknown-linux-gnu
artifact: native-linux-x64-gnu
- host: rspack-ubuntu-22.04-large
target: aarch64-unknown-linux-gnu
artifact: native-linux-arm64-gnu
cross: '--use-napi-cross'
- host: rspack-ubuntu-22.04-large
target: x86_64-unknown-linux-musl
artifact: native-linux-x64-musl
cross: '-x'
- host: rspack-ubuntu-22.04-large
target: aarch64-unknown-linux-musl
artifact: native-linux-arm64-musl
cross: '-x'
runs-on: ${{ matrix.settings.host }}
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
fetch-depth: 1
ref: ${{ github.event.inputs.branch }}
submodules: true

- name: Setup Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with:
node-version: '24'

- name: Install pnpm
run: corepack enable

- name: Setup Rust
uses: ./.github/actions/setup-rust
with:
targets: ${{ matrix.settings.target }}
cache-key: ${{ matrix.settings.target }}

- name: Install dependencies
run: pnpm install --frozen-lockfile

# musl legs cross-compile via cargo-zigbuild (napi -x); zig must be on
# PATH (@napi-rs/cli auto-installs cargo-zigbuild when missing, but not
# zig itself).
- name: Setup zig (musl cross-compile)
if: ${{ contains(matrix.settings.target, 'musl') }}
uses: mlugg/setup-zig@d1434d08867e3ee9daa34448df10607b98908d29 # v2

- name: Build napi
run: pnpm --filter @rslint/native exec napi build --platform --release --target ${{ matrix.settings.target }} ${{ matrix.settings.cross || '' }}

- name: Upload napi artifact
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: ${{ matrix.settings.artifact }}
path: crates/rslint-native/rslint.*.node
if-no-files-found: error
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,16 @@ npm/*/rslint
npm/*/rslint.exe
npm/tsgo/*/local

# napi build artifacts (generated by @napi-rs/cli; never committed)
crates/rslint-native/index.js
crates/rslint-native/index.d.ts
crates/rslint-native/*.node
# per-platform binaries (napi .node + Go CLI) land in the subpackages at
# publish time via move-artifacts; never committed
npm/rslint/*/*.node
npm/rslint/*/rslint
npm/rslint/*/rslint.exe

.idea

## vscode settings
Expand Down
2 changes: 2 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,5 @@ internal/rules/**/*.md
website/docs/en/rules/_meta.json
website/docs/en/rules/*/
packages/vscode-extension/__tests__/fixtures-monorepo/packages/broken/
crates/rslint-toolkit/index.js
crates/rslint-toolkit/index.d.ts
Loading
Loading