diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 38423a2..ef6e4fe 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,44 +6,11 @@ on: pull_request: jobs: - test: - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [ubuntu-latest] - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Install Rust stable - uses: dtolnay/rust-toolchain@stable - - - name: Cache cargo + target - uses: swatinem/rust-cache@v2 - - - name: Check formatting - run: cargo fmt --check - - - name: Clippy - run: cargo clippy -- -D warnings - - - name: Test - run: cargo test - - - name: Publish ownership snapshot tests - run: scripts/publish-ownership-snapshot-test.sh - - - name: Package - run: cargo package --locked - - - name: Install Smoke - run: | - root="$(mktemp -d)" - cargo install --path . --locked --root "$root" - "$root/bin/idx" version - scripts/live-smoke.sh --bin "$root/bin/idx" --no-build --mode mock - + # Everything that compiles Rust runs as a `nix flake check` derivation + # (fmt, clippy, tests, package verify, build, offline smoke). Crane splits + # the crate dependency build into its own derivation, and the Nix store is + # restored from the GitHub Actions cache, so unchanged derivations are + # substituted instead of rebuilt. nix: runs-on: ubuntu-latest @@ -52,14 +19,24 @@ jobs: uses: actions/checkout@v4 - name: Install Nix - uses: cachix/install-nix-action@v30 + uses: cachix/install-nix-action@v31 - - name: Build default package - run: nix build .#default + - name: Restore and save Nix store + uses: nix-community/cache-nix-action@v7 + with: + primary-key: nix-${{ runner.os }}-${{ hashFiles('flake.nix', 'flake.lock', 'Cargo.lock', 'Cargo.toml') }} + restore-prefixes-first-match: nix-${{ runner.os }}- + gc-max-store-size-linux: 8G + + - name: Flake check + run: nix flake check -L - name: Run default app run: nix run .#default -- version + - name: Publish ownership snapshot tests + run: scripts/publish-ownership-snapshot-test.sh + - name: Build ownership publisher run: | nix build .#ownership-publisher diff --git a/flake.lock b/flake.lock index 74eeefd..4e4159c 100644 --- a/flake.lock +++ b/flake.lock @@ -38,7 +38,8 @@ "inputs": { "flake-utils": "flake-utils", "nixpkgs": "nixpkgs", - "rust-overlay": "rust-overlay" + "rust-overlay": "rust-overlay", + "crane": "crane" } }, "rust-overlay": { @@ -75,6 +76,21 @@ "repo": "default", "type": "github" } + }, + "crane": { + "locked": { + "lastModified": 1790899423, + "narHash": "sha256-THGtz65UGJU+sonZidRboyCMk+9DclpMF7pwL4uDV4M=", + "owner": "ipetkov", + "repo": "crane", + "rev": "47b6b27ed9a3a9181415e4367d0c30ab2a0e0250", + "type": "github" + }, + "original": { + "owner": "ipetkov", + "repo": "crane", + "type": "github" + } } }, "root": "root", diff --git a/flake.nix b/flake.nix index 7b5d1b1..8a9d2e5 100644 --- a/flake.nix +++ b/flake.nix @@ -4,13 +4,14 @@ inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; flake-utils.url = "github:numtide/flake-utils"; + crane.url = "github:ipetkov/crane"; rust-overlay = { url = "github:oxalica/rust-overlay"; inputs.nixpkgs.follows = "nixpkgs"; }; }; - outputs = { self, nixpkgs, flake-utils, rust-overlay }: + outputs = { self, nixpkgs, flake-utils, crane, rust-overlay }: flake-utils.lib.eachDefaultSystem (system: let overlays = [ (import rust-overlay) ]; @@ -20,34 +21,43 @@ rustToolchain = pkgs.rust-bin.stable.latest.default.override { extensions = [ "rust-src" "rust-analyzer" ]; }; - rustPlatform = pkgs.makeRustPlatform { - cargo = rustToolchain; - rustc = rustToolchain; - }; + craneLib = (crane.mkLib pkgs).overrideToolchain (_: rustToolchain); runtimeDeps = with pkgs; [ curl-impersonate mupdf ]; - idxPackage = rustPlatform.buildRustPackage { + # Only the crate itself (manifests, src/, tests/), so doc or script + # edits do not invalidate the Rust derivations. + rustFiles = [ + ./Cargo.toml + ./Cargo.lock + ./src + ./tests + ]; + src = lib.fileset.toSource { + root = ./.; + fileset = lib.fileset.unions rustFiles; + }; + commonArgs = { + inherit src; pname = cargoManifest.package.name; version = cargoManifest.package.version; - src = lib.cleanSource ./.; - cargoLock = { - lockFile = ./Cargo.lock; - }; + strictDeps = true; + nativeBuildInputs = with pkgs; [ pkg-config ]; + buildInputs = with pkgs; [ openssl ]; + }; + # Dependency-only build keyed on Cargo.toml/Cargo.lock: reused from the + # Nix store (or binary cache) until the dependency graph changes. + cargoArtifacts = craneLib.buildDepsOnly commonArgs; + idxPackage = craneLib.buildPackage (commonArgs // { + inherit cargoArtifacts; doCheck = false; - nativeBuildInputs = with pkgs; [ - makeWrapper - pkg-config - ]; - buildInputs = with pkgs; [ - openssl - ]; + nativeBuildInputs = commonArgs.nativeBuildInputs ++ [ pkgs.makeWrapper ]; postInstall = '' wrapProgram "$out/bin/idx" \ --prefix PATH : "${lib.makeBinPath runtimeDeps}" ''; - }; + }); # Self-contained ownership snapshot publisher for scheduled hosts # (e.g. a NixOS systemd timer): no checkout, `nix develop` or cargo # build at run time. Wraps the repo scripts with a pinned `idx`. @@ -103,9 +113,53 @@ apps.default = { type = "app"; program = "${idxPackage}/bin/idx"; + meta.description = "idx CLI"; + }; + checks = { + default = idxPackage; + ownership-publisher = ownershipPublisher; + fmt = craneLib.cargoFmt { inherit src; }; + clippy = craneLib.cargoClippy (commonArgs // { + inherit cargoArtifacts; + cargoClippyExtraArgs = "--all-targets -- -D warnings"; + }); + test = craneLib.cargoTest (commonArgs // { + inherit cargoArtifacts; + # Ownership discover tests point IDX_CURL_IMPERSONATE_BIN at plain + # `curl` against a local fixture server. + nativeCheckInputs = [ pkgs.curl ]; + }); + # `cargo package` verification: the published crate (per the + # `include` list in Cargo.toml) must build on its own. + package = craneLib.mkCargoDerivation (commonArgs // { + inherit cargoArtifacts; + pnameSuffix = "-package"; + src = lib.fileset.toSource { + root = ./.; + fileset = lib.fileset.unions (rustFiles ++ [ + ./LICENSE + ./README.md + ]); + }; + buildPhaseCargoCommand = "cargo package --locked --offline --allow-dirty"; + doInstallCargoArtifacts = false; + installPhaseCommand = "touch $out"; + }); + smoke-mock = pkgs.runCommand "idx-smoke-mock" + { nativeBuildInputs = with pkgs; [ bash coreutils findutils gnugrep gnused gawk ]; } + '' + cp -r ${./scripts} scripts + chmod -R u+w scripts + patchShebangs scripts + # The mock provider reads fixtures relative to the working dir. + mkdir -p tests + cp -r ${./tests/fixtures} tests/fixtures + export HOME="$TMPDIR" + ${idxPackage}/bin/idx version + bash scripts/live-smoke.sh --bin ${idxPackage}/bin/idx --no-build --mode mock + touch "$out" + ''; }; - checks.default = idxPackage; - checks.ownership-publisher = ownershipPublisher; devShells.default = pkgs.mkShell { inputsFrom = [ idxPackage ]; diff --git a/result-1 b/result-1 new file mode 120000 index 0000000..2d66130 --- /dev/null +++ b/result-1 @@ -0,0 +1 @@ +/nix/store/jh7j89lzmrvvhm44dv96znzlf522p2ac-idx-cli-test-0.2.4 \ No newline at end of file