Merge #166: Use relude, default language extensions, {,text} clean-up

This commit is contained in:
Anton Latukha 2021-08-09 15:06:23 +03:00 committed by GitHub
commit 5e55781516
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 414 additions and 456 deletions

View File

@ -19,7 +19,7 @@ jobs:
strategy: strategy:
matrix: matrix:
packageRoot: [ hnix-store-remote ] packageRoot: [ hnix-store-remote ]
ghc: [ "8.10", "8.4" ] ghc: [ "9.0", "8.6" ]
defaults: defaults:
run: run:
working-directory: "./${{ matrix.packageRoot }}" working-directory: "./${{ matrix.packageRoot }}"
@ -75,5 +75,5 @@ jobs:
run: cabal v2-test $cabalConfig run: cabal v2-test $cabalConfig
- name: "Source distribution file" - name: "Source distribution file"
if: ${{ matrix.ghc == '8.10' }} if: ${{ matrix.ghc == '9.0' }}
run: cabal v2-sdist run: cabal v2-sdist

View File

@ -21,7 +21,7 @@ jobs:
# Separation between Core & Store workflows made deliberately, so when one fixes Core, CI does not falls because of the Hackage Core Remote builds with. # Separation between Core & Store workflows made deliberately, so when one fixes Core, CI does not falls because of the Hackage Core Remote builds with.
# Also singleton also use is deliberate, it allows to reference the value in the further configuration & also leaves a name in the CI & preserves matrix flexibility for the future. # Also singleton also use is deliberate, it allows to reference the value in the further configuration & also leaves a name in the CI & preserves matrix flexibility for the future.
packageRoot: [ hnix-store-core ] packageRoot: [ hnix-store-core ]
ghc: [ "8.10", "8.4" ] ghc: [ "9.0", "8.6" ]
defaults: defaults:
run: run:
working-directory: "./${{ matrix.packageRoot }}" working-directory: "./${{ matrix.packageRoot }}"
@ -67,5 +67,5 @@ jobs:
run: cabal v2-test $cabalConfig run: cabal v2-test $cabalConfig
- name: "Source distribution file" - name: "Source distribution file"
if: ${{ matrix.ghc == '8.10' }} if: ${{ matrix.ghc == '9.0' }}
run: cabal v2-sdist run: cabal v2-sdist

View File

@ -33,7 +33,7 @@ jobs:
id: HaskEnvSetup id: HaskEnvSetup
uses: haskell/actions/setup@v1 uses: haskell/actions/setup@v1
with: with:
ghc-version: '8.10' ghc-version: '9.0'
- name: "Repository update" - name: "Repository update"
run: cabal v2-update run: cabal v2-update

View File

@ -18,7 +18,7 @@ jobs:
matrix: matrix:
packageRoot: [ hnix-store-core, hnix-store-remote ] packageRoot: [ hnix-store-core, hnix-store-remote ]
# Since CI by default tests boundary GHCs, test middle versions of GHCs # Since CI by default tests boundary GHCs, test middle versions of GHCs
ghc: [ "8.8", "8.6"] ghc: [ "8.10", "8.8"]
defaults: defaults:
run: run:
working-directory: "./${{ matrix.packageRoot }}" working-directory: "./${{ matrix.packageRoot }}"

BIN
core-simple.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 310 KiB

View File

@ -48,7 +48,8 @@ library
, System.Nix.StorePath , System.Nix.StorePath
, System.Nix.StorePathMetadata , System.Nix.StorePathMetadata
build-depends: build-depends:
base >=4.10 && <5 base >=4.12 && <5
, relude
, attoparsec , attoparsec
, algebraic-graphs >= 0.5 && < 0.6 , algebraic-graphs >= 0.5 && < 0.6
, base16-bytestring , base16-bytestring
@ -72,6 +73,29 @@ library
, unix , unix
, unordered-containers , unordered-containers
, vector , vector
mixins:
base hiding (Prelude)
, relude (Relude as Prelude)
, relude
default-extensions:
OverloadedStrings
, DeriveGeneric
, DeriveDataTypeable
, DeriveFunctor
, DeriveFoldable
, DeriveTraversable
, DeriveLift
, FlexibleContexts
, FlexibleInstances
, StandaloneDeriving
, TypeApplications
, TypeSynonymInstances
, InstanceSigs
, MultiParamTypeClasses
, TupleSections
, LambdaCase
, BangPatterns
, ViewPatterns
hs-source-dirs: src hs-source-dirs: src
default-language: Haskell2010 default-language: Haskell2010
@ -100,6 +124,7 @@ test-suite format-tests
hnix-store-core hnix-store-core
, attoparsec , attoparsec
, base , base
, relude
, base16-bytestring , base16-bytestring
, base64-bytestring , base64-bytestring
, binary , binary
@ -118,4 +143,27 @@ test-suite format-tests
, temporary , temporary
, text , text
, unix , unix
mixins:
base hiding (Prelude)
, relude (Relude as Prelude)
, relude
default-extensions:
OverloadedStrings
, DeriveGeneric
, DeriveDataTypeable
, DeriveFunctor
, DeriveFoldable
, DeriveTraversable
, DeriveLift
, FlexibleContexts
, FlexibleInstances
, StandaloneDeriving
, TypeApplications
, TypeSynonymInstances
, InstanceSigs
, MultiParamTypeClasses
, TupleSections
, LambdaCase
, BangPatterns
, ViewPatterns
default-language: Haskell2010 default-language: Haskell2010

View File

