Skip to content

Commit bc0e50b

Browse files
Bump nightly to 2026-05-27 and vendor crates without nixpkgs importCargoLock (#227)
## Why Three things landed together because untangling them midway through wasn't worth a separate PR cycle. 1. **Nightly bump to `2026-05-27`** and the in-tree clippy fork promoted to a flake input (`clippy-fork`), so it's pinned alongside the rest of the toolchain. 2. **Audit pass on tautological test assertions** in `tests/default.nix` per the `AGENTS.md` guidance — checks that just restate the source got deleted. 3. **Stop using nixpkgs `rustPlatform.importCargoLock` for crate vendoring.** This was forced by crates.io rolling out a User-Agent gate: any UA containing `curl` now returns HTTP 403, which catches nixpkgs's default `curl/X.Y Nixpkgs/Z`. Rather than patch curl, `ix.buildRustPackage` now materializes its own vendor dir via the existing `resolveVendorDir` and hands it to `rustPlatform.buildRustPackage` as `cargoDeps`. One vendor builder, one URL pattern (`static.crates.io` CDN — same one cargo's sparse protocol uses), and we own all of it. ## What this means for callers - Every Rust package built via `ix.buildRustPackage` now goes through the new path. `llm-clippy` was the last holdout calling `rustPlatform.buildRustPackage` directly; it's switched. Its own clippy policy gate is off (would recurse — it IS the clippy that lints everything else); other policy gates stay on. - User-supplied `cargoHash`, `cargoDeps`, or `cargoVendorDir` still wins. The new code only fires on the default path. - FOD hashing is content-only, so cached crate tarballs survive the URL change. ## Verified locally - `nix run .#lint` clean - `nix eval` succeeds for `llm-clippy`, `dag-runner`, `ix`, `nix-cargo-unit` on `aarch64-darwin` - Full Linux builds left for CI
1 parent ddb60c3 commit bc0e50b

8 files changed

Lines changed: 130 additions & 286 deletions

File tree

flake.lock

Lines changed: 50 additions & 33 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

flake.nix

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,14 @@
3131
inputs.nixpkgs.follows = "nixpkgs";
3232
};
3333

34+
# Fork of rust-lang/rust-clippy with extra restriction lints tuned for
35+
# LLM-assisted codebases. Pinned in flake.lock so `nix flake update`
36+
# bumps it; consumed as the source tree for `packages/llm-clippy`.
37+
clippy-fork = {
38+
url = "github:indexable-inc/clippy";
39+
flake = false;
40+
};
41+
3442
# Nous Research's Hermes agent ships its own NixOS module
3543
# (`nixosModules.default`) and uv2nix-built Python closure. Pinned to
3644
# a release tag so routine bumps are review events; `nix flake update
@@ -50,6 +58,7 @@
5058
determinate,
5159
home-manager,
5260
hermes-agent,
61+
clippy-fork,
5362
...
5463
}:
5564
let
@@ -86,6 +95,7 @@
8695
determinate
8796
home-manager
8897
hermes-agent
98+
clippy-fork
8999
;
90100
};
91101
devSystems = [

lib/default.nix

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
determinate,
88
home-manager,
99
hermes-agent,
10+
clippy-fork,
1011
cliArtifacts ? { },
1112
}:
1213
let
@@ -259,6 +260,7 @@ let
259260
final
260261
buildIxRustTool
261262
rustNightlyClippyToolchainFor
263+
clippy-fork
262264
;
263265
pkgs = final;
264266
inherit (entry) path;
@@ -369,10 +371,19 @@ let
369371
"rustfmt"
370372
];
371373
};
374+
# ix.buildRustPackage closure handed to `callPackage`'d Rust packages.
375+
# Kept minimal so it stays usable from the bootstrap path (no `cargoUnit`,
376+
# no `rustWorkspace`); `buildIxRustTool` adds those for packages that need
377+
# them.
378+
ixBuildSurfaceFor = _pkgs: {
379+
buildRustPackage = innerPkgs: (rustFor innerPkgs).buildPackage;
380+
};
372381
llmClippyFor =
373382
pkgs:
374383
pkgs.callPackage (packagePath "llm-clippy") {
384+
ix = ixBuildSurfaceFor pkgs;
375385
rustToolchain = rustNightlyClippyToolchainFor pkgs;
386+
src = clippy-fork;
376387
};
377388
rustFor =
378389
pkgs:
@@ -778,6 +789,7 @@ let
778789
packageSystem
779790
cliArtifacts
780791
rustNightlyClippyToolchainFor
792+
clippy-fork
781793
ixForPackages
782794
;
783795
ix = ixForPackages;

