Files
invoiceninja-sdk/flake.nix
T
awkawbandClaude Opus 5 ad9553516e Pin the Rust toolchain and declare an MSRV of 1.88
The dev shell tracked `stable.latest` and CI used `@stable`, so nothing
pinned a version and nothing declared a floor. A consumer on an older
toolchain hit a confusing error deep in a dependency rather than cargo
saying the version was too old, and a new stable release could turn a
lint into an unrelated red build.

- `rust-version = "1.88"` in Cargo.toml. The floor comes from the
  dependency graph, not this crate's code: reqwest pulls in url -> idna
  -> idna_adapter -> icu_*, and the icu crates require 1.88. Verified by
  running the full suite against 1.88, not inferred.
- flake.nix pins the dev toolchain to 1.99.1 and adds an `msrv` devShell,
  so the floor can be rechecked with
  `nix develop .#msrv --command cargo test`.
- CI pins the main job to the same 1.99.1 and adds a job building on
  1.88. Lint and format stay on the pinned toolchain only, since their
  output varies by release.

Note the tradeoff: with CI pinned, a future stable that breaks the build
will not be noticed until the pin moves. Worth adding a non-blocking
`stable` job if that matters.

CLAUDE.md records the three places that must agree when either version
moves, and how to recheck the floor after a dependency bump.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-23 18:46:32 -04:00

52 lines
1.9 KiB
Nix

{
description = "invoiceninjars: a Rust client for the Invoice Ninja v5 API";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
rust-overlay.url = "github:oxalica/rust-overlay";
rust-overlay.inputs.nixpkgs.follows = "nixpkgs";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, rust-overlay, flake-utils, ... }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = import nixpkgs {
inherit system;
overlays = [ rust-overlay.overlays.default ];
};
# Pinned rather than `stable.latest`, so the toolchain only moves
# when this line does. `latest` drifts with the flake inputs, which
# means a lint can start failing on an unrelated change.
devVersion = "1.99.1";
# Must match `rust-version` in Cargo.toml. The floor comes from the
# dependency graph, not from this crate's own code: reqwest pulls in
# url -> idna -> idna_adapter -> icu_*, and the icu crates require
# 1.88. Check with:
# cargo metadata --format-version 1 \
# | jq -r '.packages[] | select(.rust_version) | .rust_version' \
# | sort -V | tail -1
msrvVersion = "1.88.0";
toolchainFor = version: extensions:
pkgs.rust-bin.stable.${version}.default.override { inherit extensions; };
rustToolchain = toolchainFor devVersion [ "rust-src" "rust-analyzer" "clippy" "rustfmt" ];
msrvToolchain = toolchainFor msrvVersion [ ];
shellFor = toolchain: pkgs.mkShell {
packages = [ toolchain pkgs.pkg-config pkgs.openssl ];
RUST_SRC_PATH = "${toolchain}/lib/rustlib/src/rust/library";
};
in
{
devShells.default = shellFor rustToolchain;
# Verifies the declared MSRV still builds:
# nix develop .#msrv --command cargo test
devShells.msrv = shellFor msrvToolchain;
});
}