mirror of
https://github.com/haskell-nix/hnix-store.git
synced 2024-12-12 08:07:39 +03:00
Merge pull request #231 from sorki/srk/cereal
remote: start transitioning from binary to cereal
This commit is contained in:
commit
3b06982717
@ -1 +1,7 @@
|
|||||||
packages: ./hnix-store-core/*.cabal ./hnix-store-remote/*.cabal
|
packages:
|
||||||
|
./hnix-store-core/hnix-store-core.cabal
|
||||||
|
./hnix-store-remote/hnix-store-remote.cabal
|
||||||
|
|
||||||
|
package hnix-store-remote
|
||||||
|
flags: +build-readme +io-testsuite
|
||||||
|
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
tests: True
|
tests: True
|
||||||
|
|
||||||
flags: +io-testsuite
|
|
||||||
|
|
||||||
package hnix-store-core
|
package hnix-store-core
|
||||||
ghc-options: -Wunused-packages -Wall
|
ghc-options: -Wunused-packages -Wall
|
||||||
|
|
||||||
|
@ -1,16 +1,32 @@
|
|||||||
# Next
|
# Next
|
||||||
|
|
||||||
* Changes:
|
* Changes:
|
||||||
|
* `StorePathMetadata` converted to `Metadata a` [#231](https://github.com/haskell-nix/hnix-store/pull/231)
|
||||||
* Constructors of `StorePathName` and `StorePathHashPart` are no longer
|
* Constructors of `StorePathName` and `StorePathHashPart` are no longer
|
||||||
exported. Use respective `mkStorePath..` functions. [#230](https://github.com/haskell-nix/hnix-store/pull/230)
|
exported. Use respective `mkStorePath..` functions. [#230](https://github.com/haskell-nix/hnix-store/pull/230)
|
||||||
* `StorePathSet` type alias is no more, use `HashSet StorePath` [#230](https://github.com/haskell-nix/hnix-store/pull/230)
|
* `StorePathSet` type alias is no more, use `HashSet StorePath` [#230](https://github.com/haskell-nix/hnix-store/pull/230)
|
||||||
|
* `makeStorePath` and `parsePath` now returns `Either InvalidPathError StorePath` [#231](https://github.com/haskell-nix/hnix-store/pull/231)
|
||||||
|
* `BuildResult`s `timesBuild` field changes type from `Integer` to `Int` [#231](https://github.com/haskell-nix/hnix-store/pull/231)
|
||||||
|
|
||||||
* Additions:
|
* Additions:
|
||||||
|
* `Default StoreDir` instance [#231](https://github.com/haskell-nix/hnix-store/pull/231)
|
||||||
|
* `System.Nix.StorePath.storePathHashPartToText` [#231](https://github.com/haskell-nix/hnix-store/pull/231)
|
||||||
|
* Added `Generic` and `Show` instances for
|
||||||
|
`Signature` and `NarSignature` [#231](https://github.com/haskell-nix/hnix-store/pull/231)
|
||||||
|
* Added `Eq` and `Ord` instances for `SomeNamedDigest` [#231](https://github.com/haskell-nix/hnix-store/pull/231)
|
||||||
|
* `BuildStatus` grows `NoSubstituters` and `ResolvesToAlreadyValid` constructors [#231](https://github.com/haskell-nix/hnix-store/pull/231)
|
||||||
|
* `InvalidPathError` replacing previous stringy error [#231](https://github.com/haskell-nix/hnix-store/pull/231)
|
||||||
* Added `Arbitrary` instances for (exported by default) [#230](https://github.com/haskell-nix/hnix-store/pull/230)
|
* Added `Arbitrary` instances for (exported by default) [#230](https://github.com/haskell-nix/hnix-store/pull/230)
|
||||||
* `StorePath`
|
* `StorePath`
|
||||||
* `StorePathName`
|
* `StorePathName`
|
||||||
* `StorePathHashPart`
|
* `StorePathHashPart`
|
||||||
* `StoreDir`
|
* `StoreDir`
|
||||||
|
* Added `Arbitrary` instances for [#231](https://github.com/haskell-nix/hnix-store/pull/231)
|
||||||
|
* `BuildMode`
|
||||||
|
* `BuildStatus`
|
||||||
|
* `BuildResult`
|
||||||
|
* `Derivation StorePath Text`
|
||||||
|
* `DerivationOutput StorePath Text`
|
||||||
|
|
||||||
# [0.7.0.0](https://github.com/haskell-nix/hnix-store/compare/core-0.6.1.0...core-0.7.0.0) 2023-11-15
|
# [0.7.0.0](https://github.com/haskell-nix/hnix-store/compare/core-0.6.1.0...core-0.7.0.0) 2023-11-15
|
||||||
|
|
||||||
|
@ -1 +0,0 @@
|
|||||||
packages: .
|
|
@ -60,6 +60,8 @@ library
|
|||||||
, case-insensitive
|
, case-insensitive
|
||||||
, cereal
|
, cereal
|
||||||
, containers
|
, containers
|
||||||
|
, data-default-class
|
||||||
|
, generic-arbitrary < 1.1
|
||||||
-- Required for cryptonite low-level type convertion
|
-- Required for cryptonite low-level type convertion
|
||||||
, memory
|
, memory
|
||||||
, cryptonite
|
, cryptonite
|
||||||
@ -71,6 +73,7 @@ library
|
|||||||
, mtl
|
, mtl
|
||||||
, nix-derivation >= 1.1.1 && <2
|
, nix-derivation >= 1.1.1 && <2
|
||||||
, QuickCheck
|
, QuickCheck
|
||||||
|
, quickcheck-instances
|
||||||
, saltine
|
, saltine
|
||||||
, time
|
, time
|
||||||
, text
|
, text
|
||||||
@ -89,9 +92,13 @@ library
|
|||||||
, DeriveFoldable
|
, DeriveFoldable
|
||||||
, DeriveTraversable
|
, DeriveTraversable
|
||||||
, DeriveLift
|
, DeriveLift
|
||||||
|
, DerivingStrategies
|
||||||
|
, DerivingVia
|
||||||
, FlexibleContexts
|
, FlexibleContexts
|
||||||
, FlexibleInstances
|
, FlexibleInstances
|
||||||
, StandaloneDeriving
|
, StandaloneDeriving
|
||||||
|
, ScopedTypeVariables
|
||||||
|
, RecordWildCards
|
||||||
, TypeApplications
|
, TypeApplications
|
||||||
, TypeSynonymInstances
|
, TypeSynonymInstances
|
||||||
, InstanceSigs
|
, InstanceSigs
|
||||||
@ -116,6 +123,7 @@ test-suite format-tests
|
|||||||
main-is: Driver.hs
|
main-is: Driver.hs
|
||||||
other-modules:
|
other-modules:
|
||||||
Derivation
|
Derivation
|
||||||
|
ContentAddressableAddress
|
||||||
NarFormat
|
NarFormat
|
||||||
Hash
|
Hash
|
||||||
StorePath
|
StorePath
|
||||||
@ -134,9 +142,11 @@ test-suite format-tests
|
|||||||
, bytestring
|
, bytestring
|
||||||
, containers
|
, containers
|
||||||
, cryptonite
|
, cryptonite
|
||||||
|
, data-default-class
|
||||||
, directory
|
, directory
|
||||||
, filepath
|
, filepath
|
||||||
, process
|
, process
|
||||||
|
, nix-derivation >= 1.1.1 && <2
|
||||||
, tasty
|
, tasty
|
||||||
, tasty-golden
|
, tasty-golden
|
||||||
, hspec
|
, hspec
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
{-# language RecordWildCards #-}
|
-- due to recent generic-arbitrary
|
||||||
|
{-# OPTIONS_GHC -fconstraint-solver-iterations=0 #-}
|
||||||
{-|
|
{-|
|
||||||
Description : Build related types
|
Description : Build related types
|
||||||
Maintainer : srk <srk@48.io>
|
Maintainer : srk <srk@48.io>
|
||||||
@ -8,15 +9,18 @@ module System.Nix.Build
|
|||||||
, BuildStatus(..)
|
, BuildStatus(..)
|
||||||
, BuildResult(..)
|
, BuildResult(..)
|
||||||
, buildSuccess
|
, buildSuccess
|
||||||
)
|
) where
|
||||||
where
|
|
||||||
|
|
||||||
import Data.Time ( UTCTime )
|
import Data.Time (UTCTime)
|
||||||
|
import Test.QuickCheck (Arbitrary(..))
|
||||||
|
import Test.QuickCheck.Arbitrary.Generic (GenericArbitrary(..))
|
||||||
|
import Test.QuickCheck.Instances ()
|
||||||
|
|
||||||
-- 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
|
||||||
data BuildMode = Normal | Repair | Check
|
data BuildMode = Normal | Repair | Check
|
||||||
deriving (Eq, Ord, Enum, Show)
|
deriving (Eq, Generic, Ord, Enum, Show)
|
||||||
|
deriving Arbitrary via GenericArbitrary BuildMode
|
||||||
|
|
||||||
data BuildStatus =
|
data BuildStatus =
|
||||||
Built
|
Built
|
||||||
@ -32,8 +36,10 @@ data BuildStatus =
|
|||||||
| DependencyFailed
|
| DependencyFailed
|
||||||
| LogLimitExceeded
|
| LogLimitExceeded
|
||||||
| NotDeterministic
|
| NotDeterministic
|
||||||
deriving (Eq, Ord, Enum, Show)
|
| ResolvesToAlreadyValid
|
||||||
|
| NoSubstituters
|
||||||
|
deriving (Eq, Generic, Ord, Enum, Show)
|
||||||
|
deriving Arbitrary via GenericArbitrary BuildStatus
|
||||||
|
|
||||||
-- | Result of the build
|
-- | Result of the build
|
||||||
data BuildResult = BuildResult
|
data BuildResult = BuildResult
|
||||||
@ -42,7 +48,7 @@ data BuildResult = BuildResult
|
|||||||
, -- | possible build error message
|
, -- | possible build error message
|
||||||
errorMessage :: !(Maybe Text)
|
errorMessage :: !(Maybe Text)
|
||||||
, -- | How many times this build was performed
|
, -- | How many times this build was performed
|
||||||
timesBuilt :: !Integer
|
timesBuilt :: !Int
|
||||||
, -- | If timesBuilt > 1, whether some builds did not produce the same result
|
, -- | If timesBuilt > 1, whether some builds did not produce the same result
|
||||||
isNonDeterministic :: !Bool
|
isNonDeterministic :: !Bool
|
||||||
, -- Start time of this build
|
, -- Start time of this build
|
||||||
@ -50,7 +56,8 @@ data BuildResult = BuildResult
|
|||||||
, -- Stop time of this build
|
, -- Stop time of this build
|
||||||
stopTime :: !UTCTime
|
stopTime :: !UTCTime
|
||||||
}
|
}
|
||||||
deriving (Eq, Ord, Show)
|
deriving (Eq, Generic, Ord, Show)
|
||||||
|
deriving Arbitrary via GenericArbitrary BuildResult
|
||||||
|
|
||||||
buildSuccess :: BuildResult -> Bool
|
buildSuccess :: BuildResult -> Bool
|
||||||
buildSuccess BuildResult {..} =
|
buildSuccess BuildResult {..} =
|
||||||
|
@ -1,32 +1,48 @@
|
|||||||
|
-- due to recent generic-arbitrary
|
||||||
|
{-# OPTIONS_GHC -Wno-orphans -fconstraint-solver-iterations=0 #-}
|
||||||
|
|
||||||
module System.Nix.Derivation
|
module System.Nix.Derivation
|
||||||
( parseDerivation
|
( parseDerivation
|
||||||
, buildDerivation
|
, buildDerivation
|
||||||
)
|
) where
|
||||||
where
|
|
||||||
|
|
||||||
import qualified Data.Text.Lazy.Builder as Text.Lazy
|
import Data.Attoparsec.Text.Lazy (Parser)
|
||||||
( Builder )
|
import Data.Text.Lazy.Builder (Builder)
|
||||||
import qualified Data.Attoparsec.Text.Lazy as Text.Lazy
|
import Test.QuickCheck (Arbitrary(..))
|
||||||
( Parser )
|
import Test.QuickCheck.Arbitrary.Generic (GenericArbitrary(..))
|
||||||
import Nix.Derivation ( Derivation )
|
import Test.QuickCheck.Instances ()
|
||||||
import qualified Nix.Derivation as Derivation
|
|
||||||
import System.Nix.StorePath ( StoreDir
|
|
||||||
, StorePath
|
|
||||||
, storePathToFilePath
|
|
||||||
)
|
|
||||||
import qualified System.Nix.StorePath as StorePath
|
|
||||||
|
|
||||||
|
import Nix.Derivation (Derivation, DerivationOutput)
|
||||||
|
import System.Nix.StorePath (StoreDir, StorePath)
|
||||||
|
|
||||||
|
import qualified Data.Attoparsec.Text.Lazy
|
||||||
|
import qualified Data.Text.Lazy
|
||||||
|
|
||||||
parseDerivation :: StoreDir -> Text.Lazy.Parser (Derivation StorePath Text)
|
import qualified Nix.Derivation
|
||||||
|
import qualified System.Nix.StorePath
|
||||||
|
|
||||||
|
deriving via GenericArbitrary (Derivation StorePath Text)
|
||||||
|
instance Arbitrary (Derivation StorePath Text)
|
||||||
|
deriving via GenericArbitrary (DerivationOutput StorePath Text)
|
||||||
|
instance Arbitrary (DerivationOutput StorePath Text)
|
||||||
|
|
||||||
|
parseDerivation :: StoreDir -> Parser (Derivation StorePath Text)
|
||||||
parseDerivation expectedRoot =
|
parseDerivation expectedRoot =
|
||||||
Derivation.parseDerivationWith
|
Nix.Derivation.parseDerivationWith
|
||||||
("\"" *> StorePath.pathParser expectedRoot <* "\"")
|
pathParser
|
||||||
Derivation.textParser
|
Nix.Derivation.textParser
|
||||||
|
where
|
||||||
|
pathParser = do
|
||||||
|
text <- Nix.Derivation.textParser
|
||||||
|
case Data.Attoparsec.Text.Lazy.parseOnly
|
||||||
|
(System.Nix.StorePath.pathParser expectedRoot)
|
||||||
|
(Data.Text.Lazy.fromStrict text)
|
||||||
|
of
|
||||||
|
Right p -> pure p
|
||||||
|
Left e -> fail e
|
||||||
|
|
||||||
buildDerivation :: StoreDir -> Derivation StorePath Text -> Text.Lazy.Builder
|
buildDerivation :: StoreDir -> Derivation StorePath Text -> Builder
|
||||||
buildDerivation storeDir =
|
buildDerivation storeDir =
|
||||||
Derivation.buildDerivationWith
|
Nix.Derivation.buildDerivationWith
|
||||||
(show . storePathToFilePath storeDir)
|
(show . System.Nix.StorePath.storePathToText storeDir)
|
||||||
show
|
show
|
||||||
|
@ -19,6 +19,7 @@ module System.Nix.Internal.Hash
|
|||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
|
||||||
|
import Crypto.Hash (Digest)
|
||||||
import qualified Text.Show
|
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
|
||||||
@ -45,11 +46,23 @@ instance NamedAlgo C.SHA512 where
|
|||||||
algoName = "sha512"
|
algoName = "sha512"
|
||||||
|
|
||||||
-- | A digest whose 'NamedAlgo' is not known at compile time.
|
-- | A digest whose 'NamedAlgo' is not known at compile time.
|
||||||
data SomeNamedDigest = forall a . NamedAlgo a => SomeDigest (C.Digest a)
|
data SomeNamedDigest = forall a . NamedAlgo a => SomeDigest (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) -> toString $ "SomeDigest " <> algoName @hashType <> ":" <> encodeDigestWith NixBase32 digest
|
SomeDigest (digest :: Digest hashType) -> toString $ "SomeDigest " <> algoName @hashType <> ":" <> encodeDigestWith NixBase32 digest
|
||||||
|
|
||||||
|
instance Eq SomeNamedDigest where
|
||||||
|
(==) (SomeDigest (a :: Digest aType))
|
||||||
|
(SomeDigest (b :: Digest bType))
|
||||||
|
= algoName @aType == algoName @bType
|
||||||
|
&& encodeDigestWith NixBase32 a == encodeDigestWith NixBase32 b
|
||||||
|
|
||||||
|
instance Ord SomeNamedDigest where
|
||||||
|
(<=) (SomeDigest (a :: Digest aType))
|
||||||
|
(SomeDigest (b :: Digest bType))
|
||||||
|
= algoName @aType <= algoName @bType
|
||||||
|
&& encodeDigestWith NixBase32 a <= encodeDigestWith NixBase32 b
|
||||||
|
|
||||||
mkNamedDigest :: Text -> Text -> Either String SomeNamedDigest
|
mkNamedDigest :: Text -> Text -> Either String SomeNamedDigest
|
||||||
mkNamedDigest name sriHash =
|
mkNamedDigest name sriHash =
|
||||||
|
@ -47,7 +47,7 @@ import qualified System.Nix.Internal.Nar.Options as Nar
|
|||||||
-- of the actions the parser can take, and @ParserState@ for the
|
-- of the actions the parser can take, and @ParserState@ for the
|
||||||
-- internals of the parser
|
-- internals of the parser
|
||||||
newtype NarParser m a = NarParser
|
newtype NarParser m a = NarParser
|
||||||
{ runNarParser ::
|
{ _runNarParser ::
|
||||||
State.StateT
|
State.StateT
|
||||||
ParserState
|
ParserState
|
||||||
(Except.ExceptT
|
(Except.ExceptT
|
||||||
@ -554,15 +554,12 @@ testParser' :: (m ~ IO) => FilePath -> IO (Either String ())
|
|||||||
testParser' fp =
|
testParser' fp =
|
||||||
withFile fp ReadMode $ \h -> runParser Nar.narEffectsIO parseNar h "tmp"
|
withFile fp ReadMode $ \h -> runParser Nar.narEffectsIO parseNar h "tmp"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- | Distance to the next multiple of 8
|
-- | Distance to the next multiple of 8
|
||||||
padLen :: Int -> Int
|
padLen :: Int -> Int
|
||||||
padLen n = (8 - n) `mod` 8
|
padLen n = (8 - n) `mod` 8
|
||||||
|
|
||||||
|
-- | Debugging helper
|
||||||
dbgState :: IO.MonadIO m => NarParser m ()
|
_dbgState :: IO.MonadIO m => NarParser m ()
|
||||||
dbgState = do
|
_dbgState = do
|
||||||
s <- State.get
|
s <- State.get
|
||||||
IO.liftIO $ print (tokenStack s, directoryStack s)
|
IO.liftIO $ print (tokenStack s, directoryStack s)
|
||||||
|
@ -23,7 +23,7 @@ import qualified Crypto.Saltine.Internal.ByteSizes as NaClSizes
|
|||||||
|
|
||||||
-- | A NaCl signature.
|
-- | A NaCl signature.
|
||||||
newtype Signature = Signature ByteString
|
newtype Signature = Signature ByteString
|
||||||
deriving (Eq, Ord)
|
deriving (Eq, Generic, Ord, Show)
|
||||||
|
|
||||||
instance IsEncoding Signature where
|
instance IsEncoding Signature where
|
||||||
decode s
|
decode s
|
||||||
@ -42,4 +42,4 @@ data NarSignature = NarSignature
|
|||||||
, -- | The archive's signature.
|
, -- | The archive's signature.
|
||||||
sig :: Signature
|
sig :: Signature
|
||||||
}
|
}
|
||||||
deriving (Eq, Ord)
|
deriving (Eq, Generic, Ord, Show)
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
|
{-# OPTIONS_GHC -Wno-orphans #-}
|
||||||
{-|
|
{-|
|
||||||
Description : Representation of Nix store paths.
|
Description : Representation of Nix store paths.
|
||||||
-}
|
-}
|
||||||
{-# language ConstraintKinds #-}
|
{-# language ConstraintKinds #-}
|
||||||
{-# language RecordWildCards #-}
|
{-# language DeriveAnyClass #-}
|
||||||
{-# language GeneralizedNewtypeDeriving #-}
|
|
||||||
{-# language ScopedTypeVariables #-}
|
|
||||||
{-# language AllowAmbiguousTypes #-}
|
{-# language AllowAmbiguousTypes #-}
|
||||||
{-# language DataKinds #-}
|
{-# language DataKinds #-}
|
||||||
|
|
||||||
@ -16,22 +15,31 @@ module System.Nix.Internal.StorePath
|
|||||||
, StorePathHashPart(..)
|
, StorePathHashPart(..)
|
||||||
, mkStorePathHashPart
|
, mkStorePathHashPart
|
||||||
, ContentAddressableAddress(..)
|
, ContentAddressableAddress(..)
|
||||||
|
, contentAddressableAddressBuilder
|
||||||
|
, contentAddressableAddressParser
|
||||||
|
, digestBuilder
|
||||||
, NarHashMode(..)
|
, NarHashMode(..)
|
||||||
, -- * Manipulating 'StorePathName'
|
, -- * Manipulating 'StorePathName'
|
||||||
makeStorePathName
|
makeStorePathName
|
||||||
, validStorePathName
|
, validStorePathName
|
||||||
|
-- * Reason why a path is not valid
|
||||||
|
, InvalidPathError(..)
|
||||||
, -- * Rendering out 'StorePath's
|
, -- * Rendering out 'StorePath's
|
||||||
storePathToFilePath
|
storePathToFilePath
|
||||||
, storePathToRawFilePath
|
, storePathToRawFilePath
|
||||||
, storePathToText
|
, storePathToText
|
||||||
, storePathToNarInfo
|
, storePathToNarInfo
|
||||||
|
, storePathHashPartToText
|
||||||
, -- * Parsing 'StorePath's
|
, -- * Parsing 'StorePath's
|
||||||
parsePath
|
parsePath
|
||||||
, pathParser
|
, pathParser
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
|
||||||
|
import Data.Default.Class (Default(def))
|
||||||
|
import Data.Text.Lazy.Builder (Builder)
|
||||||
import qualified Relude.Unsafe as Unsafe
|
import qualified Relude.Unsafe as Unsafe
|
||||||
|
import qualified System.Nix.Hash
|
||||||
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
|
||||||
@ -39,17 +47,23 @@ import qualified System.Nix.Internal.Base32 as Nix.Base32
|
|||||||
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 qualified Data.Text as Text
|
import qualified Data.Text as Text
|
||||||
|
import qualified Data.Text.Encoding
|
||||||
|
import qualified Data.Text.Lazy.Builder
|
||||||
import Data.Attoparsec.Text.Lazy ( Parser
|
import Data.Attoparsec.Text.Lazy ( Parser
|
||||||
, (<?>)
|
, (<?>)
|
||||||
)
|
)
|
||||||
|
import qualified Data.Attoparsec.ByteString.Char8
|
||||||
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 Crypto.Hash ( SHA256
|
import Crypto.Hash ( SHA256
|
||||||
, Digest
|
, Digest
|
||||||
, HashAlgorithm
|
, HashAlgorithm
|
||||||
|
, hash
|
||||||
)
|
)
|
||||||
|
|
||||||
import Test.QuickCheck
|
import Test.QuickCheck (Arbitrary(arbitrary), listOf, elements)
|
||||||
|
import Test.QuickCheck.Arbitrary.Generic (GenericArbitrary(..))
|
||||||
|
import Test.QuickCheck.Instances ()
|
||||||
|
|
||||||
-- | A path in a Nix store.
|
-- | A path in a Nix store.
|
||||||
--
|
--
|
||||||
@ -68,7 +82,7 @@ data StorePath = StorePath
|
|||||||
-- hello-1.2.3).
|
-- hello-1.2.3).
|
||||||
storePathName :: !StorePathName
|
storePathName :: !StorePathName
|
||||||
}
|
}
|
||||||
deriving (Eq, Ord, Show)
|
deriving (Eq, Generic, Ord, Show)
|
||||||
|
|
||||||
instance Hashable StorePath where
|
instance Hashable StorePath where
|
||||||
hashWithSalt s StorePath{..} =
|
hashWithSalt s StorePath{..} =
|
||||||
@ -88,7 +102,7 @@ instance Arbitrary StorePath where
|
|||||||
newtype StorePathName = StorePathName
|
newtype StorePathName = StorePathName
|
||||||
{ -- | Extract the contents of the name.
|
{ -- | Extract the contents of the name.
|
||||||
unStorePathName :: Text
|
unStorePathName :: Text
|
||||||
} deriving (Eq, Hashable, Ord, Show)
|
} deriving (Eq, Generic, Hashable, Ord, Show)
|
||||||
|
|
||||||
instance Arbitrary StorePathName where
|
instance Arbitrary StorePathName where
|
||||||
arbitrary = StorePathName . toText <$> ((:) <$> s1 <*> listOf sn)
|
arbitrary = StorePathName . toText <$> ((:) <$> s1 <*> listOf sn)
|
||||||
@ -102,7 +116,7 @@ newtype StorePathHashPart = StorePathHashPart
|
|||||||
{ -- | Extract the contents of the hash.
|
{ -- | Extract the contents of the hash.
|
||||||
unStorePathHashPart :: ByteString
|
unStorePathHashPart :: ByteString
|
||||||
}
|
}
|
||||||
deriving (Eq, Hashable, Ord, Show)
|
deriving (Eq, Generic, Hashable, Ord, Show)
|
||||||
|
|
||||||
instance Arbitrary StorePathHashPart where
|
instance Arbitrary StorePathHashPart where
|
||||||
arbitrary = mkStorePathHashPart @SHA256 . Bytes.Char8.pack <$> arbitrary
|
arbitrary = mkStorePathHashPart @SHA256 . Bytes.Char8.pack <$> arbitrary
|
||||||
@ -112,8 +126,9 @@ mkStorePathHashPart
|
|||||||
. HashAlgorithm hashAlgo
|
. HashAlgorithm hashAlgo
|
||||||
=> ByteString
|
=> ByteString
|
||||||
-> StorePathHashPart
|
-> StorePathHashPart
|
||||||
mkStorePathHashPart = coerce . mkStorePathHash @hashAlgo
|
mkStorePathHashPart = StorePathHashPart . mkStorePathHash @hashAlgo
|
||||||
|
|
||||||
|
-- TODO(srk): split into its own module + .Builder/.Parser
|
||||||
-- | An address for a content-addressable store path, i.e. one whose
|
-- | An address for a content-addressable store path, i.e. one whose
|
||||||
-- store path hash is purely a function of its contents (as opposed to
|
-- store path hash is purely a function of its contents (as opposed to
|
||||||
-- paths that are derivation outputs, whose hashes are a function of
|
-- paths that are derivation outputs, whose hashes are a function of
|
||||||
@ -132,6 +147,67 @@ data ContentAddressableAddress
|
|||||||
-- addToStore. It is addressed according to some hash algorithm
|
-- addToStore. It is addressed according to some hash algorithm
|
||||||
-- applied to the nar serialization via some 'NarHashMode'.
|
-- applied to the nar serialization via some 'NarHashMode'.
|
||||||
Fixed !NarHashMode !SomeNamedDigest
|
Fixed !NarHashMode !SomeNamedDigest
|
||||||
|
deriving (Eq, Generic, Ord, Show)
|
||||||
|
|
||||||
|
-- TODO(srk): extend to all hash types
|
||||||
|
instance Arbitrary (Digest SHA256) where
|
||||||
|
arbitrary = hash @ByteString <$> arbitrary
|
||||||
|
|
||||||
|
instance Arbitrary SomeNamedDigest where
|
||||||
|
arbitrary = SomeDigest @SHA256 <$> arbitrary
|
||||||
|
|
||||||
|
deriving via GenericArbitrary ContentAddressableAddress
|
||||||
|
instance Arbitrary ContentAddressableAddress
|
||||||
|
|
||||||
|
-- | Builder for `ContentAddressableAddress`
|
||||||
|
contentAddressableAddressBuilder :: ContentAddressableAddress -> Builder
|
||||||
|
contentAddressableAddressBuilder (Text digest) =
|
||||||
|
"text:"
|
||||||
|
<> digestBuilder digest
|
||||||
|
contentAddressableAddressBuilder (Fixed narHashMode (SomeDigest (digest :: Digest hashAlgo))) =
|
||||||
|
"fixed:"
|
||||||
|
<> (if narHashMode == Recursive then "r:" else mempty)
|
||||||
|
-- <> Data.Text.Lazy.Builder.fromText (System.Nix.Hash.algoName @hashAlgo)
|
||||||
|
<> digestBuilder digest
|
||||||
|
|
||||||
|
-- | Builder for @Digest@s
|
||||||
|
digestBuilder :: forall hashAlgo . (NamedAlgo hashAlgo) => Digest hashAlgo -> Builder
|
||||||
|
digestBuilder digest =
|
||||||
|
Data.Text.Lazy.Builder.fromText (System.Nix.Hash.algoName @hashAlgo)
|
||||||
|
<> ":"
|
||||||
|
<> Data.Text.Lazy.Builder.fromText (encodeDigestWith NixBase32 digest)
|
||||||
|
|
||||||
|
-- | Parser for content addressable field
|
||||||
|
contentAddressableAddressParser :: Data.Attoparsec.ByteString.Char8.Parser ContentAddressableAddress
|
||||||
|
contentAddressableAddressParser = caText <|> caFixed
|
||||||
|
where
|
||||||
|
-- | Parser for @text:sha256:<h>@
|
||||||
|
--caText :: Parser ContentAddressableAddress
|
||||||
|
caText = do
|
||||||
|
_ <- "text:sha256:"
|
||||||
|
digest <- decodeDigestWith @SHA256 NixBase32 <$> parseHash
|
||||||
|
either fail pure $ Text <$> digest
|
||||||
|
|
||||||
|
-- | Parser for @fixed:<r?>:<ht>:<h>@
|
||||||
|
--caFixed :: Parser ContentAddressableAddress
|
||||||
|
caFixed = do
|
||||||
|
_ <- "fixed:"
|
||||||
|
narHashMode <- (Recursive <$ "r:") <|> (RegularFile <$ "")
|
||||||
|
digest <- parseTypedDigest
|
||||||
|
either fail pure $ Fixed narHashMode <$> digest
|
||||||
|
|
||||||
|
--parseTypedDigest :: Parser (Either String SomeNamedDigest)
|
||||||
|
parseTypedDigest = mkNamedDigest <$> parseHashType <*> parseHash
|
||||||
|
|
||||||
|
--parseHashType :: Parser Text
|
||||||
|
parseHashType =
|
||||||
|
Data.Text.Encoding.decodeUtf8
|
||||||
|
<$> ("sha256" <|> "sha512" <|> "sha1" <|> "md5") <* (":" <|> "-")
|
||||||
|
|
||||||
|
--parseHash :: Parser Text
|
||||||
|
parseHash =
|
||||||
|
Data.Text.Encoding.decodeUtf8
|
||||||
|
<$> Data.Attoparsec.ByteString.Char8.takeWhile1 (/= ':')
|
||||||
|
|
||||||
-- | Schemes for hashing a Nix archive.
|
-- | Schemes for hashing a Nix archive.
|
||||||
--
|
--
|
||||||
@ -143,19 +219,36 @@ data NarHashMode
|
|||||||
| -- | Hash an arbitrary nar, including a non-executable regular
|
| -- | Hash an arbitrary nar, including a non-executable regular
|
||||||
-- file if so desired.
|
-- file if so desired.
|
||||||
Recursive
|
Recursive
|
||||||
|
deriving (Eq, Enum, Generic, Hashable, Ord, Show)
|
||||||
|
|
||||||
makeStorePathName :: Text -> Either String StorePathName
|
deriving via GenericArbitrary NarHashMode
|
||||||
|
instance Arbitrary NarHashMode
|
||||||
|
|
||||||
|
-- | Reason why a path is not valid
|
||||||
|
data InvalidPathError =
|
||||||
|
EmptyName
|
||||||
|
| PathTooLong
|
||||||
|
| LeadingDot
|
||||||
|
| InvalidCharacter
|
||||||
|
| HashDecodingFailure String
|
||||||
|
| RootDirMismatch
|
||||||
|
{ rdMismatchExpected :: StoreDir
|
||||||
|
, rdMismatchGot :: StoreDir
|
||||||
|
}
|
||||||
|
deriving (Eq, Generic, Hashable, Ord, Show)
|
||||||
|
|
||||||
|
makeStorePathName :: Text -> Either InvalidPathError StorePathName
|
||||||
makeStorePathName n =
|
makeStorePathName n =
|
||||||
if validStorePathName n
|
if validStorePathName n
|
||||||
then pure $ StorePathName n
|
then pure $ StorePathName n
|
||||||
else Left $ reasonInvalid n
|
else Left $ reasonInvalid n
|
||||||
|
|
||||||
reasonInvalid :: Text -> String
|
reasonInvalid :: Text -> InvalidPathError
|
||||||
reasonInvalid n
|
reasonInvalid n
|
||||||
| n == "" = "Empty name"
|
| n == "" = EmptyName
|
||||||
| Text.length n > 211 = "Path too long"
|
| Text.length n > 211 = PathTooLong
|
||||||
| Text.head n == '.' = "Leading dot"
|
| Text.head n == '.' = LeadingDot
|
||||||
| otherwise = "Invalid character"
|
| otherwise = InvalidCharacter
|
||||||
|
|
||||||
validStorePathName :: Text -> Bool
|
validStorePathName :: Text -> Bool
|
||||||
validStorePathName n =
|
validStorePathName n =
|
||||||
@ -183,17 +276,20 @@ type RawFilePath = ByteString
|
|||||||
-- do not know their own store dir by design.
|
-- do not know their own store dir by design.
|
||||||
newtype StoreDir = StoreDir {
|
newtype StoreDir = StoreDir {
|
||||||
unStoreDir :: RawFilePath
|
unStoreDir :: RawFilePath
|
||||||
} deriving (Eq, Hashable, Ord, Show)
|
} deriving (Eq, Generic, Hashable, Ord, Show)
|
||||||
|
|
||||||
instance Arbitrary StoreDir where
|
instance Arbitrary StoreDir where
|
||||||
arbitrary = StoreDir . ("/" <>) . Bytes.Char8.pack <$> arbitrary
|
arbitrary = StoreDir . ("/" <>) . Bytes.Char8.pack <$> arbitrary
|
||||||
|
|
||||||
|
instance Default StoreDir where
|
||||||
|
def = StoreDir "/nix/store"
|
||||||
|
|
||||||
-- | Render a 'StorePath' as a 'RawFilePath'.
|
-- | Render a 'StorePath' as a 'RawFilePath'.
|
||||||
storePathToRawFilePath :: StoreDir -> StorePath -> RawFilePath
|
storePathToRawFilePath :: StoreDir -> StorePath -> RawFilePath
|
||||||
storePathToRawFilePath storeDir StorePath{..} =
|
storePathToRawFilePath storeDir StorePath{..} =
|
||||||
unStoreDir storeDir <> "/" <> hashPart <> "-" <> name
|
unStoreDir storeDir <> "/" <> hashPart <> "-" <> name
|
||||||
where
|
where
|
||||||
hashPart = encodeUtf8 $ encodeWith NixBase32 $ coerce storePathHash
|
hashPart = encodeUtf8 $ storePathHashPartToText storePathHash
|
||||||
name = encodeUtf8 $ unStorePathName storePathName
|
name = encodeUtf8 $ unStorePathName storePathName
|
||||||
|
|
||||||
-- | Render a 'StorePath' as a 'FilePath'.
|
-- | Render a 'StorePath' as a 'FilePath'.
|
||||||
@ -210,14 +306,26 @@ storePathToNarInfo :: StorePath -> Bytes.Char8.ByteString
|
|||||||
storePathToNarInfo StorePath{..} =
|
storePathToNarInfo StorePath{..} =
|
||||||
encodeUtf8 $ encodeWith NixBase32 (coerce storePathHash) <> ".narinfo"
|
encodeUtf8 $ encodeWith NixBase32 (coerce storePathHash) <> ".narinfo"
|
||||||
|
|
||||||
|
-- | Render a 'StorePathHashPart' as a 'Text'.
|
||||||
|
-- This is used by remote store / database
|
||||||
|
-- via queryPathFromHashPart
|
||||||
|
storePathHashPartToText :: StorePathHashPart -> Text
|
||||||
|
storePathHashPartToText = encodeWith NixBase32 . unStorePathHashPart
|
||||||
|
|
||||||
-- | Parse `StorePath` from `Bytes.Char8.ByteString`, checking
|
-- | Parse `StorePath` from `Bytes.Char8.ByteString`, checking
|
||||||
-- that store directory matches `expectedRoot`.
|
-- that store directory matches `expectedRoot`.
|
||||||
parsePath :: StoreDir -> Bytes.Char8.ByteString -> Either String StorePath
|
parsePath
|
||||||
|
:: StoreDir
|
||||||
|
-> Bytes.Char8.ByteString
|
||||||
|
-> Either InvalidPathError 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 "-" $ toText fname
|
(storeBasedHashPart, namePart) = Text.breakOn "-" $ toText fname
|
||||||
storeHash = decodeWith NixBase32 storeBasedHashPart
|
hashPart = bimap
|
||||||
|
HashDecodingFailure
|
||||||
|
StorePathHashPart
|
||||||
|
$ 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
|
||||||
@ -226,9 +334,12 @@ parsePath expectedRoot x =
|
|||||||
storeDir =
|
storeDir =
|
||||||
if expectedRootS == rootDir'
|
if expectedRootS == rootDir'
|
||||||
then pure rootDir'
|
then pure rootDir'
|
||||||
else Left $ "Root store dir mismatch, expected" <> expectedRootS <> "got" <> rootDir'
|
else Left $ RootDirMismatch
|
||||||
|
{ rdMismatchExpected = expectedRoot
|
||||||
|
, rdMismatchGot = StoreDir $ Bytes.Char8.pack rootDir
|
||||||
|
}
|
||||||
in
|
in
|
||||||
either Left (pure $ StorePath <$> coerce storeHash <*> name) storeDir
|
either Left (pure $ StorePath <$> hashPart <*> name) storeDir
|
||||||
|
|
||||||
pathParser :: StoreDir -> Parser StorePath
|
pathParser :: StoreDir -> Parser StorePath
|
||||||
pathParser expectedRoot = do
|
pathParser expectedRoot = do
|
||||||
@ -257,8 +368,12 @@ pathParser expectedRoot = do
|
|||||||
<?> "Path name contains invalid character"
|
<?> "Path name contains invalid character"
|
||||||
|
|
||||||
let name = makeStorePathName $ Text.cons c0 rest
|
let name = makeStorePathName $ Text.cons c0 rest
|
||||||
|
hashPart = bimap
|
||||||
|
HashDecodingFailure
|
||||||
|
StorePathHashPart
|
||||||
|
digest
|
||||||
|
|
||||||
either
|
either
|
||||||
fail
|
(fail . show)
|
||||||
pure
|
pure
|
||||||
(StorePath <$> coerce digest <*> name)
|
(StorePath <$> hashPart <*> name)
|
||||||
|
@ -10,16 +10,22 @@ module System.Nix.StorePath
|
|||||||
, mkStorePathHashPart
|
, mkStorePathHashPart
|
||||||
, unStorePathHashPart
|
, unStorePathHashPart
|
||||||
, ContentAddressableAddress(..)
|
, ContentAddressableAddress(..)
|
||||||
|
, contentAddressableAddressBuilder
|
||||||
|
, contentAddressableAddressParser
|
||||||
|
, digestBuilder
|
||||||
, NarHashMode(..)
|
, NarHashMode(..)
|
||||||
, -- * Manipulating 'StorePathName'
|
, -- * Manipulating 'StorePathName'
|
||||||
makeStorePathName
|
makeStorePathName
|
||||||
, unStorePathName
|
, unStorePathName
|
||||||
, validStorePathName
|
, validStorePathName
|
||||||
|
-- * Reason why a path is not valid
|
||||||
|
, InvalidPathError(..)
|
||||||
, -- * Rendering out 'StorePath's
|
, -- * Rendering out 'StorePath's
|
||||||
storePathToFilePath
|
storePathToFilePath
|
||||||
, storePathToRawFilePath
|
, storePathToRawFilePath
|
||||||
, storePathToText
|
, storePathToText
|
||||||
, storePathToNarInfo
|
, storePathToNarInfo
|
||||||
|
, storePathHashPartToText
|
||||||
, -- * Parsing 'StorePath's
|
, -- * Parsing 'StorePath's
|
||||||
parsePath
|
parsePath
|
||||||
, pathParser
|
, pathParser
|
||||||
|
@ -1,27 +1,29 @@
|
|||||||
{-|
|
{-|
|
||||||
Description : Metadata about Nix store paths.
|
Description : Metadata about Nix store paths.
|
||||||
-}
|
-}
|
||||||
module System.Nix.StorePathMetadata where
|
module System.Nix.StorePathMetadata
|
||||||
|
( Metadata(..)
|
||||||
|
, StorePathTrust(..)
|
||||||
|
) where
|
||||||
|
|
||||||
import System.Nix.StorePath ( StorePath
|
import Data.Time (UTCTime)
|
||||||
, ContentAddressableAddress
|
|
||||||
)
|
|
||||||
import System.Nix.Hash ( SomeNamedDigest )
|
|
||||||
import Data.Time ( UTCTime )
|
|
||||||
import System.Nix.Signature ( NarSignature )
|
|
||||||
|
|
||||||
-- | Metadata about a 'StorePath'
|
import System.Nix.Hash (SomeNamedDigest)
|
||||||
data StorePathMetadata = StorePathMetadata
|
import System.Nix.Signature (NarSignature)
|
||||||
|
import System.Nix.StorePath (ContentAddressableAddress)
|
||||||
|
|
||||||
|
-- | Metadata (typically about a 'StorePath')
|
||||||
|
data Metadata a = Metadata
|
||||||
{ -- | The path this metadata is about
|
{ -- | The path this metadata is about
|
||||||
path :: !StorePath
|
path :: !a
|
||||||
, -- | The path to the derivation file that built this path, if any
|
, -- | The path to the derivation file that built this path, if any
|
||||||
-- and known.
|
-- and known.
|
||||||
deriverPath :: !(Maybe StorePath)
|
deriverPath :: !(Maybe a)
|
||||||
, -- TODO should this be optional?
|
, -- TODO should this be optional?
|
||||||
-- | The hash of the nar serialization of the path.
|
-- | The hash of the nar serialization of the path.
|
||||||
narHash :: !SomeNamedDigest
|
narHash :: !SomeNamedDigest
|
||||||
, -- | The paths that this path directly references
|
, -- | The paths that this path directly references
|
||||||
references :: !(HashSet StorePath)
|
references :: !(HashSet a)
|
||||||
, -- | When was this path registered valid in the store?
|
, -- | When was this path registered valid in the store?
|
||||||
registrationTime :: !UTCTime
|
registrationTime :: !UTCTime
|
||||||
, -- | The size of the nar serialization of the path, in bytes.
|
, -- | The size of the nar serialization of the path, in bytes.
|
||||||
@ -38,7 +40,7 @@ data StorePathMetadata = StorePathMetadata
|
|||||||
-- There is no guarantee from this type alone that this address
|
-- There is no guarantee from this type alone that this address
|
||||||
-- is actually correct for this store path.
|
-- is actually correct for this store path.
|
||||||
contentAddressableAddress :: !(Maybe ContentAddressableAddress)
|
contentAddressableAddress :: !(Maybe ContentAddressableAddress)
|
||||||
}
|
} deriving (Eq, Generic, Ord, Show)
|
||||||
|
|
||||||
-- | How much do we trust the path, based on its provenance?
|
-- | How much do we trust the path, based on its provenance?
|
||||||
data StorePathTrust
|
data StorePathTrust
|
||||||
@ -47,4 +49,4 @@ data StorePathTrust
|
|||||||
| -- | It was built elsewhere (and substituted or similar) and so
|
| -- | It was built elsewhere (and substituted or similar) and so
|
||||||
-- is less trusted
|
-- is less trusted
|
||||||
BuiltElsewhere
|
BuiltElsewhere
|
||||||
deriving (Show, Eq, Ord)
|
deriving (Eq, Enum, Generic, Ord, Show)
|
||||||
|
21
hnix-store-core/tests/ContentAddressableAddress.hs
Normal file
21
hnix-store-core/tests/ContentAddressableAddress.hs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
|
||||||
|
module ContentAddressableAddress where
|
||||||
|
|
||||||
|
import Test.Tasty.QuickCheck
|
||||||
|
import System.Nix.StorePath (ContentAddressableAddress, contentAddressableAddressBuilder, contentAddressableAddressParser)
|
||||||
|
|
||||||
|
import qualified Data.Attoparsec.ByteString.Char8
|
||||||
|
import qualified Data.Text.Lazy
|
||||||
|
import qualified Data.Text.Lazy.Builder
|
||||||
|
import qualified Data.Text.Encoding
|
||||||
|
|
||||||
|
prop_caAddrRoundTrip :: ContentAddressableAddress -> Property
|
||||||
|
prop_caAddrRoundTrip = \caAddr ->
|
||||||
|
Data.Attoparsec.ByteString.Char8.parseOnly contentAddressableAddressParser
|
||||||
|
( Data.Text.Encoding.encodeUtf8
|
||||||
|
. Data.Text.Lazy.toStrict
|
||||||
|
. Data.Text.Lazy.Builder.toLazyText
|
||||||
|
$ contentAddressableAddressBuilder caAddr
|
||||||
|
)
|
||||||
|
=== pure caAddr
|
||||||
|
|
@ -5,14 +5,18 @@ import Test.Tasty ( TestTree
|
|||||||
, testGroup
|
, testGroup
|
||||||
)
|
)
|
||||||
import Test.Tasty.Golden ( goldenVsFile )
|
import Test.Tasty.Golden ( goldenVsFile )
|
||||||
|
import Test.Tasty.QuickCheck
|
||||||
|
|
||||||
import System.Nix.StorePath ( StoreDir(..) )
|
import Nix.Derivation ( Derivation )
|
||||||
|
import System.Nix.StorePath ( StoreDir(..), StorePath )
|
||||||
import System.Nix.Derivation ( parseDerivation
|
import System.Nix.Derivation ( parseDerivation
|
||||||
, buildDerivation
|
, buildDerivation
|
||||||
)
|
)
|
||||||
|
|
||||||
|
import Data.Default.Class (Default(def))
|
||||||
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 ()
|
||||||
@ -24,10 +28,10 @@ processDerivation source dest = do
|
|||||||
(Data.Text.IO.writeFile dest
|
(Data.Text.IO.writeFile dest
|
||||||
. toText
|
. toText
|
||||||
. Data.Text.Lazy.Builder.toLazyText
|
. Data.Text.Lazy.Builder.toLazyText
|
||||||
. buildDerivation (StoreDir "/nix/store")
|
. buildDerivation def
|
||||||
)
|
)
|
||||||
(Data.Attoparsec.Text.parseOnly
|
(Data.Attoparsec.Text.parseOnly
|
||||||
(parseDerivation $ StoreDir "/nix/store")
|
(parseDerivation def)
|
||||||
contents
|
contents
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -46,3 +50,17 @@ test_derivation =
|
|||||||
drv = fp <> show n <> ".drv"
|
drv = fp <> show n <> ".drv"
|
||||||
act = fp <> show n <> ".actual"
|
act = fp <> show n <> ".actual"
|
||||||
fp = "tests/samples/example"
|
fp = "tests/samples/example"
|
||||||
|
|
||||||
|
-- TODO(srk): this won't roundtrip as Arbitrary Text
|
||||||
|
-- contains wild stuff like control characters and UTF8 sequences.
|
||||||
|
-- Either fix in nix-derivation or use wrapper type
|
||||||
|
-- (but we use Nix.Derivation.textParser so we need Text for now)
|
||||||
|
xprop_derivationRoundTrip :: StoreDir -> Derivation StorePath Text -> Property
|
||||||
|
xprop_derivationRoundTrip = \sd drv ->
|
||||||
|
Data.Attoparsec.Text.parseOnly (parseDerivation sd)
|
||||||
|
( Data.Text.Lazy.toStrict
|
||||||
|
$ Data.Text.Lazy.Builder.toLazyText
|
||||||
|
$ buildDerivation sd drv
|
||||||
|
)
|
||||||
|
=== pure drv
|
||||||
|
|
||||||
|
@ -561,16 +561,16 @@ sampleLinkToDirectoryBaseline = B64.decodeLenient $ BSL.concat
|
|||||||
getBigFileSize :: IO Int64
|
getBigFileSize :: IO Int64
|
||||||
getBigFileSize = fromMaybe 5000000 . readMaybe <$> (getEnv "HNIX_BIG_FILE_SIZE" <|> pure "")
|
getBigFileSize = fromMaybe 5000000 . readMaybe <$> (getEnv "HNIX_BIG_FILE_SIZE" <|> pure "")
|
||||||
|
|
||||||
|
-- TODO: implement and use in generator #232
|
||||||
-- | 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
|
||||||
mkLink = undefined -- TODO
|
_mkLink = undefined
|
||||||
|
|
||||||
mkBigFile :: FilePath -> IO ()
|
mkBigFile :: FilePath -> IO ()
|
||||||
mkBigFile path = do
|
mkBigFile path = do
|
||||||
|
@ -1 +0,0 @@
|
|||||||
packages: .
|
|
@ -42,6 +42,14 @@ common commons
|
|||||||
, LambdaCase
|
, LambdaCase
|
||||||
, BangPatterns
|
, BangPatterns
|
||||||
, ViewPatterns
|
, ViewPatterns
|
||||||
|
build-depends:
|
||||||
|
base >=4.12 && <5
|
||||||
|
default-language: Haskell2010
|
||||||
|
|
||||||
|
common tests
|
||||||
|
import: commons
|
||||||
|
build-tool-depends:
|
||||||
|
tasty-discover:tasty-discover
|
||||||
|
|
||||||
flag io-testsuite
|
flag io-testsuite
|
||||||
default:
|
default:
|
||||||
@ -52,7 +60,7 @@ flag io-testsuite
|
|||||||
|
|
||||||
flag build-readme
|
flag build-readme
|
||||||
default:
|
default:
|
||||||
True
|
False
|
||||||
description:
|
description:
|
||||||
Build README.lhs example
|
Build README.lhs example
|
||||||
|
|
||||||
@ -61,21 +69,21 @@ library
|
|||||||
exposed-modules:
|
exposed-modules:
|
||||||
System.Nix.Store.Remote
|
System.Nix.Store.Remote
|
||||||
, System.Nix.Store.Remote.Binary
|
, System.Nix.Store.Remote.Binary
|
||||||
, System.Nix.Store.Remote.Builders
|
, System.Nix.Store.Remote.Serialize
|
||||||
|
, System.Nix.Store.Remote.Serialize.Prim
|
||||||
, System.Nix.Store.Remote.Logger
|
, System.Nix.Store.Remote.Logger
|
||||||
, System.Nix.Store.Remote.Parsers
|
|
||||||
, System.Nix.Store.Remote.Protocol
|
, System.Nix.Store.Remote.Protocol
|
||||||
, System.Nix.Store.Remote.Types
|
, System.Nix.Store.Remote.Types
|
||||||
, System.Nix.Store.Remote.Util
|
, System.Nix.Store.Remote.Util
|
||||||
|
|
||||||
build-depends:
|
build-depends:
|
||||||
base >=4.12 && <5
|
|
||||||
, relude >= 1.0
|
|
||||||
, attoparsec
|
, attoparsec
|
||||||
, binary
|
, binary
|
||||||
, bytestring
|
, bytestring
|
||||||
|
, cereal
|
||||||
, containers
|
, containers
|
||||||
, cryptonite
|
, cryptonite
|
||||||
|
, data-default-class
|
||||||
, text
|
, text
|
||||||
, time
|
, time
|
||||||
, network
|
, network
|
||||||
@ -84,15 +92,13 @@ library
|
|||||||
, unordered-containers
|
, unordered-containers
|
||||||
, hnix-store-core >= 0.7 && <0.8
|
, hnix-store-core >= 0.7 && <0.8
|
||||||
, transformers
|
, transformers
|
||||||
mixins:
|
, vector
|
||||||
base hiding (Prelude)
|
|
||||||
, relude (Relude as Prelude)
|
|
||||||
, relude
|
|
||||||
hs-source-dirs: src
|
hs-source-dirs: src
|
||||||
default-language: Haskell2010
|
|
||||||
ghc-options: -Wall
|
ghc-options: -Wall
|
||||||
|
|
||||||
executable remote-readme
|
executable remote-readme
|
||||||
|
if !flag(build-readme)
|
||||||
|
buildable: False
|
||||||
build-depends:
|
build-depends:
|
||||||
base >=4.12 && <5
|
base >=4.12 && <5
|
||||||
, hnix-store-remote
|
, hnix-store-remote
|
||||||
@ -102,45 +108,58 @@ executable remote-readme
|
|||||||
main-is: README.lhs
|
main-is: README.lhs
|
||||||
ghc-options: -pgmL markdown-unlit -Wall
|
ghc-options: -pgmL markdown-unlit -Wall
|
||||||
|
|
||||||
|
test-suite remote
|
||||||
|
import: tests
|
||||||
|
type: exitcode-stdio-1.0
|
||||||
|
main-is: Driver.hs
|
||||||
|
hs-source-dirs: tests
|
||||||
|
ghc-options: -Wall
|
||||||
|
other-modules:
|
||||||
|
SerializeSpec
|
||||||
|
build-depends:
|
||||||
|
hnix-store-core
|
||||||
|
, hnix-store-remote
|
||||||
|
, nix-derivation
|
||||||
|
, bytestring
|
||||||
|
, cereal
|
||||||
|
, text
|
||||||
|
, time
|
||||||
|
, hspec
|
||||||
|
, tasty
|
||||||
|
, tasty-hspec
|
||||||
|
, tasty-quickcheck
|
||||||
|
, quickcheck-instances
|
||||||
|
, unordered-containers
|
||||||
|
|
||||||
test-suite remote-io
|
test-suite remote-io
|
||||||
import: commons
|
import: tests
|
||||||
|
|
||||||
if !flag(io-testsuite) || os(darwin)
|
if !flag(io-testsuite) || os(darwin)
|
||||||
buildable: False
|
buildable: False
|
||||||
|
|
||||||
type: exitcode-stdio-1.0
|
type: exitcode-stdio-1.0
|
||||||
main-is: Driver.hs
|
main-is: Driver.hs
|
||||||
|
hs-source-dirs: tests-io
|
||||||
-- See https://github.com/redneb/hs-linux-namespaces/issues/3
|
-- See https://github.com/redneb/hs-linux-namespaces/issues/3
|
||||||
ghc-options: -rtsopts -fprof-auto "-with-rtsopts -V0"
|
ghc-options: -rtsopts -fprof-auto "-with-rtsopts -V0"
|
||||||
other-modules:
|
other-modules:
|
||||||
NixDaemon
|
NixDaemon
|
||||||
, Spec
|
, Spec
|
||||||
hs-source-dirs: tests-io
|
|
||||||
build-depends:
|
build-depends:
|
||||||
base
|
hnix-store-core
|
||||||
, bytestring
|
|
||||||
, relude
|
|
||||||
, hnix-store-core >= 0.3
|
|
||||||
, hnix-store-remote
|
, hnix-store-remote
|
||||||
|
, bytestring
|
||||||
, containers
|
, containers
|
||||||
, cryptonite
|
, cryptonite
|
||||||
, directory
|
, directory
|
||||||
, process
|
, process
|
||||||
, filepath
|
, filepath
|
||||||
, hspec-expectations-lifted
|
, hspec-expectations-lifted
|
||||||
, quickcheck-text
|
, text
|
||||||
, tasty
|
, tasty
|
||||||
, hspec
|
, hspec
|
||||||
, tasty-hspec
|
, tasty-hspec
|
||||||
, tasty-quickcheck
|
|
||||||
, linux-namespaces
|
, linux-namespaces
|
||||||
, temporary
|
, temporary
|
||||||
, unix
|
, unix
|
||||||
, unordered-containers
|
, unordered-containers
|
||||||
build-tool-depends:
|
|
||||||
tasty-discover:tasty-discover
|
|
||||||
mixins:
|
|
||||||
base hiding (Prelude)
|
|
||||||
, relude (Relude as Prelude)
|
|
||||||
, relude
|
|
||||||
default-language: Haskell2010
|
|
||||||
|
@ -35,7 +35,13 @@ module System.Nix.Store.Remote
|
|||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
|
||||||
import Prelude hiding ( putText )
|
import Data.HashSet (HashSet)
|
||||||
|
import Data.Map (Map)
|
||||||
|
import Data.Text (Text)
|
||||||
|
import qualified Control.Monad
|
||||||
|
import qualified Data.Attoparsec.ByteString.Char8
|
||||||
|
import qualified Data.Text.Encoding
|
||||||
|
--
|
||||||
import qualified Data.ByteString.Lazy as BSL
|
import qualified Data.ByteString.Lazy as BSL
|
||||||
|
|
||||||
import Nix.Derivation ( Derivation )
|
import Nix.Derivation ( Derivation )
|
||||||
@ -50,18 +56,17 @@ import System.Nix.Hash ( NamedAlgo(..)
|
|||||||
import System.Nix.StorePath ( StorePath
|
import System.Nix.StorePath ( StorePath
|
||||||
, StorePathName
|
, StorePathName
|
||||||
, StorePathHashPart
|
, StorePathHashPart
|
||||||
|
, InvalidPathError
|
||||||
)
|
)
|
||||||
import System.Nix.StorePathMetadata ( StorePathMetadata(..)
|
import System.Nix.StorePathMetadata ( Metadata(..)
|
||||||
, StorePathTrust(..)
|
, StorePathTrust(..)
|
||||||
)
|
)
|
||||||
import System.Nix.Internal.Base ( encodeWith )
|
|
||||||
|
|
||||||
import qualified Data.Binary.Put
|
import qualified Data.Binary.Put
|
||||||
import qualified Data.Map.Strict
|
import qualified Data.Map.Strict
|
||||||
import qualified Data.Set
|
import qualified Data.Set
|
||||||
|
|
||||||
import qualified System.Nix.StorePath
|
import qualified System.Nix.StorePath
|
||||||
import qualified System.Nix.Store.Remote.Parsers
|
|
||||||
|
|
||||||
import System.Nix.Store.Remote.Binary
|
import System.Nix.Store.Remote.Binary
|
||||||
import System.Nix.Store.Remote.Types
|
import System.Nix.Store.Remote.Types
|
||||||
@ -81,11 +86,11 @@ addToStore
|
|||||||
-> RepairFlag -- ^ Only used by local store backend
|
-> RepairFlag -- ^ Only used by local store backend
|
||||||
-> MonadStore StorePath
|
-> MonadStore StorePath
|
||||||
addToStore name source recursive repair = do
|
addToStore name source recursive repair = do
|
||||||
when (unRepairFlag repair)
|
Control.Monad.when (unRepairFlag repair)
|
||||||
$ error "repairing is not supported when building through the Nix daemon"
|
$ error "repairing is not supported when building through the Nix daemon"
|
||||||
|
|
||||||
runOpArgsIO AddToStore $ \yield -> do
|
runOpArgsIO AddToStore $ \yield -> do
|
||||||
yield $ toStrict $ Data.Binary.Put.runPut $ do
|
yield $ BSL.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" && (unRecursive recursive)
|
putBool $ not $ System.Nix.Hash.algoName @a == "sha256" && (unRecursive recursive)
|
||||||
putBool (unRecursive recursive)
|
putBool (unRecursive recursive)
|
||||||
@ -104,7 +109,7 @@ addTextToStore
|
|||||||
-> RepairFlag -- ^ Repair flag, must be `False` in case of remote backend
|
-> RepairFlag -- ^ Repair flag, must be `False` in case of remote backend
|
||||||
-> MonadStore StorePath
|
-> MonadStore StorePath
|
||||||
addTextToStore name text references' repair = do
|
addTextToStore name text references' repair = do
|
||||||
when (unRepairFlag repair)
|
Control.Monad.when (unRepairFlag repair)
|
||||||
$ error "repairing is not supported when building through the Nix daemon"
|
$ error "repairing is not supported when building through the Nix daemon"
|
||||||
|
|
||||||
storeDir <- getStoreDir
|
storeDir <- getStoreDir
|
||||||
@ -117,14 +122,14 @@ addTextToStore name text references' repair = do
|
|||||||
addSignatures :: StorePath -> [BSL.ByteString] -> MonadStore ()
|
addSignatures :: StorePath -> [BSL.ByteString] -> MonadStore ()
|
||||||
addSignatures p signatures = do
|
addSignatures p signatures = do
|
||||||
storeDir <- getStoreDir
|
storeDir <- getStoreDir
|
||||||
void $ simpleOpArgs AddSignatures $ do
|
Control.Monad.void $ simpleOpArgs AddSignatures $ do
|
||||||
putPath storeDir p
|
putPath storeDir p
|
||||||
putByteStrings signatures
|
putByteStrings signatures
|
||||||
|
|
||||||
addIndirectRoot :: StorePath -> MonadStore ()
|
addIndirectRoot :: StorePath -> MonadStore ()
|
||||||
addIndirectRoot pn = do
|
addIndirectRoot pn = do
|
||||||
storeDir <- getStoreDir
|
storeDir <- getStoreDir
|
||||||
void $ simpleOpArgs AddIndirectRoot $ putPath storeDir pn
|
Control.Monad.void $ simpleOpArgs AddIndirectRoot $ putPath storeDir pn
|
||||||
|
|
||||||
-- | Add temporary garbage collector root.
|
-- | Add temporary garbage collector root.
|
||||||
--
|
--
|
||||||
@ -132,7 +137,7 @@ addIndirectRoot pn = do
|
|||||||
addTempRoot :: StorePath -> MonadStore ()
|
addTempRoot :: StorePath -> MonadStore ()
|
||||||
addTempRoot pn = do
|
addTempRoot pn = do
|
||||||
storeDir <- getStoreDir
|
storeDir <- getStoreDir
|
||||||
void $ simpleOpArgs AddTempRoot $ putPath storeDir pn
|
Control.Monad.void $ simpleOpArgs AddTempRoot $ putPath storeDir pn
|
||||||
|
|
||||||
-- | Build paths if they are an actual derivations.
|
-- | Build paths if they are an actual derivations.
|
||||||
--
|
--
|
||||||
@ -140,7 +145,7 @@ addTempRoot pn = do
|
|||||||
buildPaths :: HashSet StorePath -> BuildMode -> MonadStore ()
|
buildPaths :: HashSet StorePath -> BuildMode -> MonadStore ()
|
||||||
buildPaths ps bm = do
|
buildPaths ps bm = do
|
||||||
storeDir <- getStoreDir
|
storeDir <- getStoreDir
|
||||||
void $ simpleOpArgs BuildPaths $ do
|
Control.Monad.void $ simpleOpArgs BuildPaths $ do
|
||||||
putPaths storeDir ps
|
putPaths storeDir ps
|
||||||
putInt $ fromEnum bm
|
putInt $ fromEnum bm
|
||||||
|
|
||||||
@ -166,7 +171,7 @@ buildDerivation p drv buildMode = do
|
|||||||
ensurePath :: StorePath -> MonadStore ()
|
ensurePath :: StorePath -> MonadStore ()
|
||||||
ensurePath pn = do
|
ensurePath pn = do
|
||||||
storeDir <- getStoreDir
|
storeDir <- getStoreDir
|
||||||
void $ simpleOpArgs EnsurePath $ putPath storeDir pn
|
Control.Monad.void $ simpleOpArgs EnsurePath $ putPath storeDir pn
|
||||||
|
|
||||||
-- | Find garbage collector roots.
|
-- | Find garbage collector roots.
|
||||||
findRoots :: MonadStore (Map BSL.ByteString StorePath)
|
findRoots :: MonadStore (Map BSL.ByteString StorePath)
|
||||||
@ -177,18 +182,18 @@ findRoots = do
|
|||||||
getSocketIncremental
|
getSocketIncremental
|
||||||
$ getMany
|
$ getMany
|
||||||
$ (,)
|
$ (,)
|
||||||
<$> (fromStrict <$> getByteStringLen)
|
<$> (BSL.fromStrict <$> getByteStringLen)
|
||||||
<*> getPath sd
|
<*> getPath sd
|
||||||
|
|
||||||
r <- catRights res
|
r <- catRights res
|
||||||
pure $ Data.Map.Strict.fromList r
|
pure $ Data.Map.Strict.fromList r
|
||||||
where
|
where
|
||||||
catRights :: [(a, Either String b)] -> MonadStore [(a, b)]
|
catRights :: [(a, Either InvalidPathError b)] -> MonadStore [(a, b)]
|
||||||
catRights = mapM ex
|
catRights = mapM ex
|
||||||
|
|
||||||
ex :: (a, Either [Char] b) -> MonadStore (a, b)
|
ex :: (a, Either InvalidPathError 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: " <> fromString e
|
ex (_x, Left e ) = error $ "Unable to decode root: " <> show e
|
||||||
|
|
||||||
isValidPathUncached :: StorePath -> MonadStore Bool
|
isValidPathUncached :: StorePath -> MonadStore Bool
|
||||||
isValidPathUncached p = do
|
isValidPathUncached p = do
|
||||||
@ -218,24 +223,24 @@ querySubstitutablePaths ps = do
|
|||||||
runOpArgs QuerySubstitutablePaths $ putPaths storeDir ps
|
runOpArgs QuerySubstitutablePaths $ putPaths storeDir ps
|
||||||
sockGetPaths
|
sockGetPaths
|
||||||
|
|
||||||
queryPathInfoUncached :: StorePath -> MonadStore StorePathMetadata
|
queryPathInfoUncached :: StorePath -> MonadStore (Metadata StorePath)
|
||||||
queryPathInfoUncached path = do
|
queryPathInfoUncached path = do
|
||||||
storeDir <- getStoreDir
|
storeDir <- getStoreDir
|
||||||
runOpArgs QueryPathInfo $ do
|
runOpArgs QueryPathInfo $ do
|
||||||
putPath storeDir path
|
putPath storeDir path
|
||||||
|
|
||||||
valid <- sockGetBool
|
valid <- sockGetBool
|
||||||
unless valid $ error "Path is not valid"
|
Control.Monad.unless valid $ error "Path is not valid"
|
||||||
|
|
||||||
deriverPath <- sockGetPathMay
|
deriverPath <- sockGetPathMay
|
||||||
|
|
||||||
narHashText <- decodeUtf8 <$> sockGetStr
|
narHashText <- Data.Text.Encoding.decodeUtf8 <$> sockGetStr
|
||||||
let
|
let
|
||||||
narHash =
|
narHash =
|
||||||
case
|
case
|
||||||
decodeDigestWith @SHA256 NixBase32 narHashText
|
decodeDigestWith @SHA256 NixBase32 narHashText
|
||||||
of
|
of
|
||||||
Left e -> error $ fromString e
|
Left e -> error e
|
||||||
Right x -> SomeDigest x
|
Right x -> SomeDigest x
|
||||||
|
|
||||||
references <- sockGetPaths
|
references <- sockGetPaths
|
||||||
@ -252,14 +257,16 @@ queryPathInfoUncached path = do
|
|||||||
|
|
||||||
contentAddressableAddress =
|
contentAddressableAddress =
|
||||||
case
|
case
|
||||||
System.Nix.Store.Remote.Parsers.parseContentAddressableAddress caString
|
Data.Attoparsec.ByteString.Char8.parseOnly
|
||||||
|
System.Nix.StorePath.contentAddressableAddressParser
|
||||||
|
caString
|
||||||
of
|
of
|
||||||
Left e -> error $ fromString e
|
Left e -> error e
|
||||||
Right x -> Just x
|
Right x -> Just x
|
||||||
|
|
||||||
trust = if ultimate then BuiltLocally else BuiltElsewhere
|
trust = if ultimate then BuiltLocally else BuiltElsewhere
|
||||||
|
|
||||||
pure $ StorePathMetadata{..}
|
pure $ Metadata{..}
|
||||||
|
|
||||||
queryReferrers :: StorePath -> MonadStore (HashSet StorePath)
|
queryReferrers :: StorePath -> MonadStore (HashSet StorePath)
|
||||||
queryReferrers p = do
|
queryReferrers p = do
|
||||||
@ -288,11 +295,8 @@ queryDerivationOutputNames p = do
|
|||||||
queryPathFromHashPart :: StorePathHashPart -> MonadStore StorePath
|
queryPathFromHashPart :: StorePathHashPart -> MonadStore StorePath
|
||||||
queryPathFromHashPart storePathHash = do
|
queryPathFromHashPart storePathHash = do
|
||||||
runOpArgs QueryPathFromHashPart
|
runOpArgs QueryPathFromHashPart
|
||||||
$ putByteStringLen
|
$ putText
|
||||||
$ encodeUtf8
|
$ System.Nix.StorePath.storePathHashPartToText storePathHash
|
||||||
$ encodeWith NixBase32
|
|
||||||
$ System.Nix.StorePath.unStorePathHashPart
|
|
||||||
storePathHash
|
|
||||||
sockGetPath
|
sockGetPath
|
||||||
|
|
||||||
queryMissing
|
queryMissing
|
||||||
@ -316,10 +320,10 @@ queryMissing ps = do
|
|||||||
pure (willBuild, willSubstitute, unknown, downloadSize', narSize')
|
pure (willBuild, willSubstitute, unknown, downloadSize', narSize')
|
||||||
|
|
||||||
optimiseStore :: MonadStore ()
|
optimiseStore :: MonadStore ()
|
||||||
optimiseStore = void $ simpleOp OptimiseStore
|
optimiseStore = Control.Monad.void $ simpleOp OptimiseStore
|
||||||
|
|
||||||
syncWithGC :: MonadStore ()
|
syncWithGC :: MonadStore ()
|
||||||
syncWithGC = void $ simpleOp SyncWithGC
|
syncWithGC = Control.Monad.void $ simpleOp SyncWithGC
|
||||||
|
|
||||||
-- returns True on errors
|
-- returns True on errors
|
||||||
verifyStore :: CheckFlag -> RepairFlag -> MonadStore Bool
|
verifyStore :: CheckFlag -> RepairFlag -> MonadStore Bool
|
||||||
|
@ -4,6 +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.ByteString (ByteString)
|
||||||
import Data.Binary.Get
|
import Data.Binary.Get
|
||||||
import Data.Binary.Put
|
import Data.Binary.Put
|
||||||
import qualified Data.ByteString.Lazy as BSL
|
import qualified Data.ByteString.Lazy as BSL
|
||||||
@ -45,7 +47,7 @@ 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 $ toStrict st
|
pure $ BSL.toStrict st
|
||||||
where unpad x = replicateM x getWord8
|
where unpad x = replicateM x getWord8
|
||||||
|
|
||||||
getByteStrings :: Get [ByteString]
|
getByteStrings :: Get [ByteString]
|
||||||
|
@ -1,36 +0,0 @@
|
|||||||
{-# language AllowAmbiguousTypes #-}
|
|
||||||
{-# language ScopedTypeVariables #-}
|
|
||||||
{-# language RankNTypes #-}
|
|
||||||
|
|
||||||
module System.Nix.Store.Remote.Builders
|
|
||||||
( buildContentAddressableAddress
|
|
||||||
)
|
|
||||||
where
|
|
||||||
|
|
||||||
import qualified Data.Text.Lazy as TL
|
|
||||||
import Crypto.Hash ( Digest )
|
|
||||||
import System.Nix.StorePath ( ContentAddressableAddress(..)
|
|
||||||
)
|
|
||||||
|
|
||||||
import Data.Text.Lazy.Builder ( Builder )
|
|
||||||
import qualified Data.Text.Lazy.Builder as TL
|
|
||||||
|
|
||||||
import System.Nix.Hash
|
|
||||||
|
|
||||||
-- | Marshall `ContentAddressableAddress` to `Text`
|
|
||||||
-- in form suitable for remote protocol usage.
|
|
||||||
buildContentAddressableAddress :: ContentAddressableAddress -> TL.Text
|
|
||||||
buildContentAddressableAddress =
|
|
||||||
TL.toLazyText . contentAddressableAddressBuilder
|
|
||||||
|
|
||||||
contentAddressableAddressBuilder :: ContentAddressableAddress -> Builder
|
|
||||||
contentAddressableAddressBuilder (Text digest) =
|
|
||||||
"text:" <> digestBuilder digest
|
|
||||||
contentAddressableAddressBuilder (Fixed _narHashMode (SomeDigest (digest :: Digest hashAlgo))) =
|
|
||||||
"fixed:"
|
|
||||||
<> TL.fromText (System.Nix.Hash.algoName @hashAlgo)
|
|
||||||
<> digestBuilder digest
|
|
||||||
|
|
||||||
digestBuilder :: Digest a -> Builder
|
|
||||||
digestBuilder =
|
|
||||||
TL.fromText . encodeDigestWith NixBase32
|
|
@ -8,8 +8,10 @@ module System.Nix.Store.Remote.Logger
|
|||||||
where
|
where
|
||||||
|
|
||||||
|
|
||||||
import Prelude hiding ( Last )
|
|
||||||
import Control.Monad.Except ( throwError )
|
import Control.Monad.Except ( throwError )
|
||||||
|
import Control.Monad.IO.Class (liftIO)
|
||||||
|
import Control.Monad.Reader (asks)
|
||||||
|
import Control.Monad.State.Strict (get)
|
||||||
import Data.Binary.Get
|
import Data.Binary.Get
|
||||||
|
|
||||||
import Network.Socket.ByteString ( recv )
|
import Network.Socket.ByteString ( recv )
|
||||||
@ -19,6 +21,8 @@ import System.Nix.Store.Remote.Types
|
|||||||
import System.Nix.Store.Remote.Util
|
import System.Nix.Store.Remote.Util
|
||||||
|
|
||||||
|
|
||||||
|
import qualified Control.Monad
|
||||||
|
|
||||||
controlParser :: Get Logger
|
controlParser :: Get Logger
|
||||||
controlParser = do
|
controlParser = do
|
||||||
ctrl <- getInt
|
ctrl <- getInt
|
||||||
@ -70,12 +74,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 $ fromString msg
|
go (Fail _leftover _consumed msg) = error msg
|
||||||
|
|
||||||
getFields :: Get [Field]
|
getFields :: Get [Field]
|
||||||
getFields = do
|
getFields = do
|
||||||
cnt <- getInt
|
cnt <- getInt
|
||||||
replicateM cnt getField
|
Control.Monad.replicateM cnt getField
|
||||||
|
|
||||||
getField :: Get Field
|
getField :: Get Field
|
||||||
getField = do
|
getField = do
|
||||||
|
@ -1,51 +0,0 @@
|
|||||||
{-# language AllowAmbiguousTypes #-}
|
|
||||||
{-# language ScopedTypeVariables #-}
|
|
||||||
{-# language RankNTypes #-}
|
|
||||||
{-# language DataKinds #-}
|
|
||||||
|
|
||||||
module System.Nix.Store.Remote.Parsers
|
|
||||||
( parseContentAddressableAddress
|
|
||||||
)
|
|
||||||
where
|
|
||||||
|
|
||||||
import Data.Attoparsec.ByteString.Char8
|
|
||||||
import System.Nix.Hash
|
|
||||||
import System.Nix.StorePath ( ContentAddressableAddress(..)
|
|
||||||
, NarHashMode(..)
|
|
||||||
)
|
|
||||||
import Crypto.Hash ( SHA256 )
|
|
||||||
|
|
||||||
-- | Parse `ContentAddressableAddress` from `ByteString`
|
|
||||||
parseContentAddressableAddress
|
|
||||||
:: ByteString -> Either String ContentAddressableAddress
|
|
||||||
parseContentAddressableAddress =
|
|
||||||
Data.Attoparsec.ByteString.Char8.parseOnly contentAddressableAddressParser
|
|
||||||
|
|
||||||
-- | Parser for content addressable field
|
|
||||||
contentAddressableAddressParser :: Parser ContentAddressableAddress
|
|
||||||
contentAddressableAddressParser = caText <|> caFixed
|
|
||||||
|
|
||||||
-- | Parser for @text:sha256:<h>@
|
|
||||||
caText :: Parser ContentAddressableAddress
|
|
||||||
caText = do
|
|
||||||
_ <- "text:sha256:"
|
|
||||||
digest <- decodeDigestWith @SHA256 NixBase32 <$> parseHash
|
|
||||||
either fail pure $ Text <$> digest
|
|
||||||
|
|
||||||
-- | Parser for @fixed:<r?>:<ht>:<h>@
|
|
||||||
caFixed :: Parser ContentAddressableAddress
|
|
||||||
caFixed = do
|
|
||||||
_ <- "fixed:"
|
|
||||||
narHashMode <- (Recursive <$ "r:") <|> (RegularFile <$ "")
|
|
||||||
digest <- parseTypedDigest
|
|
||||||
either fail pure $ Fixed narHashMode <$> digest
|
|
||||||
|
|
||||||
parseTypedDigest :: Parser (Either String SomeNamedDigest)
|
|
||||||
parseTypedDigest = mkNamedDigest <$> parseHashType <*> parseHash
|
|
||||||
|
|
||||||
parseHashType :: Parser Text
|
|
||||||
parseHashType =
|
|
||||||
decodeUtf8 <$> ("sha256" <|> "sha512" <|> "sha1" <|> "md5") <* (":" <|> "-")
|
|
||||||
|
|
||||||
parseHash :: Parser Text
|
|
||||||
parseHash = decodeUtf8 <$> takeWhile1 (/= ':')
|
|
@ -16,15 +16,19 @@ module System.Nix.Store.Remote.Protocol
|
|||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
|
||||||
import qualified Relude.Unsafe as Unsafe
|
import qualified Control.Monad
|
||||||
|
|
||||||
import Control.Exception ( bracket )
|
import Control.Exception ( bracket )
|
||||||
import Control.Monad.Except
|
import Control.Monad.Except
|
||||||
|
import Control.Monad.Reader (asks, runReaderT)
|
||||||
|
import Control.Monad.State.Strict
|
||||||
|
|
||||||
|
import Data.Default.Class (Default(def))
|
||||||
|
import qualified Data.Bool
|
||||||
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 as S
|
import qualified Network.Socket as S
|
||||||
@ -123,28 +127,28 @@ opNum QueryMissing = 40
|
|||||||
|
|
||||||
|
|
||||||
simpleOp :: WorkerOp -> MonadStore Bool
|
simpleOp :: WorkerOp -> MonadStore Bool
|
||||||
simpleOp op = simpleOpArgs op pass
|
simpleOp op = simpleOpArgs op $ pure ()
|
||||||
|
|
||||||
simpleOpArgs :: WorkerOp -> Put -> MonadStore Bool
|
simpleOpArgs :: WorkerOp -> Put -> MonadStore Bool
|
||||||
simpleOpArgs op args = do
|
simpleOpArgs op args = do
|
||||||
runOpArgs op args
|
runOpArgs op args
|
||||||
err <- gotError
|
err <- gotError
|
||||||
bool
|
Data.Bool.bool
|
||||||
sockGetBool
|
sockGetBool
|
||||||
(do
|
(do
|
||||||
Error _num msg <- Unsafe.head <$> getError
|
Error _num msg <- 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 pass
|
runOp op = runOpArgs op $ pure ()
|
||||||
|
|
||||||
runOpArgs :: WorkerOp -> Put -> MonadStore ()
|
runOpArgs :: WorkerOp -> Put -> MonadStore ()
|
||||||
runOpArgs op args =
|
runOpArgs op args =
|
||||||
runOpArgsIO
|
runOpArgsIO
|
||||||
op
|
op
|
||||||
(\encode -> encode $ toStrict $ runPut args)
|
(\encode -> encode $ Data.ByteString.Lazy.toStrict $ runPut args)
|
||||||
|
|
||||||
runOpArgsIO
|
runOpArgsIO
|
||||||
:: WorkerOp
|
:: WorkerOp
|
||||||
@ -160,12 +164,12 @@ runOpArgsIO op encoder = do
|
|||||||
out <- processOutput
|
out <- processOutput
|
||||||
modify (\(a, b) -> (a, b <> out))
|
modify (\(a, b) -> (a, b <> out))
|
||||||
err <- gotError
|
err <- gotError
|
||||||
when err $ do
|
Control.Monad.when err $ do
|
||||||
Error _num msg <- Unsafe.head <$> getError
|
Error _num msg <- 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])
|
||||||
runStore = runStoreOpts defaultSockPath $ StoreDir "/nix/store"
|
runStore = runStoreOpts defaultSockPath def
|
||||||
|
|
||||||
runStoreOpts
|
runStoreOpts
|
||||||
:: FilePath -> StoreDir -> MonadStore a -> IO (Either String a, [Logger])
|
:: FilePath -> StoreDir -> MonadStore a -> IO (Either String a, [Logger])
|
||||||
@ -198,11 +202,11 @@ runStoreOpts' sockFamily sockAddr storeRootDir code =
|
|||||||
vermagic <- liftIO $ recv soc 16
|
vermagic <- liftIO $ recv soc 16
|
||||||
let
|
let
|
||||||
(magic2, _daemonProtoVersion) =
|
(magic2, _daemonProtoVersion) =
|
||||||
flip runGet (fromStrict vermagic)
|
flip runGet (Data.ByteString.Lazy.fromStrict vermagic)
|
||||||
$ (,)
|
$ (,)
|
||||||
<$> (getInt :: Get Int)
|
<$> (getInt :: Get Int)
|
||||||
<*> (getInt :: Get Int)
|
<*> (getInt :: Get Int)
|
||||||
unless (magic2 == workerMagic2) $ error "Worker magic 2 mismatch"
|
Control.Monad.unless (magic2 == workerMagic2) $ error "Worker magic 2 mismatch"
|
||||||
|
|
||||||
sockPut $ putInt protoVersion -- clientVersion
|
sockPut $ putInt protoVersion -- clientVersion
|
||||||
sockPut $ putInt (0 :: Int) -- affinity
|
sockPut $ putInt (0 :: Int) -- affinity
|
||||||
|
106
hnix-store-remote/src/System/Nix/Store/Remote/Serialize.hs
Normal file
106
hnix-store-remote/src/System/Nix/Store/Remote/Serialize.hs
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
{-# OPTIONS_GHC -Wno-orphans #-}
|
||||||
|
{-|
|
||||||
|
Description : Serialize instances for complex types
|
||||||
|
Maintainer : srk <srk@48.io>
|
||||||
|
|-}
|
||||||
|
module System.Nix.Store.Remote.Serialize where
|
||||||
|
|
||||||
|
import Data.Serialize (Serialize(..))
|
||||||
|
import Data.Serialize.Get (Get)
|
||||||
|
import Data.Serialize.Put (Putter)
|
||||||
|
import Data.Text (Text)
|
||||||
|
|
||||||
|
import qualified Data.Bool
|
||||||
|
import qualified Data.Map
|
||||||
|
import qualified Data.Set
|
||||||
|
import qualified Data.Text
|
||||||
|
import qualified Data.Vector
|
||||||
|
|
||||||
|
import Nix.Derivation (Derivation(..), DerivationOutput(..))
|
||||||
|
import System.Nix.Build (BuildMode(..), BuildStatus(..), BuildResult(..))
|
||||||
|
import System.Nix.StorePath (StoreDir, StorePath)
|
||||||
|
import System.Nix.Store.Remote.Serialize.Prim
|
||||||
|
|
||||||
|
instance Serialize Text where
|
||||||
|
get = getText
|
||||||
|
put = putText
|
||||||
|
|
||||||
|
instance Serialize BuildMode where
|
||||||
|
get = getEnum
|
||||||
|
put = putEnum
|
||||||
|
|
||||||
|
instance Serialize BuildStatus where
|
||||||
|
get = getEnum
|
||||||
|
put = putEnum
|
||||||
|
|
||||||
|
instance Serialize BuildResult where
|
||||||
|
get = do
|
||||||
|
status <- get
|
||||||
|
errorMessage <-
|
||||||
|
(\em -> Data.Bool.bool (Just em) Nothing (Data.Text.null em))
|
||||||
|
<$> get
|
||||||
|
timesBuilt <- getInt
|
||||||
|
isNonDeterministic <- getBool
|
||||||
|
startTime <- getTime
|
||||||
|
stopTime <- getTime
|
||||||
|
pure $ BuildResult{..}
|
||||||
|
|
||||||
|
put BuildResult{..} = do
|
||||||
|
put status
|
||||||
|
case errorMessage of
|
||||||
|
Just err -> putText err
|
||||||
|
Nothing -> putText mempty
|
||||||
|
putInt timesBuilt
|
||||||
|
putBool isNonDeterministic
|
||||||
|
putTime startTime
|
||||||
|
putTime stopTime
|
||||||
|
|
||||||
|
getDerivation
|
||||||
|
:: StoreDir
|
||||||
|
-> Get (Derivation StorePath Text)
|
||||||
|
getDerivation storeDir = do
|
||||||
|
outputs <-
|
||||||
|
Data.Map.fromList
|
||||||
|
<$> (getMany $ do
|
||||||
|
outputName <- get
|
||||||
|
path <- getPathOrFail storeDir
|
||||||
|
hashAlgo <- get
|
||||||
|
hash <- get
|
||||||
|
pure (outputName, DerivationOutput{..})
|
||||||
|
)
|
||||||
|
|
||||||
|
-- Our type is Derivation, but in Nix
|
||||||
|
-- the type sent over the wire is BasicDerivation
|
||||||
|
-- which omits inputDrvs
|
||||||
|
inputDrvs <- pure mempty
|
||||||
|
inputSrcs <-
|
||||||
|
Data.Set.fromList
|
||||||
|
<$> getMany (getPathOrFail storeDir)
|
||||||
|
|
||||||
|
platform <- get
|
||||||
|
builder <- get
|
||||||
|
args <-
|
||||||
|
Data.Vector.fromList
|
||||||
|
<$> getMany get
|
||||||
|
|
||||||
|
env <-
|
||||||
|
Data.Map.fromList
|
||||||
|
<$> getMany ((,) <$> get <*> get)
|
||||||
|
pure Derivation{..}
|
||||||
|
|
||||||
|
putDerivation :: StoreDir -> Putter (Derivation StorePath Text)
|
||||||
|
putDerivation storeDir Derivation{..} = do
|
||||||
|
flip putMany (Data.Map.toList outputs)
|
||||||
|
$ \(outputName, DerivationOutput{..}) -> do
|
||||||
|
putText outputName
|
||||||
|
putPath storeDir path
|
||||||
|
putText hashAlgo
|
||||||
|
putText hash
|
||||||
|
|
||||||
|
putMany (putPath storeDir) inputSrcs
|
||||||
|
putText platform
|
||||||
|
putText builder
|
||||||
|
putMany putText args
|
||||||
|
|
||||||
|
flip putMany (Data.Map.toList env)
|
||||||
|
$ \(a1, a2) -> putText a1 *> putText a2
|
188
hnix-store-remote/src/System/Nix/Store/Remote/Serialize/Prim.hs
Normal file
188
hnix-store-remote/src/System/Nix/Store/Remote/Serialize/Prim.hs
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
{-|
|
||||||
|
Description : Nix-like serialization primitives
|
||||||
|
Maintainer : srk <srk@48.io>
|
||||||
|
|-}
|
||||||
|
module System.Nix.Store.Remote.Serialize.Prim where
|
||||||
|
|
||||||
|
import Data.ByteString (ByteString)
|
||||||
|
import Data.Fixed (Uni)
|
||||||
|
import Data.HashSet (HashSet)
|
||||||
|
import Data.Serialize.Get (Get)
|
||||||
|
import Data.Serialize.Put (Putter)
|
||||||
|
import Data.Text (Text)
|
||||||
|
import Data.Time (NominalDiffTime, UTCTime)
|
||||||
|
import System.Nix.StorePath (StoreDir, StorePath, InvalidPathError)
|
||||||
|
|
||||||
|
import qualified Control.Monad
|
||||||
|
import qualified Data.HashSet
|
||||||
|
import qualified Data.Serialize.Get
|
||||||
|
import qualified Data.Serialize.Put
|
||||||
|
import qualified Data.ByteString
|
||||||
|
import qualified Data.Text.Encoding
|
||||||
|
import qualified Data.Time.Clock.POSIX
|
||||||
|
import qualified System.Nix.StorePath
|
||||||
|
|
||||||
|
-- * Int
|
||||||
|
|
||||||
|
-- | Deserialize Nix like integer
|
||||||
|
getInt :: Get Int
|
||||||
|
getInt = fromIntegral <$> Data.Serialize.Get.getWord64le
|
||||||
|
|
||||||
|
-- | Serialize Nix like integer
|
||||||
|
putInt :: Putter Int
|
||||||
|
putInt = Data.Serialize.Put.putWord64le . fromIntegral
|
||||||
|
|
||||||
|
-- * Bool
|
||||||
|
|
||||||
|
-- | Deserialize @Bool@ from integer
|
||||||
|
getBool :: Get Bool
|
||||||
|
getBool = (== 1) <$> (getInt :: Get Int)
|
||||||
|
|
||||||
|
-- | Serialize @Bool@ into integer
|
||||||
|
putBool :: Putter Bool
|
||||||
|
putBool True = putInt (1 :: Int)
|
||||||
|
putBool False = putInt (0 :: Int)
|
||||||
|
|
||||||
|
-- * Enum
|
||||||
|
|
||||||
|
-- | Deserialize @Enum@ to integer
|
||||||
|
getEnum :: Enum a => Get a
|
||||||
|
getEnum = toEnum <$> getInt
|
||||||
|
|
||||||
|
-- | Serialize @Enum@ to integer
|
||||||
|
putEnum :: Enum a => Putter a
|
||||||
|
putEnum = putInt . fromEnum
|
||||||
|
|
||||||
|
-- * UTCTime
|
||||||
|
|
||||||
|
-- | Deserialize @UTCTime@ from integer
|
||||||
|
-- Only 1 second precision.
|
||||||
|
getTime :: Get UTCTime
|
||||||
|
getTime =
|
||||||
|
Data.Time.Clock.POSIX.posixSecondsToUTCTime
|
||||||
|
. seconds
|
||||||
|
<$> getInt
|
||||||
|
where
|
||||||
|
-- fancy (*10^12), from Int to Uni to Pico(seconds)
|
||||||
|
seconds :: Int -> NominalDiffTime
|
||||||
|
seconds n = realToFrac (toEnum n :: Uni)
|
||||||
|
|
||||||
|
-- | Serialize @UTCTime@ to integer
|
||||||
|
-- Only 1 second precision.
|
||||||
|
putTime :: Putter UTCTime
|
||||||
|
putTime =
|
||||||
|
putInt
|
||||||
|
. seconds
|
||||||
|
. Data.Time.Clock.POSIX.utcTimeToPOSIXSeconds
|
||||||
|
where
|
||||||
|
-- fancy (`div`10^12), from Pico to Uni to Int
|
||||||
|
seconds :: NominalDiffTime -> Int
|
||||||
|
seconds = (fromEnum :: Uni -> Int) . realToFrac
|
||||||
|
|
||||||
|
-- * Combinators
|
||||||
|
|
||||||
|
-- | Deserialize a list
|
||||||
|
getMany :: Get a -> Get [a]
|
||||||
|
getMany parser = do
|
||||||
|
count <- getInt
|
||||||
|
Control.Monad.replicateM count parser
|
||||||
|
|
||||||
|
-- | Serialize a list
|
||||||
|
putMany :: Foldable t => Putter a -> Putter (t a)
|
||||||
|
putMany printer xs = do
|
||||||
|
putInt (length xs)
|
||||||
|
mapM_ printer xs
|
||||||
|
|
||||||
|
-- * ByteString
|
||||||
|
|
||||||
|
-- | Deserialize length prefixed string
|
||||||
|
-- into @ByteString@, checking for correct padding
|
||||||
|
getByteString :: Get ByteString
|
||||||
|
getByteString = do
|
||||||
|
len <- getInt
|
||||||
|
st <- Data.Serialize.Get.getByteString len
|
||||||
|
Control.Monad.when (len `mod` 8 /= 0) $ do
|
||||||
|
pads <- unpad $ fromIntegral $ 8 - (len `mod` 8)
|
||||||
|
Control.Monad.unless
|
||||||
|
(all (== 0) pads)
|
||||||
|
$ fail $ "No zeroes" <> show (st, len, pads)
|
||||||
|
pure st
|
||||||
|
where unpad x = Control.Monad.replicateM x Data.Serialize.Get.getWord8
|
||||||
|
|
||||||
|
-- | Serialize @ByteString@ using length
|
||||||
|
-- prefixed string packing with padding to 8 bytes
|
||||||
|
putByteString :: Putter ByteString
|
||||||
|
putByteString x = do
|
||||||
|
putInt len
|
||||||
|
Data.Serialize.Put.putByteString x
|
||||||
|
Control.Monad.when
|
||||||
|
(len `mod` 8 /= 0)
|
||||||
|
$ pad $ 8 - (len `mod` 8)
|
||||||
|
where
|
||||||
|
len :: Int
|
||||||
|
len = fromIntegral $ Data.ByteString.length x
|
||||||
|
pad count = Control.Monad.replicateM_ count (Data.Serialize.Put.putWord8 0)
|
||||||
|
|
||||||
|
-- | Deserialize a list of @ByteString@s
|
||||||
|
getByteStrings :: Get [ByteString]
|
||||||
|
getByteStrings = getMany getByteString
|
||||||
|
|
||||||
|
-- | Serialize a list of @ByteString@s
|
||||||
|
putByteStrings :: Foldable t => Putter (t ByteString)
|
||||||
|
putByteStrings = putMany putByteString
|
||||||
|
|
||||||
|
-- * Text
|
||||||
|
|
||||||
|
-- | Deserialize @Text@
|
||||||
|
getText :: Get Text
|
||||||
|
getText = Data.Text.Encoding.decodeUtf8 <$> getByteString
|
||||||
|
|
||||||
|
-- | Serialize @Text@
|
||||||
|
putText :: Putter Text
|
||||||
|
putText = putByteString . Data.Text.Encoding.encodeUtf8
|
||||||
|
|
||||||
|
-- | Deserialize a list of @Text@s
|
||||||
|
getTexts :: Get [Text]
|
||||||
|
getTexts = fmap Data.Text.Encoding.decodeUtf8 <$> getByteStrings
|
||||||
|
|
||||||
|
-- | Serialize a list of @Text@s
|
||||||
|
putTexts :: (Functor f, Foldable f) => Putter (f Text)
|
||||||
|
putTexts = putByteStrings . fmap Data.Text.Encoding.encodeUtf8
|
||||||
|
|
||||||
|
-- * StorePath
|
||||||
|
|
||||||
|
-- | Deserialize @StorePath@, checking
|
||||||
|
-- that @StoreDir@ matches expected value
|
||||||
|
getPath :: StoreDir -> Get (Either InvalidPathError StorePath)
|
||||||
|
getPath sd =
|
||||||
|
System.Nix.StorePath.parsePath sd <$> getByteString
|
||||||
|
|
||||||
|
-- | Deserialize @StorePath@, checking
|
||||||
|
-- that @StoreDir@ matches expected value
|
||||||
|
getPathOrFail :: StoreDir -> Get StorePath
|
||||||
|
getPathOrFail sd =
|
||||||
|
getPath sd
|
||||||
|
>>= either
|
||||||
|
(fail . show)
|
||||||
|
pure
|
||||||
|
|
||||||
|
-- | Serialize @StorePath@ with its associated @StoreDir@
|
||||||
|
putPath :: StoreDir -> Putter StorePath
|
||||||
|
putPath storeDir =
|
||||||
|
putByteString
|
||||||
|
. System.Nix.StorePath.storePathToRawFilePath storeDir
|
||||||
|
|
||||||
|
-- | Deserialize a @HashSet@ of @StorePath@s
|
||||||
|
getPaths :: StoreDir -> Get (HashSet (Either InvalidPathError StorePath))
|
||||||
|
getPaths sd =
|
||||||
|
Data.HashSet.fromList
|
||||||
|
. fmap (System.Nix.StorePath.parsePath sd)
|
||||||
|
<$> getByteStrings
|
||||||
|
|
||||||
|
-- | Serialize a @HashSet@ of @StorePath@s
|
||||||
|
putPaths :: StoreDir -> Putter (HashSet StorePath)
|
||||||
|
putPaths storeDir =
|
||||||
|
putByteStrings
|
||||||
|
. Data.HashSet.toList
|
||||||
|
. Data.HashSet.map
|
||||||
|
(System.Nix.StorePath.storePathToRawFilePath storeDir)
|
@ -33,13 +33,21 @@ module System.Nix.Store.Remote.Types
|
|||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
|
||||||
import Control.Monad.Trans.State.Strict (mapStateT)
|
import Control.Monad.Except (ExceptT)
|
||||||
import Control.Monad.Trans.Except (mapExceptT)
|
import Control.Monad.Reader (ReaderT, asks)
|
||||||
|
import Control.Monad.State.Strict (StateT, gets, modify)
|
||||||
|
import Data.ByteString (ByteString)
|
||||||
|
import Network.Socket (Socket)
|
||||||
|
|
||||||
import qualified Data.ByteString.Lazy as BSL
|
import qualified Data.ByteString.Lazy as BSL
|
||||||
import Network.Socket ( Socket )
|
|
||||||
|
import Control.Monad.Trans.State.Strict (mapStateT)
|
||||||
|
import Control.Monad.Trans.Except (mapExceptT)
|
||||||
|
import Control.Monad.Trans.Reader (withReaderT)
|
||||||
|
|
||||||
import System.Nix.StorePath ( StoreDir )
|
import System.Nix.StorePath ( StoreDir )
|
||||||
|
|
||||||
|
|
||||||
data StoreConfig = StoreConfig
|
data StoreConfig = StoreConfig
|
||||||
{ storeDir :: StoreDir
|
{ storeDir :: StoreDir
|
||||||
, storeSocket :: Socket
|
, storeSocket :: Socket
|
||||||
|
@ -1,11 +1,18 @@
|
|||||||
{-# 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 (throwError)
|
||||||
import Control.Monad.Except
|
import Control.Monad.Reader (asks)
|
||||||
|
import Control.Monad.IO.Class (MonadIO(..))
|
||||||
|
import Data.ByteString (ByteString)
|
||||||
|
import Data.HashSet (HashSet)
|
||||||
|
import Data.Text (Text)
|
||||||
|
import Data.Either (rights)
|
||||||
|
|
||||||
import Data.Binary.Get
|
import Data.Binary.Get
|
||||||
import Data.Binary.Put
|
import Data.Binary.Put
|
||||||
|
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
|
||||||
@ -34,7 +41,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 $ fromString msg
|
go (Fail _leftover _consumed msg) = error msg
|
||||||
|
|
||||||
getSocketIncremental :: Get a -> MonadStore a
|
getSocketIncremental :: Get a -> MonadStore a
|
||||||
getSocketIncremental = genericIncremental sockGet8
|
getSocketIncremental = genericIncremental sockGet8
|
||||||
@ -47,7 +54,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 $ toStrict $ runPut p
|
liftIO $ sendAll soc $ BSL.toStrict $ runPut p
|
||||||
|
|
||||||
sockGet :: Get a -> MonadStore a
|
sockGet :: Get a -> MonadStore a
|
||||||
sockGet = getSocketIncremental
|
sockGet = getSocketIncremental
|
||||||
@ -69,7 +76,7 @@ sockGetPath = do
|
|||||||
sd <- getStoreDir
|
sd <- getStoreDir
|
||||||
pth <- getSocketIncremental (getPath sd)
|
pth <- getSocketIncremental (getPath sd)
|
||||||
either
|
either
|
||||||
throwError
|
(throwError . show)
|
||||||
pure
|
pure
|
||||||
pth
|
pth
|
||||||
|
|
||||||
@ -89,16 +96,16 @@ sockGetPaths = do
|
|||||||
getSocketIncremental (getPaths sd)
|
getSocketIncremental (getPaths sd)
|
||||||
|
|
||||||
bsToText :: ByteString -> Text
|
bsToText :: ByteString -> Text
|
||||||
bsToText = decodeUtf8
|
bsToText = T.decodeUtf8
|
||||||
|
|
||||||
textToBS :: Text -> ByteString
|
textToBS :: Text -> ByteString
|
||||||
textToBS = encodeUtf8
|
textToBS = T.encodeUtf8
|
||||||
|
|
||||||
bslToText :: BSL.ByteString -> Text
|
bslToText :: BSL.ByteString -> Text
|
||||||
bslToText = toText . TL.decodeUtf8
|
bslToText = TL.toStrict . TL.decodeUtf8
|
||||||
|
|
||||||
textToBSL :: Text -> BSL.ByteString
|
textToBSL :: Text -> BSL.ByteString
|
||||||
textToBSL = TL.encodeUtf8 . toLText
|
textToBSL = TL.encodeUtf8 . TL.fromStrict
|
||||||
|
|
||||||
putText :: Text -> Put
|
putText :: Text -> Put
|
||||||
putText = putByteStringLen . textToBSL
|
putText = putByteStringLen . textToBSL
|
||||||
@ -106,7 +113,7 @@ putText = putByteStringLen . textToBSL
|
|||||||
putTexts :: [Text] -> Put
|
putTexts :: [Text] -> Put
|
||||||
putTexts = putByteStrings . fmap textToBSL
|
putTexts = putByteStrings . fmap textToBSL
|
||||||
|
|
||||||
getPath :: StoreDir -> Get (Either String StorePath)
|
getPath :: StoreDir -> Get (Either InvalidPathError StorePath)
|
||||||
getPath sd = parsePath sd <$> getByteStringLen
|
getPath sd = parsePath sd <$> getByteStringLen
|
||||||
|
|
||||||
getPaths :: StoreDir -> Get (HashSet StorePath)
|
getPaths :: StoreDir -> Get (HashSet StorePath)
|
||||||
@ -114,11 +121,11 @@ getPaths sd =
|
|||||||
Data.HashSet.fromList . rights . fmap (parsePath sd) <$> getByteStrings
|
Data.HashSet.fromList . rights . fmap (parsePath sd) <$> getByteStrings
|
||||||
|
|
||||||
putPath :: StoreDir -> StorePath -> Put
|
putPath :: StoreDir -> StorePath -> Put
|
||||||
putPath storeDir = putByteStringLen . fromStrict . storePathToRawFilePath storeDir
|
putPath storeDir = putByteStringLen . BSL.fromStrict . storePathToRawFilePath storeDir
|
||||||
|
|
||||||
putPaths :: StoreDir -> HashSet StorePath -> Put
|
putPaths :: StoreDir -> HashSet StorePath -> Put
|
||||||
putPaths storeDir = putByteStrings . Data.HashSet.toList . Data.HashSet.map
|
putPaths storeDir = putByteStrings . Data.HashSet.toList . Data.HashSet.map
|
||||||
(fromStrict . storePathToRawFilePath storeDir)
|
(BSL.fromStrict . storePathToRawFilePath storeDir)
|
||||||
|
|
||||||
putBool :: Bool -> Put
|
putBool :: Bool -> Put
|
||||||
putBool True = putInt (1 :: Int)
|
putBool True = putInt (1 :: Int)
|
||||||
|
@ -1,9 +1,17 @@
|
|||||||
{-# language DataKinds #-}
|
{-# language DataKinds #-}
|
||||||
{-# language ScopedTypeVariables #-}
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
|
||||||
module NixDaemon where
|
module NixDaemon where
|
||||||
|
|
||||||
import qualified System.Environment as Env
|
import Data.Text (Text)
|
||||||
|
import Data.Either ( isRight
|
||||||
|
, isLeft
|
||||||
|
)
|
||||||
|
import Data.Bool ( bool )
|
||||||
|
import Control.Monad ( void )
|
||||||
|
import Control.Monad.IO.Class ( liftIO )
|
||||||
|
|
||||||
|
import qualified System.Environment
|
||||||
import Control.Exception ( bracket )
|
import Control.Exception ( bracket )
|
||||||
import Control.Concurrent ( threadDelay )
|
import Control.Concurrent ( threadDelay )
|
||||||
import qualified Data.ByteString.Char8 as BSC
|
import qualified Data.ByteString.Char8 as BSC
|
||||||
@ -34,7 +42,7 @@ import System.Nix.Nar ( dumpPath )
|
|||||||
|
|
||||||
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 <- Env.lookupEnv "PATH"
|
mPath <- System.Environment.lookupEnv "PATH"
|
||||||
|
|
||||||
(_, _, _, ph) <-
|
(_, _, _, ph) <-
|
||||||
P.createProcess (P.proc proc args)
|
P.createProcess (P.proc proc args)
|
||||||
@ -44,13 +52,13 @@ createProcessEnv fp proc args = do
|
|||||||
pure ph
|
pure ph
|
||||||
|
|
||||||
mockedEnv :: Maybe String -> FilePath -> [(String, FilePath)]
|
mockedEnv :: Maybe String -> FilePath -> [(String, FilePath)]
|
||||||
mockedEnv mEnvPath fp = (fp </>) <<$>>
|
mockedEnv mEnvPath fp =
|
||||||
[ ("NIX_STORE_DIR" , "store")
|
[ ("NIX_STORE_DIR" , fp </> "store")
|
||||||
, ("NIX_LOCALSTATE_DIR", "var")
|
, ("NIX_LOCALSTATE_DIR", fp </> "var")
|
||||||
, ("NIX_LOG_DIR" , "var" </> "log")
|
, ("NIX_LOG_DIR" , fp </> "var" </> "log")
|
||||||
, ("NIX_STATE_DIR" , "var" </> "nix")
|
, ("NIX_STATE_DIR" , fp </> "var" </> "nix")
|
||||||
, ("NIX_CONF_DIR" , "etc")
|
, ("NIX_CONF_DIR" , fp </> "etc")
|
||||||
, ("HOME" , "home")
|
, ("HOME" , fp </> "home")
|
||||||
-- , ("NIX_REMOTE", "daemon")
|
-- , ("NIX_REMOTE", "daemon")
|
||||||
] <> foldMap (\x -> [("PATH", x)]) mEnvPath
|
] <> foldMap (\x -> [("PATH", x)]) mEnvPath
|
||||||
|
|
||||||
@ -60,12 +68,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))
|
||||||
pass
|
(pure ())
|
||||||
ex
|
ex
|
||||||
|
|
||||||
writeConf :: FilePath -> IO ()
|
writeConf :: FilePath -> IO ()
|
||||||
writeConf fp =
|
writeConf fp =
|
||||||
writeFileText fp $ unlines
|
writeFile fp $ unlines
|
||||||
[ "build-users-group = "
|
[ "build-users-group = "
|
||||||
, "trusted-users = root"
|
, "trusted-users = root"
|
||||||
, "allowed-users = *"
|
, "allowed-users = *"
|
||||||
@ -136,7 +144,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 >> pass) `checks` check
|
Hspec.it name $ \run -> run (void $ action) `checks` check
|
||||||
|
|
||||||
itRights
|
itRights
|
||||||
:: (Show a, Show b, Show c, Monad m)
|
:: (Show a, Show b, Show c, Monad m)
|
||||||
|
1
hnix-store-remote/tests/Driver.hs
Normal file
1
hnix-store-remote/tests/Driver.hs
Normal file
@ -0,0 +1 @@
|
|||||||
|
{-# OPTIONS_GHC -F -pgmF tasty-discover #-}
|
159
hnix-store-remote/tests/SerializeSpec.hs
Normal file
159
hnix-store-remote/tests/SerializeSpec.hs
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
{-# LANGUAGE NumericUnderscores #-}
|
||||||
|
module SerializeSpec where
|
||||||
|
|
||||||
|
import Data.ByteString (ByteString)
|
||||||
|
import Data.Fixed (Uni)
|
||||||
|
import Data.HashSet (HashSet)
|
||||||
|
import Data.Serialize (Serialize(..))
|
||||||
|
import Data.Serialize.Get (Get, runGet)
|
||||||
|
import Data.Serialize.Put (Putter, runPut)
|
||||||
|
import Data.Text (Text)
|
||||||
|
import Data.Time (NominalDiffTime)
|
||||||
|
import Test.Hspec (Spec, describe, it, shouldBe)
|
||||||
|
import Test.Tasty.QuickCheck
|
||||||
|
import Test.QuickCheck.Instances ()
|
||||||
|
|
||||||
|
import qualified Data.Either
|
||||||
|
import qualified Data.HashSet
|
||||||
|
import qualified Data.Time.Clock.POSIX
|
||||||
|
import qualified System.Nix.Build
|
||||||
|
|
||||||
|
import Nix.Derivation (Derivation(..))
|
||||||
|
import System.Nix.Build (BuildMode, BuildStatus)
|
||||||
|
import System.Nix.Derivation ()
|
||||||
|
import System.Nix.StorePath (StoreDir, StorePath)
|
||||||
|
import System.Nix.Store.Remote.Serialize (getDerivation, putDerivation)
|
||||||
|
import System.Nix.Store.Remote.Serialize.Prim
|
||||||
|
|
||||||
|
roundTrip :: (Eq a, Show a) => Putter a -> Get a -> a -> Property
|
||||||
|
roundTrip p g a = res === Right a
|
||||||
|
where res = runGet g (runPut (p a))
|
||||||
|
|
||||||
|
-- * Prim
|
||||||
|
-- ** Int
|
||||||
|
|
||||||
|
prop_int :: Int -> Property
|
||||||
|
prop_int = roundTrip putInt getInt
|
||||||
|
|
||||||
|
-- ** Bool
|
||||||
|
|
||||||
|
prop_bool :: Bool -> Property
|
||||||
|
prop_bool = roundTrip putBool getBool
|
||||||
|
|
||||||
|
-- ** UTCTime
|
||||||
|
|
||||||
|
prop_time :: Int -> Property
|
||||||
|
prop_time =
|
||||||
|
roundTrip
|
||||||
|
(putTime . Data.Time.Clock.POSIX.posixSecondsToUTCTime . toSeconds)
|
||||||
|
(fromSeconds . Data.Time.Clock.POSIX.utcTimeToPOSIXSeconds <$> getTime)
|
||||||
|
where
|
||||||
|
-- scale to seconds and back
|
||||||
|
toSeconds :: Int -> NominalDiffTime
|
||||||
|
toSeconds n = realToFrac (toEnum n :: Uni)
|
||||||
|
fromSeconds :: NominalDiffTime -> Int
|
||||||
|
fromSeconds = (fromEnum :: Uni -> Int) . realToFrac
|
||||||
|
|
||||||
|
-- ** Combinators
|
||||||
|
|
||||||
|
prop_many :: [Int] -> Property
|
||||||
|
prop_many = roundTrip (putMany putInt) (getMany getInt)
|
||||||
|
|
||||||
|
-- ** ByteString
|
||||||
|
|
||||||
|
prop_bytestring :: ByteString -> Property
|
||||||
|
prop_bytestring = roundTrip putByteString getByteString
|
||||||
|
|
||||||
|
prop_bytestrings :: [ByteString] -> Property
|
||||||
|
prop_bytestrings = roundTrip putByteStrings getByteStrings
|
||||||
|
|
||||||
|
-- ** Text
|
||||||
|
|
||||||
|
prop_text :: Text -> Property
|
||||||
|
prop_text = roundTrip putText getText
|
||||||
|
|
||||||
|
prop_texts :: [Text] -> Property
|
||||||
|
prop_texts = roundTrip putTexts getTexts
|
||||||
|
|
||||||
|
-- ** StorePath
|
||||||
|
|
||||||
|
prop_path :: StoreDir -> StorePath -> Property
|
||||||
|
prop_path = \sd ->
|
||||||
|
roundTrip
|
||||||
|
(putPath sd)
|
||||||
|
(Data.Either.fromRight undefined <$> getPath sd)
|
||||||
|
|
||||||
|
prop_paths :: StoreDir -> HashSet StorePath -> Property
|
||||||
|
prop_paths = \sd ->
|
||||||
|
roundTrip
|
||||||
|
(putPaths sd)
|
||||||
|
(Data.HashSet.map (Data.Either.fromRight undefined) <$> getPaths sd)
|
||||||
|
|
||||||
|
-- * Serialize
|
||||||
|
roundTripS :: (Eq a, Serialize a, Show a) => a -> Property
|
||||||
|
roundTripS a = res === Right a
|
||||||
|
where res = runGet get (runPut (put a))
|
||||||
|
|
||||||
|
-- ** Text
|
||||||
|
|
||||||
|
prop_Text :: Text -> Property
|
||||||
|
prop_Text = roundTripS
|
||||||
|
|
||||||
|
-- ** BuildMode
|
||||||
|
|
||||||
|
prop_buildMode :: BuildMode -> Property
|
||||||
|
prop_buildMode = roundTripS
|
||||||
|
|
||||||
|
-- ** BuildStatus
|
||||||
|
|
||||||
|
prop_buildStatus :: BuildStatus -> Property
|
||||||
|
prop_buildStatus = roundTripS
|
||||||
|
|
||||||
|
-- ** BuildResult
|
||||||
|
|
||||||
|
prop_buildResult :: Property
|
||||||
|
prop_buildResult =
|
||||||
|
forAll (arbitrary `suchThat` ((/= Just "") . System.Nix.Build.errorMessage))
|
||||||
|
$ \br ->
|
||||||
|
roundTripS
|
||||||
|
$ br { System.Nix.Build.startTime = Data.Time.Clock.POSIX.posixSecondsToUTCTime 0
|
||||||
|
, System.Nix.Build.stopTime = Data.Time.Clock.POSIX.posixSecondsToUTCTime 0
|
||||||
|
}
|
||||||
|
|
||||||
|
-- ** Enums
|
||||||
|
|
||||||
|
spec_buildEnums :: Spec
|
||||||
|
spec_buildEnums =
|
||||||
|
let it' name constr value = it name $ runPut (put constr) `shouldBe` runPut (putInt value)
|
||||||
|
in do
|
||||||
|
describe "Build enum order matches Nix" $ do
|
||||||
|
it' "Normal" System.Nix.Build.Normal 0
|
||||||
|
it' "Repair" System.Nix.Build.Repair 1
|
||||||
|
it' "Check" System.Nix.Build.Check 2
|
||||||
|
|
||||||
|
describe "BuildStatus enum order matches Nix" $ do
|
||||||
|
it' "Built" System.Nix.Build.Built 0
|
||||||
|
it' "Substituted" System.Nix.Build.Substituted 1
|
||||||
|
it' "AlreadyValid" System.Nix.Build.AlreadyValid 2
|
||||||
|
it' "PermanentFailure" System.Nix.Build.PermanentFailure 3
|
||||||
|
it' "InputRejected" System.Nix.Build.InputRejected 4
|
||||||
|
it' "OutputRejected" System.Nix.Build.OutputRejected 5
|
||||||
|
it' "TransientFailure" System.Nix.Build.TransientFailure 6
|
||||||
|
it' "CachedFailure" System.Nix.Build.CachedFailure 7
|
||||||
|
it' "TimedOut" System.Nix.Build.TimedOut 8
|
||||||
|
it' "MiscFailure" System.Nix.Build.MiscFailure 9
|
||||||
|
it' "DependencyFailed" System.Nix.Build.DependencyFailed 10
|
||||||
|
it' "LogLimitExceeded" System.Nix.Build.LogLimitExceeded 11
|
||||||
|
it' "NotDeterministic" System.Nix.Build.NotDeterministic 12
|
||||||
|
it' "ResolvesToAlreadyValid" System.Nix.Build.ResolvesToAlreadyValid 13
|
||||||
|
it' "NoSubstituters" System.Nix.Build.NoSubstituters 14
|
||||||
|
|
||||||
|
-- ** Derivation
|
||||||
|
|
||||||
|
prop_derivation :: StoreDir -> Derivation StorePath Text -> Property
|
||||||
|
prop_derivation sd drv =
|
||||||
|
roundTrip
|
||||||
|
(putDerivation sd)
|
||||||
|
(getDerivation sd)
|
||||||
|
-- inputDrvs is not used in remote protocol serialization
|
||||||
|
(drv { inputDrvs = mempty })
|
Loading…
Reference in New Issue
Block a user