@ -1,4 +1,4 @@
{-# LANGUAGE RecordWildCards #-} {-# language RecordWildCards #-}
{-| {-|
Description : Build related types Description : Build related types
Maintainer : srk <srk@48.io> Maintainer : srk <srk@48.io>
@ -12,7 +12,6 @@ module System.Nix.Build
where where
import Data.Time ( UTCTime ) import Data.Time ( UTCTime )
import Data.Text ( Text )
-- keep the order of these Enums to match enums from reference implementations -- keep the order of these Enums to match enums from reference implementations
-- src/libstore/store-api.hh -- src/libstore/store-api.hh

View File

@ -1,4 +1,3 @@
{-# LANGUAGE OverloadedStrings #-}
module System.Nix.Derivation module System.Nix.Derivation
( parseDerivation ( parseDerivation
@ -6,11 +5,8 @@ module System.Nix.Derivation
) )
where where
import Data.Text ( Text )
import qualified Data.Text as Text
import qualified Data.Text.Lazy.Builder as Text.Lazy import qualified Data.Text.Lazy.Builder as Text.Lazy
( Builder ) ( Builder )
import qualified Data.Text.Lazy.Builder as Text.Lazy.Builder
import qualified Data.Attoparsec.Text.Lazy as Text.Lazy import qualified Data.Attoparsec.Text.Lazy as Text.Lazy
( Parser ) ( Parser )
import Nix.Derivation ( Derivation ) import Nix.Derivation ( Derivation )
@ -29,7 +25,5 @@ parseDerivation expectedRoot =
buildDerivation :: Derivation StorePath Text -> Text.Lazy.Builder buildDerivation :: Derivation StorePath Text -> Text.Lazy.Builder
buildDerivation = buildDerivation =
Derivation.buildDerivationWith Derivation.buildDerivationWith
(string . Text.pack . show) (show . show)
string show
where
string = Text.Lazy.Builder.fromText . Text.pack . show

View File

@ -1,4 +1,4 @@
{-# LANGUAGE CPP #-} {-# language CPP #-}
module System.Nix.Internal.Base module System.Nix.Internal.Base
( BaseEncoding(Base16,NixBase32,Base64) ( BaseEncoding(Base16,NixBase32,Base64)
@ -7,9 +7,6 @@ module System.Nix.Internal.Base
) )
where where
import qualified Data.Text as T
import qualified Data.Text.Encoding as T
import qualified Data.ByteString as Bytes
import qualified Data.ByteString.Base16 as Base16 import qualified Data.ByteString.Base16 as Base16
import qualified System.Nix.Base32 as Base32 -- Nix has own Base32 encoding import qualified System.Nix.Base32 as Base32 -- Nix has own Base32 encoding
import qualified Data.ByteString.Base64 as Base64 import qualified Data.ByteString.Base64 as Base64
@ -24,22 +21,22 @@ data BaseEncoding
-- | Encode @ByteString@ with @Base@ encoding, produce @Text@. -- | Encode @ByteString@ with @Base@ encoding, produce @Text@.
encodeWith :: BaseEncoding -> Bytes.ByteString -> T.Text encodeWith :: BaseEncoding -> ByteString -> Text
encodeWith Base16 = T.decodeUtf8 . Base16.encode encodeWith Base16 = decodeUtf8 . Base16.encode
encodeWith NixBase32 = Base32.encode encodeWith NixBase32 = Base32.encode
encodeWith Base64 = T.decodeUtf8 . Base64.encode encodeWith Base64 = decodeUtf8 . Base64.encode
-- | Take the input & @Base@ encoding witness -> decode into @Text@. -- | Take the input & @Base@ encoding witness -> decode into @Text@.
decodeWith :: BaseEncoding -> T.Text -> Either String Bytes.ByteString decodeWith :: BaseEncoding -> Text -> Either String ByteString
#if MIN_VERSION_base16_bytestring(1,0,0) #if MIN_VERSION_base16_bytestring(1,0,0)
decodeWith Base16 = Base16.decode . T.encodeUtf8 decodeWith Base16 = Base16.decode . encodeUtf8
#else #else
decodeWith Base16 = lDecode -- this tacit sugar simply makes GHC pleased with number of args decodeWith Base16 = lDecode -- this tacit sugar simply makes GHC pleased with number of args
where where
lDecode t = lDecode t =
case Base16.decode (T.encodeUtf8 t) of case Base16.decode (encodeUtf8 t) of
(x, "") -> pure $ x (x, "") -> pure $ x
_ -> Left $ "Unable to decode base16 string" <> T.unpack t _ -> Left $ "Unable to decode base16 string" <> toString t
#endif #endif
decodeWith NixBase32 = Base32.decode decodeWith NixBase32 = Base32.decode
decodeWith Base64 = Base64.decode . T.encodeUtf8 decodeWith Base64 = Base64.decode . encodeUtf8

View File

@ -6,18 +6,12 @@ module System.Nix.Internal.Base32
where where
import Data.Bool ( bool )
import Data.Maybe ( fromMaybe )
import Data.ByteString ( ByteString )
import qualified Data.ByteString as Bytes import qualified Data.ByteString as Bytes
import qualified Data.ByteString.Char8 as Bytes.Char8 import qualified Data.ByteString.Char8 as Bytes.Char8
import qualified Data.Text import qualified Data.Text
import Data.Vector ( Vector ) import Data.Vector ( Vector )
import qualified Data.Vector as Vector import qualified Data.Vector as Vector
import Data.Text ( Text )
import Data.Bits ( shiftR ) import Data.Bits ( shiftR )
import Data.Word ( Word8 )
import Data.List ( unfoldr )
import Numeric ( readInt ) import Numeric ( readInt )
@ -27,7 +21,7 @@ digits32 = Vector.fromList "0123456789abcdfghijklmnpqrsvwxyz"
-- | Encode a 'BS.ByteString' in Nix's base32 encoding -- | Encode a 'BS.ByteString' in Nix's base32 encoding
encode :: ByteString -> Text encode :: ByteString -> Text
encode c = Data.Text.pack $ takeCharPosFromDict <$> [nChar - 1, nChar - 2 .. 0] encode c = toText $ takeCharPosFromDict <$> [nChar - 1, nChar - 2 .. 0]
where where
-- Each base32 character gives us 5 bits of information, while -- Each base32 character gives us 5 bits of information, while
-- each byte gives is 8. Because 'div' rounds down, we need to add -- each byte gives is 8. Because 'div' rounds down, we need to add
@ -74,9 +68,9 @@ unsafeDecode what =
(\c -> fromMaybe (error "character not in digits32") (\c -> fromMaybe (error "character not in digits32")
$ Vector.findIndex (== c) digits32 $ Vector.findIndex (== c) digits32
) )
(Data.Text.unpack what) (toString what)
of of
[(i, _)] -> Right $ padded $ integerToBS i [(i, _)] -> pure $ padded $ integerToBS i
x -> Left $ "Can't decode: readInt returned " <> show x x -> Left $ "Can't decode: readInt returned " <> show x
where where
padded x padded x

View File

@ -2,15 +2,12 @@
Description : Cryptographic hashing interface for hnix-store, on top Description : Cryptographic hashing interface for hnix-store, on top
of the cryptohash family of libraries. of the cryptohash family of libraries.
-} -}
{-# LANGUAGE AllowAmbiguousTypes #-} {-# language AllowAmbiguousTypes #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# language TypeFamilies #-}
{-# LANGUAGE TypeFamilies #-} {-# language ScopedTypeVariables #-}
{-# LANGUAGE ScopedTypeVariables #-} {-# language DataKinds #-}
{-# LANGUAGE TypeApplications #-} {-# language ExistentialQuantification #-}
{-# LANGUAGE DataKinds #-} {-# language CPP #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE CPP #-}
module System.Nix.Internal.Hash module System.Nix.Internal.Hash
( NamedAlgo(..) ( NamedAlgo(..)
@ -22,9 +19,9 @@ module System.Nix.Internal.Hash
) )
where where
import qualified Text.Show
import qualified Crypto.Hash as C import qualified Crypto.Hash as C
import qualified Data.ByteString as BS import qualified Data.ByteString as BS
import Data.Text (Text)
import qualified Data.Text as T import qualified Data.Text as T
import System.Nix.Internal.Base import System.Nix.Internal.Base
import Data.ByteArray import Data.ByteArray
@ -52,27 +49,27 @@ data SomeNamedDigest = forall a . NamedAlgo a => SomeDigest (C.Digest a)
instance Show SomeNamedDigest where instance Show SomeNamedDigest where
show sd = case sd of show sd = case sd of
SomeDigest (digest :: C.Digest hashType) -> T.unpack $ "SomeDigest " <> algoName @hashType <> ":" <> encodeDigestWith NixBase32 digest SomeDigest (digest :: C.Digest hashType) -> toString $ "SomeDigest " <> algoName @hashType <> ":" <> encodeDigestWith NixBase32 digest
mkNamedDigest :: Text -> Text -> Either String SomeNamedDigest mkNamedDigest :: Text -> Text -> Either String SomeNamedDigest
mkNamedDigest name sriHash = mkNamedDigest name sriHash =
let (sriName, h) = T.breakOnEnd "-" sriHash in let (sriName, h) = T.breakOnEnd "-" sriHash in
if sriName == "" || sriName == name <> "-" if sriName == "" || sriName == name <> "-"
then mkDigest h then mkDigest h
else Left $ T.unpack $ "Sri hash method " <> sriName <> " does not match the required hash type " <> name else Left $ toString $ "Sri hash method " <> sriName <> " does not match the required hash type " <> name
where where
mkDigest h = case name of mkDigest h = case name of
"md5" -> SomeDigest <$> decodeGo C.MD5 h "md5" -> SomeDigest <$> decodeGo C.MD5 h
"sha1" -> SomeDigest <$> decodeGo C.SHA1 h "sha1" -> SomeDigest <$> decodeGo C.SHA1 h
"sha256" -> SomeDigest <$> decodeGo C.SHA256 h "sha256" -> SomeDigest <$> decodeGo C.SHA256 h
"sha512" -> SomeDigest <$> decodeGo C.SHA512 h "sha512" -> SomeDigest <$> decodeGo C.SHA512 h
_ -> Left $ "Unknown hash name: " <> T.unpack name _ -> Left $ "Unknown hash name: " <> toString name
decodeGo :: forall a . NamedAlgo a => a -> Text -> Either String (C.Digest a) decodeGo :: forall a . NamedAlgo a => a -> Text -> Either String (C.Digest a)
decodeGo a h decodeGo a h
| size == base16Len = decodeDigestWith Base16 h | size == base16Len = decodeDigestWith Base16 h
| size == base32Len = decodeDigestWith NixBase32 h | size == base32Len = decodeDigestWith NixBase32 h
| size == base64Len = decodeDigestWith Base64 h | size == base64Len = decodeDigestWith Base64 h
| otherwise = Left $ T.unpack sriHash <> " is not a valid " <> T.unpack name <> " hash. Its length (" <> show size <> ") does not match any of " <> show [base16Len, base32Len, base64Len] | otherwise = Left $ toString sriHash <> " is not a valid " <> toString name <> " hash. Its length (" <> show size <> ") does not match any of " <> show [base16Len, base32Len, base64Len]
where where
size = T.length h size = T.length h
hsize = C.hashDigestSize a hsize = C.hashDigestSize a
@ -100,8 +97,3 @@ decodeDigestWith b x =
maybeToRight maybeToRight
("Cryptonite was not able to convert '(ByteString -> Digest a)' for: '" <> show bs <>"'.") ("Cryptonite was not able to convert '(ByteString -> Digest a)' for: '" <> show bs <>"'.")
(toEither . C.digestFromByteString) bs (toEither . C.digestFromByteString) bs
where
-- To not depend on @extra@
maybeToRight :: b -> Maybe a -> Either b a
maybeToRight _ (Just r) = pure r
maybeToRight y Nothing = Left y

View File

@ -1,7 +1,6 @@
{-# LANGUAGE FlexibleContexts #-} {-# language KindSignatures #-}
{-# LANGUAGE KindSignatures #-} {-# language RankNTypes #-}
{-# LANGUAGE RankNTypes #-} {-# language ScopedTypeVariables #-}
{-# LANGUAGE ScopedTypeVariables #-}
module System.Nix.Internal.Nar.Effects module System.Nix.Internal.Nar.Effects
( NarEffects(..) ( NarEffects(..)
@ -10,7 +9,6 @@ module System.Nix.Internal.Nar.Effects
import qualified Data.ByteString as Bytes import qualified Data.ByteString as Bytes
import qualified Data.ByteString.Lazy as Bytes.Lazy import qualified Data.ByteString.Lazy as Bytes.Lazy
import Data.Int (Int64)
import qualified System.Directory as Directory import qualified System.Directory as Directory
import System.Posix.Files ( createSymbolicLink import System.Posix.Files ( createSymbolicLink
, fileSize , fileSize
@ -79,7 +77,7 @@ streamStringOutIO
-> m () -> m ()
streamStringOutIO f getChunk = streamStringOutIO f getChunk =
Exception.Lifted.bracket Exception.Lifted.bracket
(IO.liftIO $ IO.openFile f IO.WriteMode) (IO.liftIO $ IO.openFile f WriteMode)
(IO.liftIO . IO.hClose) (IO.liftIO . IO.hClose)
go go
`Exception.Lifted.catch` `Exception.Lifted.catch`
@ -89,7 +87,7 @@ streamStringOutIO f getChunk =
go handle = do go handle = do
chunk <- getChunk chunk <- getChunk
case chunk of case chunk of
Nothing -> pure () Nothing -> pass
Just c -> do Just c -> do
IO.liftIO $ Bytes.hPut handle c IO.liftIO $ Bytes.hPut handle c
go handle go handle

View File

@ -1,11 +1,8 @@
-- | A streaming parser for the NAR format -- | A streaming parser for the NAR format
{-# LANGUAGE FlexibleContexts #-} {-# language GeneralizedNewtypeDeriving #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# language ScopedTypeVariables #-}
{-# LANGUAGE OverloadedStrings #-} {-# language TypeFamilies #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE TypeFamilies #-}
module System.Nix.Internal.Nar.Parser module System.Nix.Internal.Nar.Parser
( runParser ( runParser
@ -15,14 +12,11 @@ module System.Nix.Internal.Nar.Parser
) )
where where
import qualified Relude.Unsafe as Unsafe
import qualified Algebra.Graph as Graph import qualified Algebra.Graph as Graph
import qualified Algebra.Graph.ToGraph as Graph import qualified Algebra.Graph.ToGraph as Graph
import qualified Control.Concurrent as Concurrent import qualified Control.Concurrent as Concurrent
import qualified Control.Exception.Lifted as Exception.Lifted import qualified Control.Exception.Lifted as Exception.Lifted
import Control.Monad ( forM
, when
, forM_
)
import qualified Control.Monad.Except as Except import qualified Control.Monad.Except as Except
import qualified Control.Monad.Fail as Fail import qualified Control.Monad.Fail as Fail
import qualified Control.Monad.IO.Class as IO import qualified Control.Monad.IO.Class as IO
@ -30,19 +24,11 @@ import qualified Control.Monad.Reader as Reader
import qualified Control.Monad.State as State import qualified Control.Monad.State as State
import qualified Control.Monad.Trans as Trans import qualified Control.Monad.Trans as Trans
import qualified Control.Monad.Trans.Control as Base import qualified Control.Monad.Trans.Control as Base
import Data.ByteString ( ByteString )
import qualified Data.ByteString as Bytes import qualified Data.ByteString as Bytes
import Data.Bool ( bool )
import qualified Data.Either as Either
import Data.Int ( Int64 )
import qualified Data.IORef as IORef
import qualified Data.List as List import qualified Data.List as List
import qualified Data.Map as Map import qualified Data.Map as Map
import Data.Maybe ( catMaybes )
import qualified Data.Serialize as Serialize import qualified Data.Serialize as Serialize
import Data.Text ( Text )
import qualified Data.Text as Text import qualified Data.Text as Text
import qualified Data.Text.Encoding as Text
import qualified System.Directory as Directory import qualified System.Directory as Directory
import System.FilePath as FilePath import System.FilePath as FilePath
import qualified System.IO as IO import qualified System.IO as IO
@ -86,15 +72,15 @@ runParser
-- ^ A parser to run, such as @parseNar@ -- ^ A parser to run, such as @parseNar@
-> IO.Handle -> IO.Handle
-- ^ A handle the stream containg the NAR. It should already be -- ^ A handle the stream containg the NAR. It should already be
-- open and in @IO.ReadMode@ -- open and in @ReadMode@
-> FilePath -> FilePath
-- ^ The root file system object to be created by the NAR -- ^ The root file system object to be created by the NAR
-> m (Either String a) -> m (Either String a)
runParser effs (NarParser action) h target = do runParser effs (NarParser action) h target = do
unpackResult <- unpackResult <-
Reader.runReaderT (Except.runExceptT $ State.evalStateT action state0) effs runReaderT (runExceptT $ State.evalStateT action state0) effs
`Exception.Lifted.catch` exceptionHandler `Exception.Lifted.catch` exceptionHandler
when (Either.isLeft unpackResult) cleanup when (isLeft unpackResult) cleanup
pure unpackResult pure unpackResult
where where
@ -123,7 +109,7 @@ runParser effs (NarParser action) h target = do
instance Trans.MonadTrans NarParser where instance Trans.MonadTrans NarParser where
lift act = NarParser $ (Trans.lift . Trans.lift . Trans.lift) act lift act = NarParser $ (lift . lift . lift) act
data ParserState = ParserState data ParserState = ParserState
@ -177,7 +163,7 @@ parseSymlink = do
(dir, file) <- currentDirectoryAndFile (dir, file) <- currentDirectoryAndFile
pushLink $ pushLink $
LinkInfo LinkInfo
{ linkTarget = Text.unpack target { linkTarget = toString target
, linkFile = file , linkFile = file
, linkPWD = dir , linkPWD = dir
} }
@ -185,7 +171,7 @@ parseSymlink = do
currentDirectoryAndFile :: Monad m => NarParser m (FilePath, FilePath) currentDirectoryAndFile :: Monad m => NarParser m (FilePath, FilePath)
currentDirectoryAndFile = do currentDirectoryAndFile = do
dirStack <- State.gets directoryStack dirStack <- State.gets directoryStack
pure (List.foldr1 (</>) (List.reverse $ drop 1 dirStack), head dirStack) pure (List.foldr1 (</>) (List.reverse $ drop 1 dirStack), Unsafe.head dirStack)
-- | Internal data type representing symlinks encountered in the NAR -- | Internal data type representing symlinks encountered in the NAR
@ -220,7 +206,7 @@ parseFile = do
-- Set up for defining `getChunk` -- Set up for defining `getChunk`
narHandle <- State.gets handle narHandle <- State.gets handle
bytesLeftVar <- IO.liftIO $ IORef.newIORef fSize bytesLeftVar <- IO.liftIO $ newIORef fSize
let let
-- getChunk tracks the number of total bytes we still need to get from the -- getChunk tracks the number of total bytes we still need to get from the
@ -228,13 +214,13 @@ parseFile = do
-- chunk we read) -- chunk we read)
getChunk :: m (Maybe ByteString) getChunk :: m (Maybe ByteString)
getChunk = do getChunk = do
bytesLeft <- IO.liftIO $ IORef.readIORef bytesLeftVar bytesLeft <- IO.liftIO $ readIORef bytesLeftVar
if bytesLeft == 0 if bytesLeft == 0
then pure Nothing then pure Nothing
else do else do
chunk <- IO.liftIO $ Bytes.hGetSome narHandle $ fromIntegral $ min 10000 bytesLeft chunk <- IO.liftIO $ Bytes.hGetSome narHandle $ fromIntegral $ min 10000 bytesLeft
when (Bytes.null chunk) (Fail.fail "ZERO BYTES") when (Bytes.null chunk) (Fail.fail "ZERO BYTES")
IO.liftIO $ IORef.modifyIORef bytesLeftVar $ \n -> n - fromIntegral (Bytes.length chunk) IO.liftIO $ modifyIORef bytesLeftVar $ \n -> n - fromIntegral (Bytes.length chunk)
-- This short pause is necessary for letting the garbage collector -- This short pause is necessary for letting the garbage collector
-- clean up chunks from previous runs. Without it, heap memory usage can -- clean up chunks from previous runs. Without it, heap memory usage can
@ -243,12 +229,12 @@ parseFile = do
pure $ Just chunk pure $ Just chunk
target <- currentFile target <- currentFile
streamFile <- Reader.asks Nar.narStreamFile streamFile <- asks Nar.narStreamFile
Trans.lift (streamFile target getChunk) lift (streamFile target getChunk)
when (s == "executable") $ do when (s == "executable") $ do
effs :: Nar.NarEffects m <- Reader.ask effs :: Nar.NarEffects m <- ask
Trans.lift $ do lift $ do
p <- Nar.narGetPerms effs target p <- Nar.narGetPerms effs target
Nar.narSetPerms effs target (p { Directory.executable = True }) Nar.narSetPerms effs target (p { Directory.executable = True })
@ -259,9 +245,9 @@ parseFile = do
-- handles for target files longer than needed -- handles for target files longer than needed
parseDirectory :: (IO.MonadIO m, Fail.MonadFail m) => NarParser m () parseDirectory :: (IO.MonadIO m, Fail.MonadFail m) => NarParser m ()
parseDirectory = do parseDirectory = do
createDirectory <- Reader.asks Nar.narCreateDir createDirectory <- asks Nar.narCreateDir
target <- currentFile target <- currentFile
Trans.lift $ createDirectory target lift $ createDirectory target
parseEntryOrFinish parseEntryOrFinish
where where
@ -282,7 +268,7 @@ parseDirectory = do
parens $ do parens $ do
expectStr "name" expectStr "name"
fName <- parseStr fName <- parseStr
pushFileName (Text.unpack fName) pushFileName (toString fName)
expectStr "node" expectStr "node"
parens parseFSO parens parseFSO
popFileName popFileName
@ -307,7 +293,7 @@ parseStr = do
strBytes <- consume $ fromIntegral len strBytes <- consume $ fromIntegral len
expectRawString expectRawString
(Bytes.replicate (fromIntegral $ padLen $ fromIntegral len) 0) (Bytes.replicate (fromIntegral $ padLen $ fromIntegral len) 0)
pure $ Text.decodeUtf8 strBytes pure $ decodeUtf8 strBytes
-- | Get an Int64 describing the length of the upcoming string, -- | Get an Int64 describing the length of the upcoming string,
@ -386,13 +372,13 @@ parens act = do
-- (Targets must be created before the links that target them) -- (Targets must be created before the links that target them)
createLinks :: IO.MonadIO m => NarParser m () createLinks :: IO.MonadIO m => NarParser m ()
createLinks = do createLinks = do
createLink <- Reader.asks Nar.narCreateLink createLink <- asks Nar.narCreateLink
allLinks <- State.gets links allLinks <- State.gets links
sortedLinks <- IO.liftIO $ sortLinksIO allLinks sortedLinks <- IO.liftIO $ sortLinksIO allLinks
forM_ sortedLinks $ \li -> do forM_ sortedLinks $ \li -> do
pwd <- IO.liftIO Directory.getCurrentDirectory pwd <- IO.liftIO Directory.getCurrentDirectory
IO.liftIO $ Directory.setCurrentDirectory (linkPWD li) IO.liftIO $ Directory.setCurrentDirectory (linkPWD li)
Trans.lift $ createLink (linkTarget li) (linkFile li) lift $ createLink (linkTarget li) (linkFile li)
IO.liftIO $ Directory.setCurrentDirectory pwd IO.liftIO $ Directory.setCurrentDirectory pwd
where where
@ -447,7 +433,7 @@ consume n = do
popStr :: Monad m => NarParser m (Maybe Text) popStr :: Monad m => NarParser m (Maybe Text)
popStr = do popStr = do
s <- State.get s <- State.get
case List.uncons (tokenStack s) of case uncons (tokenStack s) of
Nothing -> pure Nothing Nothing -> pure Nothing
Just (x, xs) -> do Just (x, xs) -> do
State.put $ s { tokenStack = xs } State.put $ s { tokenStack = xs }
@ -492,14 +478,14 @@ pushLink linkInfo = State.modify (\s -> s { links = linkInfo : links s })
testParser :: (m ~ IO) => NarParser m a -> ByteString -> m (Either String a) testParser :: (m ~ IO) => NarParser m a -> ByteString -> m (Either String a)
testParser p b = do testParser p b = do
Bytes.writeFile tmpFileName b Bytes.writeFile tmpFileName b
IO.withFile tmpFileName IO.ReadMode $ \h -> withFile tmpFileName ReadMode $ \h ->
runParser Nar.narEffectsIO p h tmpFileName runParser Nar.narEffectsIO p h tmpFileName
where where
tmpFileName = "tmp" tmpFileName = "tmp"
testParser' :: (m ~ IO) => FilePath -> IO (Either String ()) testParser' :: (m ~ IO) => FilePath -> IO (Either String ())
testParser' fp = testParser' fp =
IO.withFile fp IO.ReadMode $ \h -> runParser Nar.narEffectsIO parseNar h "tmp" withFile fp ReadMode $ \h -> runParser Nar.narEffectsIO parseNar h "tmp"

View File

@ -1,7 +1,6 @@
-- | Stream out a NAR file from a regular file -- | Stream out a NAR file from a regular file
{-# LANGUAGE OverloadedStrings #-} {-# language ScopedTypeVariables #-}
{-# LANGUAGE ScopedTypeVariables #-}
module System.Nix.Internal.Nar.Streamer module System.Nix.Internal.Nar.Streamer
( streamNarIO ( streamNarIO
@ -9,18 +8,11 @@ module System.Nix.Internal.Nar.Streamer
) )
where where
import Control.Monad ( forM_
, when
)
import qualified Control.Monad.IO.Class as IO import qualified Control.Monad.IO.Class as IO
import Data.Bool ( bool )
import Data.ByteString ( ByteString )
import qualified Data.ByteString as Bytes import qualified Data.ByteString as Bytes
import qualified Data.ByteString.Char8 as Bytes.Char8 import qualified Data.ByteString.Char8 as Bytes.Char8
import qualified Data.ByteString.Lazy as Bytes.Lazy import qualified Data.ByteString.Lazy as Bytes.Lazy
import qualified Data.List as List
import qualified Data.Serialize as Serial import qualified Data.Serialize as Serial
import GHC.Int ( Int64 )
import qualified System.Directory as Directory import qualified System.Directory as Directory
import System.FilePath ( (</>) ) import System.FilePath ( (</>) )
@ -66,7 +58,7 @@ streamNarIO yield effs basePath = do
when isDir $ do when isDir $ do
fs <- IO.liftIO (Nar.narListDir effs path) fs <- IO.liftIO (Nar.narListDir effs path)
yield $ strs ["type", "directory"] yield $ strs ["type", "directory"]
forM_ (List.sort fs) $ \f -> do forM_ (sort fs) $ \f -> do
yield $ str "entry" yield $ str "entry"
parens $ do parens $ do
let fullName = path </> f let fullName = path </> f

View File

@ -1,8 +1,7 @@
{-| {-|
Description : Nix-relevant interfaces to NaCl signatures. Description : Nix-relevant interfaces to NaCl signatures.
-} -}
{-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# language CPP #-}
{-# LANGUAGE CPP #-}
module System.Nix.Internal.Signature module System.Nix.Internal.Signature
( Signature ( Signature
@ -11,9 +10,7 @@ module System.Nix.Internal.Signature
where where
import Data.ByteString ( ByteString )
import qualified Data.ByteString as Bytes import qualified Data.ByteString as Bytes
import Data.Coerce ( coerce )
import Crypto.Saltine.Core.Sign ( PublicKey ) import Crypto.Saltine.Core.Sign ( PublicKey )
import Crypto.Saltine.Class ( IsEncoding(..) ) import Crypto.Saltine.Class ( IsEncoding(..) )

View File

@ -1,14 +1,12 @@
{-| {-|
Description : Representation of Nix store paths. Description : Representation of Nix store paths.
-} -}
{-# LANGUAGE TypeApplications #-} {-# language ConstraintKinds #-}
{-# LANGUAGE OverloadedStrings #-} {-# language RecordWildCards #-}
{-# LANGUAGE ConstraintKinds #-} {-# language GeneralizedNewtypeDeriving #-}
{-# LANGUAGE RecordWildCards #-} {-# language ScopedTypeVariables #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# language AllowAmbiguousTypes #-}
{-# LANGUAGE ScopedTypeVariables #-} {-# language DataKinds #-}
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE DataKinds #-}
module System.Nix.Internal.StorePath module System.Nix.Internal.StorePath
( -- * Basic store path types ( -- * Basic store path types
@ -32,25 +30,21 @@ module System.Nix.Internal.StorePath
, pathParser , pathParser
) )
where where
import qualified Relude.Unsafe as Unsafe
import qualified Text.Show
import System.Nix.Internal.Hash import System.Nix.Internal.Hash
import System.Nix.Internal.Base import System.Nix.Internal.Base
import qualified System.Nix.Internal.Base32 as Nix.Base32 import qualified System.Nix.Internal.Base32 as Nix.Base32
import Data.ByteString ( ByteString )
import qualified Data.ByteString.Char8 as Bytes.Char8 import qualified Data.ByteString.Char8 as Bytes.Char8
import qualified Data.Char as Char import qualified Data.Char as Char
import Data.Text ( Text )
import qualified Data.Text as Text import qualified Data.Text as Text
import qualified Data.Text.Encoding as Text
( encodeUtf8 )
import Data.Attoparsec.Text.Lazy ( Parser import Data.Attoparsec.Text.Lazy ( Parser
, (<?>) , (<?>)
) )
import qualified Data.Attoparsec.Text.Lazy as Parser.Text.Lazy import qualified Data.Attoparsec.Text.Lazy as Parser.Text.Lazy
import qualified System.FilePath as FilePath import qualified System.FilePath as FilePath
import Data.Hashable ( Hashable(..) )
import Data.HashSet ( HashSet )
import Data.Coerce ( coerce )
import Crypto.Hash ( SHA256 import Crypto.Hash ( SHA256
, Digest , Digest
) )
@ -173,8 +167,8 @@ storePathToRawFilePath StorePath{..} =
root <> "/" <> hashPart <> "-" <> name root <> "/" <> hashPart <> "-" <> name
where where
root = Bytes.Char8.pack storePathRoot root = Bytes.Char8.pack storePathRoot
hashPart = Text.encodeUtf8 $ encodeWith NixBase32 $ coerce storePathHash hashPart = encodeUtf8 $ encodeWith NixBase32 $ coerce storePathHash
name = Text.encodeUtf8 $ unStorePathName storePathName name = encodeUtf8 $ unStorePathName storePathName
-- | Render a 'StorePath' as a 'FilePath'. -- | Render a 'StorePath' as a 'FilePath'.
storePathToFilePath :: StorePath -> FilePath storePathToFilePath :: StorePath -> FilePath
@ -182,13 +176,13 @@ storePathToFilePath = Bytes.Char8.unpack . storePathToRawFilePath
-- | Render a 'StorePath' as a 'Text'. -- | Render a 'StorePath' as a 'Text'.
storePathToText :: StorePath -> Text storePathToText :: StorePath -> Text
storePathToText = Text.pack . Bytes.Char8.unpack . storePathToRawFilePath storePathToText = toText . Bytes.Char8.unpack . storePathToRawFilePath
-- | Build `narinfo` suffix from `StorePath` which -- | Build `narinfo` suffix from `StorePath` which
-- can be used to query binary caches. -- can be used to query binary caches.
storePathToNarInfo :: StorePath -> Bytes.Char8.ByteString storePathToNarInfo :: StorePath -> Bytes.Char8.ByteString
storePathToNarInfo StorePath{..} = storePathToNarInfo StorePath{..} =
Text.encodeUtf8 $ encodeWith NixBase32 (coerce storePathHash) <> ".narinfo" encodeUtf8 $ encodeWith NixBase32 (coerce storePathHash) <> ".narinfo"
-- | Parse `StorePath` from `Bytes.Char8.ByteString`, checking -- | Parse `StorePath` from `Bytes.Char8.ByteString`, checking
-- that store directory matches `expectedRoot`. -- that store directory matches `expectedRoot`.
@ -196,15 +190,15 @@ parsePath :: FilePath -> Bytes.Char8.ByteString -> Either String StorePath
parsePath expectedRoot x = parsePath expectedRoot x =
let let
(rootDir, fname) = FilePath.splitFileName . Bytes.Char8.unpack $ x (rootDir, fname) = FilePath.splitFileName . Bytes.Char8.unpack $ x
(storeBasedHashPart, namePart) = Text.breakOn "-" $ Text.pack fname (storeBasedHashPart, namePart) = Text.breakOn "-" $ toText fname
storeHash = decodeWith NixBase32 storeBasedHashPart storeHash = decodeWith NixBase32 storeBasedHashPart
name = makeStorePathName . Text.drop 1 $ namePart name = makeStorePathName . Text.drop 1 $ namePart
--rootDir' = dropTrailingPathSeparator rootDir --rootDir' = dropTrailingPathSeparator rootDir
-- cannot use ^^ as it drops multiple slashes /a/b/// -> /a/b -- cannot use ^^ as it drops multiple slashes /a/b/// -> /a/b
rootDir' = init rootDir rootDir' = Unsafe.init rootDir
storeDir = storeDir =
if expectedRoot == rootDir' if expectedRoot == rootDir'
then Right rootDir' then pure rootDir'
else Left $ "Root store dir mismatch, expected" <> expectedRoot <> "got" <> rootDir' else Left $ "Root store dir mismatch, expected" <> expectedRoot <> "got" <> rootDir'
in in
StorePath <$> coerce storeHash <*> name <*> storeDir StorePath <$> coerce storeHash <*> name <*> storeDir
@ -212,7 +206,7 @@ parsePath expectedRoot x =
pathParser :: FilePath -> Parser StorePath pathParser :: FilePath -> Parser StorePath
pathParser expectedRoot = do pathParser expectedRoot = do
_ <- _ <-
Parser.Text.Lazy.string (Text.pack expectedRoot) Parser.Text.Lazy.string (toText expectedRoot)
<?> "Store root mismatch" -- e.g. /nix/store <?> "Store root mismatch" -- e.g. /nix/store
_ <- Parser.Text.Lazy.char '/' _ <- Parser.Text.Lazy.char '/'

View File

@ -1,6 +1,5 @@
{-# LANGUAGE ScopedTypeVariables #-} {-# language ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-} {-# language DataKinds #-}
{-# LANGUAGE DataKinds #-}
module System.Nix.Internal.Truncation module System.Nix.Internal.Truncation
( truncateInNixWay ( truncateInNixWay
@ -8,10 +7,6 @@ module System.Nix.Internal.Truncation
where where
import qualified Data.ByteString as Bytes import qualified Data.ByteString as Bytes
import Data.Bits (xor)
import Data.List (foldl')
import Data.Word (Word8)
import Data.Bool (bool)
-- | Bytewise truncation of a 'Digest'. -- | Bytewise truncation of a 'Digest'.
-- --

View File

@ -3,10 +3,8 @@ Description : Generating and consuming NAR files
Maintainer : Shea Levy <shea@shealevy.com> Maintainer : Shea Levy <shea@shealevy.com>
-} -}
{-# LANGUAGE FlexibleContexts #-} {-# language ScopedTypeVariables #-}
{-# LANGUAGE OverloadedStrings #-} {-# language TypeFamilies #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
module System.Nix.Nar module System.Nix.Nar
@ -46,7 +44,7 @@ import qualified System.Nix.Internal.Nar.Streamer as Nar
-- | Pack the filesystem object at @FilePath@ into a NAR and stream it into the -- | Pack the filesystem object at @FilePath@ into a NAR and stream it into the
-- @IO.Handle@ -- @IO.Handle@
-- The handle should aleady be open and in @IO.WriteMode@. -- The handle should aleady be open and in @WriteMode@.
buildNarIO buildNarIO
:: Nar.NarEffects IO :: Nar.NarEffects IO
-> FilePath -> FilePath

View File

@ -1,22 +1,14 @@
{-# LANGUAGE DataKinds #-} {-# language DataKinds #-}
{-# LANGUAGE OverloadedStrings #-} {-# language ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE ScopedTypeVariables #-}
module System.Nix.ReadonlyStore where module System.Nix.ReadonlyStore where
import Data.ByteString ( ByteString )
import qualified Data.ByteString as BS import qualified Data.ByteString as BS
import Data.List ( sort )
import qualified Data.Text as T
import qualified Data.HashSet as HS import qualified Data.HashSet as HS
import Data.Text.Encoding
import System.Nix.Hash import System.Nix.Hash
import System.Nix.Nar import System.Nix.Nar
import System.Nix.StorePath import System.Nix.StorePath
import Control.Monad.State.Strict
import Data.Coerce ( coerce )
import Crypto.Hash ( Context import Crypto.Hash ( Context
, Digest , Digest
, hash , hash
@ -45,7 +37,7 @@ makeStorePath fp ty h nm = StorePath (coerce storeHash) nm fp
ty:fmap encodeUtf8 ty:fmap encodeUtf8
[ algoName @h [ algoName @h
, encodeDigestWith Base16 h , encodeDigestWith Base16 h
, T.pack fp , toText fp
, coerce nm , coerce nm
] ]

View File

@ -8,9 +8,7 @@ import System.Nix.StorePath ( StorePath
, ContentAddressableAddress , ContentAddressableAddress
) )
import System.Nix.Hash ( SomeNamedDigest ) import System.Nix.Hash ( SomeNamedDigest )
import Data.Set ( Set )
import Data.Time ( UTCTime ) import Data.Time ( UTCTime )
import Data.Word ( Word64 )
import System.Nix.Signature ( NarSignature ) import System.Nix.Signature ( NarSignature )
-- | Metadata about a 'StorePath' -- | Metadata about a 'StorePath'

View File

@ -1,19 +1,13 @@
{-# LANGUAGE TypeApplications #-} {-# language DataKinds #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE DataKinds #-}
{-# OPTIONS_GHC -Wno-orphans #-} {-# OPTIONS_GHC -Wno-orphans #-}
module Arbitrary where module Arbitrary where
import Control.Monad ( replicateM )
import qualified Data.ByteString.Char8 as BSC import qualified Data.ByteString.Char8 as BSC
import qualified Data.Text as T
import Test.Tasty.QuickCheck import Test.Tasty.QuickCheck
import System.Nix.Internal.StorePath import System.Nix.Internal.StorePath
import Control.Applicative ( liftA3 )
import Data.Coerce ( coerce )
import Crypto.Hash ( SHA256 import Crypto.Hash ( SHA256
, Digest , Digest
, hash , hash
@ -29,7 +23,7 @@ dir :: Gen String
dir = ('/':) <$> listOf1 (elements $ '/':['a'..'z']) dir = ('/':) <$> listOf1 (elements $ '/':['a'..'z'])
instance Arbitrary StorePathName where instance Arbitrary StorePathName where
arbitrary = StorePathName . T.pack <$> ((:) <$> s1 <*> listOf sn) arbitrary = StorePathName . toText <$> ((:) <$> s1 <*> listOf sn)
where where
alphanum = ['a' .. 'z'] <> ['A' .. 'Z'] <> ['0' .. '9'] alphanum = ['a' .. 'z'] <> ['A' .. 'Z'] <> ['0' .. '9']
s1 = elements $ alphanum <> "+-_?=" s1 = elements $ alphanum <> "+-_?="
@ -47,11 +41,10 @@ newtype NixLike = NixLike {getNixLike :: StorePath}
instance Arbitrary NixLike where instance Arbitrary NixLike where
arbitrary = arbitrary =
NixLike <$> NixLike <$>
(liftA3 StorePath liftA3 StorePath
arbitraryTruncatedDigest arbitraryTruncatedDigest
arbitrary arbitrary
(pure "/nix/store") (pure "/nix/store")
)
where where
-- 160-bit hash, 20 bytes, 32 chars in base32 -- 160-bit hash, 20 bytes, 32 chars in base32
arbitraryTruncatedDigest = coerce . BSC.pack <$> replicateM 20 genSafeChar arbitraryTruncatedDigest = coerce . BSC.pack <$> replicateM 20 genSafeChar

View File

@ -12,7 +12,6 @@ import System.Nix.Derivation ( parseDerivation
import qualified Data.Attoparsec.Text import qualified Data.Attoparsec.Text
import qualified Data.Text.IO import qualified Data.Text.IO
import qualified Data.Text.Lazy
import qualified Data.Text.Lazy.Builder import qualified Data.Text.Lazy.Builder
processDerivation :: FilePath -> FilePath -> IO () processDerivation :: FilePath -> FilePath -> IO ()
@ -22,7 +21,7 @@ processDerivation source dest = do
fail fail
-- It seems to be derivation. -- It seems to be derivation.
(Data.Text.IO.writeFile dest (Data.Text.IO.writeFile dest
. Data.Text.Lazy.toStrict . toText
. Data.Text.Lazy.Builder.toLazyText . Data.Text.Lazy.Builder.toLazyText
. buildDerivation . buildDerivation
) )

View File

@ -1,18 +1,12 @@
{-# LANGUAGE DataKinds #-} {-# language DataKinds #-}
{-# LANGUAGE OverloadedStrings #-} {-# language ScopedTypeVariables #-}
{-# LANGUAGE ScopedTypeVariables #-} {-# language CPP #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE CPP #-}
module Hash where module Hash where
import Control.Monad ( forM_ )
import Data.ByteString ( ByteString )
import qualified Data.ByteString.Char8 as BSC
import qualified Data.ByteString.Base16 as B16 import qualified Data.ByteString.Base16 as B16
import qualified System.Nix.Base32 as B32 import qualified System.Nix.Base32 as B32
import qualified Data.ByteString.Base64.Lazy as B64 import qualified Data.ByteString.Base64.Lazy as B64
import qualified Data.ByteString.Lazy as BSL
import Test.Hspec import Test.Hspec
import Test.Tasty.QuickCheck import Test.Tasty.QuickCheck
@ -21,11 +15,11 @@ import System.Nix.Hash
import System.Nix.StorePath import System.Nix.StorePath
import Arbitrary import Arbitrary
import System.Nix.Internal.Base import System.Nix.Internal.Base
import Data.Coerce ( coerce )
import Crypto.Hash ( MD5 import Crypto.Hash ( MD5
, SHA1 , SHA1
, SHA256 , SHA256
, hash , hash
, Digest
) )
spec_hash :: Spec spec_hash :: Spec
@ -33,29 +27,28 @@ spec_hash = do
describe "hashing parity with nix-store" $ do describe "hashing parity with nix-store" $ do
it "produces (base32 . sha256) of \"nix-output:foo\" the same as Nix does at the moment for placeholder \"foo\"" $ cmp "produces (base32 . sha256) of \"nix-output:foo\" the same as Nix does at the moment for placeholder \"foo\""
shouldBe (encodeDigestWith NixBase32 (hash @ByteString @SHA256 "nix-output:foo")) NixBase32 (hash @ByteString @SHA256) "nix-output:foo" "1x0ymrsy7yr7i9wdsqy9khmzc1yy7nvxw6rdp72yzn50285s67j5"
"1x0ymrsy7yr7i9wdsqy9khmzc1yy7nvxw6rdp72yzn50285s67j5" cmp "produces (base16 . md5) of \"Hello World\" the same as the thesis"
it "produces (base16 . md5) of \"Hello World\" the same as the thesis" $ Base16 (hash @ByteString @MD5) "Hello World" "b10a8db164e0754105b7a99be72e3fe5"
shouldBe (encodeDigestWith Base16 (hash @ByteString @MD5 "Hello World")) cmp "produces (base32 . sha1) of \"Hello World\" the same as the thesis"
"b10a8db164e0754105b7a99be72e3fe5" NixBase32 (hash @ByteString @SHA1) "Hello World" "s23c9fs0v32pf6bhmcph5rbqsyl5ak8a"
it "produces (base32 . sha1) of \"Hello World\" the same as the thesis" $
shouldBe (encodeDigestWith NixBase32 (hash @ByteString @SHA1 "Hello World"))
"s23c9fs0v32pf6bhmcph5rbqsyl5ak8a"
-- The example in question: -- The example in question:
-- https://nixos.org/nixos/nix-pills/nix-store-paths.html -- https://nixos.org/nixos/nix-pills/nix-store-paths.html
it "produces same base32 as nix pill flat file example" $ do it "produces same base32 as nix pill flat file example" $ do
let exampleStr = shouldBe (encodeWith NixBase32 $ coerce $ mkStorePathHashPart "source:sha256:2bfef67de873c54551d884fdab3055d84d573e654efa79db3c0d7b98883f9ee3:/nix/store:myfile")
"source:sha256:2bfef67de873c54551d884fdab3055d84d573e654efa79db3"
<> "c0d7b98883f9ee3:/nix/store:myfile"
shouldBe (encodeWith NixBase32 $ coerce $ mkStorePathHashPart exampleStr)
"xv2iccirbrvklck36f1g7vldn5v58vck" "xv2iccirbrvklck36f1g7vldn5v58vck"
where
cmp :: String -> BaseEncoding -> (ByteString -> Digest a) -> ByteString -> Text -> SpecWith ()
cmp t b f s h =
it t $
shouldBe (encodeDigestWith b $ f s) h
-- | Test that Nix-like base32 encoding roundtrips -- | Test that Nix-like base32 encoding roundtrips
prop_nixBase32Roundtrip :: Property prop_nixBase32Roundtrip :: Property
prop_nixBase32Roundtrip = forAllShrink nonEmptyString genericShrink $ prop_nixBase32Roundtrip = forAllShrink nonEmptyString genericShrink $
\x -> pure (BSC.pack x) === (B32.decode . B32.encode . BSC.pack $ x) \x -> pure (encodeUtf8 x) === (B32.decode . B32.encode . encodeUtf8 $ x)
-- | API variants -- | API variants
prop_nixBase16Roundtrip :: StorePathHashPart -> Property prop_nixBase16Roundtrip :: StorePathHashPart -> Property
@ -68,48 +61,68 @@ spec_nixhash = do
describe "hashing parity with nix-nash" $ do describe "hashing parity with nix-nash" $ do
let cmp
samples = [ "b16 encoded . b32 decoded should equal original b16"
( "800d59cfcd3c05e900cb4e214be48f6b886a08df" B16.encode B32.decode b32s b16s
, "vw46m23bizj4n8afrc0fj19wrp7mj3c0"
, "gA1Zz808BekAy04hS+SPa4hqCN8="
)
, ( "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
, "1b8m03r63zqhnjf7l5wnldhh7c134ap5vpj0850ymkq1iyzicy5s"
, "ungWv48Bz+pBQUDeXa4iI7ADYaOWF3qctBD/YfIAFa0="
)
, ( "204a8fc6dda82f0a0ced7beb8e08a41657c16ef468b228a8279be331a703c33596fd15c13b1b07f9aa1d3bea57789ca031ad85c7a71dd70354ec631238ca3445"
, "12k9jiq29iyqm03swfsgiw5mlqs173qazm3n7daz43infy12pyrcdf30fkk3qwv4yl2ick8yipc2mqnlh48xsvvxl60lbx8vp38yji0"
, "IEqPxt2oLwoM7XvrjgikFlfBbvRosiioJ5vjMacDwzWW/RXBOxsH+aodO+pXeJygMa2Fx6cd1wNU7GMSOMo0RQ=="
)
]
it "b16 encoded . b32 decoded should equal original b16" $ cmp
forM_ samples $ \(b16, b32, _b64) -> shouldBe (B16.encode <$> B32.decode b32) (Right b16) "b64 encoded . b32 decoded should equal original b64"
(B64.encode . fromStrict) B32.decode b32s b64s
it "b64 encoded . b32 decoded should equal original b64" $ cmp
forM_ samples $ \(_b16, b32, b64) -> shouldBe (B64.encode . BSL.fromStrict <$> B32.decode b32) (Right b64) "b32 encoded . b64 decoded should equal original b32"
(B32.encode . toStrict) B64.decode b64s b32s
it "b32 encoded . b64 decoded should equal original b32" $ cmp
forM_ samples $ \(_b16, b32, b64) -> shouldBe (B32.encode . BSL.toStrict <$> B64.decode b64 ) (Right b32) "b16 encoded . b64 decoded should equal original b16"
(B16.encode . toStrict) B64.decode b64s b16s
it "b16 encoded . b64 decoded should equal original b16" $
forM_ samples $ \(b16, _b32, b64) -> shouldBe (B16.encode . BSL.toStrict <$> B64.decode b64 ) (Right b16)
it "b32 encoded . b16 decoded should equal original b32" $
forM_ samples $ \(b16, b32, _b64) -> shouldBe (B32.encode
#if MIN_VERSION_base16_bytestring(1,0,0) #if MIN_VERSION_base16_bytestring(1,0,0)
<$> B16.decode b16) (Right b32) cmp
#else "b32 encoded . b16 decoded should equal original b32"
$ fst $ B16.decode b16) (b32) B32.encode B16.decode b16s b32s
#endif cmp
"b64 encoded . b16 decoded should equal original b64"
(B64.encode . fromStrict) B16.decode b16s b64s
#else
it "b32 encoded . b16 decoded should equal original b32" $
traverse_ (\ b -> shouldBe (B32.encode $ fst $ B16.decode $ fst b) (snd b)) $ zip b16s b32s
it "b64 encoded . b16 decoded should equal original b64" $ it "b64 encoded . b16 decoded should equal original b64" $
forM_ samples $ \(b16, _b32, b64) -> shouldBe (B64.encode . BSL.fromStrict traverse_ (\ b -> shouldBe (B64.encode . fromStrict $ fst $ B16.decode $ fst b) (snd b)) $ zip b16s b64s
#if MIN_VERSION_base16_bytestring(1,0,0)
<$> B16.decode b16) (Right b64)
#else
$ fst $ B16.decode b16 ) (b64)
#endif #endif
where
cmp
:: ( Eq b
, Show b
)
=> String
-> (a -> b)
-> (c -> Either String a)
-> [c]
-> [b]
-> SpecWith ()
cmp s f1 f2 b1 b2 = it s $ traverse_ (uncurry shouldBe . bimap (fmap f1 . f2) pure) $ zip b1 b2
b16s = takeAxis (\(a,_,_) -> a)
b32s = takeAxis (\(_,b,_) -> b)
b64s = takeAxis (\(_,_,c) -> c)
takeAxis f = fmap f samples
samples =
[ ( "800d59cfcd3c05e900cb4e214be48f6b886a08df"
, "vw46m23bizj4n8afrc0fj19wrp7mj3c0"
, "gA1Zz808BekAy04hS+SPa4hqCN8="
)
, ( "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
, "1b8m03r63zqhnjf7l5wnldhh7c134ap5vpj0850ymkq1iyzicy5s"
, "ungWv48Bz+pBQUDeXa4iI7ADYaOWF3qctBD/YfIAFa0="
)
, ( "204a8fc6dda82f0a0ced7beb8e08a41657c16ef468b228a8279be331a703c33596fd15c13b1b07f9aa1d3bea57789ca031ad85c7a71dd70354ec631238ca3445"
, "12k9jiq29iyqm03swfsgiw5mlqs173qazm3n7daz43infy12pyrcdf30fkk3qwv4yl2ick8yipc2mqnlh48xsvvxl60lbx8vp38yji0"
, "IEqPxt2oLwoM7XvrjgikFlfBbvRosiioJ5vjMacDwzWW/RXBOxsH+aodO+pXeJygMa2Fx6cd1wNU7GMSOMo0RQ=="
)
]

View File

@ -1,16 +1,10 @@
{-# LANGUAGE CPP #-} {-# language CPP #-}
{-# LANGUAGE OverloadedStrings #-} {-# language ScopedTypeVariables #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE TypeApplications #-}
module NarFormat where module NarFormat where
import Control.Applicative (many, optional, (<|>))
import qualified Control.Concurrent as Concurrent import qualified Control.Concurrent as Concurrent
import Control.Exception (SomeException, try) import Control.Exception (try)
import Control.Monad (replicateM, void,
when)
import Data.Binary.Get (Get, getByteString, import Data.Binary.Get (Get, getByteString,
getInt64le, getInt64le,
getLazyByteString, runGet) getLazyByteString, runGet)
@ -21,11 +15,8 @@ import qualified Data.ByteString.Base64.Lazy as B64
import qualified Data.ByteString.Char8 as BSC import qualified Data.ByteString.Char8 as BSC
import qualified Data.ByteString.Lazy as BSL import qualified Data.ByteString.Lazy as BSL
import qualified Data.ByteString.Lazy.Char8 as BSLC import qualified Data.ByteString.Lazy.Char8 as BSLC
import Data.Int
import qualified Data.Map as Map import qualified Data.Map as Map
import Data.Maybe (fromMaybe)
import qualified Data.Text as T import qualified Data.Text as T
import qualified Data.Text.Encoding as E
import System.Directory ( doesDirectoryExist import System.Directory ( doesDirectoryExist
, doesPathExist , doesPathExist
, removeDirectoryRecursive , removeDirectoryRecursive
@ -43,7 +34,6 @@ import Test.Hspec
import qualified Test.Tasty.HUnit as HU import qualified Test.Tasty.HUnit as HU
import Test.Tasty.QuickCheck import Test.Tasty.QuickCheck
import qualified Text.Printf as Printf import qualified Text.Printf as Printf
import Text.Read (readMaybe)
import qualified System.Nix.Internal.Nar.Streamer as Nar import qualified System.Nix.Internal.Nar.Streamer as Nar
import System.Nix.Nar import System.Nix.Nar
@ -55,7 +45,7 @@ withBytesAsHandle bytes act = do
Temp.withSystemTempFile "nar-test-file-XXXXX" $ \tmpFile h -> do Temp.withSystemTempFile "nar-test-file-XXXXX" $ \tmpFile h -> do
IO.hClose h IO.hClose h
BSL.writeFile tmpFile bytes BSL.writeFile tmpFile bytes
IO.withFile tmpFile IO.ReadMode act withFile tmpFile ReadMode act
spec_narEncoding :: Spec spec_narEncoding :: Spec
spec_narEncoding = do spec_narEncoding = do
@ -74,7 +64,7 @@ spec_narEncoding = do
res <- withBytesAsHandle (runPut (putNar n)) $ \h -> do res <- withBytesAsHandle (runPut (putNar n)) $ \h -> do
unpackNarIO narEffectsIO h packageFilePath unpackNarIO narEffectsIO h packageFilePath
res `shouldBe` Right () res `shouldBe` pass
e' <- doesPathExist packageFilePath e' <- doesPathExist packageFilePath
e' `shouldBe` True e' `shouldBe` True
@ -84,7 +74,7 @@ spec_narEncoding = do
IO.hClose h IO.hClose h
BSL.readFile tmpFile BSL.readFile tmpFile
res' `shouldBe` (runPut $ putNar n) res' `shouldBe` runPut (putNar n)
-- For a Haskell embedded Nar, check that encoding it gives -- For a Haskell embedded Nar, check that encoding it gives
-- the same bytestring as `nix-store --dump` -- the same bytestring as `nix-store --dump`
@ -160,10 +150,10 @@ unit_packSelfSrcDir = Temp.withSystemTempDirectory "nar-test" $ \tmpDir -> do
Right _ -> do Right _ -> do
let go dir = do let go dir = do
srcHere <- doesDirectoryExist dir srcHere <- doesDirectoryExist dir
case srcHere of bool
False -> pure () pass
True -> do (do
IO.withFile narFilePath IO.WriteMode $ \h -> withFile narFilePath WriteMode $ \h ->
buildNarIO narEffectsIO "src" h buildNarIO narEffectsIO "src" h
hnixNar <- BSL.readFile narFilePath hnixNar <- BSL.readFile narFilePath
nixStoreNar <- getNixStoreDump "src" nixStoreNar <- getNixStoreDump "src"
@ -171,6 +161,8 @@ unit_packSelfSrcDir = Temp.withSystemTempDirectory "nar-test" $ \tmpDir -> do
"src dir serializes the same between hnix-store and nix-store" "src dir serializes the same between hnix-store and nix-store"
hnixNar hnixNar
nixStoreNar nixStoreNar
)
srcHere
go "src" go "src"
go "hnix-store-core/src" go "hnix-store-core/src"
-- ||||||| merged common ancestors -- ||||||| merged common ancestors
@ -182,7 +174,7 @@ unit_packSelfSrcDir = Temp.withSystemTempDirectory "nar-test" $ \tmpDir -> do
-- nixStoreNar -- nixStoreNar
-- ======= -- =======
-- let narFile = tmpDir </> "src.nar" -- let narFile = tmpDir </> "src.nar"
-- IO.withFile narFile IO.WriteMode $ \h -> -- withFile narFile WriteMode $ \h ->
-- buildNarIO narEffectsIO "src" h -- buildNarIO narEffectsIO "src" h
-- hnixNar <- BSL.readFile narFile -- hnixNar <- BSL.readFile narFile
-- nixStoreNar <- getNixStoreDump "src" -- nixStoreNar <- getNixStoreDump "src"
@ -201,7 +193,7 @@ test_streamLargeFileToNar = HU.testCaseSteps "streamLargeFileToNar" $ \step -> d
-- BSL.writeFile narFileName =<< buildNarIO narEffectsIO bigFileName -- BSL.writeFile narFileName =<< buildNarIO narEffectsIO bigFileName
-- --
step "create nar file" step "create nar file"
IO.withFile narFileName IO.WriteMode $ \h -> withFile narFileName WriteMode $ \h ->
buildNarIO narEffectsIO bigFileName h buildNarIO narEffectsIO bigFileName h
step "assert bounded memory" step "assert bounded memory"
@ -230,32 +222,32 @@ test_streamManyFilesToNar = HU.testCaseSteps "streamManyFilesToNar" $ \step ->
_run = do _run = do
filesPrecount <- countProcessFiles filesPrecount <- countProcessFiles
IO.withFile "hnar" IO.WriteMode $ \h -> withFile "hnar" WriteMode $ \h ->
buildNarIO narEffectsIO narFilePath h buildNarIO narEffectsIO narFilePath h
filesPostcount <- countProcessFiles filesPostcount <- countProcessFiles
pure $ (-) <$> filesPostcount <*> filesPrecount pure $ (-) <$> filesPostcount <*> filesPrecount
step "create test files" step "create test files"
Directory.createDirectory packagePath Directory.createDirectory packagePath
flip mapM_ [0..1000] $ \i -> do forM_ [0..1000] $ \i -> do
BSL.writeFile (Printf.printf (packagePath </> "%08d") (i :: Int)) "hi\n" BSL.writeFile (Printf.printf (packagePath </> "%08d") (i :: Int)) "hi\n"
Concurrent.threadDelay 50 Concurrent.threadDelay 50
filesPrecount <- countProcessFiles filesPrecount <- countProcessFiles
step "pack nar" step "pack nar"
IO.withFile narFilePath IO.WriteMode $ \h -> withFile narFilePath WriteMode $ \h ->
buildNarIO narEffectsIO packagePath h buildNarIO narEffectsIO packagePath h
step "unpack nar" step "unpack nar"
r <- IO.withFile narFilePath IO.ReadMode $ \h -> r <- withFile narFilePath ReadMode $ \h ->
unpackNarIO narEffectsIO h packagePath' unpackNarIO narEffectsIO h packagePath'
r `shouldBe` Right () r `shouldBe` pass
step "check constant file usage" step "check constant file usage"
filesPostcount <- countProcessFiles filesPostcount <- countProcessFiles
case ((-) <$> filesPostcount <*> filesPrecount) of case (-) <$> filesPostcount <*> filesPrecount of
Nothing -> pure () Nothing -> pass
Just c -> c `shouldSatisfy` (< 50) Just c -> c `shouldSatisfy` (< 50)
-- step "check file exists" -- step "check file exists"
@ -303,7 +295,7 @@ filesystemNixStore testErrorName n = do
assertExists nixNarFile assertExists nixNarFile
-- hnix converts those files to nar -- hnix converts those files to nar
IO.withFile hnixNarFile IO.WriteMode $ \h -> withFile hnixNarFile WriteMode $ \h ->
buildNarIO narEffectsIO testFile h buildNarIO narEffectsIO testFile h
assertExists hnixNarFile assertExists hnixNarFile
@ -320,7 +312,7 @@ assertBoundedMemory = do
bytes <- max_live_bytes <$> getRTSStats bytes <- max_live_bytes <$> getRTSStats
bytes < 100 * 1000 * 1000 `shouldBe` True bytes < 100 * 1000 * 1000 `shouldBe` True
#else #else
pure () pass
#endif #endif
@ -353,16 +345,16 @@ packThenExtract testName setup =
step $ "Build NAR from " <> narFilePath <> " to " <> hnixNarFile step $ "Build NAR from " <> narFilePath <> " to " <> hnixNarFile
-- narBS <- buildNarIO narEffectsIO narFile -- narBS <- buildNarIO narEffectsIO narFile
IO.withFile hnixNarFile IO.WriteMode $ \h -> withFile hnixNarFile WriteMode $ \h ->
buildNarIO narEffectsIO narFilePath h buildNarIO narEffectsIO narFilePath h
-- BSL.writeFile hnixNarFile narBS -- BSL.writeFile hnixNarFile narBS
step $ "Unpack NAR to " <> outputFile step $ "Unpack NAR to " <> outputFile
_narHandle <- IO.withFile nixNarFile IO.ReadMode $ \h -> _narHandle <- withFile nixNarFile ReadMode $ \h ->
unpackNarIO narEffectsIO h outputFile unpackNarIO narEffectsIO h outputFile
pure () pass
-- | Count file descriptors owned by the current process -- | Count file descriptors owned by the current process
countProcessFiles :: IO (Maybe Int) countProcessFiles :: IO (Maybe Int)
@ -373,7 +365,7 @@ countProcessFiles = do
then pure Nothing then pure Nothing
else do else do
let fdDir = "/proc/" <> show pid <> "/fd" let fdDir = "/proc/" <> show pid <> "/fd"
fds <- P.readProcess "ls" [fdDir] "" fds <- toText <$> P.readProcess "ls" [fdDir] ""
pure $ pure $ length $ words fds pure $ pure $ length $ words fds
@ -538,8 +530,8 @@ getBigFileSize = fromMaybe 5000000 . readMaybe <$> (getEnv "HNIX_BIG_FILE_SIZE"
-- | Add a link to a FileSystemObject. This is useful -- | Add a link to a FileSystemObject. This is useful
-- when creating Arbitrary FileSystemObjects. It -- when creating Arbitrary FileSystemObjects. It
-- isn't implemented yet -- isn't implemented yet
mkLink :: mkLink
FilePath -- ^ Target :: FilePath -- ^ Target
-> FilePath -- ^ Link -> FilePath -- ^ Link
-> FileSystemObject -- ^ FileSystemObject to add link to -> FileSystemObject -- ^ FileSystemObject to add link to
-> FileSystemObject -> FileSystemObject
@ -554,11 +546,9 @@ mkBigFile path = do
-- | Construct FilePathPart from Text by checking that there -- | Construct FilePathPart from Text by checking that there
-- are no '/' or '\\NUL' characters -- are no '/' or '\\NUL' characters
filePathPart :: BSC.ByteString -> Maybe FilePathPart filePathPart :: BSC.ByteString -> Maybe FilePathPart
filePathPart p = case BSC.any (`elem` ['/', '\NUL']) p of filePathPart p = if BSC.any (`elem` ['/', '\NUL']) p then Nothing else Just $ FilePathPart p
False -> Just $ FilePathPart p
True -> Nothing
data Nar = Nar { narFile :: FileSystemObject } newtype Nar = Nar { narFile :: FileSystemObject }
deriving (Eq, Show) deriving (Eq, Show)
-- | A FileSystemObject (FSO) is an anonymous entity that can be NAR archived -- | A FileSystemObject (FSO) is an anonymous entity that can be NAR archived
@ -623,11 +613,11 @@ putNar (Nar file) = header <> parens (putFile file)
strs ["type", "regular"] strs ["type", "regular"]
>> (if isExec == Nar.Executable >> (if isExec == Nar.Executable
then strs ["executable", ""] then strs ["executable", ""]
else pure ()) else pass)
>> putContents fSize contents >> putContents fSize contents
putFile (SymLink target) = putFile (SymLink target) =
strs ["type", "symlink", "target", BSL.fromStrict $ E.encodeUtf8 target] strs ["type", "symlink", "target", fromStrict $ encodeUtf8 target]
-- toList sorts the entries by FilePathPart before serializing -- toList sorts the entries by FilePathPart before serializing
putFile (Directory entries) = putFile (Directory entries) =
@ -638,7 +628,7 @@ putNar (Nar file) = header <> parens (putFile file)
str "entry" str "entry"
parens $ do parens $ do
str "name" str "name"
str (BSL.fromStrict name) str (fromStrict name)
str "node" str "node"
parens (putFile fso) parens (putFile fso)
@ -650,7 +640,7 @@ putNar (Nar file) = header <> parens (putFile file)
in int len <> pad len t in int len <> pad len t
putContents :: Int64 -> BSL.ByteString -> Put putContents :: Int64 -> BSL.ByteString -> Put
putContents fSize bs = str "contents" <> int fSize <> (pad fSize bs) putContents fSize bs = str "contents" <> int fSize <> pad fSize bs
int :: Integral a => a -> Put int :: Integral a => a -> Put
int n = putInt64le $ fromIntegral n int n = putInt64le $ fromIntegral n
@ -698,18 +688,18 @@ getNar = fmap Nar $ header >> parens getFile
assertStr_ "type" assertStr_ "type"
assertStr_ "symlink" assertStr_ "symlink"
assertStr_ "target" assertStr_ "target"
fmap (SymLink . E.decodeUtf8 . BSL.toStrict) str fmap (SymLink . decodeUtf8) str
getEntry = do getEntry = do
assertStr_ "entry" assertStr_ "entry"
parens $ do parens $ do
assertStr_ "name" assertStr_ "name"
name <- E.decodeUtf8 . BSL.toStrict <$> str name <- str
assertStr_ "node" assertStr_ "node"
file <- parens getFile file <- parens getFile
maybe (fail $ "Bad FilePathPart: " <> show name) maybe (fail $ "Bad FilePathPart: " <> show name)
(pure . (,file)) (pure . (,file))
(filePathPart $ E.encodeUtf8 name) (filePathPart $ toStrict name)
-- Fetch a length-prefixed, null-padded string -- Fetch a length-prefixed, null-padded string
str = fmap snd sizedStr str = fmap snd sizedStr

View File

@ -1,7 +1,5 @@
{-# LANGUAGE DataKinds #-} {-# language DataKinds #-}
{-# LANGUAGE OverloadedStrings #-} {-# language ScopedTypeVariables #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
module StorePath where module StorePath where
@ -14,18 +12,18 @@ import Arbitrary
-- | Test that Nix(OS) like paths roundtrip -- | Test that Nix(OS) like paths roundtrip
prop_storePathRoundtrip :: NixLike -> NixLike -> Property prop_storePathRoundtrip :: NixLike -> NixLike -> Property
prop_storePathRoundtrip (_ :: NixLike) = \(NixLike x) -> prop_storePathRoundtrip (_ :: NixLike) (NixLike x) =
(parsePath "/nix/store" $ storePathToRawFilePath x) === Right x parsePath "/nix/store" (storePathToRawFilePath x) === pure x
-- | Test that any `StorePath` roundtrips -- | Test that any `StorePath` roundtrips
prop_storePathRoundtrip' :: StorePath -> Property prop_storePathRoundtrip' :: StorePath -> Property
prop_storePathRoundtrip' x = prop_storePathRoundtrip' x =
(parsePath (storePathRoot x) $ storePathToRawFilePath x) === Right x parsePath (storePathRoot x) (storePathToRawFilePath x) === pure x
prop_storePathRoundtripParser :: NixLike -> NixLike -> Property prop_storePathRoundtripParser :: NixLike -> NixLike -> Property
prop_storePathRoundtripParser (_ :: NixLike) = \(NixLike x) -> prop_storePathRoundtripParser (_ :: NixLike) (NixLike x) =
(Data.Attoparsec.Text.parseOnly (pathParser $ storePathRoot x) $ storePathToText x) === Right x Data.Attoparsec.Text.parseOnly (pathParser $ storePathRoot x) (storePathToText x) === pure x
prop_storePathRoundtripParser' :: StorePath -> Property prop_storePathRoundtripParser' :: StorePath -> Property
prop_storePathRoundtripParser' x = prop_storePathRoundtripParser' x =
(Data.Attoparsec.Text.parseOnly (pathParser $ storePathRoot x) $ storePathToText x) === Right x Data.Attoparsec.Text.parseOnly (pathParser $ storePathRoot x) (storePathToText x) === pure x

View File

@ -39,7 +39,8 @@ library
, System.Nix.Store.Remote.Util , System.Nix.Store.Remote.Util
build-depends: build-depends:
base >=4.10 && <5 base >=4.12 && <5
, relude
, attoparsec , attoparsec
, binary , binary
, bytestring , bytestring
@ -52,6 +53,29 @@ library
, mtl , mtl
, unordered-containers , unordered-containers
, hnix-store-core >= 0.5 && <0.6 , hnix-store-core >= 0.5 && <0.6
mixins:
base hiding (Prelude)
, relude (Relude as Prelude)
, relude
default-extensions:
OverloadedStrings
, DeriveGeneric
, DeriveDataTypeable
, DeriveFunctor
, DeriveFoldable
, DeriveTraversable
, DeriveLift
, FlexibleContexts
, FlexibleInstances
, StandaloneDeriving
, TypeApplications
, TypeSynonymInstances
, InstanceSigs
, MultiParamTypeClasses
, TupleSections
, LambdaCase
, BangPatterns
, ViewPatterns
hs-source-dirs: src hs-source-dirs: src
default-language: Haskell2010 default-language: Haskell2010
ghc-options: -Wall ghc-options: -Wall
@ -75,6 +99,7 @@ test-suite hnix-store-remote-tests
tasty-discover:tasty-discover tasty-discover:tasty-discover
build-depends: build-depends:
base base
, relude
, hnix-store-core >= 0.3 , hnix-store-core >= 0.3
, hnix-store-remote , hnix-store-remote
, containers , containers
@ -95,4 +120,27 @@ test-suite hnix-store-remote-tests
, unix , unix
, unordered-containers , unordered-containers
, vector , vector
mixins:
base hiding (Prelude)
, relude (Relude as Prelude)
, relude
default-extensions:
OverloadedStrings
, DeriveGeneric
, DeriveDataTypeable
, DeriveFunctor
, DeriveFoldable
, DeriveTraversable
, DeriveLift
, FlexibleContexts
, FlexibleInstances
, StandaloneDeriving
, TypeApplications
, TypeSynonymInstances
, InstanceSigs
, MultiParamTypeClasses
, TupleSections
, LambdaCase
, BangPatterns
, ViewPatterns
default-language: Haskell2010 default-language: Haskell2010

View File

@ -1,11 +1,9 @@
{-# LANGUAGE AllowAmbiguousTypes #-} {-# language AllowAmbiguousTypes #-}
{-# LANGUAGE KindSignatures #-} {-# language KindSignatures #-}
{-# LANGUAGE OverloadedStrings #-} {-# language RankNTypes #-}
{-# LANGUAGE RankNTypes #-} {-# language ScopedTypeVariables #-}
{-# LANGUAGE ScopedTypeVariables #-} {-# language DataKinds #-}
{-# LANGUAGE DataKinds #-} {-# language RecordWildCards #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE RecordWildCards #-}
module System.Nix.Store.Remote module System.Nix.Store.Remote
( addToStore ( addToStore
, addTextToStore , addTextToStore
@ -35,19 +33,14 @@ module System.Nix.Store.Remote
) )
where where
import Control.Monad ( void import Prelude hiding ( putText )
, unless import qualified Data.ByteString.Lazy as BSL
, when
)
import Data.ByteString.Lazy ( ByteString )
import Data.Map.Strict ( Map )
import Data.Text ( Text )
import Nix.Derivation ( Derivation ) import Nix.Derivation ( Derivation )
import System.Nix.Build ( BuildMode import System.Nix.Build ( BuildMode
, BuildResult , BuildResult
) )
import System.Nix.Hash ( NamedAlgo import System.Nix.Hash ( NamedAlgo(..)
, SomeNamedDigest(..) , SomeNamedDigest(..)
, BaseEncoding(NixBase32) , BaseEncoding(NixBase32)
, decodeDigestWith , decodeDigestWith
@ -63,13 +56,10 @@ import System.Nix.StorePathMetadata ( StorePathMetadata(..)
import System.Nix.Internal.Base ( encodeWith ) import System.Nix.Internal.Base ( encodeWith )
import qualified Data.Binary.Put import qualified Data.Binary.Put
import qualified Data.ByteString.Lazy
import qualified Data.Map.Strict import qualified Data.Map.Strict
import qualified Data.Set import qualified Data.Set
import qualified Data.Text.Encoding
import qualified System.Nix.Nar import qualified System.Nix.Nar
import qualified System.Nix.Hash
import qualified System.Nix.StorePath import qualified System.Nix.StorePath
import qualified System.Nix.Store.Remote.Parsers import qualified System.Nix.Store.Remote.Parsers
@ -78,7 +68,6 @@ import System.Nix.Store.Remote.Types
import System.Nix.Store.Remote.Protocol import System.Nix.Store.Remote.Protocol
import System.Nix.Store.Remote.Util import System.Nix.Store.Remote.Util
import Crypto.Hash ( SHA256 ) import Crypto.Hash ( SHA256 )
import Data.Coerce ( coerce )
type RepairFlag = Bool type RepairFlag = Bool
type CheckFlag = Bool type CheckFlag = Bool
@ -97,7 +86,7 @@ addToStore
addToStore name pth recursive _pathFilter _repair = do addToStore name pth recursive _pathFilter _repair = do
runOpArgsIO AddToStore $ \yield -> do runOpArgsIO AddToStore $ \yield -> do
yield $ Data.ByteString.Lazy.toStrict $ Data.Binary.Put.runPut $ do yield $ toStrict $ Data.Binary.Put.runPut $ do
putText $ System.Nix.StorePath.unStorePathName name putText $ System.Nix.StorePath.unStorePathName name
putBool $ not $ System.Nix.Hash.algoName @a == "sha256" && recursive putBool $ not $ System.Nix.Hash.algoName @a == "sha256" && recursive
@ -129,7 +118,7 @@ addTextToStore name text references' repair = do
putPaths references' putPaths references'
sockGetPath sockGetPath
addSignatures :: StorePath -> [ByteString] -> MonadStore () addSignatures :: StorePath -> [BSL.ByteString] -> MonadStore ()
addSignatures p signatures = do addSignatures p signatures = do
void $ simpleOpArgs AddSignatures $ do void $ simpleOpArgs AddSignatures $ do
putPath p putPath p
@ -171,15 +160,14 @@ buildDerivation p drv buildMode = do
-- Intentionally the only warning that should pop-up. -- Intentionally the only warning that should pop-up.
putInt (0 :: Integer) putInt (0 :: Integer)
res <- getSocketIncremental getBuildResult getSocketIncremental getBuildResult
pure res
ensurePath :: StorePath -> MonadStore () ensurePath :: StorePath -> MonadStore ()
ensurePath pn = do ensurePath pn = do
void $ simpleOpArgs EnsurePath $ putPath pn void $ simpleOpArgs EnsurePath $ putPath pn
-- | Find garbage collector roots. -- | Find garbage collector roots.
findRoots :: MonadStore (Map ByteString StorePath) findRoots :: MonadStore (Map BSL.ByteString StorePath)
findRoots = do findRoots = do
runOp FindRoots runOp FindRoots
sd <- getStoreDir sd <- getStoreDir
@ -187,7 +175,7 @@ findRoots = do
getSocketIncremental getSocketIncremental
$ getMany $ getMany
$ (,) $ (,)
<$> (Data.ByteString.Lazy.fromStrict <$> getByteStringLen) <$> (fromStrict <$> getByteStringLen)
<*> getPath sd <*> getPath sd
r <- catRights res r <- catRights res
@ -198,7 +186,7 @@ findRoots = do
ex :: (a, Either [Char] b) -> MonadStore (a, b) ex :: (a, Either [Char] b) -> MonadStore (a, b)
ex (x , Right y) = pure (x, y) ex (x , Right y) = pure (x, y)
ex (_x, Left e ) = error $ "Unable to decode root: " <> e ex (_x, Left e ) = error $ "Unable to decode root: " <> fromString e
isValidPathUncached :: StorePath -> MonadStore Bool isValidPathUncached :: StorePath -> MonadStore Bool
isValidPathUncached p = do isValidPathUncached p = do
@ -235,13 +223,13 @@ queryPathInfoUncached path = do
deriverPath <- sockGetPathMay deriverPath <- sockGetPathMay
narHashText <- Data.Text.Encoding.decodeUtf8 <$> sockGetStr narHashText <- decodeUtf8 <$> sockGetStr
let let
narHash = narHash =
case case
decodeDigestWith @SHA256 NixBase32 narHashText decodeDigestWith @SHA256 NixBase32 narHashText
of of
Left e -> error e Left e -> error $ fromString e
Right x -> SomeDigest x Right x -> SomeDigest x
references <- sockGetPaths references <- sockGetPaths
@ -260,7 +248,7 @@ queryPathInfoUncached path = do
case case
System.Nix.Store.Remote.Parsers.parseContentAddressableAddress caString System.Nix.Store.Remote.Parsers.parseContentAddressableAddress caString
of of
Left e -> error e Left e -> error $ fromString e
Right x -> Just x Right x -> Just x
trust = if ultimate then BuiltLocally else BuiltElsewhere trust = if ultimate then BuiltLocally else BuiltElsewhere
@ -291,9 +279,7 @@ queryPathFromHashPart :: StorePathHashPart -> MonadStore StorePath
queryPathFromHashPart storePathHash = do queryPathFromHashPart storePathHash = do
runOpArgs QueryPathFromHashPart runOpArgs QueryPathFromHashPart
$ putByteStringLen $ putByteStringLen
$ Data.ByteString.Lazy.fromStrict $ encodeUtf8 (encodeWith NixBase32 $ coerce storePathHash)
$ Data.Text.Encoding.encodeUtf8
$ encodeWith NixBase32 $ coerce storePathHash
sockGetPath sockGetPath
queryMissing queryMissing

View File

@ -4,10 +4,8 @@ Maintainer : srk <srk@48.io>
|-} |-}
module System.Nix.Store.Remote.Binary where module System.Nix.Store.Remote.Binary where
import Control.Monad
import Data.Binary.Get import Data.Binary.Get
import Data.Binary.Put import Data.Binary.Put
import Data.ByteString ( ByteString )
import qualified Data.ByteString.Lazy as BSL import qualified Data.ByteString.Lazy as BSL
putInt :: Integral a => a -> Put putInt :: Integral a => a -> Put
@ -35,7 +33,7 @@ putByteStringLen x = do
where where
len :: Int len :: Int
len = fromIntegral $ BSL.length x len = fromIntegral $ BSL.length x
pad count = sequence_ $ replicate count (putWord8 0) pad count = replicateM_ count (putWord8 0)
putByteStrings :: Foldable t => t BSL.ByteString -> Put putByteStrings :: Foldable t => t BSL.ByteString -> Put
putByteStrings = putMany putByteStringLen putByteStrings = putMany putByteStringLen
@ -47,8 +45,8 @@ getByteStringLen = do
when (len `mod` 8 /= 0) $ do when (len `mod` 8 /= 0) $ do
pads <- unpad $ fromIntegral $ 8 - (len `mod` 8) pads <- unpad $ fromIntegral $ 8 - (len `mod` 8)
unless (all (== 0) pads) $ fail $ "No zeroes" <> show (st, len, pads) unless (all (== 0) pads) $ fail $ "No zeroes" <> show (st, len, pads)
pure $ BSL.toStrict st pure $ toStrict st
where unpad x = sequence $ replicate x getWord8 where unpad x = replicateM x getWord8
getByteStrings :: Get [ByteString] getByteStrings :: Get [ByteString]
getByteStrings = getMany getByteStringLen getByteStrings = getMany getByteStringLen

View File

@ -1,38 +1,36 @@
{-# LANGUAGE AllowAmbiguousTypes #-} {-# language AllowAmbiguousTypes #-}
{-# LANGUAGE OverloadedStrings #-} {-# language ScopedTypeVariables #-}
{-# LANGUAGE ScopedTypeVariables #-} {-# language RankNTypes #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TypeApplications #-}
module System.Nix.Store.Remote.Builders module System.Nix.Store.Remote.Builders
( buildContentAddressableAddress ( buildContentAddressableAddress
) )
where where
import Data.Text.Lazy ( Text ) import qualified Data.Text.Lazy as TL
import Crypto.Hash ( Digest ) import Crypto.Hash ( Digest )
import System.Nix.StorePath ( ContentAddressableAddress(..) import System.Nix.StorePath ( ContentAddressableAddress(..)
) )
import Data.Text.Lazy.Builder ( Builder ) import Data.Text.Lazy.Builder ( Builder )
import qualified Data.Text.Lazy.Builder import qualified Data.Text.Lazy.Builder as TL
import System.Nix.Hash import System.Nix.Hash
-- | Marshall `ContentAddressableAddress` to `Text` -- | Marshall `ContentAddressableAddress` to `Text`
-- in form suitable for remote protocol usage. -- in form suitable for remote protocol usage.
buildContentAddressableAddress :: ContentAddressableAddress -> Text buildContentAddressableAddress :: ContentAddressableAddress -> TL.Text
buildContentAddressableAddress = buildContentAddressableAddress =
Data.Text.Lazy.Builder.toLazyText . contentAddressableAddressBuilder TL.toLazyText . contentAddressableAddressBuilder
contentAddressableAddressBuilder :: ContentAddressableAddress -> Builder contentAddressableAddressBuilder :: ContentAddressableAddress -> Builder
contentAddressableAddressBuilder (Text digest) = contentAddressableAddressBuilder (Text digest) =
"text:" <> digestBuilder digest "text:" <> digestBuilder digest
contentAddressableAddressBuilder (Fixed _narHashMode (SomeDigest (digest :: Digest hashAlgo))) = contentAddressableAddressBuilder (Fixed _narHashMode (SomeDigest (digest :: Digest hashAlgo))) =
"fixed:" "fixed:"
<> (Data.Text.Lazy.Builder.fromText $ System.Nix.Hash.algoName @hashAlgo) <> TL.fromText (System.Nix.Hash.algoName @hashAlgo)
<> digestBuilder digest <> digestBuilder digest
digestBuilder :: Digest a -> Builder digestBuilder :: Digest a -> Builder
digestBuilder = digestBuilder =
Data.Text.Lazy.Builder.fromText . encodeDigestWith NixBase32 TL.fromText . encodeDigestWith NixBase32

View File

@ -1,4 +1,4 @@
{-# LANGUAGE RankNTypes #-} {-# language RankNTypes #-}
module System.Nix.Store.Remote.Logger module System.Nix.Store.Remote.Logger
( Logger(..) ( Logger(..)
@ -8,9 +8,8 @@ module System.Nix.Store.Remote.Logger
where where
import Control.Monad.Except import Prelude hiding ( Last )
import Control.Monad.Reader ( asks ) import Control.Monad.Except ( throwError )
import Control.Monad.State ( get )
import Data.Binary.Get import Data.Binary.Get
import Network.Socket.ByteString ( recv ) import Network.Socket.ByteString ( recv )
@ -60,8 +59,7 @@ processOutput = go decoder
sockPut $ putByteStringLen part sockPut $ putByteStringLen part
clearData clearData
next <- go decoder go decoder
pure next
-- we should probably handle Read here as well -- we should probably handle Read here as well
x -> do x -> do
@ -72,12 +70,12 @@ processOutput = go decoder
chunk <- liftIO (Just <$> recv soc 8) chunk <- liftIO (Just <$> recv soc 8)
go (k chunk) go (k chunk)
go (Fail _leftover _consumed msg) = error msg go (Fail _leftover _consumed msg) = error $ fromString msg
getFields :: Get [Field] getFields :: Get [Field]
getFields = do getFields = do
cnt <- getInt cnt <- getInt
sequence $ replicate cnt getField replicateM cnt getField
getField :: Get Field getField :: Get Field
getField = do getField = do

View File

@ -1,20 +1,14 @@
{-# LANGUAGE AllowAmbiguousTypes #-} {-# language AllowAmbiguousTypes #-}
{-# LANGUAGE OverloadedStrings #-} {-# language ScopedTypeVariables #-}
{-# LANGUAGE ScopedTypeVariables #-} {-# language RankNTypes #-}
{-# LANGUAGE RankNTypes #-} {-# language DataKinds #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeApplications #-}
module System.Nix.Store.Remote.Parsers module System.Nix.Store.Remote.Parsers
( parseContentAddressableAddress ( parseContentAddressableAddress
) )
where where
import Control.Applicative ( (<|>) )
import Data.Attoparsec.ByteString.Char8 import Data.Attoparsec.ByteString.Char8
import Data.ByteString.Char8
import Data.Text ( Text )
import Data.Text.Encoding ( decodeUtf8 )
import System.Nix.Hash import System.Nix.Hash
import System.Nix.StorePath ( ContentAddressableAddress(..) import System.Nix.StorePath ( ContentAddressableAddress(..)
, NarHashMode(..) , NarHashMode(..)
@ -42,7 +36,7 @@ caText = do
caFixed :: Parser ContentAddressableAddress caFixed :: Parser ContentAddressableAddress
caFixed = do caFixed = do
_ <- "fixed:" _ <- "fixed:"
narHashMode <- (pure Recursive <$> "r:") <|> (pure RegularFile <$> "") narHashMode <- (Recursive <$ "r:") <|> (RegularFile <$ "")
digest <- parseTypedDigest digest <- parseTypedDigest
either fail pure $ Fixed narHashMode <$> digest either fail pure $ Fixed narHashMode <$> digest

View File

@ -1,6 +1,5 @@
{-# LANGUAGE DataKinds #-} {-# language DataKinds #-}
{-# LANGUAGE ScopedTypeVariables #-} {-# language ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
module System.Nix.Store.Remote.Protocol module System.Nix.Store.Remote.Protocol
( WorkerOp(..) ( WorkerOp(..)
, simpleOp , simpleOp
@ -13,18 +12,15 @@ module System.Nix.Store.Remote.Protocol
) )
where where
import qualified Relude.Unsafe as Unsafe
import Data.Bool ( bool )
import Control.Exception ( bracket ) import Control.Exception ( bracket )
import Control.Monad.Except import Control.Monad.Except
import Control.Monad.Reader
import Control.Monad.State
import Data.Binary.Get import Data.Binary.Get
import Data.Binary.Put import Data.Binary.Put
import qualified Data.ByteString import qualified Data.ByteString
import qualified Data.ByteString.Char8 import qualified Data.ByteString.Char8
import qualified Data.ByteString.Lazy
import Network.Socket ( SockAddr(SockAddrUnix) ) import Network.Socket ( SockAddr(SockAddrUnix) )
import qualified Network.Socket import qualified Network.Socket
@ -122,7 +118,7 @@ opNum QueryMissing = 40
simpleOp :: WorkerOp -> MonadStore Bool simpleOp :: WorkerOp -> MonadStore Bool
simpleOp op = simpleOpArgs op $ pure () simpleOp op = simpleOpArgs op pass
simpleOpArgs :: WorkerOp -> Put -> MonadStore Bool simpleOpArgs :: WorkerOp -> Put -> MonadStore Bool
simpleOpArgs op args = do simpleOpArgs op args = do
@ -131,19 +127,19 @@ simpleOpArgs op args = do
bool bool
sockGetBool sockGetBool
(do (do
Error _num msg <- head <$> getError Error _num msg <- Unsafe.head <$> getError
throwError $ Data.ByteString.Char8.unpack msg throwError $ Data.ByteString.Char8.unpack msg
) )
err err
runOp :: WorkerOp -> MonadStore () runOp :: WorkerOp -> MonadStore ()
runOp op = runOpArgs op $ pure () runOp op = runOpArgs op pass
runOpArgs :: WorkerOp -> Put -> MonadStore () runOpArgs :: WorkerOp -> Put -> MonadStore ()
runOpArgs op args = runOpArgs op args =
runOpArgsIO runOpArgsIO
op op
(\encode -> encode $ Data.ByteString.Lazy.toStrict $ runPut args) (\encode -> encode $ toStrict $ runPut args)
runOpArgsIO runOpArgsIO
:: WorkerOp :: WorkerOp
@ -160,7 +156,7 @@ runOpArgsIO op encoder = do
modify (\(a, b) -> (a, b <> out)) modify (\(a, b) -> (a, b <> out))
err <- gotError err <- gotError
when err $ do when err $ do
Error _num msg <- head <$> getError Error _num msg <- Unsafe.head <$> getError
throwError $ Data.ByteString.Char8.unpack msg throwError $ Data.ByteString.Char8.unpack msg
runStore :: MonadStore a -> IO (Either String a, [Logger]) runStore :: MonadStore a -> IO (Either String a, [Logger])
@ -190,7 +186,7 @@ runStoreOpts sockPath storeRootDir code = do
vermagic <- liftIO $ recv soc 16 vermagic <- liftIO $ recv soc 16
let let
(magic2, _daemonProtoVersion) = (magic2, _daemonProtoVersion) =
flip runGet (Data.ByteString.Lazy.fromStrict vermagic) flip runGet (fromStrict vermagic)
$ (,) $ (,)
<$> (getInt :: Get Int) <$> (getInt :: Get Int)
<*> (getInt :: Get Int) <*> (getInt :: Get Int)

View File

@ -1,7 +1,6 @@
{-# LANGUAGE DataKinds #-} {-# language DataKinds #-}
{-# LANGUAGE KindSignatures #-} {-# language KindSignatures #-}
{-# LANGUAGE TypeApplications #-} {-# language ScopedTypeVariables #-}
{-# LANGUAGE ScopedTypeVariables #-}
module System.Nix.Store.Remote.Types module System.Nix.Store.Remote.Types
( MonadStore ( MonadStore
, StoreConfig(..) , StoreConfig(..)
@ -18,12 +17,8 @@ module System.Nix.Store.Remote.Types
where where
import Data.ByteString ( ByteString )
import qualified Data.ByteString.Lazy as BSL import qualified Data.ByteString.Lazy as BSL
import Network.Socket ( Socket ) import Network.Socket ( Socket )
import Control.Monad.Except
import Control.Monad.Reader
import Control.Monad.State
data StoreConfig = StoreConfig data StoreConfig = StoreConfig
{ storeDir :: FilePath { storeDir :: FilePath
@ -61,13 +56,13 @@ isError (Error _ _) = True
isError _ = False isError _ = False
gotError :: MonadStore Bool gotError :: MonadStore Bool
gotError = any isError . snd <$> get gotError = gets (any isError . snd)
getError :: MonadStore [Logger] getError :: MonadStore [Logger]
getError = filter isError . snd <$> get getError = gets (filter isError . snd)
getLog :: MonadStore [Logger] getLog :: MonadStore [Logger]
getLog = snd <$> get getLog = gets snd
flushLog :: MonadStore () flushLog :: MonadStore ()
flushLog = modify (\(a, _b) -> (a, [])) flushLog = modify (\(a, _b) -> (a, []))
@ -79,4 +74,4 @@ clearData :: MonadStore ()
clearData = modify (\(_, b) -> (Nothing, b)) clearData = modify (\(_, b) -> (Nothing, b))
getStoreDir :: MonadStore FilePath getStoreDir :: MonadStore FilePath
getStoreDir = storeDir <$> ask getStoreDir = asks storeDir

View File

@ -1,20 +1,14 @@
{-# LANGUAGE OverloadedStrings #-} {-# language RecordWildCards #-}
{-# LANGUAGE RecordWildCards #-}
module System.Nix.Store.Remote.Util where module System.Nix.Store.Remote.Util where
import Prelude hiding ( putText )
import Control.Monad.Except import Control.Monad.Except
import Control.Monad.Reader
import Data.Either
import Data.Binary.Get import Data.Binary.Get
import Data.Binary.Put import Data.Binary.Put
import Data.Text ( Text )
import qualified Data.Text.Encoding as T
import qualified Data.Text.Lazy as TL
import qualified Data.Text.Lazy.Encoding as TL import qualified Data.Text.Lazy.Encoding as TL
import Data.Time import Data.Time
import Data.Time.Clock.POSIX import Data.Time.Clock.POSIX
import Data.ByteString ( ByteString )
import qualified Data.ByteString.Char8 as BSC import qualified Data.ByteString.Char8 as BSC
import qualified Data.ByteString.Lazy as BSL import qualified Data.ByteString.Lazy as BSL
@ -40,7 +34,7 @@ genericIncremental getsome parser = go decoder
go (Partial k ) = do go (Partial k ) = do
chunk <- getsome chunk <- getsome
go (k chunk) go (k chunk)
go (Fail _leftover _consumed msg) = error msg go (Fail _leftover _consumed msg) = error $ fromString msg
getSocketIncremental :: Get a -> MonadStore a getSocketIncremental :: Get a -> MonadStore a
getSocketIncremental = genericIncremental sockGet8 getSocketIncremental = genericIncremental sockGet8
@ -53,7 +47,7 @@ getSocketIncremental = genericIncremental sockGet8
sockPut :: Put -> MonadStore () sockPut :: Put -> MonadStore ()
sockPut p = do sockPut p = do
soc <- asks storeSocket soc <- asks storeSocket
liftIO $ sendAll soc $ BSL.toStrict $ runPut p liftIO $ sendAll soc $ toStrict $ runPut p
sockGet :: Get a -> MonadStore a sockGet :: Get a -> MonadStore a
sockGet = getSocketIncremental sockGet = getSocketIncremental
@ -95,16 +89,16 @@ sockGetPaths = do
getSocketIncremental (getPaths sd) getSocketIncremental (getPaths sd)
bsToText :: ByteString -> Text bsToText :: ByteString -> Text
bsToText = T.decodeUtf8 bsToText = decodeUtf8
textToBS :: Text -> ByteString textToBS :: Text -> ByteString
textToBS = T.encodeUtf8 textToBS = encodeUtf8
bslToText :: BSL.ByteString -> Text bslToText :: BSL.ByteString -> Text
bslToText = TL.toStrict . TL.decodeUtf8 bslToText = toText . TL.decodeUtf8
textToBSL :: Text -> BSL.ByteString textToBSL :: Text -> BSL.ByteString
textToBSL = TL.encodeUtf8 . TL.fromStrict textToBSL = TL.encodeUtf8 . toLText
putText :: Text -> Put putText :: Text -> Put
putText = putByteStringLen . textToBSL putText = putByteStringLen . textToBSL
@ -120,11 +114,11 @@ getPaths sd =
Data.HashSet.fromList . rights . fmap (parsePath sd) <$> getByteStrings Data.HashSet.fromList . rights . fmap (parsePath sd) <$> getByteStrings
putPath :: StorePath -> Put putPath :: StorePath -> Put
putPath = putByteStringLen . BSL.fromStrict . storePathToRawFilePath putPath = putByteStringLen . fromStrict . storePathToRawFilePath
putPaths :: StorePathSet -> Put putPaths :: StorePathSet -> Put
putPaths = putByteStrings . Data.HashSet.toList . Data.HashSet.map putPaths = putByteStrings . Data.HashSet.toList . Data.HashSet.map
(BSL.fromStrict . storePathToRawFilePath) (fromStrict . storePathToRawFilePath)
putBool :: Bool -> Put putBool :: Bool -> Put
putBool True = putInt (1 :: Int) putBool True = putInt (1 :: Int)
@ -170,4 +164,4 @@ putDerivation Derivation{..} = do
putMany putText args putMany putText args
flip putMany (Data.Map.toList env) flip putMany (Data.Map.toList env)
$ \(first, second) -> putText first >> putText second $ \(a1, a2) -> putText a1 *> putText a2

View File

@ -1,12 +1,7 @@
{-# LANGUAGE OverloadedStrings #-} {-# language DataKinds #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeApplications #-}
module Derivation where module Derivation where
import Control.Monad.IO.Class ( liftIO )
import Data.Text ( Text )
import Nix.Derivation ( Derivation(..) import Nix.Derivation ( Derivation(..)
, DerivationOutput(..) , DerivationOutput(..)
) )
@ -21,7 +16,6 @@ import System.Nix.Store.Remote ( MonadStore
import qualified Data.Map import qualified Data.Map
import qualified Data.Set import qualified Data.Set
import qualified Data.Text import qualified Data.Text
import qualified Data.Text.Lazy
import qualified Data.Text.Lazy.Builder import qualified Data.Text.Lazy.Builder
import qualified Data.Vector import qualified Data.Vector
import qualified System.Nix.Derivation import qualified System.Nix.Derivation
@ -69,7 +63,7 @@ withDerivation action = withBuildScript $ \buildScript -> withBash $ \bash ->
pth <- addTextToStore pth <- addTextToStore
"hnix-store-derivation" "hnix-store-derivation"
( Data.Text.Lazy.toStrict ( toText
$ Data.Text.Lazy.Builder.toLazyText $ Data.Text.Lazy.Builder.toLazyText
$ System.Nix.Derivation.buildDerivation d $ System.Nix.Derivation.buildDerivation d
) )

View File

@ -1,23 +1,14 @@
{-# LANGUAGE DataKinds #-} {-# language DataKinds #-}
{-# LANGUAGE OverloadedStrings #-} {-# language ScopedTypeVariables #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
module NixDaemon where module NixDaemon where
import Data.Bool ( bool ) import qualified System.Environment as Env
import Control.Monad ( void )
import Control.Monad.IO.Class ( liftIO )
import Control.Exception ( bracket ) import Control.Exception ( bracket )
import Control.Concurrent ( threadDelay ) import Control.Concurrent ( threadDelay )
import Data.Either ( isRight
, isLeft
)
import Data.Text ( Text )
import qualified Data.HashSet as HS import qualified Data.HashSet as HS
import qualified Data.Map.Strict as M import qualified Data.Map.Strict as M
import System.Directory import System.Directory
import qualified System.Environment
import System.IO.Temp import System.IO.Temp
import qualified System.Process as P import qualified System.Process as P
import System.Posix.User as U import System.Posix.User as U
@ -42,24 +33,24 @@ import Crypto.Hash ( SHA256
createProcessEnv :: FilePath -> String -> [String] -> IO P.ProcessHandle createProcessEnv :: FilePath -> String -> [String] -> IO P.ProcessHandle
createProcessEnv fp proc args = do createProcessEnv fp proc args = do
mPath <- System.Environment.lookupEnv "PATH" mPath <- Env.lookupEnv "PATH"
(_, _, _, ph) <- (_, _, _, ph) <-
P.createProcess (P.proc proc args) P.createProcess (P.proc proc args)
{ P.cwd = Just $ fp { P.cwd = Just fp
, P.env = Just $ mockedEnv mPath fp , P.env = Just $ mockedEnv mPath fp
} }
pure ph pure ph
mockedEnv :: Maybe String -> FilePath -> [(String, FilePath)] mockedEnv :: Maybe String -> FilePath -> [(String, FilePath)]
mockedEnv mEnvPath fp = mockedEnv mEnvPath fp = (fp </>) <<$>>
[ ("NIX_STORE_DIR" , fp </> "store") [ ("NIX_STORE_DIR" , "store")
, ("NIX_LOCALSTATE_DIR", fp </> "var") , ("NIX_LOCALSTATE_DIR", "var")
, ("NIX_LOG_DIR" , fp </> "var" </> "log") , ("NIX_LOG_DIR" , "var" </> "log")
, ("NIX_STATE_DIR" , fp </> "var" </> "nix") , ("NIX_STATE_DIR" , "var" </> "nix")
, ("NIX_CONF_DIR" , fp </> "etc") , ("NIX_CONF_DIR" , "etc")
-- , ("NIX_REMOTE", "daemon") -- , ("NIX_REMOTE", "daemon")
] <> (maybe [] (\x -> [("PATH", x)]) mEnvPath) ] <> foldMap (\x -> [("PATH", x)]) mEnvPath
waitSocket :: FilePath -> Int -> IO () waitSocket :: FilePath -> Int -> IO ()
waitSocket _ 0 = fail "No socket" waitSocket _ 0 = fail "No socket"
@ -67,12 +58,12 @@ waitSocket fp x = do
ex <- doesFileExist fp ex <- doesFileExist fp
bool bool
(threadDelay 100000 >> waitSocket fp (x - 1)) (threadDelay 100000 >> waitSocket fp (x - 1))
(pure ()) pass
ex ex
writeConf :: FilePath -> IO () writeConf :: FilePath -> IO ()
writeConf fp = writeConf fp =
writeFile fp $ unlines writeFile fp $ toString $ unlines
[ "build-users-group = " [ "build-users-group = "
, "trusted-users = root" , "trusted-users = root"
, "allowed-users = *" , "allowed-users = *"
@ -131,7 +122,7 @@ withNixDaemon action =
bracket (startDaemon path) bracket (startDaemon path)
(P.terminateProcess . fst) (P.terminateProcess . fst)
(\x -> action . snd $ x) (action . snd)
checks :: (Show a, Show b) => IO (a, b) -> (a -> Bool) -> IO () checks :: (Show a, Show b) => IO (a, b) -> (a -> Bool) -> IO ()
checks action check = action >>= (`Hspec.shouldSatisfy` (check . fst)) checks action check = action >>= (`Hspec.shouldSatisfy` (check . fst))
@ -143,7 +134,7 @@ it
-> (a -> Bool) -> (a -> Bool)
-> Hspec.SpecWith (m () -> IO (a, b)) -> Hspec.SpecWith (m () -> IO (a, b))
it name action check = it name action check =
Hspec.it name $ \run -> (run (action >> pure ())) `checks` check Hspec.it name $ \run -> run (action >> pass) `checks` check
itRights itRights
:: (Show a, Show b, Show c, Monad m) :: (Show a, Show b, Show c, Monad m)
@ -168,8 +159,7 @@ withPath action = do
dummy :: MonadStore StorePath dummy :: MonadStore StorePath
dummy = do dummy = do
let Right n = makeStorePathName "dummy" let Right n = makeStorePathName "dummy"
res <- addToStore @SHA256 n "dummy" False (pure True) False addToStore @SHA256 n "dummy" False (pure True) False
pure res
invalidPath :: StorePath invalidPath :: StorePath
invalidPath = invalidPath =
@ -204,30 +194,30 @@ spec_protocol = Hspec.around withNixDaemon $
verifyStore True True `shouldReturn` False verifyStore True True `shouldReturn` False
context "addTextToStore" $ context "addTextToStore" $
itRights "adds text to store" $ withPath $ const pure () itRights "adds text to store" $ withPath pure
context "isValidPathUncached" $ do context "isValidPathUncached" $ do
itRights "validates path" $ withPath $ \path -> do itRights "validates path" $ withPath $ \path -> do
liftIO $ putStrLn $ show path liftIO $ print path
(isValidPathUncached path) `shouldReturn` True isValidPathUncached path `shouldReturn` True
itLefts "fails on invalid path" $ isValidPathUncached $ invalidPath itLefts "fails on invalid path" $ isValidPathUncached invalidPath
context "queryAllValidPaths" $ do context "queryAllValidPaths" $ do
itRights "empty query" $ queryAllValidPaths itRights "empty query" queryAllValidPaths
itRights "non-empty query" $ withPath $ \path -> itRights "non-empty query" $ withPath $ \path ->
queryAllValidPaths `shouldReturn` (HS.fromList [path]) queryAllValidPaths `shouldReturn` HS.fromList [path]
context "queryPathInfoUncached" $ context "queryPathInfoUncached" $
itRights "queries path info" $ withPath $ queryPathInfoUncached itRights "queries path info" $ withPath queryPathInfoUncached
context "ensurePath" $ context "ensurePath" $
itRights "simple ensure" $ withPath $ ensurePath itRights "simple ensure" $ withPath ensurePath
context "addTempRoot" $ context "addTempRoot" $
itRights "simple addition" $ withPath $ addTempRoot itRights "simple addition" $ withPath addTempRoot
context "addIndirectRoot" $ context "addIndirectRoot" $
itRights "simple addition" $ withPath $ addIndirectRoot itRights "simple addition" $ withPath addIndirectRoot
context "buildPaths" $ do context "buildPaths" $ do
itRights "build Normal" $ withPath $ \path -> do itRights "build Normal" $ withPath $ \path -> do
@ -243,13 +233,13 @@ spec_protocol = Hspec.around withNixDaemon $
buildPaths pathSet Repair buildPaths pathSet Repair
context "roots" $ context "findRoots" $ do context "roots" $ context "findRoots" $ do
itRights "empty roots" $ (findRoots `shouldReturn` M.empty) itRights "empty roots" (findRoots `shouldReturn` M.empty)
itRights "path added as a temp root" $ withPath $ \_ -> do itRights "path added as a temp root" $ withPath $ \_ -> do
roots <- findRoots roots <- findRoots
roots `shouldSatisfy` ((== 1) . M.size) roots `shouldSatisfy` ((== 1) . M.size)
context "optimiseStore" $ itRights "optimises" $ optimiseStore context "optimiseStore" $ itRights "optimises" optimiseStore
context "queryMissing" $ context "queryMissing" $
itRights "queries" $ withPath $ \path -> do itRights "queries" $ withPath $ \path -> do
@ -268,8 +258,8 @@ spec_protocol = Hspec.around withNixDaemon $
itRights "valid dummy" $ do itRights "valid dummy" $ do
path <- dummy path <- dummy
liftIO $ putStrLn $ show path liftIO $ print path
(isValidPathUncached path) `shouldReturn` True isValidPathUncached path `shouldReturn` True
context "derivation" $ context "derivation" $
itRights "build derivation" $ itRights "build derivation" $

View File

@ -1,7 +1,7 @@
module Util where module Util where
import Data.Text.Arbitrary import Data.Text.Arbitrary ()
import System.Nix.Store.Remote.Util import System.Nix.Store.Remote.Util
import Test.Tasty.QuickCheck import Test.Tasty.QuickCheck