diff --git a/.claude/hooks/check-formatting.sh b/.claude/hooks/check-formatting.sh index 47ec82c4..c7d3e33b 100755 --- a/.claude/hooks/check-formatting.sh +++ b/.claude/hooks/check-formatting.sh @@ -10,15 +10,28 @@ if [ ! -f "Cargo.toml" ]; then exit 0 fi -# Run cargo +nightly fmt --all --check -if cargo +nightly fmt --all --check 2>&1; then +# Pinned nightly toolchain for rustfmt (single source of truth shared with CI, +# .githooks/pre-push and flake.nix). Falls back to floating `nightly`. +NIGHTLY="$(tr -d '[:space:]' < rustfmt-toolchain 2>/dev/null || echo nightly)" + +if command -v cargo-+nightly >/dev/null 2>&1; then + # Nix dev shell: `cargo +nightly fmt` is wrapped to the pinned nightly. + TOOLCHAIN="nightly" +else + # rustup: pin by dated toolchain name; install it if missing (no-op if present). + TOOLCHAIN="$NIGHTLY" + rustup toolchain install "$NIGHTLY" --component rustfmt --profile minimal >/dev/null 2>&1 || true +fi + +# Run cargo fmt with the pinned nightly +if cargo "+$TOOLCHAIN" fmt --all --check 2>&1; then # Formatting is correct - echo '{"continue": true, "systemMessage": "✓ Code formatting verified with cargo +nightly fmt"}' + echo "{\"continue\": true, \"systemMessage\": \"✓ Code formatting verified with cargo +$TOOLCHAIN fmt ($NIGHTLY)\"}" exit 0 else # Auto-fix formatting - cargo +nightly fmt --all 2>&1 + cargo "+$TOOLCHAIN" fmt --all 2>&1 - echo '{"continue": true, "systemMessage": "✓ Formatting applied with cargo +nightly fmt --all."}' + echo "{\"continue\": true, \"systemMessage\": \"✓ Formatting applied with cargo +$TOOLCHAIN fmt --all ($NIGHTLY).\"}" exit 0 fi diff --git a/.githooks/pre-push b/.githooks/pre-push index 0b9a0460..9289c249 100755 --- a/.githooks/pre-push +++ b/.githooks/pre-push @@ -14,9 +14,14 @@ if ! cargo machete; then exit 1 fi -# Formatting check -if ! cargo +nightly fmt --all -- --check; then - echo "❌ Formatting issues (run 'cargo +nightly fmt --all')" +# Formatting check (pinned nightly — see rustfmt-toolchain) +FMT_TOOLCHAIN="$(tr -d '[:space:]' < rustfmt-toolchain 2>/dev/null || echo nightly)" +if command -v cargo-+nightly >/dev/null 2>&1; then + # Nix dev shell wraps `cargo +nightly fmt` to the pinned nightly. + FMT_TOOLCHAIN="nightly" +fi +if ! cargo "+$FMT_TOOLCHAIN" fmt --all -- --check; then + echo "❌ Formatting issues (run 'cargo +$FMT_TOOLCHAIN fmt --all')" exit 1 fi diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index 52cc68fc..201f410a 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -10,6 +10,7 @@ on: - "rust-toolchain.toml" - "flake.nix" - "rustfmt.toml" + - "rustfmt-toolchain" - "clippy.toml" - ".github/workflows/linter.yml" pull_request: @@ -21,6 +22,7 @@ on: - "rust-toolchain.toml" - "flake.nix" - "rustfmt.toml" + - "rustfmt-toolchain" - "clippy.toml" - ".github/workflows/linter.yml" @@ -55,11 +57,15 @@ jobs: - name: Install `oas3-gen` run: cargo install oas3-gen@0.24.0 --locked - - name: Install `rustfmt` - run: rustup +nightly component add rustfmt + - name: Read pinned rustfmt toolchain + id: rustfmt + run: echo "toolchain=$(cat rustfmt-toolchain)" >> "$GITHUB_OUTPUT" + + - name: Install pinned nightly `rustfmt` + run: rustup toolchain install ${{ steps.rustfmt.outputs.toolchain }} --component rustfmt --profile minimal - name: Check formatting - run: cargo +nightly fmt --all -- --check + run: cargo +${{ steps.rustfmt.outputs.toolchain }} fmt --all -- --check - name: Run Clippy run: cargo clippy --locked --all-targets --all-features diff --git a/AGENTS.md b/AGENTS.md index a52e4dac..c90d1da9 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -50,11 +50,15 @@ Environment: - Recommended dev setup: `nix develop` (see `pluto/CONTRIBUTING.md`). - Rust toolchain is pinned in `pluto/rust-toolchain.toml`. +- The exact nightly used for `rustfmt` is pinned in `pluto/rustfmt-toolchain` + (CI, git hooks and the Nix dev shell all read it). Inside `nix develop`, + `cargo +nightly fmt` is wrapped to that pinned nightly; with rustup, use the + dated toolchain, e.g. `cargo +"$(cat rustfmt-toolchain)" fmt`. Commands (run from `pluto/`): ```bash -cargo +nightly fmt --all --check +cargo +nightly fmt --all --check # nightly pinned via rustfmt-toolchain cargo clippy --workspace --all-targets --all-features -- -D warnings cargo test --workspace --all-features cargo deny check --hide-inclusion-graph diff --git a/flake.nix b/flake.nix index 57d0f0c8..3792a1d6 100644 --- a/flake.nix +++ b/flake.nix @@ -22,11 +22,19 @@ rustToolchain = pkgs.rust-bin.fromRustupToolchainFile ./rust-toolchain.toml; # `cargo +nightly fmt` is the project-wide formatting command. + # The exact nightly is pinned in `rustfmt-toolchain` (format: + # `nightly-YYYY-MM-DD`) so this dev shell, CI and the git hooks all use + # the same formatter and a new nightly can never silently change output. + # NOTE: `rust-overlay` must know about this date; run + # `nix flake update rust-overlay` if evaluation fails for a newer pin. + fmtNightlyDate = pkgs.lib.removePrefix "nightly-" + (pkgs.lib.fileContents ./rustfmt-toolchain); + # Nix does not use rustup, so we provide the following workarounds: - # - Expose a standalone binary that runs the nightly formatter, and - # - Wrap `cargo` to ensure that `cargo +nightly fmt` uses the nightly formatter. + # - Expose a standalone binary that runs the pinned nightly formatter, and + # - Wrap `cargo` to ensure that `cargo +nightly fmt` uses that formatter. rustfmtNightly = pkgs.writeShellScriptBin "rustfmt-nightly" '' - exec ${pkgs.rust-bin.selectLatestNightlyWith (toolchain: toolchain.rustfmt)}/bin/rustfmt "$@" + exec ${pkgs.rust-bin.nightly.${fmtNightlyDate}.rustfmt}/bin/rustfmt "$@" ''; cargoNightly = pkgs.writeShellScriptBin "cargo-+nightly" '' shift diff --git a/rustfmt-toolchain b/rustfmt-toolchain new file mode 100644 index 00000000..e9c0e6d4 --- /dev/null +++ b/rustfmt-toolchain @@ -0,0 +1 @@ +nightly-2026-08-30