nova-cache 0.2.1.0 → 0.2.2.0
raw patch · 5 files changed
+117/−36 lines, 5 files
Files
- CHANGELOG.md +9/−0
- README.md +15/−8
- nova-cache.cabal +27/−3
- test/CompressionTest.hs +66/−0
- test/Main.hs +0/−25
CHANGELOG.md view
@@ -1,5 +1,14 @@ # Changelog +## 0.2.2.0 — 2026-02-23++- Gate `NovaCache.Compression` and `lzma` dependency behind a `compression`+ cabal flag (default on, backwards compatible)+- Consumers that only need hashing/NAR/narinfo can build with `-compression`+ to avoid the system `liblzma` C dependency+- Compression tests moved to separate test suite (`nova-cache-compression-test`)+- No changes to any library source modules+ ## 0.2.1.0 — 2026-02-22 - Server: `validateNarInfo` wired into PUT handler — rejects malformed uploads
README.md view
@@ -25,7 +25,7 @@ - **NAR** — Binary serialization and deserialization of the Nix ARchive format using `Builder` monoid composition - **NarInfo** — Parse and render the key-value narinfo text format - **Signing** — Ed25519 fingerprint signing and verification for binary cache trust-- **Compression** — xz compress/decompress for NAR transport+- **Compression** — xz compress/decompress for NAR transport (behind `compression` cabal flag, default on) - **Store** — Filesystem storage backend for narinfo and NAR files - **Validate** — Pure protocol validation: field semantics, content hashes, Ed25519 signatures — all errors collected, not short-circuited - **Server** — Optional WAI/Warp HTTP server implementing the cache protocol (behind `server` cabal flag)@@ -279,13 +279,13 @@ └──────────────────────────────────────────┘ │ IO Boundary (thin)- ┌──────────────────────────────────────────┐- │ Compression Store Server (flag) │- └──────────────────────────────────────────┘+ ┌──────────────────────────────────────────────────────┐+ │ Compression (flag) Store Server (flag) │+ └──────────────────────────────────────────────────────┘ ``` -- **9 modules**, 7 pure + 2 at the IO boundary-- **74 tests**, hand-rolled harness, no framework dependencies+- **9 modules**, 7 pure + 2 at the IO boundary (Compression optional via flag)+- **54 core tests + 3 compression tests**, hand-rolled harness, no framework dependencies - **Zero partial functions** — total by construction - **Strict by default** — bang patterns on all data fields @@ -400,11 +400,18 @@ ## Build & Test ```bash-cabal build # Build library-cabal test # Run all tests (74 tests, 9 groups)+cabal build # Build library (compression on by default)+cabal build -f-compression # Build without lzma C dependency+cabal test # Run all tests cabal build --ghc-options="-Werror" # Warnings as errors cabal build --flag server # Build with WAI server cabal haddock # Generate docs+```++Consumers that only need hashing, NAR, or narinfo can disable the `compression` flag to avoid the system `liblzma` dependency:++```cabal+constraints: nova-cache -compression ``` ---
nova-cache.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.0 name: nova-cache-version: 0.2.1.0+version: 0.2.2.0 synopsis: Pure Nix binary cache protocol library description: A focused, minimal, pure-first library implementing the full Nix binary@@ -27,10 +27,14 @@ default: False manual: True +flag compression+ description: Enable LZMA/XZ compression (requires system liblzma)+ default: True+ manual: True+ library exposed-modules: NovaCache.Base32- NovaCache.Compression NovaCache.Hash NovaCache.NAR NovaCache.NarInfo@@ -39,6 +43,10 @@ NovaCache.StorePath NovaCache.Validate + if flag(compression)+ exposed-modules: NovaCache.Compression+ build-depends: lzma >= 0.0.1 && < 0.1+ build-depends: base >= 4.16 && < 5 , base64-bytestring >= 1.2 && < 1.3@@ -47,7 +55,6 @@ , crypton >= 1.0 && < 2 , directory >= 1.3 && < 1.4 , filepath >= 1.4 && < 1.6- , lzma >= 0.0.1 && < 0.1 , memory >= 0.18 && < 1 , text >= 2.0 && < 2.2 , unix >= 2.7 && < 2.9@@ -102,6 +109,23 @@ , nova-cache , memory >= 0.18 && < 1 , text >= 2.0 && < 2.2++test-suite nova-cache-compression-test+ if !flag(compression)+ buildable: False++ type: exitcode-stdio-1.0+ main-is: CompressionTest.hs+ hs-source-dirs: test+ default-language: Haskell2010+ default-extensions:+ OverloadedStrings+ ghc-options: -Wall -Wcompat++ build-depends:+ base >= 4.16 && < 5+ , bytestring >= 0.11 && < 0.13+ , nova-cache source-repository head type: git
+ test/CompressionTest.hs view
@@ -0,0 +1,66 @@+module Main (main) where++import qualified Data.ByteString as BS+import qualified NovaCache.Compression as Compression+import System.Exit (exitFailure, exitSuccess)+import System.IO (hFlush, stdout)++-- | Run a named test, short-circuit on first failure.+test :: String -> IO Bool -> IO Bool+test name action = do+ putStr (" " ++ name ++ "... ")+ hFlush stdout+ result <- action+ if result+ then do+ putStrLn "OK"+ pure True+ else do+ putStrLn "FAILED"+ pure False++-- | Assert equality.+assertEqual :: (Eq a, Show a) => String -> a -> a -> IO Bool+assertEqual label expected actual+ | expected == actual = pure True+ | otherwise = do+ putStrLn ""+ putStrLn (" " ++ label)+ putStrLn (" expected: " ++ show expected)+ putStrLn (" actual: " ++ show actual)+ pure False++-- | Assert a Bool is True.+assertTrue :: String -> Bool -> IO Bool+assertTrue _ True = pure True+assertTrue label False = do+ putStrLn ""+ putStrLn (" " ++ label ++ ": expected True")+ pure False++main :: IO ()+main = do+ putStrLn "nova-cache compression tests"+ putStrLn "=============================="+ putStrLn ""+ putStrLn "Compression:"+ ok1 <-+ test "compress/decompress roundtrip" $+ let input = BS.pack [72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]+ compressed = Compression.compressXz input+ decompressed = Compression.decompressXz compressed+ in assertEqual "roundtrip" input decompressed+ ok2 <-+ test "compress/decompress roundtrip (empty)" $+ let compressed = Compression.compressXz BS.empty+ decompressed = Compression.decompressXz compressed+ in assertEqual "roundtrip empty" BS.empty decompressed+ ok3 <-+ test "compressed is smaller for repetitive data" $+ let input = BS.replicate 10000 0x42+ compressed = Compression.compressXz input+ in assertTrue "smaller" (BS.length compressed < BS.length input)+ putStrLn ""+ if ok1 && ok2 && ok3+ then putStrLn "All compression tests passed." >> exitSuccess+ else putStrLn "Some compression tests failed." >> exitFailure
test/Main.hs view
@@ -9,7 +9,6 @@ import qualified Data.Text as T import qualified Data.Text.Encoding as TE import qualified NovaCache.Base32 as Base32-import qualified NovaCache.Compression as Compression import qualified NovaCache.Hash as Hash import qualified NovaCache.NAR as NAR import qualified NovaCache.NarInfo as NarInfo@@ -122,7 +121,6 @@ testNAR, testNarInfo, testSigning,- testCompression, testFileStore, testValidate ]@@ -490,29 +488,6 @@ NarInfo.niSigs = [], NarInfo.niCA = Nothing }---- ------------------------------------------------------------------------------ Compression tests--- -----------------------------------------------------------------------------testCompression :: IO Bool-testCompression =- runGroup- "Compression"- [ test "compress/decompress roundtrip" $- let input = BS.pack [72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]- compressed = Compression.compressXz input- decompressed = Compression.decompressXz compressed- in assertEqual "roundtrip" input decompressed,- test "compress/decompress roundtrip (empty)" $- let compressed = Compression.compressXz BS.empty- decompressed = Compression.decompressXz compressed- in assertEqual "roundtrip empty" BS.empty decompressed,- test "compressed is smaller for repetitive data" $- let input = BS.replicate 10000 0x42- compressed = Compression.compressXz input- in assertTrue "smaller" (BS.length compressed < BS.length input)- ] -- --------------------------------------------------------------------------- -- FileStore tests