lib/languages/rust.nix

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ let
2323
consumer that calls `toolchain pkgs { channel = "nightly"; version
2424
= languages.rust.defaultNightlyDate; }`.
2525
*/
26-
defaultNightlyDate = "2026-05-17";
26+
defaultNightlyDate = "2026-05-27";
2727

2828
/**
2929
Toolchain components everyone gets by default. The shape mirrors

lib/rust.nix

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -258,11 +258,15 @@ let
258258
'';
259259
pkgs.linkFarm "cargo-vendor-dir" vendorEntries;
260260

261+
# Both registry shapes resolve to the same CDN artifact. `static.crates.io` is
262+
# the direct CloudFront URL cargo's sparse protocol uses; the older
263+
# `api.crates.io/api/v1/crates/.../download` endpoint just 302s here and, as
264+
# of 2026-05, rejects curl's default User-Agent with HTTP 403.
265+
cratesIoDownloadUrl =
266+
pkg: "https://static.crates.io/crates/${pkg.name}/${pkg.name}-${pkg.version}.crate";
261267
registryDownloadUrls = {
262-
"registry+https://github.com/rust-lang/crates.io-index" =
263-
pkg: "https://crates.io/api/v1/crates/${pkg.name}/${pkg.version}/download";
264-
"sparse+https://index.crates.io/" =
265-
pkg: "https://static.crates.io/crates/${pkg.name}/${pkg.name}-${pkg.version}.crate";
268+
"registry+https://github.com/rust-lang/crates.io-index" = cratesIoDownloadUrl;
269+
"sparse+https://index.crates.io/" = cratesIoDownloadUrl;
266270
};
267271

268272
parseGitSource =
@@ -684,27 +688,44 @@ let
684688
cargoTestFlags =
685689
(rawArgs.cargoTestFlags or [ ])
686690
++ lib.optionals (testEnabled && args.policy.tests.useNextest) [ "--no-tests=pass" ];
691+
# Vendor through our own fetcher (`resolveVendorDir` -> `static.crates.io`)
692+
# instead of letting nixpkgs's `importCargoLock` re-fetch each crate via
693+
# the legacy `crates.io/api/v1/crates/.../download` URL. The legacy
694+
# endpoint is now gated on User-Agent (no `curl/...`) and is a redirect
695+
# to the same CDN anyway, so going direct is both unblocked and faster.
696+
# Surface the vendor dir as `cargoDeps` (absolute store path); the
697+
# cargo-setup hook expects `cargoVendorDir` to be in-source, not a
698+
# `/nix/store` path. User-supplied `cargoHash`, `cargoDeps`, or
699+
# `cargoVendorDir` still wins.
700+
bareVendorDir = resolveVendorDir {
701+
inherit (args) cargoLock outputHashes vendorDir;
702+
sourceOverrides = rawArgs.sourceOverrides or { };
703+
};
704+
# nixpkgs's `cargoSetupPostPatchHook` diffs `$cargoDeps/Cargo.lock`
705+
# against the lockfile in the source tree. `resolveVendorDir` only
706+
# emits the per-crate symlinks, so re-attach the lockfile here.
707+
defaultCargoDeps = pkgs.runCommand "cargo-deps" { } ''
708+
mkdir -p "$out"
709+
cp -RL ${bareVendorDir}/. "$out/"
710+
cp ${cargoLockFile args.cargoLock} "$out/Cargo.lock"
711+
'';
687712
buildArgs =
688713
builtins.removeAttrs rawArgs [
689714
"cargoArgs"
690715
"cargoExtraConfig"
716+
"cargoLock"
691717
"cargoTestFlags"
692718
"outputHashes"
693719
"policy"
694720
"rustPlatform"
695721
"rustToolchain"
722+
"sourceOverrides"
696723
"vendorDir"
697724
]
698725
//
699-
lib.optionalAttrs
700-
(
701-
!(rawArgs ? cargoLock)
702-
&& !(rawArgs ? cargoHash)
703-
&& !(rawArgs ? cargoDeps)
704-
&& !(rawArgs ? cargoVendorDir)
705-
)
726+
lib.optionalAttrs (!(rawArgs ? cargoHash) && !(rawArgs ? cargoDeps) && !(rawArgs ? cargoVendorDir))
706727
{
707-
cargoLock.lockFile = cargoLockFile args.cargoLock;
728+
cargoDeps = defaultCargoDeps;
708729
}
709730
// {
710731
nativeBuildInputs = (rawArgs.nativeBuildInputs or [ ]) ++ nativeBuildInputsForPolicy args.policy;

packages/llm-clippy/default.nix

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,33 @@
11
{
2+
ix,
23
lib,
34
makeWrapper,
45
pkgs,
6+
src,
57
rustToolchain ? null,
68
}:
79

810
let
9-
src = pkgs.fetchFromGitHub {
10-
owner = "indexable-inc";
11-
repo = "clippy";
12-
rev = "c5f8f62dacfc666fa29615b13f777bb7404a1e60";
13-
hash = "sha256-pFGUPLgM0lSDz8Iv3FLapQAJJV507B1DmJp4pKxp6JA=";
14-
};
15-
1611
toolchain =
1712
if rustToolchain != null then
1813
rustToolchain
1914
else
2015
pkgs.rust-bin.fromRustupToolchainFile (src + "/rust-toolchain.toml");
2116

22-
rustPlatform = pkgs.makeRustPlatform {
23-
cargo = toolchain;
24-
rustc = toolchain;
25-
};
26-
2717
rustcLibPathVar =
2818
if pkgs.stdenv.hostPlatform.isDarwin then "DYLD_LIBRARY_PATH" else "LD_LIBRARY_PATH";
2919
in
30-
rustPlatform.buildRustPackage {
20+
ix.buildRustPackage pkgs {
3121
pname = "llm-clippy";
3222
version = "0.1.97";
3323

3424
inherit src;
25+
rustToolchain = toolchain;
26+
# Vendor through ix.buildRustPackage's `resolveVendorDir`, which fetches from
27+
# `static.crates.io`. Upstream indexable-inc/clippy ships no Cargo.lock, so
28+
# the patch plants one into $sourceRoot for cargo at build time; the same
29+
# file is what `cargoLock.lockFile` points at for vendoring.
3530
cargoLock.lockFile = ./Cargo.lock;
36-
# Upstream indexable-inc/clippy ships no Cargo.lock. cargoLock.lockFile only
37-
# vendors and validates ("ERROR: Missing Cargo.lock from src" if it isn't
38-
# present), so cargoPatches is how we plant the file into $sourceRoot.
3931
cargoPatches = [ ./cargo-lock.patch ];
4032

4133
nativeBuildInputs = [ makeWrapper ];
@@ -46,9 +38,13 @@ rustPlatform.buildRustPackage {
4638
pkgs.libiconv
4739
];
4840
doCheck = false;
41+
# llm-clippy IS the clippy that lints every other repo Rust package, so its
42+
# own clippy check cannot reach for `llmClippyFor` again - it would recurse
43+
# forever back into this build. Machete and other policy gates stay on.
44+
policy.clippy.enable = false;
4945

5046
# This Clippy fork links against rustc_private crates from its Rust toolchain.
51-
RUSTC_BOOTSTRAP = "1";
47+
env.RUSTC_BOOTSTRAP = "1";
5248

5349
postInstall = ''
5450
for bin in "$out/bin/cargo-clippy" "$out/bin/clippy-driver"; do

0 commit comments

Comments
 (0)