Skip to content

feat(unicorn): add prefer-array-last-methods rule #9959

feat(unicorn): add prefer-array-last-methods rule

feat(unicorn): add prefer-array-last-methods rule #9959

Workflow file for this run

name: CI
on:
push:
branches: [main]
pull_request:
types: [opened, synchronize]
workflow_dispatch:
permissions:
# Required: allow read access to the content for analysis.
contents: read
# Optional: allow read access to pull requests. Use with `only-new-issues` option.
pull-requests: read
# Required: allow writing check results and annotations
checks: write
# Required: allow updating commit statuses
statuses: write
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}
cancel-in-progress: ${{ github.ref_name != 'main' }}
jobs:
changes:
name: Detect changes
runs-on: rspack-ubuntu-22.04-mini
permissions:
contents: read
pull-requests: read
outputs:
changed: ${{ steps.changes.outputs.changed }}
go-mode: ${{ steps.changes.outputs.go-mode }}
go-changed-files: ${{ steps.changes.outputs.go-changed-files }}
steps:
- name: Checkout code
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
fetch-depth: 1
- name: Detect changes
id: changes
uses: ./.github/actions/detect-changes
test-go:
name: Test Go
needs: changes
if: needs.changes.outputs.go-mode != 'skip'
runs-on: ${{ matrix.runner }}
timeout-minutes: 60
strategy:
fail-fast: false
matrix:
runner:
- rspack-ubuntu-22.04-large
- rspack-windows-2022-large
include: >-
${{ fromJSON(
(github.ref == 'refs/heads/main' || startsWith(github.head_ref || github.ref_name, 'chore/release-')) &&
'[{"runner":"rspack-darwin-15.6.1-medium"}]' || '[]'
) }}
steps:
- name: Checkout code
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
submodules: true
fetch-depth: 1
- name: Test Go
uses: ./.github/actions/test-go
with:
go-version: '1.27.0'
mode: ${{ needs.changes.outputs.go-mode }}
changed-files: ${{ needs.changes.outputs.go-changed-files }}
lint:
name: Lint
needs: changes
runs-on: rspack-ubuntu-22.04-large
steps:
- name: Checkout code
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
submodules: true
- name: Setup Node.js
uses: ./.github/actions/setup-node
- name: Format
run: pnpm format:check
- name: Setup Go
if: needs.changes.outputs.changed == 'true'
uses: ./.github/actions/setup-go
with:
go-version: 1.27.0
cache-name: ci-go-lint
- name: golangci-lint
if: needs.changes.outputs.changed == 'true'
uses: golangci/golangci-lint-action@4afd733a84b1f43292c63897423277bb7f4313a9 # v8
with:
version: v2.13.2
args: --timeout=15m ./cmd/... ./internal/...
- name: go fmt
if: needs.changes.outputs.changed == 'true'
run: npm run format:go
- name: Check Spell
run: pnpm check-spell
test-node:
name: Test npm packages
needs: changes
if: needs.changes.outputs.changed == 'true'
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [rspack-ubuntu-22.04-large]
go-version: ['1.27.0']
steps:
- name: Checkout code
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
submodules: true
- name: Setup Go
uses: ./.github/actions/setup-go
with:
go-version: ${{ matrix.go-version }}
cache-name: ci-test-node
- name: Setup Node.js
uses: ./.github/actions/setup-node
# napi build drops the host .node into its @rslint/native-{tuple} package;
# the worker loads it at test runtime.
- name: Setup Rust
uses: ./.github/actions/setup-rust
- name: Build napi parser (native)
run: pnpm --filter @rslint/native run build
# Rule options JSON Schema dump for the generate-rule-option-types
# rslib plugin (see packages/rslint/plugins/generate-rule-option-types.ts).
# Without this, `pnpm run build` below still succeeds but the plugin
# skips with a warning and dist/index.d.ts never gets typed rule
# options — so the vscode-extension e2e suite that exercises them
# (suite-rule-option-types) needs it in place first.
- name: Dump rule schemas
run: go run ./tools/dump_rule_schemas > packages/rslint/rule-schemas.json
- name: Build
run: pnpm run build
# `Build` already compiled every package behind ./cmd/rslint, so this
# relink is link-only off the warm cache. It measures the stripped
# executable users download, not the unstripped one tested above.
- name: Measure rslint binary size
env:
BINARY_SIZE_GO_VERSION: ${{ matrix.go-version }}
run: |
go build -ldflags="-s -w" -o "${RUNNER_TEMP}/rslint-release" ./cmd/rslint
node scripts/binary-size.mjs record "${RUNNER_TEMP}/rslint-release" "${RUNNER_TEMP}/binary-size.json"
# Uploaded on every run, main included: a pull request's baseline is the
# artifact of the run that measured the commit its build was merged onto.
- name: Upload rslint binary size
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: rslint-binary-size
path: ${{ runner.temp }}/binary-size.json
if-no-files-found: error
- name: Dogfooding
run: pnpm run lint --format github
- name: TypeCheck
run: pnpm typecheck
- name: Install xvfb and dependencies
run: |
sudo apt update
sudo apt install -y libasound2 libgbm1 libgtk-3-0 libnss3 xvfb
- name: VSCode Test Cache
uses: lynx-infra/cache@5c6160a6a4c7fca80a2f3057bb9dfc9513fcb732
with:
# Cache only immutable VS Code distributions. The v2 namespace keeps
# legacy archives containing user data/extensions from being restored.
path: packages/vscode-extension/.vscode-test/vscode-*
key: vscode-distributions-v2-${{ matrix.os }}
restore-keys: |
vscode-distributions-v2-${{ matrix.os }}-
- name: Test
run: xvfb-run -a pnpm run test
binary-size:
name: Binary Size
needs: test-node
# Reports only, deliberately kept out of `done` below: porting a rule
# legitimately grows the binary.
if: ${{ github.event_name == 'pull_request' && !cancelled() }}
runs-on: rspack-ubuntu-22.04-mini
permissions:
contents: read
# Reading the base commit's run and downloading its artifact.
actions: read
pull-requests: write
steps:
- name: Checkout code
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
fetch-depth: 1
- name: Setup Node
uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6
with:
node-version: '24'
# Missing when `test-node` failed before the measurement.
- name: Download this run's measurement
id: head-size
continue-on-error: true
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: rslint-binary-size
path: head-size
# On failure `run-id` stays empty and the report omits the comparison.
- name: Find the base measurement
id: base
if: steps.head-size.outcome == 'success'
continue-on-error: true
env:
GITHUB_TOKEN: ${{ github.token }}
run: node scripts/binary-size.mjs base-run head-size/binary-size.json
- name: Download the base measurement
if: steps.base.outputs.run-id != ''
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: rslint-binary-size
path: base-size
run-id: ${{ steps.base.outputs.run-id }}
github-token: ${{ github.token }}
- name: Write report
if: steps.head-size.outcome == 'success'
run: |
node scripts/binary-size.mjs report head-size/binary-size.json base-size/binary-size.json > binary-size-report.md
cat binary-size-report.md >> "$GITHUB_STEP_SUMMARY"
# A fork's token is read-only, so this fails there and the job summary
# written above stays the report.
- name: Comment on the pull request
if: steps.head-size.outcome == 'success'
continue-on-error: true
env:
GITHUB_TOKEN: ${{ github.token }}
run: |
if ! node scripts/binary-size.mjs comment binary-size-report.md; then
echo "Binary size comment failed; report follows:"
cat binary-size-report.md
exit 1
fi
test-node-windows:
name: Test npm packages (rspack-windows-2022-large)
needs: changes
if: needs.changes.outputs.changed == 'true'
runs-on: rspack-windows-2022-large
steps:
- name: Checkout code
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
submodules: true
fetch-depth: 1
- name: Disable CPU hogs (Windows)
if: runner.os == 'Windows'
continue-on-error: true
uses: ./.github/actions/windows-disable-cpu-hogs
- name: Setup Go
uses: ./.github/actions/setup-go
with:
go-version: 1.27.0
cache-name: ci-test-node-windows
- name: Setup Node.js
uses: ./.github/actions/setup-node
# napi build drops the host (win32) .node into its @rslint/native-{tuple}
# package; the worker loads it at test runtime.
- name: Setup Rust
uses: ./.github/actions/setup-rust
- name: Build napi parser (native)
run: pnpm --filter @rslint/native run build
- name: Build rslint.exe
shell: pwsh
run: go build -o npm/rslint/win32-x64-msvc/rslint.exe ./cmd/rslint
# Rule options JSON Schema dump for the generate-rule-option-types
# rslib plugin (see packages/rslint/plugins/generate-rule-option-types.ts).
# test-vscode-windows has no Go toolchain, so it's generated here (where
# Go is already set up) and handed off the same way rslint.exe is below.
- name: Dump rule schemas
shell: pwsh
run: go run ./tools/dump_rule_schemas > rule-schemas.json
# vscode-extension (package "rslint") is excluded here - it's tested on
# windows-latest instead, since the vscode-extension test does not run
# reliably on this self-hosted runner.
- name: Build
run: pnpm -r --filter=@rslint/test-tools... build
- name: Test
run: pnpm -r --filter=@rslint/test-tools... test
- name: Upload rslint.exe
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: ci-test-rslint-windows-amd64
path: npm/rslint/win32-x64-msvc/rslint.exe
retention-days: 1
if-no-files-found: error
- name: Upload rule schemas
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: ci-test-rule-schemas-windows
path: rule-schemas.json
retention-days: 1
if-no-files-found: error
test-vscode-windows:
name: Test vscode-extension (windows-latest)
runs-on: windows-latest
needs: test-node-windows
steps:
- name: Checkout code
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
submodules: true
- name: Setup Node.js
uses: ./.github/actions/setup-node
# napi build drops the host (win32) .node into its @rslint/native-{tuple}
# package; the worker loads it 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:
name: ci-test-rslint-windows-amd64
path: npm/rslint/win32-x64-msvc
- name: Download rule schemas
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: ci-test-rule-schemas-windows
path: packages/rslint
# build:js only - skip @rslint/core's build:bin, which invokes `go
# build` directly. The host rslint.exe was already built on the
# self-hosted Windows runner and downloaded above; this job has no Go
# toolchain set up and shouldn't need one.
- name: Build
shell: bash
run: |
pnpm --filter @rslint/api build
pnpm --filter @rslint/core run build:js
pnpm --filter rslint build
- name: VSCode Test Cache
uses: lynx-infra/cache@5c6160a6a4c7fca80a2f3057bb9dfc9513fcb732
with:
# Cache only immutable VS Code distributions. The v2 namespace keeps
# legacy archives containing user data/extensions from being restored.
path: packages/vscode-extension/.vscode-test/vscode-*
key: vscode-distributions-v2-windows
restore-keys: |
vscode-distributions-v2-windows-
- name: Test
run: pnpm --filter rslint test
test-wasm:
name: Test WASM
needs: changes
if: needs.changes.outputs.changed == 'true'
runs-on: rspack-ubuntu-22.04-large
steps:
- name: Checkout code
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
submodules: true
- name: Setup Go
uses: ./.github/actions/setup-go
with:
go-version: 1.27.0
cache-name: ci-test-wasm
- name: Setup Node.js
uses: ./.github/actions/setup-node
# build:js is one rslib build (library surface + eslint-plugin worker).
# The worker's native loader is bundled from core sources and resolves the
# platform .node at runtime, so no napi build / Rust is needed to compile.
- name: Build
run: |
pnpm --filter '@rslint/core' build:js
pnpm --filter '@rslint/wasm' build
test-rust:
name: Test Rust
needs: changes
if: needs.changes.outputs.changed == 'true'
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [macos-latest]
steps:
- name: Checkout code
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
submodules: true
- name: Setup Rust
uses: ./.github/actions/setup-rust
with:
components: rustfmt, clippy
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
# "unexpected argument 'fmt' found / Usage: rustup-init[EXE] [OPTIONS]".
cache-bin: 'false'
- name: Check formatting
run: cargo fmt --all -- --check
- name: Run clippy
run: cargo clippy --all-targets --all-features -- -D warnings
- name: Setup Node.js
uses: ./.github/actions/setup-node
- name: Setup Go
uses: ./.github/actions/setup-go
with:
go-version: 1.27.0
cache-name: ci-test-wasm
- name: Build tsgo server
run: pnpm --filter '@rslint/tsgo-server' build
- name: Run tests
run: cargo test --verbose
benchmark-go:
needs: changes
if: ${{ needs.changes.outputs.changed == 'true' && (startsWith(github.ref_name, 'chore/release-') || startsWith(github.head_ref, 'chore/release-')) }}
permissions:
contents: read
id-token: write
uses: ./.github/workflows/benchmark-go.yml
benchmark-cli:
needs: changes
if: ${{ needs.changes.outputs.changed == 'true' && (startsWith(github.ref_name, 'chore/release-') || startsWith(github.head_ref, 'chore/release-')) }}
permissions:
contents: read
id-token: write
uses: ./.github/workflows/benchmark-cli.yml
done:
needs:
- changes
- test-go
- test-node
- test-node-windows
- test-vscode-windows
- lint
- test-wasm
- test-rust
if: always()
runs-on: rspack-ubuntu-22.04-mini
name: CI Done
steps:
- run: exit 1
# The Go matrix has its own PR scope and requires every selected platform to pass.
# Other test jobs retain the existing documentation filter.
if: >-
${{ always() && (
contains(needs.*.result, 'failure') ||
contains(needs.*.result, 'cancelled') ||
needs.changes.result != 'success' ||
needs.lint.result != 'success' ||
(needs.changes.outputs.go-mode != 'skip' && needs.test-go.result != 'success') ||
(needs.changes.outputs.changed != 'false' && (
needs.test-node.result != 'success' ||
needs.test-node-windows.result != 'success' ||
needs.test-vscode-windows.result != 'success' ||
needs.test-wasm.result != 'success' ||
needs.test-rust.result != 'success'
))
) }}