name: CI permissions: {} on: push: branches: [main] pull_request: workflow_dispatch: concurrency: group: ${{ github.workflow }}-${{ github.ref_name }}-${{ github.event.pull_request.number || github.sha }} cancel-in-progress: true env: CARGO_INCREMENTAL: 0 CARGO_NET_RETRY: 10 CARGO_TERM_COLOR: always RUSTUP_MAX_RETRIES: 10 PACKAGE_NAME: ruff PYTHON_VERSION: "3.13" jobs: determine_changes: name: "Determine changes" runs-on: ubuntu-latest outputs: # Flag that is raised when any code that affects parser is changed parser: ${{ steps.check_parser.outputs.changed }} # Flag that is raised when any code that affects linter is changed linter: ${{ steps.check_linter.outputs.changed }} # Flag that is raised when any code that affects formatter is changed formatter: ${{ steps.check_formatter.outputs.changed }} # Flag that is raised when any code is changed # This is superset of the linter and formatter code: ${{ steps.check_code.outputs.changed }} # Flag that is raised when any code that affects the fuzzer is changed fuzz: ${{ steps.check_fuzzer.outputs.changed }} # Flag that is set to "true" when code related to ty changes. ty: ${{ steps.check_ty.outputs.changed }} # Flag that is set to "true" when code related to the py-fuzzer folder changes. py-fuzzer: ${{ steps.check_py_fuzzer.outputs.changed }} # Flag that is set to "true" when code related to the playground changes. playground: ${{ steps.check_playground.outputs.changed }} steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: fetch-depth: 0 persist-credentials: false - name: Determine merge base id: merge_base env: BASE_REF: ${{ github.event.pull_request.base.ref || 'main' }} run: | sha=$(git merge-base HEAD "origin/${BASE_REF}") echo "sha=${sha}" >> "$GITHUB_OUTPUT" - name: Check if the parser code changed id: check_parser env: MERGE_BASE: ${{ steps.merge_base.outputs.sha }} run: | if git diff --quiet "${MERGE_BASE}...HEAD" -- \ ':Cargo.toml' \ ':Cargo.lock' \ ':crates/ruff_python_trivia/**' \ ':crates/ruff_source_file/**' \ ':crates/ruff_text_size/**' \ ':crates/ruff_python_ast/**' \ ':crates/ruff_python_parser/**' \ ':.github/workflows/ci.yaml' \ ; then echo "changed=false" >> "$GITHUB_OUTPUT" else echo "changed=true" >> "$GITHUB_OUTPUT" fi - name: Check if the linter code changed id: check_linter env: MERGE_BASE: ${{ steps.merge_base.outputs.sha }} run: | if git diff --quiet "${MERGE_BASE}...HEAD" -- ':Cargo.toml' \ ':Cargo.lock' \ ':crates/**' \ ':!crates/ty*/**' \ ':!crates/ruff_python_formatter/**' \ ':!crates/ruff_formatter/**' \ ':!crates/ruff_dev/**' \ ':scripts/*' \ ':python/**' \ ':.github/workflows/ci.yaml' \ ; then echo "changed=false" >> "$GITHUB_OUTPUT" else echo "changed=true" >> "$GITHUB_OUTPUT" fi - name: Check if the formatter code changed id: check_formatter env: MERGE_BASE: ${{ steps.merge_base.outputs.sha }} run: | if git diff --quiet "${MERGE_BASE}...HEAD" -- ':Cargo.toml' \ ':Cargo.lock' \ ':crates/ruff_python_formatter/**' \ ':crates/ruff_formatter/**' \ ':crates/ruff_python_trivia/**' \ ':crates/ruff_python_ast/**' \ ':crates/ruff_source_file/**' \ ':crates/ruff_python_index/**' \ ':crates/ruff_python_index/**' \ ':crates/ruff_text_size/**' \ ':crates/ruff_python_parser/**' \ ':scripts/*' \ ':python/**' \ ':.github/workflows/ci.yaml' \ ; then echo "changed=false" >> "$GITHUB_OUTPUT" else echo "changed=true" >> "$GITHUB_OUTPUT" fi - name: Check if the fuzzer code changed id: check_fuzzer env: MERGE_BASE: ${{ steps.merge_base.outputs.sha }} run: | if git diff --quiet "${MERGE_BASE}...HEAD" -- ':Cargo.toml' \ ':Cargo.lock' \ ':fuzz/fuzz_targets/**' \ ':.github/workflows/ci.yaml' \ ; then echo "changed=false" >> "$GITHUB_OUTPUT" else echo "changed=true" >> "$GITHUB_OUTPUT" fi - name: Check if the py-fuzzer code changed id: check_py_fuzzer env: MERGE_BASE: ${{ steps.merge_base.outputs.sha }} run: | if git diff --quiet "${MERGE_BASE}...HEAD" -- 'python/py_fuzzer/**' \ ; then echo "changed=false" >> "$GITHUB_OUTPUT" else echo "changed=true" >> "$GITHUB_OUTPUT" fi - name: Check if there was any code related change id: check_code env: MERGE_BASE: ${{ steps.merge_base.outputs.sha }} run: | # NOTE: Do not exclude all Markdown files here, but rather use # specific exclude patterns like 'docs/**'), because tests for # 'ty' are written in Markdown. if git diff --quiet "${MERGE_BASE}...HEAD" -- \ ':!docs/**' \ ':!assets/**' \ ; then echo "changed=false" >> "$GITHUB_OUTPUT" else echo "changed=true" >> "$GITHUB_OUTPUT" fi - name: Check if there was any playground related change id: check_playground env: MERGE_BASE: ${{ steps.merge_base.outputs.sha }} run: | if git diff --quiet "${MERGE_BASE}...HEAD" -- \ ':playground/**' \ ; then echo "changed=false" >> "$GITHUB_OUTPUT" else echo "changed=true" >> "$GITHUB_OUTPUT" fi - name: Check if the ty code changed id: check_ty env: MERGE_BASE: ${{ steps.merge_base.outputs.sha }} run: | if git diff --quiet "${MERGE_BASE}...HEAD" -- \ ':Cargo.toml' \ ':Cargo.lock' \ ':crates/ty*/**' \ ':crates/ruff_db/**' \ ':crates/ruff_annotate_snippets/**' \ ':crates/ruff_python_ast/**' \ ':crates/ruff_python_parser/**' \ ':crates/ruff_python_trivia/**' \ ':crates/ruff_source_file/**' \ ':crates/ruff_text_size/**' \ ':crates/ruff_benchmark/**' \ ':.github/workflows/ci.yaml' \ ; then echo "changed=false" >> "$GITHUB_OUTPUT" else echo "changed=true" >> "$GITHUB_OUTPUT" fi cargo-fmt: name: "cargo fmt" runs-on: ubuntu-latest timeout-minutes: 10 steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false - name: "Install Rust toolchain" run: rustup component add rustfmt - run: cargo fmt --all --check cargo-clippy: name: "cargo clippy" runs-on: ubuntu-latest needs: determine_changes if: ${{ needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main' }} timeout-minutes: 20 steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false - uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1 - name: "Install Rust toolchain" run: | rustup component add clippy rustup target add wasm32-unknown-unknown - name: "Clippy" run: cargo clippy --workspace --all-targets --all-features --locked -- -D warnings - name: "Clippy (wasm)" run: cargo clippy -p ruff_wasm -p ty_wasm --target wasm32-unknown-unknown --all-features --locked -- -D warnings cargo-test-linux: name: "cargo test (linux)" runs-on: depot-ubuntu-22.04-16 needs: determine_changes if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-test') && (needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main') }} timeout-minutes: 20 steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false - uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1 - name: "Install Rust toolchain" run: rustup show - name: "Install mold" uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 - name: "Install cargo nextest" uses: taiki-e/install-action@522492a8c115f1b6d4d318581f09638e9442547b # v2.62.21 with: tool: cargo-nextest - name: "Install cargo insta" uses: taiki-e/install-action@522492a8c115f1b6d4d318581f09638e9442547b # v2.62.21 with: tool: cargo-insta - name: "Install uv" uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0 with: enable-cache: "true" - name: ty mdtests (GitHub annotations) if: ${{ needs.determine_changes.outputs.ty == 'true' }} env: NO_COLOR: 1 MDTEST_GITHUB_ANNOTATIONS_FORMAT: 1 # Ignore errors if this step fails; we want to continue to later steps in the workflow anyway. # This step is just to get nice GitHub annotations on the PR diff in the files-changed tab. run: cargo test -p ty_python_semantic --test mdtest || true - name: "Run tests" shell: bash env: NEXTEST_PROFILE: "ci" run: cargo insta test --all-features --unreferenced reject --test-runner nextest # Check for broken links in the documentation. - run: cargo doc --all --no-deps env: RUSTDOCFLAGS: "-D warnings" # Use --document-private-items so that all our doc comments are kept in # sync, not just public items. Eventually we should do this for all # crates; for now add crates here as they are warning-clean to prevent # regression. - run: cargo doc --no-deps -p ty_python_semantic -p ty -p ty_test -p ruff_db --document-private-items env: # Setting RUSTDOCFLAGS because `cargo doc --check` isn't yet implemented (https://github.com/rust-lang/cargo/issues/10025). RUSTDOCFLAGS: "-D warnings" - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: ruff path: target/debug/ruff - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: ty path: target/debug/ty cargo-test-linux-release: name: "cargo test (linux, release)" runs-on: depot-ubuntu-22.04-16 needs: determine_changes if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-test') && (needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main') }} timeout-minutes: 20 steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false - uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1 - name: "Install Rust toolchain" run: rustup show - name: "Install mold" uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 - name: "Install cargo nextest" uses: taiki-e/install-action@522492a8c115f1b6d4d318581f09638e9442547b # v2.62.21 with: tool: cargo-nextest - name: "Install cargo insta" uses: taiki-e/install-action@522492a8c115f1b6d4d318581f09638e9442547b # v2.62.21 with: tool: cargo-insta - name: "Install uv" uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0 with: enable-cache: "true" - name: "Run tests" shell: bash env: NEXTEST_PROFILE: "ci" run: cargo insta test --release --all-features --unreferenced reject --test-runner nextest cargo-test-windows: name: "cargo test (windows)" runs-on: depot-windows-2022-16 needs: determine_changes if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-test') && (needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main') }} timeout-minutes: 20 steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false - uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1 - name: "Install Rust toolchain" run: rustup show - name: "Install cargo nextest" uses: taiki-e/install-action@522492a8c115f1b6d4d318581f09638e9442547b # v2.62.21 with: tool: cargo-nextest - name: "Install uv" uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0 with: enable-cache: "true" - name: "Run tests" shell: bash env: NEXTEST_PROFILE: "ci" # Workaround for . RUSTUP_WINDOWS_PATH_ADD_BIN: 1 run: | cargo nextest run --all-features --profile ci cargo test --all-features --doc cargo-test-wasm: name: "cargo test (wasm)" runs-on: ubuntu-latest needs: determine_changes if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-test') && (needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main') }} timeout-minutes: 10 steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false - uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1 - name: "Install Rust toolchain" run: rustup target add wasm32-unknown-unknown - uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 with: node-version: 22 cache: "npm" cache-dependency-path: playground/package-lock.json - uses: jetli/wasm-pack-action@0d096b08b4e5a7de8c28de67e11e945404e9eefa # v0.4.0 with: version: v0.13.1 - name: "Test ruff_wasm" run: | cd crates/ruff_wasm wasm-pack test --node - name: "Test ty_wasm" run: | cd crates/ty_wasm wasm-pack test --node cargo-build-release: name: "cargo build (release)" runs-on: macos-latest if: ${{ github.ref == 'refs/heads/main' }} timeout-minutes: 20 steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false - uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1 - name: "Install Rust toolchain" run: rustup show - name: "Install mold" uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 - name: "Build" run: cargo build --release --locked cargo-build-msrv: name: "cargo build (msrv)" runs-on: depot-ubuntu-latest-8 needs: determine_changes if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-test') && (needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main') }} timeout-minutes: 20 steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false - uses: SebRollen/toml-action@b1b3628f55fc3a28208d4203ada8b737e9687876 # v1.2.0 id: msrv with: file: "Cargo.toml" field: "workspace.package.rust-version" - uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1 - name: "Install Rust toolchain" env: MSRV: ${{ steps.msrv.outputs.value }} run: rustup default "${MSRV}" - name: "Install mold" uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 - name: "Build tests" shell: bash env: MSRV: ${{ steps.msrv.outputs.value }} run: cargo "+${MSRV}" test --no-run --all-features cargo-fuzz-build: name: "cargo fuzz build" runs-on: ubuntu-latest needs: determine_changes if: ${{ github.ref == 'refs/heads/main' || needs.determine_changes.outputs.fuzz == 'true' || needs.determine_changes.outputs.code == 'true' }} timeout-minutes: 10 steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false - uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1 with: workspaces: "fuzz -> target" - name: "Install Rust toolchain" run: rustup show - name: "Install cargo-binstall" uses: cargo-bins/cargo-binstall@38e8f5e4c386b611d51e8aa997b9a06a3c8eb67a # v1.15.6 - name: "Install cargo-fuzz" # Download the latest version from quick install and not the github releases because github releases only has MUSL targets. run: cargo binstall cargo-fuzz --force --disable-strategies crate-meta-data --no-confirm - run: cargo fuzz build -s none fuzz-parser: name: "fuzz parser" runs-on: ubuntu-latest needs: - cargo-test-linux - determine_changes if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-test') && (needs.determine_changes.outputs.parser == 'true' || needs.determine_changes.outputs.py-fuzzer == 'true') }} timeout-minutes: 20 env: FORCE_COLOR: 1 steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false - uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0 - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 name: Download Ruff binary to test id: download-cached-binary with: name: ruff path: ruff-to-test - name: Fuzz env: DOWNLOAD_PATH: ${{ steps.download-cached-binary.outputs.download-path }} run: | # Make executable, since artifact download doesn't preserve this chmod +x "${DOWNLOAD_PATH}/ruff" ( uvx \ --python="${PYTHON_VERSION}" \ --from=./python/py-fuzzer \ fuzz \ --test-executable="${DOWNLOAD_PATH}/ruff" \ --bin=ruff \ 0-500 ) scripts: name: "test scripts" runs-on: ubuntu-latest needs: determine_changes if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-test') && (needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main') }} timeout-minutes: 5 steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false - uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1 - name: "Install Rust toolchain" run: rustup component add rustfmt # Run all code generation scripts, and verify that the current output is # already checked into git. - run: python crates/ruff_python_ast/generate.py - run: python crates/ruff_python_formatter/generate.py - run: test -z "$(git status --porcelain)" # Verify that adding a plugin or rule produces clean code. - run: ./scripts/add_rule.py --name DoTheThing --prefix F --code 999 --linter pyflakes - run: cargo check - run: | ./scripts/add_plugin.py test --url https://pypi.org/project/-test/0.1.0/ --prefix TST ./scripts/add_rule.py --name FirstRule --prefix TST --code 001 --linter test - run: cargo check ecosystem: name: "ecosystem" runs-on: depot-ubuntu-latest-8 needs: - cargo-test-linux - determine_changes # Only runs on pull requests, since that is the only we way we can find the base version for comparison. # Ecosystem check needs linter and/or formatter changes. if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-test') && github.event_name == 'pull_request' && needs.determine_changes.outputs.code == 'true' }} timeout-minutes: 20 steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false - uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 with: python-version: ${{ env.PYTHON_VERSION }} - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 name: Download comparison Ruff binary id: ruff-target with: name: ruff path: target/debug - uses: dawidd6/action-download-artifact@20319c5641d495c8a52e688b7dc5fada6c3a9fbc # v8 name: Download baseline Ruff binary with: name: ruff branch: ${{ github.event.pull_request.base.ref }} workflow: "ci.yaml" check_artifacts: true - name: Install ruff-ecosystem run: | pip install ./python/ruff-ecosystem - name: Run `ruff check` stable ecosystem check if: ${{ needs.determine_changes.outputs.linter == 'true' }} env: DOWNLOAD_PATH: ${{ steps.ruff-target.outputs.download-path }} run: | # Make executable, since artifact download doesn't preserve this chmod +x ./ruff "${DOWNLOAD_PATH}/ruff" # Set pipefail to avoid hiding errors with tee set -eo pipefail ruff-ecosystem check ./ruff "${DOWNLOAD_PATH}/ruff" --cache ./checkouts --output-format markdown | tee ecosystem-result-check-stable cat ecosystem-result-check-stable > "$GITHUB_STEP_SUMMARY" echo "### Linter (stable)" > ecosystem-result cat ecosystem-result-check-stable >> ecosystem-result echo "" >> ecosystem-result - name: Run `ruff check` preview ecosystem check if: ${{ needs.determine_changes.outputs.linter == 'true' }} env: DOWNLOAD_PATH: ${{ steps.ruff-target.outputs.download-path }} run: | # Make executable, since artifact download doesn't preserve this chmod +x ./ruff "${DOWNLOAD_PATH}/ruff" # Set pipefail to avoid hiding errors with tee set -eo pipefail ruff-ecosystem check ./ruff "${DOWNLOAD_PATH}/ruff" --cache ./checkouts --output-format markdown --force-preview | tee ecosystem-result-check-preview cat ecosystem-result-check-preview > "$GITHUB_STEP_SUMMARY" echo "### Linter (preview)" >> ecosystem-result cat ecosystem-result-check-preview >> ecosystem-result echo "" >> ecosystem-result - name: Run `ruff format` stable ecosystem check if: ${{ needs.determine_changes.outputs.formatter == 'true' }} env: DOWNLOAD_PATH: ${{ steps.ruff-target.outputs.download-path }} run: | # Make executable, since artifact download doesn't preserve this chmod +x ./ruff "${DOWNLOAD_PATH}/ruff" # Set pipefail to avoid hiding errors with tee set -eo pipefail ruff-ecosystem format ./ruff "${DOWNLOAD_PATH}/ruff" --cache ./checkouts --output-format markdown | tee ecosystem-result-format-stable cat ecosystem-result-format-stable > "$GITHUB_STEP_SUMMARY" echo "### Formatter (stable)" >> ecosystem-result cat ecosystem-result-format-stable >> ecosystem-result echo "" >> ecosystem-result - name: Run `ruff format` preview ecosystem check if: ${{ needs.determine_changes.outputs.formatter == 'true' }} env: DOWNLOAD_PATH: ${{ steps.ruff-target.outputs.download-path }} run: | # Make executable, since artifact download doesn't preserve this chmod +x ./ruff "${DOWNLOAD_PATH}/ruff" # Set pipefail to avoid hiding errors with tee set -eo pipefail ruff-ecosystem format ./ruff "${DOWNLOAD_PATH}/ruff" --cache ./checkouts --output-format markdown --force-preview | tee ecosystem-result-format-preview cat ecosystem-result-format-preview > "$GITHUB_STEP_SUMMARY" echo "### Formatter (preview)" >> ecosystem-result cat ecosystem-result-format-preview >> ecosystem-result echo "" >> ecosystem-result - name: Export pull request number run: | echo ${{ github.event.number }} > pr-number - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 name: Upload PR Number with: name: pr-number path: pr-number - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 name: Upload Results with: name: ecosystem-result path: ecosystem-result fuzz-ty: name: "Fuzz for new ty panics" runs-on: depot-ubuntu-22.04-16 needs: - cargo-test-linux - determine_changes # Only runs on pull requests, since that is the only we way we can find the base version for comparison. if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-test') && github.event_name == 'pull_request' && (needs.determine_changes.outputs.ty == 'true' || needs.determine_changes.outputs.py-fuzzer == 'true') }} timeout-minutes: 20 steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 name: Download new ty binary id: ty-new with: name: ty path: target/debug - uses: dawidd6/action-download-artifact@20319c5641d495c8a52e688b7dc5fada6c3a9fbc # v8 name: Download baseline ty binary with: name: ty branch: ${{ github.event.pull_request.base.ref }} workflow: "ci.yaml" check_artifacts: true - uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0 - name: Fuzz env: FORCE_COLOR: 1 NEW_TY: ${{ steps.ty-new.outputs.download-path }} run: | # Make executable, since artifact download doesn't preserve this chmod +x "${PWD}/ty" "${NEW_TY}/ty" ( uvx \ --python="${PYTHON_VERSION}" \ --from=./python/py-fuzzer \ fuzz \ --test-executable="${NEW_TY}/ty" \ --baseline-executable="${PWD}/ty" \ --only-new-bugs \ --bin=ty \ 0-500 ) cargo-shear: name: "cargo shear" runs-on: ubuntu-latest needs: determine_changes if: ${{ needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main' }} steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false - uses: cargo-bins/cargo-binstall@38e8f5e4c386b611d51e8aa997b9a06a3c8eb67a # v1.15.6 - run: cargo binstall --no-confirm cargo-shear - run: cargo shear python-package: name: "python package" runs-on: ubuntu-latest timeout-minutes: 20 if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-test') }} steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false - uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 with: python-version: ${{ env.PYTHON_VERSION }} architecture: x64 - uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1 - name: "Prep README.md" run: python scripts/transform_readme.py --target pypi - name: "Build wheels" uses: PyO3/maturin-action@86b9d133d34bc1b40018696f782949dac11bd380 # v1.49.4 with: args: --out dist - name: "Test wheel" run: | pip install --force-reinstall --find-links dist "${PACKAGE_NAME}" ruff --help python -m ruff --help - name: "Remove wheels from cache" run: rm -rf target/wheels pre-commit: name: "pre-commit" runs-on: depot-ubuntu-22.04-16 timeout-minutes: 10 steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false - uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0 - uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1 - uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 with: node-version: 22 - name: "Cache pre-commit" uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4 with: path: ~/.cache/pre-commit key: pre-commit-${{ hashFiles('.pre-commit-config.yaml') }} - name: "Run pre-commit" run: | echo '```console' > "$GITHUB_STEP_SUMMARY" # Enable color output for pre-commit and remove it for the summary # Use --hook-stage=manual to enable slower pre-commit hooks that are skipped by default SKIP=cargo-fmt,clippy,dev-generate-all uvx --python="${PYTHON_VERSION}" pre-commit run --all-files --show-diff-on-failure --color=always --hook-stage=manual | \ tee >(sed -E 's/\x1B\[([0-9]{1,2}(;[0-9]{1,2})*)?[mGK]//g' >> "$GITHUB_STEP_SUMMARY") >&1 exit_code="${PIPESTATUS[0]}" echo '```' >> "$GITHUB_STEP_SUMMARY" exit "$exit_code" docs: name: "mkdocs" runs-on: ubuntu-latest timeout-minutes: 10 env: MKDOCS_INSIDERS_SSH_KEY_EXISTS: ${{ secrets.MKDOCS_INSIDERS_SSH_KEY != '' }} steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false - uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 with: python-version: "3.13" - uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1 - name: "Add SSH key" if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS == 'true' }} uses: webfactory/ssh-agent@a6f90b1f127823b31d4d4a8d96047790581349bd # v0.9.1 with: ssh-private-key: ${{ secrets.MKDOCS_INSIDERS_SSH_KEY }} - name: "Install Rust toolchain" run: rustup show - name: Install uv uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0 - name: "Install Insiders dependencies" if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS == 'true' }} run: uv pip install -r docs/requirements-insiders.txt --system - name: "Install dependencies" if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS != 'true' }} run: uv pip install -r docs/requirements.txt --system - name: "Update README File" run: python scripts/transform_readme.py --target mkdocs - name: "Generate docs" run: python scripts/generate_mkdocs.py - name: "Check docs formatting" run: python scripts/check_docs_formatted.py - name: "Build Insiders docs" if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS == 'true' }} run: mkdocs build --strict -f mkdocs.insiders.yml - name: "Build docs" if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS != 'true' }} run: mkdocs build --strict -f mkdocs.public.yml check-formatter-instability-and-black-similarity: name: "formatter instabilities and black similarity" runs-on: ubuntu-latest needs: determine_changes if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-test') && (needs.determine_changes.outputs.formatter == 'true' || github.ref == 'refs/heads/main') }} timeout-minutes: 10 steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false - uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1 - name: "Install Rust toolchain" run: rustup show - name: "Run checks" run: scripts/formatter_ecosystem_checks.sh - name: "Github step summary" run: cat target/formatter-ecosystem/stats.txt > "$GITHUB_STEP_SUMMARY" - name: "Remove checkouts from cache" run: rm -r target/formatter-ecosystem check-ruff-lsp: name: "test ruff-lsp" runs-on: ubuntu-latest timeout-minutes: 5 needs: - cargo-test-linux - determine_changes if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-test') && (needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main') }} steps: - uses: extractions/setup-just@e33e0265a09d6d736e2ee1e0eb685ef1de4669ff # v3.0.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 name: "Download ruff-lsp source" with: persist-credentials: false repository: "astral-sh/ruff-lsp" - uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 with: # installation fails on 3.13 and newer python-version: "3.12" - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 name: Download development ruff binary id: ruff-target with: name: ruff path: target/debug - name: Install ruff-lsp dependencies run: | just install - name: Run ruff-lsp tests env: DOWNLOAD_PATH: ${{ steps.ruff-target.outputs.download-path }} run: | # Setup development binary pip uninstall --yes ruff chmod +x "${DOWNLOAD_PATH}/ruff" export PATH="${DOWNLOAD_PATH}:${PATH}" ruff version just test check-playground: name: "check playground" runs-on: ubuntu-latest timeout-minutes: 5 needs: - determine_changes if: ${{ (needs.determine_changes.outputs.playground == 'true') }} steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false - name: "Install Rust toolchain" run: rustup target add wasm32-unknown-unknown - uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1 - uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 with: node-version: 22 cache: "npm" cache-dependency-path: playground/package-lock.json - uses: jetli/wasm-bindgen-action@20b33e20595891ab1a0ed73145d8a21fc96e7c29 # v0.2.0 - name: "Install Node dependencies" run: npm ci working-directory: playground - name: "Build playgrounds" run: npm run dev:wasm working-directory: playground - name: "Run TypeScript checks" run: npm run check working-directory: playground - name: "Check formatting" run: npm run fmt:check working-directory: playground benchmarks-instrumented-ruff: name: "benchmarks instrumented (ruff)" runs-on: ubuntu-24.04 needs: determine_changes if: | github.ref == 'refs/heads/main' || (needs.determine_changes.outputs.formatter == 'true' || needs.determine_changes.outputs.linter == 'true') timeout-minutes: 20 steps: - name: "Checkout Branch" uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false - uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1 - uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0 - name: "Install Rust toolchain" run: rustup show - name: "Install codspeed" uses: taiki-e/install-action@522492a8c115f1b6d4d318581f09638e9442547b # v2.62.21 with: tool: cargo-codspeed - name: "Build benchmarks" run: cargo codspeed build --features "codspeed,instrumented" --no-default-features -p ruff_benchmark --bench formatter --bench lexer --bench linter --bench parser - name: "Run benchmarks" uses: CodSpeedHQ/action@3959e9e296ef25296e93e32afcc97196f966e57f # v4.1.0 with: mode: instrumentation run: cargo codspeed run token: ${{ secrets.CODSPEED_TOKEN }} benchmarks-instrumented-ty: name: "benchmarks instrumented (ty)" runs-on: ubuntu-24.04 needs: determine_changes if: | github.ref == 'refs/heads/main' || needs.determine_changes.outputs.ty == 'true' timeout-minutes: 20 steps: - name: "Checkout Branch" uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false - uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1 - uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0 - name: "Install Rust toolchain" run: rustup show - name: "Install codspeed" uses: taiki-e/install-action@522492a8c115f1b6d4d318581f09638e9442547b # v2.62.21 with: tool: cargo-codspeed - name: "Build benchmarks" run: cargo codspeed build --features "codspeed,instrumented" --no-default-features -p ruff_benchmark --bench ty - name: "Run benchmarks" uses: CodSpeedHQ/action@3959e9e296ef25296e93e32afcc97196f966e57f # v4.1.0 with: mode: instrumentation run: cargo codspeed run token: ${{ secrets.CODSPEED_TOKEN }} benchmarks-walltime: runs-on: codspeed-macro needs: determine_changes if: ${{ github.repository == 'astral-sh/ruff' && !contains(github.event.pull_request.labels.*.name, 'no-test') && (needs.determine_changes.outputs.ty == 'true' || github.ref == 'refs/heads/main') }} timeout-minutes: 20 env: TY_LOG: ruff_benchmark=debug steps: - name: "Checkout Branch" uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false - uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1 - uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0 - name: "Install Rust toolchain" run: rustup show - name: "Install codspeed" uses: taiki-e/install-action@522492a8c115f1b6d4d318581f09638e9442547b # v2.62.21 with: tool: cargo-codspeed - name: "Build benchmarks" run: cargo codspeed build --features "codspeed,walltime" --no-default-features -p ruff_benchmark - name: "Run benchmarks" uses: CodSpeedHQ/action@3959e9e296ef25296e93e32afcc97196f966e57f # v4.1.0 env: # enabling walltime flamegraphs adds ~6 minutes to the CI time, and they don't # appear to provide much useful insight for our walltime benchmarks right now # (see https://github.com/astral-sh/ruff/pull/20419) CODSPEED_PERF_ENABLED: false with: mode: walltime run: cargo codspeed run token: ${{ secrets.CODSPEED_TOKEN }}