This repository has no description
5.4 kB
132 lines
1{
2 lib,
3 rustPlatform,
4 glibc,
5 runCommand,
6 nix,
7 zstd,
8 xz,
9 jq,
10}:
11rustPlatform.buildRustPackage (finalAttrs: {
12 pname = "lazy-store";
13 version = "0.1.0";
14 src = lib.fileset.toSource {
15 root = ./.;
16 fileset = lib.fileset.unions [
17 ./Cargo.toml
18 ./Cargo.lock
19 ./src
20 ];
21 };
22 cargoLock = {
23 lockFile = ./Cargo.lock;
24 outputHashes."fuse-backend-rs-0.14.0" = "sha256-pSnYq8IhfpxtnTS7bnE8GHAVP+yZ+hItChTEmGUP7xM=";
25 outputHashes."harmonia-file-core-3.3.0" = "sha256-aPNxxJLjRd+wVFyAcUnzrakC0BNj/N9rJWV2y8wpCAs=";
26 };
27 # Static, so that once it serves /nix/store it loads nothing from there.
28 # (Not glibc.static in buildInputs: build scripts would link against it.)
29 env.RUSTFLAGS = "-C target-feature=+crt-static -L native=${glibc.static}/lib";
30
31 doInstallCheck = true;
32 installCheckPhase = ''
33 if $READELF -l $out/bin/lazy-store | grep -q INTERP; then
34 echo "lazy-store is not static"; exit 1
35 fi
36 ($out/bin/lazy-store || true) 2>&1 | grep -q usage
37 '';
38
39 # closure and the lower layer against a file:// cache, without FUSE:
40 # nix build .#lazy-store.tests.fetch
41 passthru.tests.fetch =
42 runCommand "lazy-store-fetch-test"
43 {
44 nativeBuildInputs = [
45 finalAttrs.finalPackage
46 nix
47 zstd
48 xz
49 jq
50 ];
51 }
52 ''
53 export HOME=$TMPDIR NIX_STATE_DIR=$TMPDIR/state NIX_LOG_DIR=$TMPDIR/log
54 mkdir -p src/a/sub cache/nar out closure
55 echo hello > src/a/file
56 printf '#!/bin/sh\n' > src/a/sub/run
57 chmod +x src/a/sub/run
58 ln -s file src/a/link
59 echo single > src/b
60 cp -a src/a src/c
61
62 A=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-a
63 B=bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb-b
64 C=cccccccccccccccccccccccccccccccc-c
65 D=dddddddddddddddddddddddddddddddd-d
66 E=eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-e
67 ls='{"version":1,"root":{"type":"directory","entries":{
68 "file":{"type":"regular","size":6},
69 "link":{"type":"symlink","target":"file"},
70 "sub":{"type":"directory","entries":{"run":{"type":"regular","size":10,"executable":true}}}}}}'
71 # zstd .ls served without Content-Encoding (sniffed); b has none
72 echo "$ls" | zstd -q > cache/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.ls
73 echo "$ls" | zstd -q > cache/cccccccccccccccccccccccccccccccc.ls
74
75 nix-store --dump src/a | zstd -q > cache/nar/a.nar.zst
76 nix-store --dump src/b | xz > cache/nar/b.nar.xz
77 nix-store --dump src/c | zstd -q > cache/nar/c.nar.zst
78 info() { # name file compression hash size
79 jq -n --arg c "file://$PWD/cache" --arg u "$2" --arg z "$3" --arg h "$4" --argjson s "$5" \
80 '{($ARGS.positional[0]): {cache: $c, url: $u, compression: $z, narHash: $h, narSize: $s, fileSize: 0}}' \
81 --args "$1"
82 }
83 size() { nix-store --dump "$1" | wc -c; }
84 {
85 info $A nar/a.nar.zst zstd "sha256:$(nix-hash --type sha256 --base32 src/a)" "$(size src/a)"
86 info $B nar/b.nar.xz xz "sha256:$(nix-hash --type sha256 src/b)" "$(size src/b)"
87 # wrong hash: must not be unpacked
88 info $C nar/c.nar.zst zstd "sha256:$(nix-hash --type sha256 --base32 src/b)" "$(size src/c)"
89 } | jq -s add > out/paths.json
90
91 lazy-store fetch out $A $B
92 diff -r src/a out/cache/$A
93 diff src/b out/cache/$B
94 test "$(stat -c %a out/cache/$A/sub/run)" = 555
95 test "$(stat -c %a out/cache/$A/file)" = 444
96 test "$(readlink out/cache/$A/link)" = file
97 jq -e '.ls_count == 2 and .nar_count == 2' out/stats.json
98 if lazy-store fetch out $C; then
99 echo "NAR with a wrong hash was accepted"; exit 1
100 fi
101 test ! -e out/cache/$C
102
103 # a refers to b; d refers to e, which no cache has: d is left out
104 narinfo() { # name url references
105 printf 'StorePath: /nix/store/%s\nURL: %s\nCompression: zstd\nNarHash: sha256:%s\nNarSize: 8\nReferences: %s\nSig: test-1:%s\n' \
106 "$1" "$2" "$(nix-hash --type sha256 --base32 src/b)" "$3" "$(printf %64s | base64 -w0)" \
107 > "cache/''${1%%-*}.narinfo"
108 }
109 narinfo $A nar/a.nar.zst "$A $B"
110 narinfo $B nar/b.nar.xz ""
111 narinfo $D nar/d.nar.zst "$E"
112 printf '/nix/store/%s\n' $A $D | LAZY_STORE_CACHES="file://$PWD/cache" lazy-store closure closure
113 jq -e '.paths == 2 and .missing == ["'$E'"] and .incomplete == 1' closure/closure.json
114 jq -e 'keys == ["'$A'", "'$B'"]' closure/paths.json
115 if grep -q $D closure/db.dump; then
116 echo "path with an incomplete closure was registered"; exit 1
117 fi
118
119 # registered as .github/lazy-store.sh does, with the signatures
120 export NIX_CONFIG="experimental-features = nix-command"
121 root="local?root=$PWD/root"
122 nix-store --store "$root" --load-db < closure/db.dump
123 printf '/nix/store/%s\n' $A $B |
124 nix store copy-sigs --store "$root" -s "file://$PWD/closure/narinfo" --stdin
125 nix path-info --store "$root" --sigs /nix/store/$A | grep -q "[[:space:]]test-1:$(printf %64s | base64 -w0)$"
126 test "$(nix-store --store "$root" -q --references /nix/store/$A | wc -l)" = 2
127 touch $out
128 '';
129
130 meta.mainProgram = "lazy-store";
131 meta.platforms = lib.platforms.linux;
132})