From c826d8a296eeeef462bdafe2a1265789c8774dc7 Mon Sep 17 00:00:00 2001 From: Zach Vorhies Date: Sun, 27 Sep 2026 12:58:52 -0700 Subject: [PATCH] ci: run one unified Dylint gate on Linux --- .github/workflows/_lint.yml | 13 +++++++++++-- .github/workflows/linux-x86-lint.yml | 1 + lint | 20 ++++++++++++++------ tests/unit/test_lint_script.py | 21 ++++++++++++++++++--- 4 files changed, 44 insertions(+), 11 deletions(-) diff --git a/.github/workflows/_lint.yml b/.github/workflows/_lint.yml index 2fa8093e..6974908d 100644 --- a/.github/workflows/_lint.yml +++ b/.github/workflows/_lint.yml @@ -10,6 +10,10 @@ on: runs-on: required: true type: string + run-dylint: + required: false + type: boolean + default: false jobs: lint: @@ -77,7 +81,7 @@ jobs: # Native dylint caching: caches cargo-dylint/dylint-link binaries and # the dylint driver dir, exposing dylint-cache-hit so we can skip the # expensive cargo installs below (#170). - dylint-cache: true + dylint-cache: ${{ inputs.run-dylint }} dylint-toolchain: nightly-2026-05-28 cargo-dylint-version: "6.0.3" dylint-link-version: "6.0.3" @@ -86,10 +90,15 @@ jobs: run: uv sync --group dev - name: Prepare published Dylint tools + if: inputs.run-dylint run: env -u RUSTUP_TOOLCHAIN soldr dylint prepare - - name: Lint + - name: Lint with Dylint + if: inputs.run-dylint run: ./lint + - name: Lint without Dylint + if: ${{ !inputs.run-dylint }} + run: ./lint --skip-dylint - name: Upload failure logs if: failure() diff --git a/.github/workflows/linux-x86-lint.yml b/.github/workflows/linux-x86-lint.yml index 7d205473..289fc6fa 100644 --- a/.github/workflows/linux-x86-lint.yml +++ b/.github/workflows/linux-x86-lint.yml @@ -24,3 +24,4 @@ jobs: with: source-sha: ${{ github.event_name == 'workflow_dispatch' && inputs.candidate_sha || github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }} runs-on: ubuntu-24.04 + run-dylint: true diff --git a/lint b/lint index 0531d58c..76370eb0 100755 --- a/lint +++ b/lint @@ -3,6 +3,12 @@ set -e SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" +if [[ $# -gt 1 || ($# -eq 1 && "$1" != "--skip-dylint") ]]; then + echo "Usage: ./lint [--skip-dylint]" >&2 + exit 2 +fi +SKIP_DYLINT=${1:-} + LINT_PATHS=(src tests ci) # Python stages run first: they need no Rust toolchain, so a host without @@ -29,13 +35,15 @@ soldr cargo fmt --all --check echo Running cargo clippy soldr cargo clippy --workspace --all-targets -- -D warnings -echo Running ban_std_pathbuf dylint -export SOLDR_FORCE_MANAGED_CARGO_SUBCOMMANDS=1 -if ! env -u RUSTUP_TOOLCHAIN soldr dylint prepare; then - echo "Error: dylint prerequisites are missing: soldr dylint prepare failed" >&2 - exit 1 +if [[ "$SKIP_DYLINT" != "--skip-dylint" ]]; then + echo Running ban_std_pathbuf dylint + export SOLDR_FORCE_MANAGED_CARGO_SUBCOMMANDS=1 + if ! env -u RUSTUP_TOOLCHAIN soldr dylint prepare; then + echo "Error: dylint prerequisites are missing: soldr dylint prepare failed" >&2 + exit 1 + fi + env -u RUSTUP_TOOLCHAIN soldr dylint --all -- --workspace --all-targets fi -env -u RUSTUP_TOOLCHAIN soldr dylint --all -- --workspace --all-targets echo Linting complete! exit 0 diff --git a/tests/unit/test_lint_script.py b/tests/unit/test_lint_script.py index 86f782e2..d645fc09 100644 --- a/tests/unit/test_lint_script.py +++ b/tests/unit/test_lint_script.py @@ -29,7 +29,7 @@ def _path_entry_has_dylint_or_rustup(entry: str) -> bool: def _run_lint( - tmp_path: Path, *, ci: bool + tmp_path: Path, *, ci: bool, args: tuple[str, ...] = () ) -> tuple[subprocess.CompletedProcess[str], list[str]]: bin_dir = tmp_path / "bin" bin_dir.mkdir() @@ -58,7 +58,7 @@ def _run_lint( env.pop("CI", None) result = subprocess.run( - ["bash", str(REPO_ROOT / "lint")], + ["bash", str(REPO_ROOT / "lint"), *args], cwd=REPO_ROOT, env=env, capture_output=True, @@ -120,6 +120,13 @@ def test_missing_dylint_is_a_hard_failure_in_ci(tmp_path: Path) -> None: assert _log_has_match(log_lines, r"^uv (run )?pyright src tests ci$") +def test_explicit_skip_keeps_other_lints_but_does_not_run_dylint(tmp_path: Path) -> None: + result, log_lines = _run_lint(tmp_path, ci=True, args=("--skip-dylint",)) + assert result.returncode == 0 + assert any(line.startswith("soldr cargo clippy") for line in log_lines) + assert not any("dylint" in line for line in log_lines) + + def test_python_stages_precede_the_rust_stages(tmp_path: Path) -> None: _result, log_lines = _run_lint(tmp_path, ci=False) @@ -144,10 +151,18 @@ def test_python_stages_precede_the_rust_stages(tmp_path: Path) -> None: "windows-x86-lint.yml", ], ) -def test_native_dylint_runs_on_unlabelled_pr(workflow: str) -> None: +def test_native_lint_without_dylint_runs_on_unlabelled_pr(workflow: str) -> None: source = (REPO_ROOT / ".github" / "workflows" / workflow).read_text( encoding="utf-8" ) assert "pull_request:" in source assert "uses: ./.github/workflows/_lint.yml" in source + assert "run-dylint: true" not in source assert "contains(github.event.pull_request.labels.*.name, 'ci-full')" not in source + + +def test_only_linux_x86_runs_unified_dylint() -> None: + source = (REPO_ROOT / ".github" / "workflows" / "linux-x86-lint.yml").read_text( + encoding="utf-8" + ) + assert "run-dylint: true" in source