packages feed

hnix-store-core 0.7.0.0 → 0.8.0.0

raw patch · 40 files changed

+1823/−2907 lines, 40 filesdep +constraints-extrasdep +cryptondep +data-default-classdep −algebraic-graphsdep −binarydep −case-insensitivedep ~base16-bytestringdep ~base64-bytestringPVP ok

version bump matches the API change (PVP)

Dependencies added: constraints-extras, crypton, data-default-class, dependent-sum, dependent-sum-template, some

Dependencies removed: algebraic-graphs, binary, case-insensitive, cereal, cryptonite, directory, lifted-base, monad-control, mtl, process, relude, saltine, tasty-hunit, tasty-quickcheck, temporary, unix

Dependency ranges changed: base16-bytestring, base64-bytestring

API changes (from Hackage documentation)

- System.Nix.Build: AlreadyValid :: BuildStatus
- System.Nix.Build: Built :: BuildStatus
- System.Nix.Build: CachedFailure :: BuildStatus
- System.Nix.Build: Check :: BuildMode
- System.Nix.Build: DependencyFailed :: BuildStatus
- System.Nix.Build: InputRejected :: BuildStatus
- System.Nix.Build: LogLimitExceeded :: BuildStatus
- System.Nix.Build: MiscFailure :: BuildStatus
- System.Nix.Build: Normal :: BuildMode
- System.Nix.Build: NotDeterministic :: BuildStatus
- System.Nix.Build: OutputRejected :: BuildStatus
- System.Nix.Build: PermanentFailure :: BuildStatus
- System.Nix.Build: Repair :: BuildMode
- System.Nix.Build: Substituted :: BuildStatus
- System.Nix.Build: TimedOut :: BuildStatus
- System.Nix.Build: TransientFailure :: BuildStatus
- System.Nix.Build: [errorMessage] :: BuildResult -> !Maybe Text
- System.Nix.Build: [isNonDeterministic] :: BuildResult -> !Bool
- System.Nix.Build: [startTime] :: BuildResult -> !UTCTime
- System.Nix.Build: [status] :: BuildResult -> !BuildStatus
- System.Nix.Build: [stopTime] :: BuildResult -> !UTCTime
- System.Nix.Build: [timesBuilt] :: BuildResult -> !Integer
- System.Nix.Hash: SomeDigest :: Digest a -> SomeNamedDigest
- System.Nix.Hash: data SomeNamedDigest
- System.Nix.Internal.Base: Base16 :: BaseEncoding
- System.Nix.Internal.Base: Base64 :: BaseEncoding
- System.Nix.Internal.Base: NixBase32 :: BaseEncoding
- System.Nix.Internal.Base: data BaseEncoding
- System.Nix.Internal.Base: decodeWith :: BaseEncoding -> Text -> Either String ByteString
- System.Nix.Internal.Base: encodeWith :: BaseEncoding -> ByteString -> Text
- System.Nix.Internal.Base32: decode :: Text -> Either String ByteString
- System.Nix.Internal.Base32: digits32 :: Vector Char
- System.Nix.Internal.Base32: encode :: ByteString -> Text
- System.Nix.Internal.Hash: SomeDigest :: Digest a -> SomeNamedDigest
- System.Nix.Internal.Hash: algoName :: NamedAlgo a => Text
- System.Nix.Internal.Hash: class HashAlgorithm a => NamedAlgo a
- System.Nix.Internal.Hash: data SomeNamedDigest
- System.Nix.Internal.Hash: decodeDigestWith :: HashAlgorithm a => BaseEncoding -> Text -> Either String (Digest a)
- System.Nix.Internal.Hash: encodeDigestWith :: BaseEncoding -> Digest a -> Text
- System.Nix.Internal.Hash: instance GHC.Show.Show System.Nix.Internal.Hash.SomeNamedDigest
- System.Nix.Internal.Hash: instance System.Nix.Internal.Hash.NamedAlgo Crypto.Hash.MD5.MD5
- System.Nix.Internal.Hash: instance System.Nix.Internal.Hash.NamedAlgo Crypto.Hash.SHA1.SHA1
- System.Nix.Internal.Hash: instance System.Nix.Internal.Hash.NamedAlgo Crypto.Hash.SHA256.SHA256
- System.Nix.Internal.Hash: instance System.Nix.Internal.Hash.NamedAlgo Crypto.Hash.SHA512.SHA512
- System.Nix.Internal.Hash: mkNamedDigest :: Text -> Text -> Either String SomeNamedDigest
- System.Nix.Internal.Hash: mkStorePathHash :: forall a. HashAlgorithm a => ByteString -> ByteString
- System.Nix.Internal.Nar.Effects: NarEffects :: (FilePath -> m ByteString) -> (FilePath -> ByteString -> m ()) -> (FilePath -> m (Maybe ByteString) -> m ()) -> (FilePath -> m [FilePath]) -> (FilePath -> m ()) -> (FilePath -> FilePath -> m ()) -> (FilePath -> m Permissions) -> (FilePath -> Permissions -> m ()) -> (FilePath -> m Bool) -> (FilePath -> m Bool) -> (FilePath -> m Int64) -> (FilePath -> m FilePath) -> (FilePath -> m ()) -> (FilePath -> m ()) -> NarEffects (m :: Type -> Type)
- System.Nix.Internal.Nar.Effects: [narCreateDir] :: NarEffects (m :: Type -> Type) -> FilePath -> m ()
- System.Nix.Internal.Nar.Effects: [narCreateLink] :: NarEffects (m :: Type -> Type) -> FilePath -> FilePath -> m ()
- System.Nix.Internal.Nar.Effects: [narDeleteDir] :: NarEffects (m :: Type -> Type) -> FilePath -> m ()
- System.Nix.Internal.Nar.Effects: [narDeleteFile] :: NarEffects (m :: Type -> Type) -> FilePath -> m ()
- System.Nix.Internal.Nar.Effects: [narFileSize] :: NarEffects (m :: Type -> Type) -> FilePath -> m Int64
- System.Nix.Internal.Nar.Effects: [narGetPerms] :: NarEffects (m :: Type -> Type) -> FilePath -> m Permissions
- System.Nix.Internal.Nar.Effects: [narIsDir] :: NarEffects (m :: Type -> Type) -> FilePath -> m Bool
- System.Nix.Internal.Nar.Effects: [narIsSymLink] :: NarEffects (m :: Type -> Type) -> FilePath -> m Bool
- System.Nix.Internal.Nar.Effects: [narListDir] :: NarEffects (m :: Type -> Type) -> FilePath -> m [FilePath]
- System.Nix.Internal.Nar.Effects: [narReadFile] :: NarEffects (m :: Type -> Type) -> FilePath -> m ByteString
- System.Nix.Internal.Nar.Effects: [narReadLink] :: NarEffects (m :: Type -> Type) -> FilePath -> m FilePath
- System.Nix.Internal.Nar.Effects: [narSetPerms] :: NarEffects (m :: Type -> Type) -> FilePath -> Permissions -> m ()
- System.Nix.Internal.Nar.Effects: [narStreamFile] :: NarEffects (m :: Type -> Type) -> FilePath -> m (Maybe ByteString) -> m ()
- System.Nix.Internal.Nar.Effects: [narWriteFile] :: NarEffects (m :: Type -> Type) -> FilePath -> ByteString -> m ()
- System.Nix.Internal.Nar.Effects: data NarEffects (m :: Type -> Type)
- System.Nix.Internal.Nar.Effects: narEffectsIO :: (MonadIO m, MonadFail m, MonadBaseControl IO m) => NarEffects m
- System.Nix.Internal.Nar.Options: NarOptions :: Bool -> NarOptions
- System.Nix.Internal.Nar.Options: [optUseCaseHack] :: NarOptions -> Bool
- System.Nix.Internal.Nar.Options: caseHackSuffix :: Text
- System.Nix.Internal.Nar.Options: data NarOptions
- System.Nix.Internal.Nar.Options: defaultNarOptions :: NarOptions
- System.Nix.Internal.Nar.Parser: instance Control.Monad.Fail.MonadFail m => Control.Monad.Fail.MonadFail (System.Nix.Internal.Nar.Parser.NarParser m)
- System.Nix.Internal.Nar.Parser: instance Control.Monad.IO.Class.MonadIO m => Control.Monad.IO.Class.MonadIO (System.Nix.Internal.Nar.Parser.NarParser m)
- System.Nix.Internal.Nar.Parser: instance Control.Monad.Trans.Class.MonadTrans System.Nix.Internal.Nar.Parser.NarParser
- System.Nix.Internal.Nar.Parser: instance GHC.Base.Functor m => GHC.Base.Functor (System.Nix.Internal.Nar.Parser.NarParser m)
- System.Nix.Internal.Nar.Parser: instance GHC.Base.Monad m => Control.Monad.Error.Class.MonadError GHC.Base.String (System.Nix.Internal.Nar.Parser.NarParser m)
- System.Nix.Internal.Nar.Parser: instance GHC.Base.Monad m => Control.Monad.Reader.Class.MonadReader (System.Nix.Internal.Nar.Parser.ParserEnv m) (System.Nix.Internal.Nar.Parser.NarParser m)
- System.Nix.Internal.Nar.Parser: instance GHC.Base.Monad m => Control.Monad.State.Class.MonadState System.Nix.Internal.Nar.Parser.ParserState (System.Nix.Internal.Nar.Parser.NarParser m)
- System.Nix.Internal.Nar.Parser: instance GHC.Base.Monad m => GHC.Base.Applicative (System.Nix.Internal.Nar.Parser.NarParser m)
- System.Nix.Internal.Nar.Parser: instance GHC.Base.Monad m => GHC.Base.Monad (System.Nix.Internal.Nar.Parser.NarParser m)
- System.Nix.Internal.Nar.Parser: instance GHC.Show.Show System.Nix.Internal.Nar.Parser.LinkInfo
- System.Nix.Internal.Nar.Parser: parseNar :: (MonadIO m, MonadFail m) => NarParser m ()
- System.Nix.Internal.Nar.Parser: runParser :: forall m a. (MonadIO m, MonadBaseControl IO m) => NarEffects m -> NarParser m a -> Handle -> FilePath -> m (Either String a)
- System.Nix.Internal.Nar.Parser: runParserWithOptions :: forall m a. (MonadIO m, MonadBaseControl IO m) => NarOptions -> NarEffects m -> NarParser m a -> Handle -> FilePath -> m (Either String a)
- System.Nix.Internal.Nar.Parser: testParser :: m ~ IO => NarParser m a -> ByteString -> m (Either String a)
- System.Nix.Internal.Nar.Parser: testParser' :: m ~ IO => FilePath -> IO (Either String ())
- System.Nix.Internal.Nar.Streamer: Executable :: IsExecutable
- System.Nix.Internal.Nar.Streamer: NonExecutable :: IsExecutable
- System.Nix.Internal.Nar.Streamer: data IsExecutable
- System.Nix.Internal.Nar.Streamer: dumpPath :: forall m. MonadIO m => FilePath -> NarSource m
- System.Nix.Internal.Nar.Streamer: dumpString :: forall m. MonadIO m => ByteString -> NarSource m
- System.Nix.Internal.Nar.Streamer: instance GHC.Classes.Eq System.Nix.Internal.Nar.Streamer.IsExecutable
- System.Nix.Internal.Nar.Streamer: instance GHC.Show.Show System.Nix.Internal.Nar.Streamer.IsExecutable
- System.Nix.Internal.Nar.Streamer: streamNarIO :: forall m. MonadIO m => NarEffects IO -> FilePath -> NarSource m
- System.Nix.Internal.Nar.Streamer: streamNarIOWithOptions :: forall m. MonadIO m => NarOptions -> NarEffects IO -> FilePath -> NarSource m
- System.Nix.Internal.Nar.Streamer: type NarSource m = (ByteString -> m ()) -> m ()
- System.Nix.Internal.Signature: NarSignature :: PublicKey -> Signature -> NarSignature
- System.Nix.Internal.Signature: [publicKey] :: NarSignature -> PublicKey
- System.Nix.Internal.Signature: [sig] :: NarSignature -> Signature
- System.Nix.Internal.Signature: data NarSignature
- System.Nix.Internal.Signature: data Signature
- System.Nix.Internal.Signature: instance Crypto.Saltine.Class.IsEncoding System.Nix.Internal.Signature.Signature
- System.Nix.Internal.Signature: instance GHC.Classes.Eq System.Nix.Internal.Signature.NarSignature
- System.Nix.Internal.Signature: instance GHC.Classes.Eq System.Nix.Internal.Signature.Signature
- System.Nix.Internal.Signature: instance GHC.Classes.Ord System.Nix.Internal.Signature.NarSignature
- System.Nix.Internal.Signature: instance GHC.Classes.Ord System.Nix.Internal.Signature.Signature
- System.Nix.Internal.StorePath: Fixed :: !NarHashMode -> !SomeNamedDigest -> ContentAddressableAddress
- System.Nix.Internal.StorePath: Recursive :: NarHashMode
- System.Nix.Internal.StorePath: RegularFile :: NarHashMode
- System.Nix.Internal.StorePath: StoreDir :: RawFilePath -> StoreDir
- System.Nix.Internal.StorePath: StorePath :: !StorePathHashPart -> !StorePathName -> StorePath
- System.Nix.Internal.StorePath: StorePathHashPart :: ByteString -> StorePathHashPart
- System.Nix.Internal.StorePath: StorePathName :: Text -> StorePathName
- System.Nix.Internal.StorePath: Text :: !Digest SHA256 -> ContentAddressableAddress
- System.Nix.Internal.StorePath: [storePathHash] :: StorePath -> !StorePathHashPart
- System.Nix.Internal.StorePath: [storePathName] :: StorePath -> !StorePathName
- System.Nix.Internal.StorePath: [unStoreDir] :: StoreDir -> RawFilePath
- System.Nix.Internal.StorePath: [unStorePathName] :: StorePathName -> Text
- System.Nix.Internal.StorePath: data ContentAddressableAddress
- System.Nix.Internal.StorePath: data NarHashMode
- System.Nix.Internal.StorePath: data StorePath
- System.Nix.Internal.StorePath: instance Data.Hashable.Class.Hashable System.Nix.Internal.StorePath.StoreDir
- System.Nix.Internal.StorePath: instance Data.Hashable.Class.Hashable System.Nix.Internal.StorePath.StorePath
- System.Nix.Internal.StorePath: instance Data.Hashable.Class.Hashable System.Nix.Internal.StorePath.StorePathHashPart
- System.Nix.Internal.StorePath: instance Data.Hashable.Class.Hashable System.Nix.Internal.StorePath.StorePathName
- System.Nix.Internal.StorePath: instance GHC.Classes.Eq System.Nix.Internal.StorePath.StoreDir
- System.Nix.Internal.StorePath: instance GHC.Classes.Eq System.Nix.Internal.StorePath.StorePath
- System.Nix.Internal.StorePath: instance GHC.Classes.Eq System.Nix.Internal.StorePath.StorePathHashPart
- System.Nix.Internal.StorePath: instance GHC.Classes.Eq System.Nix.Internal.StorePath.StorePathName
- System.Nix.Internal.StorePath: instance GHC.Classes.Ord System.Nix.Internal.StorePath.StoreDir
- System.Nix.Internal.StorePath: instance GHC.Classes.Ord System.Nix.Internal.StorePath.StorePath
- System.Nix.Internal.StorePath: instance GHC.Classes.Ord System.Nix.Internal.StorePath.StorePathHashPart
- System.Nix.Internal.StorePath: instance GHC.Classes.Ord System.Nix.Internal.StorePath.StorePathName
- System.Nix.Internal.StorePath: instance GHC.Show.Show System.Nix.Internal.StorePath.StoreDir
- System.Nix.Internal.StorePath: instance GHC.Show.Show System.Nix.Internal.StorePath.StorePath
- System.Nix.Internal.StorePath: instance GHC.Show.Show System.Nix.Internal.StorePath.StorePathHashPart
- System.Nix.Internal.StorePath: instance GHC.Show.Show System.Nix.Internal.StorePath.StorePathName
- System.Nix.Internal.StorePath: makeStorePathName :: Text -> Either String StorePathName
- System.Nix.Internal.StorePath: mkStorePathHashPart :: ByteString -> StorePathHashPart
- System.Nix.Internal.StorePath: newtype StoreDir
- System.Nix.Internal.StorePath: newtype StorePathHashPart
- System.Nix.Internal.StorePath: newtype StorePathName
- System.Nix.Internal.StorePath: parsePath :: StoreDir -> ByteString -> Either String StorePath
- System.Nix.Internal.StorePath: pathParser :: StoreDir -> Parser StorePath
- System.Nix.Internal.StorePath: storePathToFilePath :: StoreDir -> StorePath -> FilePath
- System.Nix.Internal.StorePath: storePathToNarInfo :: StorePath -> ByteString
- System.Nix.Internal.StorePath: storePathToRawFilePath :: StoreDir -> StorePath -> RawFilePath
- System.Nix.Internal.StorePath: storePathToText :: StoreDir -> StorePath -> Text
- System.Nix.Internal.StorePath: type StorePathSet = HashSet StorePath
- System.Nix.Internal.StorePath: validStorePathName :: Text -> Bool
- System.Nix.Internal.Truncation: truncateInNixWay :: Int -> ByteString -> ByteString
- System.Nix.Nar: NarEffects :: (FilePath -> m ByteString) -> (FilePath -> ByteString -> m ()) -> (FilePath -> m (Maybe ByteString) -> m ()) -> (FilePath -> m [FilePath]) -> (FilePath -> m ()) -> (FilePath -> FilePath -> m ()) -> (FilePath -> m Permissions) -> (FilePath -> Permissions -> m ()) -> (FilePath -> m Bool) -> (FilePath -> m Bool) -> (FilePath -> m Int64) -> (FilePath -> m FilePath) -> (FilePath -> m ()) -> (FilePath -> m ()) -> NarEffects (m :: Type -> Type)
- System.Nix.Nar: NarOptions :: Bool -> NarOptions
- System.Nix.Nar: [narCreateDir] :: NarEffects (m :: Type -> Type) -> FilePath -> m ()
- System.Nix.Nar: [narCreateLink] :: NarEffects (m :: Type -> Type) -> FilePath -> FilePath -> m ()
- System.Nix.Nar: [narDeleteDir] :: NarEffects (m :: Type -> Type) -> FilePath -> m ()
- System.Nix.Nar: [narDeleteFile] :: NarEffects (m :: Type -> Type) -> FilePath -> m ()
- System.Nix.Nar: [narFileSize] :: NarEffects (m :: Type -> Type) -> FilePath -> m Int64
- System.Nix.Nar: [narGetPerms] :: NarEffects (m :: Type -> Type) -> FilePath -> m Permissions
- System.Nix.Nar: [narIsDir] :: NarEffects (m :: Type -> Type) -> FilePath -> m Bool
- System.Nix.Nar: [narIsSymLink] :: NarEffects (m :: Type -> Type) -> FilePath -> m Bool
- System.Nix.Nar: [narListDir] :: NarEffects (m :: Type -> Type) -> FilePath -> m [FilePath]
- System.Nix.Nar: [narReadFile] :: NarEffects (m :: Type -> Type) -> FilePath -> m ByteString
- System.Nix.Nar: [narReadLink] :: NarEffects (m :: Type -> Type) -> FilePath -> m FilePath
- System.Nix.Nar: [narSetPerms] :: NarEffects (m :: Type -> Type) -> FilePath -> Permissions -> m ()
- System.Nix.Nar: [narStreamFile] :: NarEffects (m :: Type -> Type) -> FilePath -> m (Maybe ByteString) -> m ()
- System.Nix.Nar: [narWriteFile] :: NarEffects (m :: Type -> Type) -> FilePath -> ByteString -> m ()
- System.Nix.Nar: [optUseCaseHack] :: NarOptions -> Bool
- System.Nix.Nar: buildNarIO :: NarEffects IO -> FilePath -> Handle -> IO ()
- System.Nix.Nar: data NarEffects (m :: Type -> Type)
- System.Nix.Nar: data NarOptions
- System.Nix.Nar: defaultNarOptions :: NarOptions
- System.Nix.Nar: dumpPath :: forall m. MonadIO m => FilePath -> NarSource m
- System.Nix.Nar: dumpString :: forall m. MonadIO m => ByteString -> NarSource m
- System.Nix.Nar: narEffectsIO :: (MonadIO m, MonadFail m, MonadBaseControl IO m) => NarEffects m
- System.Nix.Nar: parseNar :: (MonadIO m, MonadFail m) => NarParser m ()
- System.Nix.Nar: runParser :: forall m a. (MonadIO m, MonadBaseControl IO m) => NarEffects m -> NarParser m a -> Handle -> FilePath -> m (Either String a)
- System.Nix.Nar: runParserWithOptions :: forall m a. (MonadIO m, MonadBaseControl IO m) => NarOptions -> NarEffects m -> NarParser m a -> Handle -> FilePath -> m (Either String a)
- System.Nix.Nar: streamNarIO :: forall m. MonadIO m => NarEffects IO -> FilePath -> NarSource m
- System.Nix.Nar: streamNarIOWithOptions :: forall m. MonadIO m => NarOptions -> NarEffects IO -> FilePath -> NarSource m
- System.Nix.Nar: testParser :: m ~ IO => NarParser m a -> ByteString -> m (Either String a)
- System.Nix.Nar: testParser' :: m ~ IO => FilePath -> IO (Either String ())
- System.Nix.Nar: type NarSource m = (ByteString -> m ()) -> m ()
- System.Nix.Nar: unpackNarIO :: NarEffects IO -> Handle -> FilePath -> IO (Either String ())
- System.Nix.ReadonlyStore: computeStorePathForPath :: StoreDir -> StorePathName -> FilePath -> Bool -> (FilePath -> Bool) -> Bool -> IO StorePath
- System.Nix.ReadonlyStore: computeStorePathForText :: StoreDir -> StorePathName -> ByteString -> StorePathSet -> StorePath
- System.Nix.ReadonlyStore: makeFixedOutputPath :: forall hashAlgo. NamedAlgo hashAlgo => StoreDir -> Bool -> Digest hashAlgo -> StorePathName -> StorePath
- System.Nix.ReadonlyStore: makeStorePath :: forall h. NamedAlgo h => StoreDir -> ByteString -> Digest h -> StorePathName -> StorePath
- System.Nix.ReadonlyStore: makeTextPath :: StoreDir -> StorePathName -> Digest SHA256 -> StorePathSet -> StorePath
- System.Nix.Signature: data Signature
- System.Nix.StorePath: Fixed :: !NarHashMode -> !SomeNamedDigest -> ContentAddressableAddress
- System.Nix.StorePath: Recursive :: NarHashMode
- System.Nix.StorePath: RegularFile :: NarHashMode
- System.Nix.StorePath: StorePath :: !StorePathHashPart -> !StorePathName -> StorePath
- System.Nix.StorePath: StorePathHashPart :: ByteString -> StorePathHashPart
- System.Nix.StorePath: StorePathName :: Text -> StorePathName
- System.Nix.StorePath: Text :: !Digest SHA256 -> ContentAddressableAddress
- System.Nix.StorePath: [storePathHash] :: StorePath -> !StorePathHashPart
- System.Nix.StorePath: [storePathName] :: StorePath -> !StorePathName
- System.Nix.StorePath: [unStorePathName] :: StorePathName -> Text
- System.Nix.StorePath: data ContentAddressableAddress
- System.Nix.StorePath: data NarHashMode
- System.Nix.StorePath: makeStorePathName :: Text -> Either String StorePathName
- System.Nix.StorePath: newtype StorePathHashPart
- System.Nix.StorePath: newtype StorePathName
- System.Nix.StorePath: type StorePathSet = HashSet StorePath
- System.Nix.StorePath: validStorePathName :: Text -> Bool
- System.Nix.StorePathMetadata: BuiltElsewhere :: StorePathTrust
- System.Nix.StorePathMetadata: BuiltLocally :: StorePathTrust
- System.Nix.StorePathMetadata: StorePathMetadata :: !StorePath -> !Maybe StorePath -> !SomeNamedDigest -> !StorePathSet -> !UTCTime -> !Maybe Word64 -> !StorePathTrust -> !Set NarSignature -> !Maybe ContentAddressableAddress -> StorePathMetadata
- System.Nix.StorePathMetadata: [contentAddressableAddress] :: StorePathMetadata -> !Maybe ContentAddressableAddress
- System.Nix.StorePathMetadata: [deriverPath] :: StorePathMetadata -> !Maybe StorePath
- System.Nix.StorePathMetadata: [narBytes] :: StorePathMetadata -> !Maybe Word64
- System.Nix.StorePathMetadata: [narHash] :: StorePathMetadata -> !SomeNamedDigest
- System.Nix.StorePathMetadata: [path] :: StorePathMetadata -> !StorePath
- System.Nix.StorePathMetadata: [references] :: StorePathMetadata -> !StorePathSet
- System.Nix.StorePathMetadata: [registrationTime] :: StorePathMetadata -> !UTCTime
- System.Nix.StorePathMetadata: [sigs] :: StorePathMetadata -> !Set NarSignature
- System.Nix.StorePathMetadata: [trust] :: StorePathMetadata -> !StorePathTrust
- System.Nix.StorePathMetadata: data StorePathMetadata
- System.Nix.StorePathMetadata: data StorePathTrust
- System.Nix.StorePathMetadata: instance GHC.Classes.Eq System.Nix.StorePathMetadata.StorePathTrust
- System.Nix.StorePathMetadata: instance GHC.Classes.Ord System.Nix.StorePathMetadata.StorePathTrust
- System.Nix.StorePathMetadata: instance GHC.Show.Show System.Nix.StorePathMetadata.StorePathTrust
+ System.Nix.Base: Base16 :: BaseEncoding
+ System.Nix.Base: Base64 :: BaseEncoding
+ System.Nix.Base: NixBase32 :: BaseEncoding
+ System.Nix.Base: data BaseEncoding
+ System.Nix.Base: decodeWith :: BaseEncoding -> Text -> Either String ByteString
+ System.Nix.Base: encodeWith :: BaseEncoding -> ByteString -> Text
+ System.Nix.Base: instance GHC.Classes.Eq System.Nix.Base.BaseEncoding
+ System.Nix.Base: instance GHC.Classes.Ord System.Nix.Base.BaseEncoding
+ System.Nix.Base: instance GHC.Enum.Bounded System.Nix.Base.BaseEncoding
+ System.Nix.Base: instance GHC.Enum.Enum System.Nix.Base.BaseEncoding
+ System.Nix.Base: instance GHC.Generics.Generic System.Nix.Base.BaseEncoding
+ System.Nix.Base: instance GHC.Show.Show System.Nix.Base.BaseEncoding
+ System.Nix.Base32: digits32 :: Vector Char
+ System.Nix.Build: BuildMode_Check :: BuildMode
+ System.Nix.Build: BuildMode_Normal :: BuildMode
+ System.Nix.Build: BuildMode_Repair :: BuildMode
+ System.Nix.Build: BuildStatus_AlreadyValid :: BuildStatus
+ System.Nix.Build: BuildStatus_Built :: BuildStatus
+ System.Nix.Build: BuildStatus_CachedFailure :: BuildStatus
+ System.Nix.Build: BuildStatus_DependencyFailed :: BuildStatus
+ System.Nix.Build: BuildStatus_InputRejected :: BuildStatus
+ System.Nix.Build: BuildStatus_LogLimitExceeded :: BuildStatus
+ System.Nix.Build: BuildStatus_MiscFailure :: BuildStatus
+ System.Nix.Build: BuildStatus_NoSubstituters :: BuildStatus
+ System.Nix.Build: BuildStatus_NotDeterministic :: BuildStatus
+ System.Nix.Build: BuildStatus_OutputRejected :: BuildStatus
+ System.Nix.Build: BuildStatus_PermanentFailure :: BuildStatus
+ System.Nix.Build: BuildStatus_ResolvesToAlreadyValid :: BuildStatus
+ System.Nix.Build: BuildStatus_Substituted :: BuildStatus
+ System.Nix.Build: BuildStatus_TimedOut :: BuildStatus
+ System.Nix.Build: BuildStatus_TransientFailure :: BuildStatus
+ System.Nix.Build: [buildResultBuiltOutputs] :: BuildResult -> Maybe (Map (DerivationOutput OutputName) Realisation)
+ System.Nix.Build: [buildResultErrorMessage] :: BuildResult -> Maybe Text
+ System.Nix.Build: [buildResultIsNonDeterministic] :: BuildResult -> Maybe Bool
+ System.Nix.Build: [buildResultStartTime] :: BuildResult -> Maybe UTCTime
+ System.Nix.Build: [buildResultStatus] :: BuildResult -> BuildStatus
+ System.Nix.Build: [buildResultStopTime] :: BuildResult -> Maybe UTCTime
+ System.Nix.Build: [buildResultTimesBuilt] :: BuildResult -> Maybe Int
+ System.Nix.Build: instance GHC.Enum.Bounded System.Nix.Build.BuildMode
+ System.Nix.Build: instance GHC.Enum.Bounded System.Nix.Build.BuildStatus
+ System.Nix.Build: instance GHC.Generics.Generic System.Nix.Build.BuildMode
+ System.Nix.Build: instance GHC.Generics.Generic System.Nix.Build.BuildResult
+ System.Nix.Build: instance GHC.Generics.Generic System.Nix.Build.BuildStatus
+ System.Nix.ContentAddress: buildContentAddress :: ContentAddress -> Text
+ System.Nix.ContentAddress: contentAddressBuilder :: ContentAddress -> Builder
+ System.Nix.ContentAddress: contentAddressParser :: Parser ContentAddress
+ System.Nix.ContentAddress: data ContentAddress
+ System.Nix.ContentAddress: data ContentAddressMethod
+ System.Nix.ContentAddress: data FileIngestionMethod
+ System.Nix.ContentAddress: instance GHC.Classes.Eq System.Nix.ContentAddress.ContentAddress
+ System.Nix.ContentAddress: instance GHC.Classes.Eq System.Nix.ContentAddress.ContentAddressMethod
+ System.Nix.ContentAddress: instance GHC.Classes.Ord System.Nix.ContentAddress.ContentAddress
+ System.Nix.ContentAddress: instance GHC.Classes.Ord System.Nix.ContentAddress.ContentAddressMethod
+ System.Nix.ContentAddress: instance GHC.Generics.Generic System.Nix.ContentAddress.ContentAddress
+ System.Nix.ContentAddress: instance GHC.Generics.Generic System.Nix.ContentAddress.ContentAddressMethod
+ System.Nix.ContentAddress: instance GHC.Show.Show System.Nix.ContentAddress.ContentAddress
+ System.Nix.ContentAddress: instance GHC.Show.Show System.Nix.ContentAddress.ContentAddressMethod
+ System.Nix.ContentAddress: parseContentAddress :: Text -> Either String ContentAddress
+ System.Nix.Derivation: Derivation :: Map txt (DerivationOutput fp txt) -> Map fp (Set txt) -> Set fp -> txt -> txt -> Vector txt -> Map txt txt -> Derivation fp txt
+ System.Nix.Derivation: DerivationOutput :: fp -> txt -> txt -> DerivationOutput fp txt
+ System.Nix.Derivation: [args] :: Derivation fp txt -> Vector txt
+ System.Nix.Derivation: [builder] :: Derivation fp txt -> txt
+ System.Nix.Derivation: [env] :: Derivation fp txt -> Map txt txt
+ System.Nix.Derivation: [hashAlgo] :: DerivationOutput fp txt -> txt
+ System.Nix.Derivation: [hash] :: DerivationOutput fp txt -> txt
+ System.Nix.Derivation: [inputDrvs] :: Derivation fp txt -> Map fp (Set txt)
+ System.Nix.Derivation: [inputSrcs] :: Derivation fp txt -> Set fp
+ System.Nix.Derivation: [outputs] :: Derivation fp txt -> Map txt (DerivationOutput fp txt)
+ System.Nix.Derivation: [path] :: DerivationOutput fp txt -> fp
+ System.Nix.Derivation: [platform] :: Derivation fp txt -> txt
+ System.Nix.Derivation: data () => Derivation fp txt
+ System.Nix.Derivation: data () => DerivationOutput fp txt
+ System.Nix.DerivedPath: DerivedPath_Built :: StorePath -> OutputsSpec -> DerivedPath
+ System.Nix.DerivedPath: DerivedPath_Opaque :: StorePath -> DerivedPath
+ System.Nix.DerivedPath: OutputsSpec_All :: OutputsSpec
+ System.Nix.DerivedPath: OutputsSpec_Names :: Set OutputName -> OutputsSpec
+ System.Nix.DerivedPath: ParseOutputsError_InvalidName :: InvalidNameError -> ParseOutputsError
+ System.Nix.DerivedPath: ParseOutputsError_InvalidPath :: InvalidPathError -> ParseOutputsError
+ System.Nix.DerivedPath: ParseOutputsError_NoNames :: ParseOutputsError
+ System.Nix.DerivedPath: ParseOutputsError_NoPrefix :: StoreDir -> Text -> ParseOutputsError
+ System.Nix.DerivedPath: data DerivedPath
+ System.Nix.DerivedPath: data OutputsSpec
+ System.Nix.DerivedPath: data ParseOutputsError
+ System.Nix.DerivedPath: derivedPathToText :: StoreDir -> DerivedPath -> Text
+ System.Nix.DerivedPath: instance GHC.Classes.Eq System.Nix.DerivedPath.DerivedPath
+ System.Nix.DerivedPath: instance GHC.Classes.Eq System.Nix.DerivedPath.OutputsSpec
+ System.Nix.DerivedPath: instance GHC.Classes.Eq System.Nix.DerivedPath.ParseOutputsError
+ System.Nix.DerivedPath: instance GHC.Classes.Ord System.Nix.DerivedPath.DerivedPath
+ System.Nix.DerivedPath: instance GHC.Classes.Ord System.Nix.DerivedPath.OutputsSpec
+ System.Nix.DerivedPath: instance GHC.Classes.Ord System.Nix.DerivedPath.ParseOutputsError
+ System.Nix.DerivedPath: instance GHC.Generics.Generic System.Nix.DerivedPath.DerivedPath
+ System.Nix.DerivedPath: instance GHC.Generics.Generic System.Nix.DerivedPath.OutputsSpec
+ System.Nix.DerivedPath: instance GHC.Show.Show System.Nix.DerivedPath.DerivedPath
+ System.Nix.DerivedPath: instance GHC.Show.Show System.Nix.DerivedPath.OutputsSpec
+ System.Nix.DerivedPath: instance GHC.Show.Show System.Nix.DerivedPath.ParseOutputsError
+ System.Nix.DerivedPath: outputsSpecToText :: OutputsSpec -> Text
+ System.Nix.DerivedPath: parseDerivedPath :: StoreDir -> Text -> Either ParseOutputsError DerivedPath
+ System.Nix.DerivedPath: parseOutputsSpec :: Text -> Either ParseOutputsError OutputsSpec
+ System.Nix.Fingerprint: fingerprint :: StoreDir -> StorePath -> DSum HashAlgo Digest -> Word64 -> [StorePath] -> Text
+ System.Nix.Fingerprint: metadataFingerprint :: StoreDir -> StorePath -> Metadata StorePath -> Text
+ System.Nix.Hash: [HashAlgo_MD5] :: HashAlgo MD5
+ System.Nix.Hash: [HashAlgo_SHA1] :: HashAlgo SHA1
+ System.Nix.Hash: [HashAlgo_SHA256] :: HashAlgo SHA256
+ System.Nix.Hash: [HashAlgo_SHA512] :: HashAlgo SHA512
+ System.Nix.Hash: algoDigestBuilder :: DSum HashAlgo Digest -> Builder
+ System.Nix.Hash: algoToText :: forall t. HashAlgo t -> Text
+ System.Nix.Hash: data HashAlgo :: Type -> Type
+ System.Nix.Hash: digestBuilder :: forall hashAlgo. NamedAlgo hashAlgo => Digest hashAlgo -> Builder
+ System.Nix.Hash: instance (c Crypto.Hash.MD5.MD5, c Crypto.Hash.SHA1.SHA1, c Crypto.Hash.SHA256.SHA256, c Crypto.Hash.SHA512.SHA512) => Data.Constraint.Extras.Has c System.Nix.Hash.HashAlgo
+ System.Nix.Hash: instance Data.GADT.Internal.GCompare System.Nix.Hash.HashAlgo
+ System.Nix.Hash: instance Data.GADT.Internal.GEq System.Nix.Hash.HashAlgo
+ System.Nix.Hash: instance Data.GADT.Internal.GShow System.Nix.Hash.HashAlgo
+ System.Nix.Hash: instance System.Nix.Hash.NamedAlgo Crypto.Hash.MD5.MD5
+ System.Nix.Hash: instance System.Nix.Hash.NamedAlgo Crypto.Hash.SHA1.SHA1
+ System.Nix.Hash: instance System.Nix.Hash.NamedAlgo Crypto.Hash.SHA256.SHA256
+ System.Nix.Hash: instance System.Nix.Hash.NamedAlgo Crypto.Hash.SHA512.SHA512
+ System.Nix.Hash: textToAlgo :: Text -> Either String (Some HashAlgo)
+ System.Nix.Hash.Truncation: truncateInNixWay :: Int -> ByteString -> ByteString
+ System.Nix.OutputName: EmptyName :: InvalidNameError
+ System.Nix.OutputName: InvalidCharacters :: Text -> InvalidNameError
+ System.Nix.OutputName: LeadingDot :: InvalidNameError
+ System.Nix.OutputName: NameTooLong :: Int -> InvalidNameError
+ System.Nix.OutputName: OutputName :: Text -> OutputName
+ System.Nix.OutputName: [unOutputName] :: OutputName -> Text
+ System.Nix.OutputName: data InvalidNameError
+ System.Nix.OutputName: instance Data.Hashable.Class.Hashable System.Nix.OutputName.OutputName
+ System.Nix.OutputName: instance GHC.Classes.Eq System.Nix.OutputName.OutputName
+ System.Nix.OutputName: instance GHC.Classes.Ord System.Nix.OutputName.OutputName
+ System.Nix.OutputName: instance GHC.Generics.Generic System.Nix.OutputName.OutputName
+ System.Nix.OutputName: instance GHC.Show.Show System.Nix.OutputName.OutputName
+ System.Nix.OutputName: mkOutputName :: Text -> Either InvalidNameError OutputName
+ System.Nix.OutputName: newtype OutputName
+ System.Nix.OutputName: parseNameText :: Text -> Either InvalidNameError Text
+ System.Nix.Realisation: DerivationOutput :: DSum HashAlgo Digest -> a -> DerivationOutput a
+ System.Nix.Realisation: DerivationOutputError_Digest :: String -> DerivationOutputError
+ System.Nix.Realisation: DerivationOutputError_Name :: InvalidNameError -> DerivationOutputError
+ System.Nix.Realisation: DerivationOutputError_NoColon :: DerivationOutputError
+ System.Nix.Realisation: DerivationOutputError_NoExclamationMark :: DerivationOutputError
+ System.Nix.Realisation: DerivationOutputError_TooManyParts :: [Text] -> DerivationOutputError
+ System.Nix.Realisation: Realisation :: StorePath -> Set Signature -> Map (DerivationOutput OutputName) StorePath -> Realisation
+ System.Nix.Realisation: RealisationWithId :: (DerivationOutput OutputName, Realisation) -> RealisationWithId
+ System.Nix.Realisation: [derivationOutputHash] :: DerivationOutput a -> DSum HashAlgo Digest
+ System.Nix.Realisation: [derivationOutputOutput] :: DerivationOutput a -> a
+ System.Nix.Realisation: [realisationDependencies] :: Realisation -> Map (DerivationOutput OutputName) StorePath
+ System.Nix.Realisation: [realisationOutPath] :: Realisation -> StorePath
+ System.Nix.Realisation: [realisationSignatures] :: Realisation -> Set Signature
+ System.Nix.Realisation: [unRealisationWithId] :: RealisationWithId -> (DerivationOutput OutputName, Realisation)
+ System.Nix.Realisation: data DerivationOutput a
+ System.Nix.Realisation: data DerivationOutputError
+ System.Nix.Realisation: data Realisation
+ System.Nix.Realisation: derivationOutputBuilder :: (outputName -> Text) -> DerivationOutput outputName -> Builder
+ System.Nix.Realisation: derivationOutputParser :: (Text -> Either InvalidNameError outputName) -> Text -> Either DerivationOutputError (DerivationOutput outputName)
+ System.Nix.Realisation: instance GHC.Classes.Eq System.Nix.Realisation.DerivationOutputError
+ System.Nix.Realisation: instance GHC.Classes.Eq System.Nix.Realisation.Realisation
+ System.Nix.Realisation: instance GHC.Classes.Eq System.Nix.Realisation.RealisationWithId
+ System.Nix.Realisation: instance GHC.Classes.Eq a => GHC.Classes.Eq (System.Nix.Realisation.DerivationOutput a)
+ System.Nix.Realisation: instance GHC.Classes.Ord System.Nix.Realisation.DerivationOutputError
+ System.Nix.Realisation: instance GHC.Classes.Ord System.Nix.Realisation.Realisation
+ System.Nix.Realisation: instance GHC.Classes.Ord System.Nix.Realisation.RealisationWithId
+ System.Nix.Realisation: instance GHC.Classes.Ord a => GHC.Classes.Ord (System.Nix.Realisation.DerivationOutput a)
+ System.Nix.Realisation: instance GHC.Generics.Generic (System.Nix.Realisation.DerivationOutput a)
+ System.Nix.Realisation: instance GHC.Generics.Generic System.Nix.Realisation.Realisation
+ System.Nix.Realisation: instance GHC.Generics.Generic System.Nix.Realisation.RealisationWithId
+ System.Nix.Realisation: instance GHC.Show.Show System.Nix.Realisation.DerivationOutputError
+ System.Nix.Realisation: instance GHC.Show.Show System.Nix.Realisation.Realisation
+ System.Nix.Realisation: instance GHC.Show.Show System.Nix.Realisation.RealisationWithId
+ System.Nix.Realisation: instance GHC.Show.Show a => GHC.Show.Show (System.Nix.Realisation.DerivationOutput a)
+ System.Nix.Realisation: newtype RealisationWithId
+ System.Nix.Signature: Signature :: Signature -> Signature
+ System.Nix.Signature: instance GHC.Classes.Eq System.Nix.Signature.NarSignature
+ System.Nix.Signature: instance GHC.Classes.Eq System.Nix.Signature.Signature
+ System.Nix.Signature: instance GHC.Classes.Ord System.Nix.Signature.NarSignature
+ System.Nix.Signature: instance GHC.Classes.Ord System.Nix.Signature.Signature
+ System.Nix.Signature: instance GHC.Generics.Generic System.Nix.Signature.NarSignature
+ System.Nix.Signature: instance GHC.Generics.Generic System.Nix.Signature.Signature
+ System.Nix.Signature: instance GHC.Show.Show System.Nix.Signature.NarSignature
+ System.Nix.Signature: instance GHC.Show.Show System.Nix.Signature.Signature
+ System.Nix.Signature: narSignatureParser :: Parser NarSignature
+ System.Nix.Signature: narSignatureToText :: NarSignature -> Text
+ System.Nix.Signature: newtype Signature
+ System.Nix.Signature: parseNarSignature :: Text -> Either String NarSignature
+ System.Nix.Signature: parseSignature :: Text -> Either String Signature
+ System.Nix.Signature: signatureParser :: Parser Signature
+ System.Nix.Signature: signatureToText :: Signature -> Text
+ System.Nix.Store.Types: FileIngestionMethod_FileRecursive :: FileIngestionMethod
+ System.Nix.Store.Types: FileIngestionMethod_Flat :: FileIngestionMethod
+ System.Nix.Store.Types: PathFilter :: (FilePath -> Bool) -> PathFilter
+ System.Nix.Store.Types: RepairMode_DoRepair :: RepairMode
+ System.Nix.Store.Types: RepairMode_DontRepair :: RepairMode
+ System.Nix.Store.Types: [pathFilterFunction] :: PathFilter -> FilePath -> Bool
+ System.Nix.Store.Types: data FileIngestionMethod
+ System.Nix.Store.Types: data RepairMode
+ System.Nix.Store.Types: instance GHC.Classes.Eq System.Nix.Store.Types.FileIngestionMethod
+ System.Nix.Store.Types: instance GHC.Classes.Eq System.Nix.Store.Types.RepairMode
+ System.Nix.Store.Types: instance GHC.Classes.Ord System.Nix.Store.Types.FileIngestionMethod
+ System.Nix.Store.Types: instance GHC.Classes.Ord System.Nix.Store.Types.RepairMode
+ System.Nix.Store.Types: instance GHC.Enum.Bounded System.Nix.Store.Types.FileIngestionMethod
+ System.Nix.Store.Types: instance GHC.Enum.Bounded System.Nix.Store.Types.RepairMode
+ System.Nix.Store.Types: instance GHC.Enum.Enum System.Nix.Store.Types.FileIngestionMethod
+ System.Nix.Store.Types: instance GHC.Enum.Enum System.Nix.Store.Types.RepairMode
+ System.Nix.Store.Types: instance GHC.Generics.Generic System.Nix.Store.Types.FileIngestionMethod
+ System.Nix.Store.Types: instance GHC.Generics.Generic System.Nix.Store.Types.RepairMode
+ System.Nix.Store.Types: instance GHC.Show.Show System.Nix.Store.Types.FileIngestionMethod
+ System.Nix.Store.Types: instance GHC.Show.Show System.Nix.Store.Types.RepairMode
+ System.Nix.Store.Types: newtype PathFilter
+ System.Nix.StorePath: EmptyName :: InvalidNameError
+ System.Nix.StorePath: HashDecodingFailure :: String -> InvalidPathError
+ System.Nix.StorePath: InvalidCharacters :: Text -> InvalidNameError
+ System.Nix.StorePath: LeadingDot :: InvalidNameError
+ System.Nix.StorePath: NameTooLong :: Int -> InvalidNameError
+ System.Nix.StorePath: PathNameInvalid :: InvalidNameError -> InvalidPathError
+ System.Nix.StorePath: RootDirMismatch :: StoreDir -> StoreDir -> InvalidPathError
+ System.Nix.StorePath: [rdMismatchExpected] :: InvalidPathError -> StoreDir
+ System.Nix.StorePath: [rdMismatchGot] :: InvalidPathError -> StoreDir
+ System.Nix.StorePath: class HasStoreDir r
+ System.Nix.StorePath: data InvalidNameError
+ System.Nix.StorePath: data InvalidPathError
+ System.Nix.StorePath: data StorePathHashPart
+ System.Nix.StorePath: data StorePathName
+ System.Nix.StorePath: hasStoreDir :: HasStoreDir r => r -> StoreDir
+ System.Nix.StorePath: instance Data.Default.Class.Default System.Nix.StorePath.StoreDir
+ System.Nix.StorePath: instance Data.Hashable.Class.Hashable System.Nix.StorePath.InvalidNameError
+ System.Nix.StorePath: instance Data.Hashable.Class.Hashable System.Nix.StorePath.InvalidPathError
+ System.Nix.StorePath: instance Data.Hashable.Class.Hashable System.Nix.StorePath.StoreDir
+ System.Nix.StorePath: instance Data.Hashable.Class.Hashable System.Nix.StorePath.StorePath
+ System.Nix.StorePath: instance Data.Hashable.Class.Hashable System.Nix.StorePath.StorePathHashPart
+ System.Nix.StorePath: instance Data.Hashable.Class.Hashable System.Nix.StorePath.StorePathName
+ System.Nix.StorePath: instance GHC.Classes.Eq System.Nix.StorePath.InvalidNameError
+ System.Nix.StorePath: instance GHC.Classes.Eq System.Nix.StorePath.InvalidPathError
+ System.Nix.StorePath: instance GHC.Classes.Eq System.Nix.StorePath.StoreDir
+ System.Nix.StorePath: instance GHC.Classes.Eq System.Nix.StorePath.StorePath
+ System.Nix.StorePath: instance GHC.Classes.Eq System.Nix.StorePath.StorePathHashPart
+ System.Nix.StorePath: instance GHC.Classes.Eq System.Nix.StorePath.StorePathName
+ System.Nix.StorePath: instance GHC.Classes.Ord System.Nix.StorePath.InvalidNameError
+ System.Nix.StorePath: instance GHC.Classes.Ord System.Nix.StorePath.InvalidPathError
+ System.Nix.StorePath: instance GHC.Classes.Ord System.Nix.StorePath.StoreDir
+ System.Nix.StorePath: instance GHC.Classes.Ord System.Nix.StorePath.StorePath
+ System.Nix.StorePath: instance GHC.Classes.Ord System.Nix.StorePath.StorePathHashPart
+ System.Nix.StorePath: instance GHC.Classes.Ord System.Nix.StorePath.StorePathName
+ System.Nix.StorePath: instance GHC.Generics.Generic System.Nix.StorePath.InvalidNameError
+ System.Nix.StorePath: instance GHC.Generics.Generic System.Nix.StorePath.InvalidPathError
+ System.Nix.StorePath: instance GHC.Generics.Generic System.Nix.StorePath.StoreDir
+ System.Nix.StorePath: instance GHC.Generics.Generic System.Nix.StorePath.StorePath
+ System.Nix.StorePath: instance GHC.Generics.Generic System.Nix.StorePath.StorePathHashPart
+ System.Nix.StorePath: instance GHC.Generics.Generic System.Nix.StorePath.StorePathName
+ System.Nix.StorePath: instance GHC.Show.Show System.Nix.StorePath.InvalidNameError
+ System.Nix.StorePath: instance GHC.Show.Show System.Nix.StorePath.InvalidPathError
+ System.Nix.StorePath: instance GHC.Show.Show System.Nix.StorePath.StoreDir
+ System.Nix.StorePath: instance GHC.Show.Show System.Nix.StorePath.StorePath
+ System.Nix.StorePath: instance GHC.Show.Show System.Nix.StorePath.StorePathHashPart
+ System.Nix.StorePath: instance GHC.Show.Show System.Nix.StorePath.StorePathName
+ System.Nix.StorePath: mkStorePathName :: Text -> Either InvalidNameError StorePathName
+ System.Nix.StorePath: parseNameText :: Text -> Either InvalidNameError Text
+ System.Nix.StorePath: parsePathFromText :: StoreDir -> Text -> Either InvalidPathError StorePath
+ System.Nix.StorePath: storePathHash :: StorePath -> StorePathHashPart
+ System.Nix.StorePath: storePathHashPartToText :: StorePathHashPart -> Text
+ System.Nix.StorePath: storePathName :: StorePath -> StorePathName
+ System.Nix.StorePath: unStorePathHashPart :: StorePathHashPart -> ByteString
+ System.Nix.StorePath: unStorePathName :: StorePathName -> Text
+ System.Nix.StorePath: unsafeMakeStorePath :: StorePathHashPart -> StorePathName -> StorePath
+ System.Nix.StorePath: unsafeMakeStorePathHashPart :: ByteString -> StorePathHashPart
+ System.Nix.StorePath.Metadata: BuiltElsewhere :: StorePathTrust
+ System.Nix.StorePath.Metadata: BuiltLocally :: StorePathTrust
+ System.Nix.StorePath.Metadata: Metadata :: !Maybe a -> !DSum HashAlgo Digest -> !HashSet a -> !UTCTime -> !Maybe Word64 -> !StorePathTrust -> !Set NarSignature -> !Maybe ContentAddress -> Metadata a
+ System.Nix.StorePath.Metadata: [metadataContentAddress] :: Metadata a -> !Maybe ContentAddress
+ System.Nix.StorePath.Metadata: [metadataDeriverPath] :: Metadata a -> !Maybe a
+ System.Nix.StorePath.Metadata: [metadataNarBytes] :: Metadata a -> !Maybe Word64
+ System.Nix.StorePath.Metadata: [metadataNarHash] :: Metadata a -> !DSum HashAlgo Digest
+ System.Nix.StorePath.Metadata: [metadataReferences] :: Metadata a -> !HashSet a
+ System.Nix.StorePath.Metadata: [metadataRegistrationTime] :: Metadata a -> !UTCTime
+ System.Nix.StorePath.Metadata: [metadataSigs] :: Metadata a -> !Set NarSignature
+ System.Nix.StorePath.Metadata: [metadataTrust] :: Metadata a -> !StorePathTrust
+ System.Nix.StorePath.Metadata: data Metadata a
+ System.Nix.StorePath.Metadata: data StorePathTrust
+ System.Nix.StorePath.Metadata: instance GHC.Classes.Eq System.Nix.StorePath.Metadata.StorePathTrust
+ System.Nix.StorePath.Metadata: instance GHC.Classes.Eq a => GHC.Classes.Eq (System.Nix.StorePath.Metadata.Metadata a)
+ System.Nix.StorePath.Metadata: instance GHC.Classes.Ord System.Nix.StorePath.Metadata.StorePathTrust
+ System.Nix.StorePath.Metadata: instance GHC.Classes.Ord a => GHC.Classes.Ord (System.Nix.StorePath.Metadata.Metadata a)
+ System.Nix.StorePath.Metadata: instance GHC.Enum.Enum System.Nix.StorePath.Metadata.StorePathTrust
+ System.Nix.StorePath.Metadata: instance GHC.Generics.Generic (System.Nix.StorePath.Metadata.Metadata a)
+ System.Nix.StorePath.Metadata: instance GHC.Generics.Generic System.Nix.StorePath.Metadata.StorePathTrust
+ System.Nix.StorePath.Metadata: instance GHC.Show.Show System.Nix.StorePath.Metadata.StorePathTrust
+ System.Nix.StorePath.Metadata: instance GHC.Show.Show a => GHC.Show.Show (System.Nix.StorePath.Metadata.Metadata a)
- System.Nix.Build: BuildResult :: !BuildStatus -> !Maybe Text -> !Integer -> !Bool -> !UTCTime -> !UTCTime -> BuildResult
+ System.Nix.Build: BuildResult :: BuildStatus -> Maybe Text -> Maybe Int -> Maybe Bool -> Maybe UTCTime -> Maybe UTCTime -> Maybe (Map (DerivationOutput OutputName) Realisation) -> BuildResult
- System.Nix.Build: buildSuccess :: BuildResult -> Bool
+ System.Nix.Build: buildSuccess :: BuildStatus -> Bool
- System.Nix.Hash: mkNamedDigest :: Text -> Text -> Either String SomeNamedDigest
+ System.Nix.Hash: mkNamedDigest :: Text -> Text -> Either String (DSum HashAlgo Digest)
- System.Nix.Signature: NarSignature :: PublicKey -> Signature -> NarSignature
+ System.Nix.Signature: NarSignature :: !Text -> !Signature -> NarSignature
- System.Nix.Signature: [publicKey] :: NarSignature -> PublicKey
+ System.Nix.Signature: [publicKey] :: NarSignature -> !Text
- System.Nix.Signature: [sig] :: NarSignature -> Signature
+ System.Nix.Signature: [sig] :: NarSignature -> !Signature
- System.Nix.StorePath: mkStorePathHashPart :: ByteString -> StorePathHashPart
+ System.Nix.StorePath: mkStorePathHashPart :: forall hashAlgo. HashAlgorithm hashAlgo => ByteString -> StorePathHashPart
- System.Nix.StorePath: parsePath :: StoreDir -> ByteString -> Either String StorePath
+ System.Nix.StorePath: parsePath :: StoreDir -> ByteString -> Either InvalidPathError StorePath

Files

+ CHANGELOG.md view
@@ -0,0 +1,167 @@+# [0.8.0.0](https://github.com/haskell-nix/hnix-store/compare/core-0.7.0.0...core-0.8.0.0) 2024-07-31++* Changes:+   * `System.Nix.StorePath.makeStorePathName` renamed to `System.Nix.StorePath.mkStorePathName`+   * `System.Nix.ReadOnlyStore` moved to `hnix-store-readonly` package+     and renamed to `System.Nix.Store.ReadOnly` [#247](https://github.com/haskell-nix/hnix-store/pull/247)+   * `System.Nix.Nar*` moved to `hnix-store-nar` package [#247](https://github.com/haskell-nix/hnix-store/pull/247)+   * `Arbitrary` instances moved to `hnix-store-tests` package [#241](https://github.com/haskell-nix/hnix-store/pull/241)+   * `System.Nix.Internal` namespace was removed [#236](https://github.com/haskell-nix/hnix-store/pull/236)+   * `StorePathMetadata` converted to `Metadata a` [#231](https://github.com/haskell-nix/hnix-store/pull/231)+     * Moved to `System.Nix.StorePath.Metadata` [#236](https://github.com/haskell-nix/hnix-store/pull/236)+   * Constructors of `StorePathName` and `StorePathHashPart` are no longer+     exported. Use respective `mkStorePath..` functions. [#230](https://github.com/haskell-nix/hnix-store/pull/230)+   * `StorePath` constructor is no longer exported.+     Prefer `parsePath` or `parsePathFromText`.+     If needed to construct a path directly, use `unsafeMakeStorePath` [#247](https://github.com/haskell-nix/hnix-store/pull/247)+   * `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:+   * `System.Nix.Store.Types` with `FileIngestionMethod`, `PathFilter`, `RepairMode` types  [#247](https://github.com/haskell-nix/hnix-store/pull/247)+     `FileIngestionMethod` was previously called `NarHashMode`+   * `System.Nix.StorePath.parsePathFromText` [#236](https://github.com/haskell-nix/hnix-store/pull/236)+   * `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)+     * `StorePath`+     * `StorePathName`+     * `StorePathHashPart`+     * `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++* Changes:+    * `StorePath` no longer carries `storePathRoot` field and we+      have a stand-alone `StoreDir` type instead to be used instead of `FilePath`+      when store root directory is needed as a context [#216](https://github.com/haskell-nix/hnix-store/pull/216)++* Fixes:+    * NAR encoding and decoding now supports case-insensitive filesystems [#218](https://github.com/haskell-nix/hnix-store/pull/218)+      * The "case hack" replicates the behavior of the `use-case-hack` option in Nix, which adds a suffix to conflicting filenames.+        This feature is enabled by default on macOS (darwin).++* Additions:+      * `data NarOptions` has been added to configure NAR encoding and decoding. The `optUseCaseHack` field can be used to enable or disable the case hack [#218](https://github.com/haskell-nix/hnix-store/pull/218)+      * New `streamNarIOWithOptions` and `runParserWithOptions` functions have been added to `System.Nix.Nar` to support the new configurable options [#218](https://github.com/haskell-nix/hnix-store/pull/218)++# [0.6.1.0](https://github.com/haskell-nix/hnix-store/compare/core-0.6.0.0...core-0.6.1.0) 2023-01-02++* Fixes:++    * NAR serialization compatibility (symlinks, directory symlinks, UTF-8 handling) [#201](https://github.com/haskell-nix/hnix-store/pull/201) [#203](https://github.com/haskell-nix/hnix-store/pull/203)++# [0.6.0.0](https://github.com/haskell-nix/hnix-store/compare/core-0.5.0.0...core-0.6.0.0) 2022-06-06++* Breaking:++    * `streamNarIO` changes type and returns `NarSource m` [#177](https://github.com/haskell-nix/hnix-store/pull/177)+      * `FilePath` can turn to `NarSource m` using `dumpPath`+      * `ByteString` can turn to `NarSource m` using `dumpString`++# [0.5.0.0](https://github.com/haskell-nix/hnix-store/compare/0.4.3.0...core-0.5.0.0) 2021-06-10++* Breaking:++  * `System.Nix.Hash`:+    * Migration from packages `cryptohash-` -> `cryptonite` [#157](https://github.com/haskell-nix/hnix-store/pull/157/commits/97146b41cc87327625e02b81971aeb2fd7d66a3f)+      * rm `newtype Digest` in favour of `cryptonite: newtype Digest`+      * rm `data HashAlgorithm` in favour of `cryptonite: class HashAlgorithm`+      * rm `class ValidAlgo` in favour of `cryptonite: class HashAlgorithm`.+      * `class NamedAlgo` removed `hashSize` in favour of `cryptonite: class HashAlgorithm: hashDigestSize`. Former became a subclass of the latter.+      * rm `hash` in favour of `cryptonite: hash`+      * rm `hashLazy` in favour of `cryptonite: hashlazy`+    * Base encoding/decoding function for hashes (digests) changed (due to changes in type system & separation of specially truncated Nix Store hasing) [#157](https://github.com/haskell-nix/hnix-store/pull/157/commits/2af74986de8aef1a13dbfc955886f9935ca246a3)+      * `encode(InBase -> DigestWith)`+      * `decode(Base -> DigestWith)`+  * `System.Nix.StorePath` [#157](https://github.com/haskell-nix/hnix-store/pull/157/commits/2af74986de8aef1a13dbfc955886f9935ca246a3)+    * rm `type StorePathHashAlgo = 'Truncated 20 'SHA256` in favour of `StorePathHashPart` & `mkStorePathHashPart`.+    * rm `unStorePathName`, please use `GHC: coerce` for `StorePathName <-> Text`, `StorePathName` data constructor is provided.+  * `Internal` modules now have export lists, if something, please contact.++* Additional:++  * Support of GHC 9.0 [#157](https://github.com/haskell-nix/hnix-store/pull/157/commits/97146b41cc87327625e02b81971aeb2fd7d66a3f)+  * `System.Nix.StorePath` [#157](https://github.com/haskell-nix/hnix-store/pull/157/commits/2af74986de8aef1a13dbfc955886f9935ca246a3)+    * exposed `StorePathName` data constructor to API.+    * added `newtype StorePathHashPart = StorePathHashPart ByteString`.+      * added builder `mkStorePathHashPart :: ByteString -> StorePathHashPart`+  * `System.Nix.Hash` [#157](https://github.com/haskell-nix/hnix-store/pull/157/commits/2af74986de8aef1a13dbfc955886f9935ca246a3)+    * Nix store (which are specially truncated) hashes are now handled separately from other hashes:+      * add `mkStorePathHash` - a function to create a content into Nix storepath-style hash:+        `mkStorePathHash :: HashAlgorithm a => ByteString -> ByteString`+        but recommend to at once use `mkStorePathHashPart`.++# [0.4.3.0](https://github.com/haskell-nix/hnix-store/compare/0.4.2.0...0.4.3.0) 2021-05-30++* Additional:+  * `System.Nix.ReadonlyStore`: add a readonly `computeStorePathForPath` [b85f7c8](https://github.com/haskell-nix/hnix-store/commit/b85f7c875fe6b0bca939ffbcd8b9bd0ab1598aa0)+  * `System.Nix.ReadonlyStore`: `computeStorePathForPath`: force SHA256 as it's the only valid choice [db71ece](https://github.com/haskell-nix/hnix-store/commit/db71ecea3109c0ba270fa98a9041a8556e35217f)+  * `makeTextPath`: order the references [5fddf3c](https://github.com/haskell-nix/hnix-store/commit/5fddf3c66ba1bcabb72c4d6b6e09fb41a7acd62c)++# [0.4.2.0](https://github.com/haskell-nix/hnix-store/compare/0.4.1.0...0.4.2.0) 2021-03-12++* Additional:+  * Cabal now properly states `tasty-discover` as `build-tool-depends` [5d03ffc](https://github.com/haskell-nix/hnix-store/commit/5d03ffc4cde9448df05e84838ece70cc83b1b6c) +  * Added explicit `hie.yml` cradle description for `cabal` to help Haskell Language Server to work with monorepo [5bad385](https://github.com/haskell-nix/hnix-store/commit/b5ad38573d27e0732d0fadfebd98de1f753b4f07)+  * Removed vacuous `Setup.hs`, it was throwing-off HLS, and anyway file is vacuous and gets deprecated by Cabal itself [a5b7a61](https://github.com/haskell-nix/hnix-store/commit/a5b7a614c0e0e11147a93b9a197c2a443afa3244)+  * Nix dev env: removed GHC 8.6.5 support, afaik it is not even in Nixpkgs anymore [cf04083](https://github.com/haskell-nix/hnix-store/commit/cf04083aba98ad40d183d1e26251101816cc07ae)+  * Test suite: fixed nar test for the envs without `/proc` (test suite now works on `macOS`) [2a897ab](https://github.com/haskell-nix/hnix-store/commit/2a897ab581c0501587ce04da6d6e3a6f543b1d72)+++# [0.4.1.0](https://github.com/haskell-nix/hnix-store/compare/0.4.0.0...0.4.1.0) 2021-01-16++* Big clean-up of dependencies.++# [0.4.0.0](https://github.com/haskell-nix/hnix-store/compare/0.3.0.0...0.4.0.0) 2020-12-30++* `System.Nix.Hash` no longer exports `encodeBase16, decodeBase16` and their `Base32` counterparts.+    These were replaced by `encodeInBase` and `decodeBase` functions+    accepting `BaseEncoding` data type [#87](https://github.com/haskell-nix/hnix-store/pull/87)+* Support `base16-bytestring >= 1` [#86](https://github.com/haskell-nix/hnix-store/pull/86) [#100](https://github.com/haskell-nix/hnix-store/pull/100)++# 0.3.0.0 -- 2020-11-29++* `System.Nix.Nar` changes API to support NAR format streaming:+  * `buildNarIO :: FilePath -> Handle -> IO ()` - Create a NAR from a regular filesystem object, stream it out on the Handle+  * `unpackNarIO :: Handle -> FilePath -> IO ()` - Recreate filesystem object from a NAR file accessed by the Handle+* `StorePath` type changed to simple variant without type level+symbolic store path root.+* Added `makeFixedOutputPath` to `System.Nix.ReadonlyStore`+* Added `decodeBase16` and `decodeBase32` to `System.Nix.Hash`+* `System.Nix.StorePath` module now provides+  * `storePathToFilePath` and `storePathToText` helpers+  * `storePathToNarInfo` for converting paths to `narinfo` URLs+  * `parsePath` function+  * `pathParser` Attoparsec parser+* Added `System.Nix.Build` module+* Added `System.Nix.Derivation` module+* Removed `System.Nix.Util` module, moved to `hnix-store-remote`+* Added base64 and SHA512 hash support++# 0.2.0.0 -- 2020-03-12++Removed `System.Nix.Store`. We may reintroduce it later when multiple backends+exist and we can tell what common effects they should share.++# 0.1.0.0  -- 2019-03-18++* First version.++---++`hnix-store-core` uses [PVP Versioning][1].++[1]: https://pvp.haskell.org
− ChangeLog.md
@@ -1,125 +0,0 @@-# ChangeLog--## [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--* Breaking:-    * [(link)](https://github.com/haskell-nix/hnix-store/pull/216) `StorePath` no longer carries `storePathRoot` field and we-      have a stand-alone `StoreDir` type instead to be used instead of `FilePath`-      when store root directory is needed as a context.--* Additional:-    * [(link)](https://github.com/haskell-nix/hnix-store/pull/218) NAR encoding and decoding now supports case-insensitive filesystems.-      * The "case hack" replicates the behavior of the `use-case-hack` option in Nix, which adds a suffix to conflicting filenames.-        This feature is enabled by default on macOS (darwin).-      * `data NarOptions` has been added to configure NAR encoding and decoding. The `optUseCaseHack` field can be used to enable or disable the case hack.-      * New `streamNarIOWithOptions` and `runParserWithOptions` functions have been added to `System.Nix.Nar` to support the new configurable options.--## [0.6.1.0](https://github.com/haskell-nix/hnix-store/compare/core-0.6.0.0...core-0.6.1.0) 2023-01-02--* Fixed:--    * [(link)](https://github.com/haskell-nix/hnix-store/pull/201) [(link)](https://github.com/haskell-nix/hnix-store/pull/203) NAR serialization compatibility (symlinks, directory symlinks, UTF-8 handling)--## [0.6.0.0](https://github.com/haskell-nix/hnix-store/compare/core-0.5.0.0...core-0.6.0.0) 2022-06-06--* Breaking:--    * [(link)](https://github.com/haskell-nix/hnix-store/pull/177) `streamNarIO` changes type and returns `NarSource m`-      * `FilePath` can turn to `NarSource m` using `dumpPath`-      * `ByteString` can turn to `NarSource m` using `dumpString`--## [0.5.0.0](https://github.com/haskell-nix/hnix-store/compare/0.4.3.0...core-0.5.0.0) 2021-06-10--* Breaking:--  * `System.Nix.Hash`:-    * [(link)](https://github.com/haskell-nix/hnix-store/pull/157/commits/97146b41cc87327625e02b81971aeb2fd7d66a3f) Migration from packages `cryptohash-` -> `cryptonite`:-      * rm `newtype Digest` in favour of `cryptonite: newtype Digest`-      * rm `data HashAlgorithm` in favour of `cryptonite: class HashAlgorithm`-      * rm `class ValidAlgo` in favour of `cryptonite: class HashAlgorithm`.-      * `class NamedAlgo` removed `hashSize` in favour of `cryptonite: class HashAlgorithm: hashDigestSize`. Former became a subclass of the latter.-      * rm `hash` in favour of `cryptonite: hash`-      * rm `hashLazy` in favour of `cryptonite: hashlazy`-    * [(link)](https://github.com/haskell-nix/hnix-store/pull/157/commits/2af74986de8aef1a13dbfc955886f9935ca246a3) Base encoding/decoding function for hashes (digests) changed (due to changes in type system & separation of specially truncated Nix Store hasing):-      * `encode(InBase -> DigestWith)`-      * `decode(Base -> DigestWith)`-  * [(link)](https://github.com/haskell-nix/hnix-store/pull/157/commits/2af74986de8aef1a13dbfc955886f9935ca246a3) `System.Nix.StorePath`:-    * rm `type StorePathHashAlgo = 'Truncated 20 'SHA256` in favour of `StorePathHashPart` & `mkStorePathHashPart`.-    * rm `unStorePathName`, please use `GHC: coerce` for `StorePathName <-> Text`, `StorePathName` data constructor is provided.-  * `Internal` modules now have export lists, if something, please contact.---* Additional:--  * [(link)](https://github.com/haskell-nix/hnix-store/pull/157/commits/97146b41cc87327625e02b81971aeb2fd7d66a3f) Support of GHC 9.0.--  * [(link)](https://github.com/haskell-nix/hnix-store/pull/157/commits/2af74986de8aef1a13dbfc955886f9935ca246a3) `System.Nix.StorePath`:-    * exposed `StorePathName` data constructor to API.-    * added `newtype StorePathHashPart = StorePathHashPart ByteString`.-      * added builder `mkStorePathHashPart :: ByteString -> StorePathHashPart`-  * [(link)](https://github.com/haskell-nix/hnix-store/pull/157/commits/2af74986de8aef1a13dbfc955886f9935ca246a3) `System.Nix.Hash`:-    * Nix store (which are specially truncated) hashes are now handled separately from other hashes:-      * add `mkStorePathHash` - a function to create a content into Nix storepath-style hash:-        `mkStorePathHash :: HashAlgorithm a => ByteString -> ByteString`-        but recommend to at once use `mkStorePathHashPart`.--## [0.4.3.0](https://github.com/haskell-nix/hnix-store/compare/0.4.2.0...0.4.3.0) 2021-05-30--* Additional:-  * [(link)](https://github.com/haskell-nix/hnix-store/commit/b85f7c875fe6b0bca939ffbcd8b9bd0ab1598aa0) `System.Nix.ReadonlyStore`: add a readonly `computeStorePathForPath`-  * [(link)](https://github.com/haskell-nix/hnix-store/commit/db71ecea3109c0ba270fa98a9041a8556e35217f) `System.Nix.ReadonlyStore`: `computeStorePathForPath`: force SHA256 as it's the only valid choice-  * [(link)](https://github.com/haskell-nix/hnix-store/commit/5fddf3c66ba1bcabb72c4d6b6e09fb41a7acd62c): `makeTextPath`: order the references--## [0.4.2.0](https://github.com/haskell-nix/hnix-store/compare/0.4.1.0...0.4.2.0) 2021-03-12--* Additional:--  * [(link)](https://github.com/haskell-nix/hnix-store/commit/5d03ffc43cde9448df05e84838ece70cc83b1b6c) Cabal now properly states `tasty-discover` as `build-tool-depends`.--  * [(link)](https://github.com/haskell-nix/hnix-store/commit/b5ad38573d27e0732d0fadfebd98de1f753b4f07) added explicit `hie.yml` cradle description for `cabal` to help Haskell Language Server to work with monorepo.--  * [(link)](https://github.com/haskell-nix/hnix-store/commit/a5b7a614c0e0e11147a93b9a197c2a443afa3244) rm vacuous `Setup.hs`, it was throwing-off HLS, and anyway file is vacuous and gets deprecated by Cabal itself.--  * [(link)](https://github.com/haskell-nix/hnix-store/commit/cf04083aba98ad40d183d1e26251101816cc07ae) Nix dev env: removed GHC 8.6.5 support, afaik it is not even in Nixpkgs anymore.--  * [(link)](https://github.com/haskell-nix/hnix-store/commit/2a897ab581c0501587ce04da6d6e3a6f543b1d72) Test suite: fixed nar test for the envs without `/proc` (test suite now works on `macOS`).---## [0.4.1.0](https://github.com/haskell-nix/hnix-store/compare/0.4.0.0...0.4.1.0) 2021-01-16--* Big clean-up of dependencies.--## [0.4.0.0](https://github.com/haskell-nix/hnix-store/compare/0.3.0.0...0.4.0.0) 2020-12-30--* `System.Nix.Hash` no longer exports `encodeBase16, decodeBase16` and their `Base32` counterparts.-    These were replaced by `encodeInBase` and `decodeBase` functions-    accepting `BaseEncoding` data type [#87](https://github.com/haskell-nix/hnix-store/pull/87)-* Support `base16-bytestring >= 1` [#86](https://github.com/haskell-nix/hnix-store/pull/86) [#100](https://github.com/haskell-nix/hnix-store/pull/100)--## 0.3.0.0 -- 2020-11-29--* `System.Nix.Nar` changes API to support NAR format streaming:-  * `buildNarIO :: FilePath -> Handle -> IO ()` - Create a NAR from a regular filesystem object, stream it out on the Handle-  * `unpackNarIO :: Handle -> FilePath -> IO ()` - Recreate filesystem object from a NAR file accessed by the Handle-* `StorePath` type changed to simple variant without type level-symbolic store path root.-* Added `makeFixedOutputPath` to `System.Nix.ReadonlyStore`-* Added `decodeBase16` and `decodeBase32` to `System.Nix.Hash`-* `System.Nix.StorePath` module now provides-  * `storePathToFilePath` and `storePathToText` helpers-  * `storePathToNarInfo` for converting paths to `narinfo` URLs-  * `parsePath` function-  * `pathParser` Attoparsec parser-* Added `System.Nix.Build` module-* Added `System.Nix.Derivation` module-* Removed `System.Nix.Util` module, moved to `hnix-store-remote`-* Added base64 and SHA512 hash support--## 0.2.0.0 -- 2020-03-12--Removed `System.Nix.Store`. We may reintroduce it later when multiple backends-exist and we can tell what common effects they should share.--## 0.1.0.0  -- 2019-03-18--* First version.
README.md view
@@ -1,14 +1,5 @@ # hnix-store-core -Core effects for interacting with the Nix store.--See `NarEffects` in [System.Nix.Internal.Nar.Effects] and the [System.Nix.StorePath] for the available operations-on the store.+Core types and functions for interacting with the Nix store. -[System.Nix.Internal.Nar.Effects]: ./src/System/Nix/Internal/Nar/Effects.hs [System.Nix.StorePath]: ./src/System/Nix/StorePath.hs---# Tests-- - `ghcid --command "cabal repl test-suite:format-tests" --test="Main.main"`
hnix-store-core.cabal view
@@ -1,123 +1,110 @@ cabal-version:       2.2 name:                hnix-store-core-version:             0.7.0.0-synopsis:            Core effects for interacting with the Nix store.+version:             0.8.0.0+synopsis:            Core types used for interacting with the Nix store. description:   This package contains types and functions needed to describe-  interactions with the Nix store abstracted away from-  specific effectful context.+  interactions with the Nix store. homepage:            https://github.com/haskell-nix/hnix-store license:             Apache-2.0 license-file:        LICENSE author:              Shea Levy-maintainer:          shea@shealevy.com+maintainer:          srk@48.io copyright:           2018 Shea Levy category:            Nix build-type:          Simple+extra-doc-files:+    CHANGELOG.md extra-source-files:-    ChangeLog.md-  , README.md+    README.md   , tests/samples/example0.drv   , tests/samples/example1.drv-  , tests/fixtures/case-conflict.nar -Common commons-  if impl(ghc >= 8.10)-    ghc-options:  -Wall -Wunused-packages-  else-    ghc-options:  -Wall+common commons+  ghc-options:  -Wall+  default-extensions:+      ConstraintKinds+    , DataKinds+    , DeriveGeneric+    , DeriveDataTypeable+    , DeriveFunctor+    , DeriveFoldable+    , DeriveTraversable+    , DeriveLift+    , DerivingStrategies+    , DerivingVia+    , ExistentialQuantification+    , FlexibleContexts+    , FlexibleInstances+    , GADTs+    , ScopedTypeVariables+    , StandaloneDeriving+    , RecordWildCards+    , TypeApplications+    , TypeFamilies+    , TypeOperators+    , TypeSynonymInstances+    , InstanceSigs+    , KindSignatures+    , MultiParamTypeClasses+    , MultiWayIf+    , TupleSections+    , LambdaCase+    , BangPatterns+    , ViewPatterns+  default-language: Haskell2010  library   import: commons   exposed-modules:-      System.Nix.Base32+      System.Nix.Base+    , System.Nix.Base32     , System.Nix.Build+    , System.Nix.ContentAddress     , System.Nix.Derivation+    , System.Nix.DerivedPath+    , System.Nix.Fingerprint     , System.Nix.Hash-    , System.Nix.Internal.Base-    , System.Nix.Internal.Base32-    , System.Nix.Internal.Truncation-    , System.Nix.Internal.Hash-    , System.Nix.Internal.Nar.Parser-    , System.Nix.Internal.Nar.Streamer-    , System.Nix.Internal.Nar.Effects-    , System.Nix.Internal.Nar.Options-    , System.Nix.Internal.Signature-    , System.Nix.Internal.StorePath-    , System.Nix.Nar-    , System.Nix.ReadonlyStore+    , System.Nix.Hash.Truncation+    , System.Nix.OutputName+    , System.Nix.Realisation     , System.Nix.Signature+    , System.Nix.Store.Types     , System.Nix.StorePath-    , System.Nix.StorePathMetadata+    , System.Nix.StorePath.Metadata   build-depends:       base >=4.12 && <5-    , relude >= 1.0     , attoparsec-    , algebraic-graphs >= 0.5 && < 0.8-    , base16-bytestring-    , base64-bytestring+    , base16-bytestring >= 1.0+    , base64-bytestring >= 1.2.1     , bytestring-    , case-insensitive-    , cereal     , containers-    -- Required for cryptonite low-level type convertion-    , memory-    , cryptonite-    , directory+    , constraints-extras+    , crypton+    , data-default-class+    , dependent-sum > 0.7+    , dependent-sum-template >= 0.2.0.1 && < 0.3     , filepath     , hashable-    , lifted-base-    , monad-control-    , mtl+    -- Required for crypton low-level type convertion+    , memory     , nix-derivation >= 1.1.1 && <2-    , saltine+    , some > 1.0.5 && < 2     , time     , text-    , unix     , unordered-containers     , vector-  mixins:-      base hiding (Prelude)-    , relude (Relude as Prelude)-    , relude-  default-extensions:-      OverloadedStrings-    , DeriveGeneric-    , DeriveDataTypeable-    , DeriveFunctor-    , DeriveFoldable-    , DeriveTraversable-    , DeriveLift-    , FlexibleContexts-    , FlexibleInstances-    , StandaloneDeriving-    , TypeApplications-    , TypeSynonymInstances-    , InstanceSigs-    , MultiParamTypeClasses-    , TupleSections-    , LambdaCase-    , BangPatterns-    , ViewPatterns   hs-source-dirs:      src-  default-language:    Haskell2010 -Flag bounded_memory-  description: Run tests of constant memory use (requires +RTS -T)-  default: False--test-suite format-tests+test-suite core   import: commons-  if flag(bounded_memory)-    cpp-options: -DBOUNDED_MEMORY-    ghc-options: -rtsopts -fprof-auto   type: exitcode-stdio-1.0   main-is: Driver.hs   other-modules:-    Arbitrary     Derivation-    NarFormat+    Fingerprint     Hash+    Signature     StorePath   hs-source-dirs:     tests@@ -127,46 +114,16 @@       hnix-store-core     , attoparsec     , base-    , relude     , base16-bytestring     , base64-bytestring-    , binary     , bytestring     , containers-    , cryptonite-    , directory-    , filepath-    , process+    , crypton+    , data-default-class+    , hspec     , tasty     , tasty-golden-    , hspec     , tasty-hspec-    , tasty-hunit-    , tasty-quickcheck-    , temporary     , text-    , unix-  mixins:-      base hiding (Prelude)-    , relude (Relude as Prelude)-    , relude-  default-extensions:-      OverloadedStrings-    , DeriveGeneric-    , DeriveDataTypeable-    , DeriveFunctor-    , DeriveFoldable-    , DeriveTraversable-    , DeriveLift-    , FlexibleContexts-    , FlexibleInstances-    , StandaloneDeriving-    , TypeApplications-    , TypeSynonymInstances-    , InstanceSigs-    , MultiParamTypeClasses-    , TupleSections-    , LambdaCase-    , BangPatterns-    , ViewPatterns-  default-language: Haskell2010+    , time+    , unordered-containers
+ src/System/Nix/Base.hs view
@@ -0,0 +1,45 @@+module System.Nix.Base+  ( BaseEncoding(Base16,NixBase32,Base64)+  , encodeWith+  , decodeWith+  ) where++import Data.ByteString (ByteString)+import Data.Text (Text)+import GHC.Generics (Generic)++import qualified Data.Text.Encoding+import qualified Data.ByteString.Base16+import qualified Data.ByteString.Base64++import qualified System.Nix.Base32 -- Nix has own Base32 encoding++-- | Constructors to indicate the base encodings+data BaseEncoding+  = NixBase32+  -- | ^ Nix has a special map of Base32 encoding+  -- Placed first, since it determines Haskell optimizations of pattern matches,+  -- & NixBase seems be the most widely used in Nix.+  | Base16+  | Base64+  deriving (Bounded, Eq, Enum, Generic, Ord, Show)++-- | Encode @ByteString@ with @Base@ encoding, produce @Text@.+encodeWith :: BaseEncoding -> ByteString -> Text+encodeWith Base16 =+  Data.Text.Encoding.decodeUtf8+  . Data.ByteString.Base16.encode+encodeWith NixBase32 = System.Nix.Base32.encode+encodeWith Base64 =+  Data.Text.Encoding.decodeUtf8+  . Data.ByteString.Base64.encode++-- | Take the input & @Base@ encoding witness -> decode into @Text@.+decodeWith :: BaseEncoding -> Text -> Either String ByteString+decodeWith Base16 =+  Data.ByteString.Base16.decode+  . Data.Text.Encoding.encodeUtf8+decodeWith NixBase32 =  System.Nix.Base32.decode+decodeWith Base64 =+  Data.ByteString.Base64.decode+  . Data.Text.Encoding.encodeUtf8
src/System/Nix/Base32.hs view
@@ -4,7 +4,97 @@ module System.Nix.Base32   ( encode   , decode-  )-where+  , digits32+  ) where -import           System.Nix.Internal.Base32+import Data.ByteString (ByteString)+import Data.Text (Text)+import Data.Vector (Vector)+import Data.Word (Word8)++import qualified Data.Bits+import qualified Data.Bool+import qualified Data.ByteString+import qualified Data.ByteString.Char8+import qualified Data.List+import qualified Data.Maybe+import qualified Data.Text+import qualified Data.Vector+import qualified Numeric++-- omitted: E O U T+digits32 :: Vector Char+digits32 = Data.Vector.fromList "0123456789abcdfghijklmnpqrsvwxyz"++-- | Encode a 'BS.ByteString' in Nix's base32 encoding+encode :: ByteString -> Text+encode c = Data.Text.pack $ takeCharPosFromDict <$> [nChar - 1, nChar - 2 .. 0]+ where+  -- Each base32 character gives us 5 bits of information, while+  -- each byte gives is 8. Because 'div' rounds down, we need to add+  -- one extra character to the result, and because of that extra 1+  -- we need to subtract one from the number of bits in the+  -- bytestring to cover for the case where the number of bits is+  -- already a factor of 5. Thus, the + 1 outside of the 'div' and+  -- the - 1 inside of it.+  nChar = fromIntegral $ ((Data.ByteString.length c * 8 - 1) `div` 5) + 1++  byte  = Data.ByteString.index c . fromIntegral++  -- May need to switch to a more efficient calculation at some+  -- point.+  bAsInteger :: Integer+  bAsInteger =+    sum+      [ fromIntegral (byte j) * (256 ^ j)+        | j <- [0 .. Data.ByteString.length c - 1] ]++  takeCharPosFromDict :: Integer -> Char+  takeCharPosFromDict i = digits32 Data.Vector.! digitInd+   where+    digitInd =+      fromIntegral $+        bAsInteger `div` (32^i) `mod` 32++-- | Decode Nix's base32 encoded text+decode :: Text -> Either String ByteString+decode what =+  Data.Bool.bool+    (Left "Invalid NixBase32 string")+    (unsafeDecode what)+    (Data.Text.all (`elem` digits32) what)++-- | Decode Nix's base32 encoded text+-- Doesn't check if all elements match `digits32`+unsafeDecode :: Text -> Either String ByteString+unsafeDecode what =+  case+      Numeric.readInt+        32+        (`elem` digits32)+        (\c -> Data.Maybe.fromMaybe (error "character not in digits32")+          $ Data.Vector.findIndex (== c) digits32+        )+        (Data.Text.unpack what)+    of+      [(i, _)] -> pure $ padded $ integerToBS i+      x        -> Left $ "Can't decode: readInt returned " <> show x+ where+  padded x+    | Data.ByteString.length x < decLen = x `Data.ByteString.append` bstr+    | otherwise               = x+   where+    bstr = Data.ByteString.Char8.pack $ take (decLen - Data.ByteString.length x) (cycle "\NUL")++  decLen = Data.Text.length what * 5 `div` 8++-- | Encode an Integer to a bytestring+-- Similar to Data.Base32String (integerToBS) without `reverse`+integerToBS :: Integer -> ByteString+integerToBS 0 = Data.ByteString.pack [0]+integerToBS i+    | i > 0     = Data.ByteString.pack $ Data.List.unfoldr f i+    | otherwise = error "integerToBS not defined for negative values"+  where+    f 0 = Nothing+    f x = Just (fromInteger x :: Word8, x `Data.Bits.shiftR` 8)
src/System/Nix/Build.hs view
@@ -1,4 +1,3 @@-{-# language RecordWildCards #-} {-| Description : Build related types Maintainer  : srk <srk@48.io>@@ -6,52 +5,70 @@ module System.Nix.Build   ( BuildMode(..)   , BuildStatus(..)-  , BuildResult(..)   , buildSuccess-  )-where+  , BuildResult(..)+  ) where -import           Data.Time                      ( UTCTime )+import Data.Map (Map)+import Data.Time (UTCTime)+import Data.Text (Text)+import GHC.Generics (Generic) --- keep the order of these Enums to match enums from reference implementations+import System.Nix.OutputName (OutputName)+import System.Nix.Realisation (DerivationOutput, Realisation)++-- | Mode of the build operation+-- Keep the order of these Enums to match enums from reference implementations -- src/libstore/store-api.hh-data BuildMode = Normal | Repair | Check-  deriving (Eq, Ord, Enum, Show)+data BuildMode+  = BuildMode_Normal -- ^ Perform normal build+  | BuildMode_Repair -- ^ Try to repair corrupted or missing paths by re-building or re-downloading them+  | BuildMode_Check -- ^ Check if the build is reproducible (rebuild and compare to previous build)+  deriving (Bounded, Eq, Generic, Ord, Enum, Show) +-- | Build result status data BuildStatus =-    Built-  | Substituted-  | AlreadyValid-  | PermanentFailure-  | InputRejected-  | OutputRejected-  | TransientFailure -- possibly transient-  | CachedFailure    -- no longer used-  | TimedOut-  | MiscFailure-  | DependencyFailed-  | LogLimitExceeded-  | NotDeterministic-  deriving (Eq, Ord, Enum, Show)-+    BuildStatus_Built -- ^ Build performed successfully+  | BuildStatus_Substituted -- ^ Path substituted from cache+  | BuildStatus_AlreadyValid -- ^ Path is already valid (available in local store)+  | BuildStatus_PermanentFailure+  | BuildStatus_InputRejected+  | BuildStatus_OutputRejected+  | BuildStatus_TransientFailure -- ^ Possibly transient build failure+  | BuildStatus_CachedFailure -- ^ Obsolete+  | BuildStatus_TimedOut -- ^ Build timed out+  | BuildStatus_MiscFailure+  | BuildStatus_DependencyFailed -- ^ Build dependency failed to build+  | BuildStatus_LogLimitExceeded+  | BuildStatus_NotDeterministic+  | BuildStatus_ResolvesToAlreadyValid+  | BuildStatus_NoSubstituters+  deriving (Bounded, Eq, Generic, Ord, Enum, Show)  -- | Result of the build data BuildResult = BuildResult-  { -- | build status, MiscFailure should be default-    status             :: !BuildStatus-  , -- | possible build error message-    errorMessage       :: !(Maybe Text)-  , -- | How many times this build was performed-    timesBuilt         :: !Integer-  , -- | If timesBuilt > 1, whether some builds did not produce the same result-    isNonDeterministic :: !Bool-  ,  -- Start time of this build-    startTime          :: !UTCTime-  ,  -- Stop time of this build-    stopTime           :: !UTCTime+  { buildResultStatus             :: BuildStatus+  -- ^ Build status, MiscFailure should be the default+  , buildResultErrorMessage       :: Maybe Text+  -- ^ Possible build error message+  , buildResultTimesBuilt         :: Maybe Int+  -- ^ How many times this build was performed (since 1.29)+  , buildResultIsNonDeterministic :: Maybe Bool+  -- ^ If timesBuilt > 1, whether some builds did not produce the same result (since 1.29)+  , buildResultStartTime          :: Maybe UTCTime+  -- ^ Start time of this build (since 1.29)+  , buildResultStopTime           :: Maybe UTCTime+  -- ^ Stop time of this build (since 1.29)+  , buildResultBuiltOutputs       :: Maybe (Map (DerivationOutput OutputName) Realisation)+  -- ^ Mapping of the output names to @Realisation@s (since 1.28)+  -- (paths with additional info and their dependencies)   }-  deriving (Eq, Ord, Show)+  deriving (Eq, Generic, Ord, Show) -buildSuccess :: BuildResult -> Bool-buildSuccess BuildResult {..} =-  status `elem` [Built, Substituted, AlreadyValid]+buildSuccess :: BuildStatus -> Bool+buildSuccess x =+  x `elem`+    [ BuildStatus_Built+    , BuildStatus_Substituted+    , BuildStatus_AlreadyValid+    ]
+ src/System/Nix/ContentAddress.hs view
@@ -0,0 +1,106 @@+{-# LANGUAGE OverloadedStrings #-}++module System.Nix.ContentAddress (+    ContentAddress+  , ContentAddressMethod+  , FileIngestionMethod+  , contentAddressBuilder+  , contentAddressParser+  , buildContentAddress+  , parseContentAddress+  ) where++import Control.Applicative+import Crypto.Hash (Digest)+import Data.Attoparsec.Text (Parser)+import Data.Dependent.Sum (DSum)+import Data.Text (Text)+import Data.Text.Lazy.Builder (Builder)+import GHC.Generics (Generic)+import System.Nix.Hash (HashAlgo)+import System.Nix.Store.Types (FileIngestionMethod(..))++import qualified Data.Attoparsec.Text+import qualified Data.Text.Lazy+import qualified Data.Text.Lazy.Builder+import qualified System.Nix.Hash++data ContentAddressMethod+  = FileIngestionMethod !FileIngestionMethod+  -- ^ The path was added to the store via makeFixedOutputPath or+  -- addToStore. It is addressed according to some hash algorithm+  -- applied to the nar serialization via some 'NarHashMode'.+  | TextIngestionMethod+  -- ^ The path is a plain file added via makeTextPath or+  -- addTextToStore. It is addressed according to a sha256sum of the+  -- file contents.+  deriving (Eq, Generic, Ord, Show)++-- | 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+-- paths that are derivation outputs, whose hashes are a function of+-- the contents of the derivation file instead).+--+-- For backwards-compatibility reasons, the same information is+-- encodable in multiple ways, depending on the method used to add the+-- path to the store. These unfortunately result in separate store+-- paths.+data ContentAddress = ContentAddress+  ContentAddressMethod+  (DSum HashAlgo Digest)+  deriving (Eq, Generic, Ord, Show)++-- | Marshall `ContentAddressableAddress` to `Text`+-- in form suitable for remote protocol usage.+buildContentAddress :: ContentAddress -> Text+buildContentAddress =+  Data.Text.Lazy.toStrict+  . Data.Text.Lazy.Builder.toLazyText+  . contentAddressBuilder++contentAddressBuilder :: ContentAddress -> Builder+contentAddressBuilder (ContentAddress method digest) = case method of+  TextIngestionMethod ->+    "text:"+    <> System.Nix.Hash.algoDigestBuilder digest+  FileIngestionMethod r ->+    "fixed:"+    <> fileIngestionMethodBuilder r+    <> System.Nix.Hash.algoDigestBuilder digest++fileIngestionMethodBuilder :: FileIngestionMethod -> Builder+fileIngestionMethodBuilder = \case+  FileIngestionMethod_Flat -> ""+  FileIngestionMethod_FileRecursive -> "r:"++-- | Parse `ContentAddressableAddress` from `ByteString`+parseContentAddress+  :: Text -> Either String ContentAddress+parseContentAddress =+  Data.Attoparsec.Text.parseOnly contentAddressParser++-- | Parser for content addressable field+contentAddressParser :: Parser ContentAddress+contentAddressParser = do+  method <- parseContentAddressMethod+  digest <- parseTypedDigest+  case digest of+    Left e -> fail e+    Right x -> return $ ContentAddress method x++parseContentAddressMethod :: Parser ContentAddressMethod+parseContentAddressMethod =+      TextIngestionMethod <$ "text:"+  <|> FileIngestionMethod <$ "fixed:"+  <*> (FileIngestionMethod_FileRecursive <$ "r:" +       <|> pure FileIngestionMethod_Flat)++parseTypedDigest :: Parser (Either String (DSum HashAlgo Digest))+parseTypedDigest = System.Nix.Hash.mkNamedDigest <$> parseHashType <*> parseHash+  where+    parseHashType :: Parser Text+    parseHashType =+      ("sha256" <|> "sha512" <|> "sha1" <|> "md5") <* (":" <|> "-")++    parseHash :: Parser Text+    parseHash = Data.Attoparsec.Text.takeWhile1 (/= ':')
src/System/Nix/Derivation.hs view
@@ -1,32 +1,45 @@- module System.Nix.Derivation   ( parseDerivation   , buildDerivation-  )-where+  -- Re-exports+  , Derivation(..)+  , DerivationOutput(..)+  ) where -import qualified Data.Text.Lazy.Builder        as Text.Lazy-                                                ( Builder )-import qualified Data.Attoparsec.Text.Lazy     as Text.Lazy-                                                ( Parser )-import           Nix.Derivation                 ( Derivation )-import qualified Nix.Derivation                as Derivation-import           System.Nix.StorePath           ( StoreDir-                                                , StorePath-                                                , storePathToFilePath-                                                )-import qualified System.Nix.StorePath          as StorePath+import Data.Attoparsec.Text.Lazy (Parser)+import Data.Text (Text)+import Data.Text.Lazy.Builder (Builder) +import Nix.Derivation (Derivation(..), DerivationOutput(..))+import System.Nix.StorePath (StoreDir, StorePath) +import qualified Data.Attoparsec.Text.Lazy+import qualified Data.Text+import qualified Data.Text.Lazy+import qualified Data.Text.Lazy.Builder -parseDerivation :: StoreDir -> Text.Lazy.Parser (Derivation StorePath Text)+import qualified Nix.Derivation+import qualified System.Nix.StorePath++parseDerivation :: StoreDir -> Parser (Derivation StorePath Text) parseDerivation expectedRoot =-  Derivation.parseDerivationWith-    ("\"" *> StorePath.pathParser expectedRoot <* "\"")-    Derivation.textParser+  Nix.Derivation.parseDerivationWith+    pathParser+    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 =-  Derivation.buildDerivationWith-    (show . storePathToFilePath storeDir)-    show+  Nix.Derivation.buildDerivationWith+    (string . System.Nix.StorePath.storePathToText storeDir)+    string+  where+    string = Data.Text.Lazy.Builder.fromText . Data.Text.pack . show
+ src/System/Nix/DerivedPath.hs view
@@ -0,0 +1,115 @@+{-# LANGUAGE OverloadedStrings #-}++module System.Nix.DerivedPath (+    OutputsSpec(..)+  , DerivedPath(..)+  , ParseOutputsError(..)+  , parseOutputsSpec+  , outputsSpecToText+  , parseDerivedPath+  , derivedPathToText+  ) where++import GHC.Generics (Generic)+import Data.Set (Set)+import Data.Text (Text)+import System.Nix.OutputName (OutputName, InvalidNameError)+import System.Nix.StorePath (StoreDir(..), StorePath, InvalidPathError)++import qualified Data.Bifunctor+import qualified Data.ByteString.Char8+import qualified Data.Set+import qualified Data.Text+import qualified System.Nix.OutputName+import qualified System.Nix.StorePath++data OutputsSpec =+    OutputsSpec_All+  -- ^ Wildcard spec (^*) meaning all outputs+  | OutputsSpec_Names (Set OutputName)+  -- ^ Set of specific outputs+  deriving (Eq, Generic, Ord, Show)++data DerivedPath =+    DerivedPath_Opaque StorePath+  -- ^ Fully evaluated store path that can't be built+  -- but can be fetched+  | DerivedPath_Built StorePath OutputsSpec+  -- ^ Derivation path and the outputs built from it+  deriving (Eq, Generic, Ord, Show)++data ParseOutputsError =+    ParseOutputsError_InvalidPath InvalidPathError+  | ParseOutputsError_InvalidName InvalidNameError+  | ParseOutputsError_NoNames+  | ParseOutputsError_NoPrefix StoreDir Text+  deriving (Eq, Ord, Show)++parseOutputsSpec :: Text -> Either ParseOutputsError OutputsSpec+parseOutputsSpec t+  | t == "*" = Right OutputsSpec_All+  | otherwise = do+  names <- mapM+             ( Data.Bifunctor.first+                 ParseOutputsError_InvalidName+             . System.Nix.OutputName.mkOutputName+             )+             (Data.Text.splitOn "," t)+  if null names+    then Left ParseOutputsError_NoNames+    else Right $ OutputsSpec_Names (Data.Set.fromList names)++outputsSpecToText :: OutputsSpec -> Text+outputsSpecToText = \case+  OutputsSpec_All -> "*"+  OutputsSpec_Names ns ->+    Data.Text.intercalate+      ","+      (fmap System.Nix.OutputName.unOutputName+        (Data.Set.toList ns)+      )++parseDerivedPath+  :: StoreDir+  -> Text+  -> Either ParseOutputsError DerivedPath+parseDerivedPath root@(StoreDir sd) path =+  let -- We need to do a bit more legwork for case+      -- when StoreDir contains '!'+      -- which is generated by its Arbitrary instance+    textRoot = Data.Text.pack+               $ Data.ByteString.Char8.unpack sd++  in case Data.Text.stripPrefix textRoot path of+    Nothing -> Left $ ParseOutputsError_NoPrefix root path+    Just woRoot ->+      case Data.Text.breakOn "!" woRoot of+        (pathNoPrefix, r) ->+          if Data.Text.null r+          then DerivedPath_Opaque+               <$> (convertError+                   $ System.Nix.StorePath.parsePathFromText+                      root+                      path+                   )+          else DerivedPath_Built+               <$> (convertError+                   $ System.Nix.StorePath.parsePathFromText+                       root+                       (textRoot <> pathNoPrefix)+                   )+               <*> parseOutputsSpec (Data.Text.drop (Data.Text.length "!") r)+  where+    convertError+      :: Either InvalidPathError a+      -> Either ParseOutputsError a+    convertError = Data.Bifunctor.first ParseOutputsError_InvalidPath++derivedPathToText :: StoreDir -> DerivedPath -> Text+derivedPathToText root = \case+  DerivedPath_Opaque p ->+    System.Nix.StorePath.storePathToText root p+  DerivedPath_Built p os ->+    System.Nix.StorePath.storePathToText root p+    <> "!"+    <> outputsSpecToText os
+ src/System/Nix/Fingerprint.hs view
@@ -0,0 +1,51 @@+{-# LANGUAGE OverloadedStrings #-}++{-|+Description : Fingerprint of Nix store path metadata used for signature verification+-}+module System.Nix.Fingerprint+  ( fingerprint+  , metadataFingerprint+  ) where++import Crypto.Hash (Digest)+import Data.Dependent.Sum (DSum)+import Data.List (sort)+import Data.Maybe (fromMaybe)+import Data.Text (Text)+import Data.Text.Lazy (toStrict)+import Data.Text.Lazy.Builder (toLazyText)+import Data.Word (Word64)+import System.Nix.Hash (HashAlgo, algoDigestBuilder)+import System.Nix.StorePath+import System.Nix.StorePath.Metadata (Metadata(..))++import qualified Data.HashSet as HashSet+import qualified Data.Text as Text++-- | Produce the message signed by a NAR signature+metadataFingerprint :: StoreDir -> StorePath -> Metadata StorePath -> Text+metadataFingerprint storeDir storePath Metadata{..} = let+  narSize = fromMaybe 0 metadataNarBytes+  in fingerprint+       storeDir+       storePath+       metadataNarHash+       narSize+       (HashSet.toList metadataReferences)++-- | Produce the message signed by a NAR signature+fingerprint :: StoreDir+            -> StorePath+            -> DSum HashAlgo Digest -- ^ NAR hash+            -> Word64 -- ^ NAR size, in bytes+            -> [StorePath] -- ^ References+            -> Text+fingerprint storeDir storePath narHash narSize refs = let+  encodedStorePath = storePathToText storeDir storePath+  encodedNarHash = (toStrict . toLazyText . algoDigestBuilder) narHash+  encodedNarSize = (Text.pack . show) narSize+  sortedRefs = sort (storePathToText storeDir <$> refs)+  encodedRefs = Text.intercalate "," sortedRefs+  in Text.intercalate ";" [ "1", encodedStorePath, encodedNarHash, encodedNarSize, encodedRefs]+
src/System/Nix/Hash.hs view
@@ -1,18 +1,191 @@+{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TemplateHaskell #-}+{-# OPTIONS_GHC -Wno-orphans #-} {-|-Description : Cryptographic hashes for hnix-store.+Description : Cryptographic hashing interface for hnix-store, on top+              of the cryptohash family of libraries. -}+ module System.Nix.Hash-  ( Hash.mkStorePathHash-  , Hash.NamedAlgo(..)-  , Hash.SomeNamedDigest(..)+  ( HashAlgo(..)+  , NamedAlgo(..)+  , algoToText+  , textToAlgo+  , mkNamedDigest -  , Hash.mkNamedDigest+  , mkStorePathHash -  , Base.BaseEncoding(..)-  , Hash.encodeDigestWith-  , Hash.decodeDigestWith-  )-where+  , System.Nix.Base.BaseEncoding(..)+  , encodeDigestWith+  , decodeDigestWith -import qualified System.Nix.Internal.Hash      as Hash-import qualified System.Nix.Internal.Base      as Base+  , algoDigestBuilder+  , digestBuilder+  ) where++import Crypto.Hash (Digest, HashAlgorithm, MD5(..), SHA1(..), SHA256(..), SHA512(..))+import Data.ByteString (ByteString)+import Data.Constraint.Extras (Has(has))+import Data.Constraint.Extras.TH (deriveArgDict)+import Data.Dependent.Sum (DSum((:=>)))+import Data.GADT.Compare.TH (deriveGEq, deriveGCompare)+import Data.GADT.Show.TH (deriveGShow)+import Data.Kind (Type)+import Data.Some (Some(Some))+import Data.Text (Text)+import Data.Text.Lazy.Builder (Builder)+import System.Nix.Base (BaseEncoding(..))++import qualified Crypto.Hash+import qualified Data.ByteArray+import qualified Data.Text+import qualified Data.Text.Lazy.Builder+import qualified System.Nix.Base+import qualified System.Nix.Hash.Truncation++-- | A 'HashAlgorithm' with a canonical name, for serialization+-- purposes (e.g. SRI hashes)+class HashAlgorithm a => NamedAlgo a where+  algoName :: Text++instance NamedAlgo MD5 where+  algoName = "md5"++instance NamedAlgo SHA1 where+  algoName = "sha1"++instance NamedAlgo SHA256 where+  algoName = "sha256"++instance NamedAlgo SHA512 where+  algoName = "sha512"++data HashAlgo :: Type -> Type where+  HashAlgo_MD5 :: HashAlgo MD5+  HashAlgo_SHA1 :: HashAlgo SHA1+  HashAlgo_SHA256 :: HashAlgo SHA256+  HashAlgo_SHA512 :: HashAlgo SHA512++deriveGEq ''HashAlgo+deriveGCompare ''HashAlgo+deriveGShow ''HashAlgo+deriveArgDict ''HashAlgo++algoToText :: forall t. HashAlgo t -> Text+algoToText x = has @NamedAlgo x (algoName @t)++hashAlgoValue :: HashAlgo a -> a+hashAlgoValue = \case+  HashAlgo_MD5 -> MD5+  HashAlgo_SHA1 -> SHA1+  HashAlgo_SHA256 -> SHA256+  HashAlgo_SHA512 -> SHA512++textToAlgo :: Text -> Either String (Some HashAlgo)+textToAlgo = \case+    "md5"    -> Right $ Some HashAlgo_MD5+    "sha1"   -> Right $ Some HashAlgo_SHA1+    "sha256" -> Right $ Some HashAlgo_SHA256+    "sha512" -> Right $ Some HashAlgo_SHA512+    name     -> Left $ "Unknown hash name: " <> Data.Text.unpack name++-- | Make @DSum HashAlgo Digest@ based on provided SRI hash name+-- and its encoded form+mkNamedDigest+  :: Text -- ^ SRI name+  -> Text -- ^ base encoded hash+  -> Either String (DSum HashAlgo Digest)+mkNamedDigest name sriHash =+  let (sriName, h) = Data.Text.breakOnEnd "-" sriHash in+    if sriName == "" || sriName == name <> "-"+    then mkDigest h+    else+      Left+      $ Data.Text.unpack+      $ "Sri hash method"+      <> " "+      <> sriName+      <> " "+      <> "does not match the required hash type"+      <> " "+      <> name+ where+  mkDigest h =+    textToAlgo name+    >>= \(Some a) -> has @HashAlgorithm a $ fmap (a :=>) $ decodeGo a h+  decodeGo :: HashAlgorithm a => HashAlgo a -> Text -> Either String (Digest a)+  decodeGo a h+    | size == base16Len = decodeDigestWith Base16 h+    | size == base32Len = decodeDigestWith NixBase32 h+    | size == base64Len = decodeDigestWith Base64 h+    | otherwise =+        Left+        $ Data.Text.unpack+        $ sriHash+        <> " "+        <> "is not a valid"+        <> " "+        <> name+        <> " "+        <> "hash. Its length ("+        <> Data.Text.pack (show size)+        <> ") does not match any of"+        <> " "+        <> Data.Text.pack (show [base16Len, base32Len, base64Len])+   where+    size = Data.Text.length h+    hsize = Crypto.Hash.hashDigestSize (hashAlgoValue a)+    base16Len = hsize * 2+    base32Len = ((hsize * 8 - 1) `div` 5) + 1;+    base64Len = ((4 * hsize `div` 3) + 3) `div` 4 * 4;++mkStorePathHash+  :: forall a+   . HashAlgorithm a+  => ByteString+  -> ByteString+mkStorePathHash bs =+  System.Nix.Hash.Truncation.truncateInNixWay 20+  $ Data.ByteArray.convert+  $ Crypto.Hash.hash @ByteString @a bs++-- | Take BaseEncoding type of the output -> take the Digeest as input -> encode Digest+encodeDigestWith :: BaseEncoding -> Digest a -> Text+encodeDigestWith b = System.Nix.Base.encodeWith b . Data.ByteArray.convert++-- | Take BaseEncoding type of the input -> take the input itself -> decodeBase into Digest+decodeDigestWith+  :: HashAlgorithm a+  => BaseEncoding+  -> Text+  -> Either String (Digest a)+decodeDigestWith b x =+  do+    bs <- System.Nix.Base.decodeWith b x+    let+      toEither =+        maybeToRight+          ("Crypton was not able to convert '(ByteString -> Digest a)' for: '" <> show bs <>"'.")+    (toEither . Crypto.Hash.digestFromByteString) bs+  where+    -- To not depend on @extra@+    maybeToRight :: b -> Maybe a -> Either b a+    maybeToRight _ (Just r) = pure r+    maybeToRight y Nothing  = Left y++-- | 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+      (System.Nix.Hash.encodeDigestWith NixBase32 digest)++-- | Builder for @DSum HashAlgo Digest@s+algoDigestBuilder :: DSum HashAlgo Digest -> Builder+algoDigestBuilder (a :=> d) =+  Data.Text.Lazy.Builder.fromText (System.Nix.Hash.algoToText a)+  <> ":"+  <> Data.Text.Lazy.Builder.fromText (encodeDigestWith NixBase32 d)
+ src/System/Nix/Hash/Truncation.hs view
@@ -0,0 +1,41 @@+module System.Nix.Hash.Truncation+  ( truncateInNixWay+  ) where++import Data.Word (Word8)+import Data.ByteString (ByteString)++import qualified Data.ByteString+import qualified Data.Bits+import qualified Data.Bool+import qualified Data.List++-- | Bytewise truncation of a 'Digest'.+--+-- When truncation length is greater than the length of the bytestring+-- but less than twice the bytestring length, truncation splits the+-- bytestring into a head part (truncation length) and tail part+-- (leftover part), right-pads the leftovers with 0 to the truncation+-- length, and combines the two strings bytewise with 'xor'.+truncateInNixWay+  :: Int -> ByteString -> ByteString+--  2021-06-07: NOTE: Renamed function, since truncation can be done in a lot of ways, there is no practice of truncting hashes this way, moreover:+-- 1. <https://crypto.stackexchange.com/questions/56337/strength-of-hash-obtained-by-xor-of-parts-of-sha3>+-- 2. <https://www.reddit.com/r/crypto/comments/6olqfm/ways_to_truncated_hash/>+truncateInNixWay n c =+    Data.ByteString.pack $ fmap truncOutputByte [0 .. n-1]+  where++    truncOutputByte :: Int -> Word8+    truncOutputByte i = Data.List.foldl' (aux i) 0 [0 .. Data.ByteString.length c - 1]++    inputByte :: Int -> Word8+    inputByte j = Data.ByteString.index c j++    aux :: Int -> Word8 -> Int -> Word8+    aux i x j =+      Data.Bool.bool+        id+        (`Data.Bits.xor` inputByte j)+        (j `mod` n == i)+        x
− src/System/Nix/Internal/Base.hs
@@ -1,42 +0,0 @@-{-# language CPP #-}--module System.Nix.Internal.Base-  ( BaseEncoding(Base16,NixBase32,Base64)-  , encodeWith-  , decodeWith-  )-where--import qualified Data.ByteString.Base16 as Base16-import qualified System.Nix.Base32      as Base32  -- Nix has own Base32 encoding-import qualified Data.ByteString.Base64 as Base64---- | Constructors to indicate the base encodings-data BaseEncoding-  = NixBase32-  -- | ^ Nix has a special map of Base32 encoding-  -- Placed first, since it determines Haskell optimizations of pattern matches, & NixBase seems be the most widely used in Nix.-  | Base16-  | Base64----- | Encode @ByteString@ with @Base@ encoding, produce @Text@.-encodeWith :: BaseEncoding -> ByteString -> Text-encodeWith Base16 = decodeUtf8 . Base16.encode-encodeWith NixBase32 = Base32.encode-encodeWith Base64 = decodeUtf8 . Base64.encode---- | Take the input & @Base@ encoding witness -> decode into @Text@.-decodeWith :: BaseEncoding -> Text -> Either String ByteString-#if MIN_VERSION_base16_bytestring(1,0,0)-decodeWith Base16 = Base16.decode . encodeUtf8-#else-decodeWith Base16 = lDecode  -- this tacit sugar simply makes GHC pleased with number of args- where-  lDecode t =-    case Base16.decode (encodeUtf8 t) of-      (x, "") -> pure $ x-      _       -> Left $ "Unable to decode base16 string" <> toString t-#endif-decodeWith NixBase32 = Base32.decode-decodeWith Base64 = Base64.decode . encodeUtf8
− src/System/Nix/Internal/Base32.hs
@@ -1,93 +0,0 @@-module System.Nix.Internal.Base32- ( encode- , decode- , digits32- )-where---import qualified Data.ByteString               as Bytes-import qualified Data.ByteString.Char8         as Bytes.Char8-import qualified Data.Text-import           Data.Vector                    ( Vector )-import qualified Data.Vector                   as Vector-import           Data.Bits                      ( shiftR )-import           Numeric                        ( readInt )----- omitted: E O U T-digits32 :: Vector Char-digits32 = Vector.fromList "0123456789abcdfghijklmnpqrsvwxyz"---- | Encode a 'BS.ByteString' in Nix's base32 encoding-encode :: ByteString -> Text-encode c = toText $ takeCharPosFromDict <$> [nChar - 1, nChar - 2 .. 0]- where-  -- Each base32 character gives us 5 bits of information, while-  -- each byte gives is 8. Because 'div' rounds down, we need to add-  -- one extra character to the result, and because of that extra 1-  -- we need to subtract one from the number of bits in the-  -- bytestring to cover for the case where the number of bits is-  -- already a factor of 5. Thus, the + 1 outside of the 'div' and-  -- the - 1 inside of it.-  nChar = fromIntegral $ ((Bytes.length c * 8 - 1) `div` 5) + 1--  byte  = Bytes.index c . fromIntegral--  -- May need to switch to a more efficient calculation at some-  -- point.-  bAsInteger :: Integer-  bAsInteger =-    sum-      [ fromIntegral (byte j) * (256 ^ j)-        | j <- [0 .. Bytes.length c - 1] ]--  takeCharPosFromDict :: Integer -> Char-  takeCharPosFromDict i = digits32 Vector.! digitInd-   where-    digitInd =-      fromIntegral $-        bAsInteger `div` (32^i) `mod` 32---- | Decode Nix's base32 encoded text-decode :: Text -> Either String ByteString-decode what =-  bool-    (Left "Invalid NixBase32 string")-    (unsafeDecode what)-    (Data.Text.all (`elem` digits32) what)---- | Decode Nix's base32 encoded text--- Doesn't check if all elements match `digits32`-unsafeDecode :: Text -> Either String ByteString-unsafeDecode what =-  case-      readInt-        32-        (`elem` digits32)-        (\c -> fromMaybe (error "character not in digits32")-          $ Vector.findIndex (== c) digits32-        )-        (toString what)-    of-      [(i, _)] -> pure $ padded $ integerToBS i-      x        -> Left $ "Can't decode: readInt returned " <> show x- where-  padded x-    | Bytes.length x < decLen = x `Bytes.append` bstr-    | otherwise               = x-   where-    bstr = Bytes.Char8.pack $ take (decLen - Bytes.length x) (cycle "\NUL")--  decLen = Data.Text.length what * 5 `div` 8---- | Encode an Integer to a bytestring--- Similar to Data.Base32String (integerToBS) without `reverse`-integerToBS :: Integer -> ByteString-integerToBS 0 = Bytes.pack [0]-integerToBS i-    | i > 0     = Bytes.pack $ unfoldr f i-    | otherwise = error "integerToBS not defined for negative values"-  where-    f 0 = Nothing-    f x = Just (fromInteger x :: Word8, x `shiftR` 8)
− src/System/Nix/Internal/Hash.hs
@@ -1,99 +0,0 @@-{-|-Description : Cryptographic hashing interface for hnix-store, on top-              of the cryptohash family of libraries.--}-{-# language AllowAmbiguousTypes #-}-{-# language TypeFamilies        #-}-{-# language ScopedTypeVariables #-}-{-# language DataKinds           #-}-{-# language ExistentialQuantification #-}-{-# language CPP #-}--module System.Nix.Internal.Hash-  ( NamedAlgo(..)-  , SomeNamedDigest(..)-  , mkNamedDigest-  , encodeDigestWith-  , decodeDigestWith-  , mkStorePathHash-  )-where--import qualified Text.Show-import qualified Crypto.Hash            as C-import qualified Data.ByteString        as BS-import qualified Data.Text              as T-import           System.Nix.Internal.Base-import           Data.ByteArray-import           System.Nix.Internal.Truncation---- | A 'HashAlgorithm' with a canonical name, for serialization--- purposes (e.g. SRI hashes)-class C.HashAlgorithm a => NamedAlgo a where-  algoName :: Text--instance NamedAlgo C.MD5 where-  algoName = "md5"--instance NamedAlgo C.SHA1 where-  algoName = "sha1"--instance NamedAlgo C.SHA256 where-  algoName = "sha256"--instance NamedAlgo C.SHA512 where-  algoName = "sha512"---- | A digest whose 'NamedAlgo' is not known at compile time.-data SomeNamedDigest = forall a . NamedAlgo a => SomeDigest (C.Digest a)--instance Show SomeNamedDigest where-  show sd = case sd of-    SomeDigest (digest :: C.Digest hashType) -> toString $ "SomeDigest " <> algoName @hashType <> ":" <> encodeDigestWith NixBase32 digest--mkNamedDigest :: Text -> Text -> Either String SomeNamedDigest-mkNamedDigest name sriHash =-  let (sriName, h) = T.breakOnEnd "-" sriHash in-    if sriName == "" || sriName == name <> "-"-    then mkDigest h-    else Left $ toString $ "Sri hash method " <> sriName <> " does not match the required hash type " <> name- where-  mkDigest h = case name of-    "md5"    -> SomeDigest <$> decodeGo C.MD5    h-    "sha1"   -> SomeDigest <$> decodeGo C.SHA1   h-    "sha256" -> SomeDigest <$> decodeGo C.SHA256 h-    "sha512" -> SomeDigest <$> decodeGo C.SHA512 h-    _        -> Left $ "Unknown hash name: " <> toString name-  decodeGo :: forall a . NamedAlgo a => a -> Text -> Either String (C.Digest a)-  decodeGo a h-    | size == base16Len = decodeDigestWith Base16 h-    | size == base32Len = decodeDigestWith NixBase32 h-    | size == base64Len = decodeDigestWith Base64 h-    | otherwise = Left $ toString sriHash <> " is not a valid " <> toString name <> " hash. Its length (" <> show size <> ") does not match any of " <> show [base16Len, base32Len, base64Len]-   where-    size = T.length h-    hsize = C.hashDigestSize a-    base16Len = hsize * 2-    base32Len = ((hsize * 8 - 1) `div` 5) + 1;-    base64Len = ((4 * hsize `div` 3) + 3) `div` 4 * 4;---mkStorePathHash :: forall a . C.HashAlgorithm a => BS.ByteString -> BS.ByteString-mkStorePathHash bs =-  truncateInNixWay 20 $ convert $ C.hash @BS.ByteString @a bs---- | Take BaseEncoding type of the output -> take the Digeest as input -> encode Digest-encodeDigestWith :: BaseEncoding -> C.Digest a -> T.Text-encodeDigestWith b = encodeWith b . convert----- | Take BaseEncoding type of the input -> take the input itself -> decodeBase into Digest-decodeDigestWith :: C.HashAlgorithm a => BaseEncoding -> T.Text -> Either String (C.Digest a)-decodeDigestWith b x =-  do-    bs <- decodeWith b x-    let-      toEither =-        maybeToRight-          ("Cryptonite was not able to convert '(ByteString -> Digest a)' for: '" <> show bs <>"'.")-    (toEither . C.digestFromByteString) bs
− src/System/Nix/Internal/Nar/Effects.hs
@@ -1,98 +0,0 @@-{-# language KindSignatures      #-}-{-# language RankNTypes          #-}-{-# language ScopedTypeVariables #-}--module System.Nix.Internal.Nar.Effects-  ( NarEffects(..)-  , narEffectsIO-  ) where--import Data.Kind ()-import qualified Data.ByteString             as Bytes-import qualified Data.ByteString.Lazy        as Bytes.Lazy-import qualified System.Directory            as Directory-import           System.Posix.Files          ( createSymbolicLink-                                        , fileSize-                                        , getFileStatus-                                        , isDirectory-                                        , readSymbolicLink-                                        )-import qualified System.IO                   as IO-import qualified Control.Monad.IO.Class      as IO-import           Control.Monad.Trans.Control (MonadBaseControl)-import qualified Control.Exception.Lifted    as Exception.Lifted-import qualified Control.Monad.Fail          as MonadFail--data NarEffects (m :: Type -> Type) = NarEffects {-    narReadFile   :: FilePath -> m Bytes.Lazy.ByteString-  , narWriteFile  :: FilePath -> Bytes.Lazy.ByteString -> m ()-  , narStreamFile :: FilePath -> m (Maybe Bytes.ByteString) -> m ()-  , narListDir    :: FilePath -> m [FilePath]-  , narCreateDir  :: FilePath -> m ()-  , narCreateLink :: FilePath -> FilePath -> m ()-  , narGetPerms   :: FilePath -> m Directory.Permissions-  , narSetPerms   :: FilePath -> Directory.Permissions ->  m ()-  , narIsDir      :: FilePath -> m Bool-  , narIsSymLink  :: FilePath -> m Bool-  , narFileSize   :: FilePath -> m Int64-  , narReadLink   :: FilePath -> m FilePath-  , narDeleteDir  :: FilePath -> m ()-  , narDeleteFile :: FilePath -> m ()-}----- | A particular @NarEffects@ that uses regular POSIX for file manipulation---   You would replace this with your own @NarEffects@ if you wanted a---   different backend-narEffectsIO-  :: (IO.MonadIO m,-      MonadFail.MonadFail m,-      MonadBaseControl IO m-     ) => NarEffects m-narEffectsIO = NarEffects {-    narReadFile   = IO.liftIO . Bytes.Lazy.readFile-  , narWriteFile  = \a -> IO.liftIO . Bytes.Lazy.writeFile a-  , narStreamFile = streamStringOutIO-  , narListDir    = IO.liftIO . Directory.listDirectory-  , narCreateDir  = IO.liftIO . Directory.createDirectory-  , narCreateLink = \f -> IO.liftIO . createSymbolicLink f-  , narGetPerms   = IO.liftIO . Directory.getPermissions-  , narSetPerms   = \f -> IO.liftIO . Directory.setPermissions f-  , narIsDir      = fmap isDirectory . IO.liftIO . getFileStatus-  , narIsSymLink  = IO.liftIO . Directory.pathIsSymbolicLink-  , narFileSize   = fmap (fromIntegral . fileSize) . IO.liftIO . getFileStatus-  , narReadLink   = IO.liftIO . readSymbolicLink-  , narDeleteDir  = IO.liftIO . Directory.removeDirectoryRecursive-  , narDeleteFile = IO.liftIO . Directory.removeFile-  }----- | This default implementation for @narStreamFile@ requires @IO.MonadIO@-streamStringOutIO-  :: forall m-  .(IO.MonadIO m,-    MonadFail.MonadFail m,-    MonadBaseControl IO m-  ) => FilePath-  -> m (Maybe Bytes.ByteString)-  -> m ()-streamStringOutIO f getChunk =-  Exception.Lifted.bracket-    (IO.liftIO $ IO.openFile f WriteMode)-    (IO.liftIO . IO.hClose)-    go-  `Exception.Lifted.catch`-    cleanupException- where-  go :: IO.Handle -> m ()-  go handle = do-    chunk <- getChunk-    case chunk of-      Nothing -> pass-      Just c  -> do-        IO.liftIO $ Bytes.hPut handle c-        go handle-  cleanupException (e :: Exception.Lifted.SomeException) = do-    IO.liftIO $ Directory.removeFile f-    MonadFail.fail $-      "Failed to stream string to " <> f <> ": " <> show e
− src/System/Nix/Internal/Nar/Options.hs
@@ -1,28 +0,0 @@-module System.Nix.Internal.Nar.Options -  ( NarOptions(..)-  , defaultNarOptions-  , caseHackSuffix-  ) where--import qualified System.Info---- | Options for configuring how NAR files are encoded and decoded.-data NarOptions = NarOptions {-  optUseCaseHack :: Bool-  -- ^ Whether to enable a case hack to support case-insensitive filesystems.-  -- Equivalent to the 'use-case-hack' option in the Nix client.-  ---  -- The case hack rewrites file names to avoid collisions on case-insensitive file systems, e.g. APFS and HFS+ on macOS.-  -- Enabled by default on macOS (Darwin).-}--defaultNarOptions :: NarOptions-defaultNarOptions = NarOptions {-  optUseCaseHack =-    if System.Info.os == "darwin"-      then True-      else False-}--caseHackSuffix :: Text-caseHackSuffix = "~nix~case~hack~"
− src/System/Nix/Internal/Nar/Parser.hs
@@ -1,568 +0,0 @@--- | A streaming parser for the NAR format--{-# language GeneralizedNewtypeDeriving #-}-{-# language ScopedTypeVariables        #-}-{-# language TypeFamilies               #-}-{-# language TypeOperators              #-}--module System.Nix.Internal.Nar.Parser-  ( runParser-  , runParserWithOptions-  , parseNar-  , testParser-  , testParser'-  )-where--import qualified Relude.Unsafe as Unsafe-import qualified Algebra.Graph                   as Graph-import qualified Algebra.Graph.ToGraph           as Graph-import qualified Control.Concurrent              as Concurrent-import qualified Control.Exception.Lifted        as Exception.Lifted-import qualified Control.Monad.Except            as Except-import qualified Control.Monad.Fail              as Fail-import qualified Control.Monad.IO.Class          as IO-import qualified Control.Monad.Reader            as Reader-import qualified Control.Monad.State             as State-import qualified Control.Monad.Trans             as Trans-import qualified Control.Monad.Trans.Control     as Base-import qualified Data.ByteString                 as Bytes-import qualified Data.CaseInsensitive            as CI-import qualified Data.HashMap.Strict             as HashMap-import qualified Data.List                       as List-import qualified Data.Map                        as Map-import qualified Data.Serialize                  as Serialize-import qualified Data.Text                       as Text-import qualified System.Directory                as Directory-import           System.FilePath                 as FilePath-import qualified System.IO                       as IO--import qualified System.Nix.Internal.Nar.Effects as Nar-import qualified System.Nix.Internal.Nar.Options as Nar----- | NarParser is a monad for parsing a Nar file as a byte stream---   and reconstructing the file system objects inside---   See the definitions of @NarEffects@ for a description---   of the actions the parser can take, and @ParserState@ for the---   internals of the parser-newtype NarParser m a = NarParser-  { runNarParser ::-      State.StateT-        ParserState-        (Except.ExceptT-          String-          (Reader.ReaderT (ParserEnv m) m)-        )-        a-  }-  deriving ( Functor, Applicative, Monad, Fail.MonadFail-           , Trans.MonadIO, State.MonadState ParserState-           , Except.MonadError String-           , Reader.MonadReader (ParserEnv m)-           )---data ParserEnv m = ParserEnv-  { envNarEffects :: Nar.NarEffects m-  , envNarOptions :: Nar.NarOptions-  }---getNarEffects :: Monad m => NarParser m (Nar.NarEffects m)-getNarEffects = fmap envNarEffects ask---getNarEffect :: Monad m => (Nar.NarEffects m -> a) -> NarParser m a-getNarEffect eff = fmap eff getNarEffects---getNarOptions :: Monad m => NarParser m Nar.NarOptions-getNarOptions = fmap envNarOptions ask---- | Run a @NarParser@ over a byte stream---   This is suitable for testing the top-level NAR parser, or any of the---   smaller utilities parsers, if you have bytes appropriate for them-runParser-  :: forall m a-   . (IO.MonadIO m, Base.MonadBaseControl IO m)-  => Nar.NarEffects m-     -- ^ Provide the effects set, usually @narEffectsIO@-  -> NarParser m a-     -- ^ A parser to run, such as @parseNar@-  -> IO.Handle-     -- ^ A handle the stream containg the NAR. It should already be-     --   open and in @ReadMode@-  -> FilePath-     -- ^ The root file system object to be created by the NAR-  -> m (Either String a)-runParser effs parser h target = do-  runParserWithOptions Nar.defaultNarOptions effs parser h target--runParserWithOptions-  :: forall m a-   . (IO.MonadIO m, Base.MonadBaseControl IO m)-  => Nar.NarOptions-  -> Nar.NarEffects m-     -- ^ Provide the effects set, usually @narEffectsIO@-  -> NarParser m a-     -- ^ A parser to run, such as @parseNar@-  -> IO.Handle-     -- ^ A handle the stream containg the NAR. It should already be-     --   open and in @ReadMode@-  -> FilePath-     -- ^ The root file system object to be created by the NAR-  -> m (Either String a)-runParserWithOptions opts effs (NarParser action) h target = do-  unpackResult <--    runReaderT (runExceptT $ State.evalStateT action state0) (ParserEnv effs opts)-      `Exception.Lifted.catch` exceptionHandler-  when (isLeft unpackResult) cleanup-  pure unpackResult-- where-  state0 :: ParserState-  state0 =-    ParserState-      { tokenStack     = []-      , handle         = h-      , directoryStack = [target]-      , links          = []-      , filePaths      = HashMap.empty-      }--  exceptionHandler :: Exception.Lifted.SomeException -> m (Either String a)-  exceptionHandler e =-    pure $ Left $ "Exception while unpacking NAR file: " <> show e--  cleanup :: m ()-  cleanup =-    (\ef trg -> do-      isDir <- Nar.narIsDir ef trg-      bool-        (Nar.narDeleteFile ef trg)-        (Nar.narDeleteDir ef trg)-        isDir-    ) effs target---instance Trans.MonadTrans NarParser where-  lift act = NarParser $ (lift . lift . lift) act---data ParserState = ParserState-  { tokenStack     :: ![Text]-    -- ^ The parser can push tokens (words or punctuation)-    --   onto this stack. We use this for a very limited backtracking-    --   where the Nar format requires it-  , directoryStack :: ![String]-    -- ^ The parser knows the name of the current FSO it's targeting,-    --   and the relative directory path leading there-  , handle         :: IO.Handle-    -- ^ Handle of the input byte stream-  , links          :: [LinkInfo]-    -- ^ Unlike with files and directories, we collect symlinks-    --   from the NAR on-  , filePaths      :: HashMap.HashMap (CI.CI FilePath) Int-    -- ^ A map of case-insensitive files paths to the number of collisions encountered.-    -- See @Nar.NarOptions.optUseCaseHack@ for details.-  }------------------------------------------------------------------------------------ * Parsers for NAR components---- | Parse a NAR byte string, producing @()@.---   Parsing a NAR is mostly used for its side-effect: producing---   the file system objects packed in the NAR. That's why we pure @()@-parseNar :: (IO.MonadIO m, Fail.MonadFail m) => NarParser m ()-parseNar = do-  expectStr "nix-archive-1"-  parens parseFSO-  createLinks---parseFSO :: (IO.MonadIO m, Fail.MonadFail m) => NarParser m ()-parseFSO = do-  expectStr "type"-  matchStr-    [ ("symlink"  , parseSymlink  )-    , ("regular"  , parseFile     )-    , ("directory", parseDirectory)-    ]----- | Parse a symlink from a NAR, storing the link details in the parser state---   We remember links rather than immediately creating file system objects---   from them, because we might encounter a link in the NAR before we---   encountered its target, and in this case, creating the link will fail---   The final step of creating links is handle by @createLinks@-parseSymlink :: (IO.MonadIO m, Fail.MonadFail m) => NarParser m ()-parseSymlink = do-  expectStr "target"-  target      <- parseStr-  (dir, file) <- currentDirectoryAndFile-  pushLink $-    LinkInfo-      { linkTarget = toString target-      , linkFile   = file-      , linkPWD    = dir-      }- where-  currentDirectoryAndFile :: Monad m => NarParser m (FilePath, FilePath)-  currentDirectoryAndFile = do-    dirStack <- State.gets directoryStack-    pure (List.foldr1 (</>) (List.reverse $ drop 1 dirStack), Unsafe.head dirStack)----- | Internal data type representing symlinks encountered in the NAR-data LinkInfo = LinkInfo-  { linkTarget :: String-    -- ^ path to the symlink target, relative to the root of the unpacking NAR-  , linkFile   :: String-    -- ^ file name of the link being created-  , linkPWD    :: String-    -- ^ directory in which to create the link (relative to unpacking root)-  }-  deriving Show----- | When the NAR includes a file, we read from the NAR handle in chunks and---   write the target in chunks. This lets us avoid reading the full contents---   of the encoded file into memory-parseFile :: forall m . (IO.MonadIO m, Fail.MonadFail m) => NarParser m ()-parseFile = do--  s <- parseStr-  when (s `notElem` ["executable", "contents"]) $-    Fail.fail-      $ "Parser found " <> show s-      <> " when expecting element from "-      <> (show :: [String] -> String) ["executable", "contents"]-  when (s == "executable") $ do-    expectStr ""-    expectStr "contents"--  fSize        <- parseLength--  -- Set up for defining `getChunk`-  narHandle    <- State.gets handle-  bytesLeftVar <- IO.liftIO $ newIORef fSize--  let-    -- getChunk tracks the number of total bytes we still need to get from the-    -- file (starting at the file size, and decrementing by the size of the-    -- chunk we read)-    getChunk :: m (Maybe ByteString)-    getChunk = do-      bytesLeft <- IO.liftIO $ readIORef bytesLeftVar-      if bytesLeft == 0-        then pure Nothing-        else do-          chunk <- IO.liftIO $ Bytes.hGetSome narHandle $ fromIntegral $ min 10000 bytesLeft-          when (Bytes.null chunk) (Fail.fail "ZERO BYTES")-          IO.liftIO $ modifyIORef bytesLeftVar $ \n -> n - fromIntegral (Bytes.length chunk)--          -- This short pause is necessary for letting the garbage collector-          -- clean up chunks from previous runs. Without it, heap memory usage can-          -- quickly spike-          IO.liftIO $ Concurrent.threadDelay 10-          pure $ Just chunk--  target     <- currentFile-  streamFile <- getNarEffect Nar.narStreamFile-  lift (streamFile target getChunk)--  when (s == "executable") $ do-    effs :: Nar.NarEffects m <- getNarEffects-    lift $ do-      p <- Nar.narGetPerms effs target-      Nar.narSetPerms effs target (p { Directory.executable = True })--  expectRawString (Bytes.replicate (padLen $ fromIntegral fSize) 0)----- | Parse a NAR encoded directory, being careful not to hold onto file---   handles for target files longer than needed-parseDirectory :: (IO.MonadIO m, Fail.MonadFail m) => NarParser m ()-parseDirectory = do-  createDirectory <- getNarEffect Nar.narCreateDir-  target          <- currentFile-  lift $ createDirectory target-  parseEntryOrFinish target-- where--  parseEntryOrFinish :: (IO.MonadIO m, Fail.MonadFail m) => FilePath -> NarParser m ()-  parseEntryOrFinish path =-    -- If we reach a ")", we finished the directory's entries, and we have-    -- to put ")" back into the stream, because the outer call to @parens@-    -- expects to consume it.-    -- Otherwise, parse an entry as a fresh file system object-    matchStr-      [ ( ")"   , pushStr ")" )-      , ("entry", parseEntry path  )-      ]--  parseEntry :: (IO.MonadIO m, Fail.MonadFail m) => FilePath -> NarParser m ()-  parseEntry path = do-    opts <- getNarOptions-    parens $ do-      expectStr "name"-      fName <--        if Nar.optUseCaseHack opts then-          addCaseHack path =<< parseStr-        else-          parseStr-      pushFileName (toString fName)-      expectStr "node"-      parens parseFSO-      popFileName-    parseEntryOrFinish path--  addCaseHack :: (IO.MonadIO m, Fail.MonadFail m) => FilePath -> Text -> NarParser m Text-  addCaseHack path fName = do-    let key = path </> Text.unpack fName-    recordFilePath key-    conflictCount <- getFilePathConflictCount key-    pure $-      if conflictCount > 0 then-        fName <> Nar.caseHackSuffix <> show conflictCount-      else-        fName------------------------------------------------------------------------------------- * Utility parsers----- | Short strings guiding the NAR parsing are prefixed with their---   length, then encoded in ASCII, and padded to 8 bytes. @parseStr@---   captures this logic-parseStr :: (IO.MonadIO m, Fail.MonadFail m) => NarParser m Text-parseStr = do-  cachedStr <- popStr-  case cachedStr of-    Just str -> pure str-    Nothing  -> do-      len      <- parseLength-      strBytes <- consume $ fromIntegral len-      expectRawString-        (Bytes.replicate (fromIntegral $ padLen $ fromIntegral len) 0)-      pure $ decodeUtf8 strBytes----- | Get an Int64 describing the length of the upcoming string,---   according to NAR's encoding of ints-parseLength :: (IO.MonadIO m, Fail.MonadFail m) => NarParser m Int64-parseLength = do-  eightBytes <- consume 8-  either-    (\e -> Fail.fail $ "parseLength failed to decode int64: " <> e)-    pure-    (Serialize.runGet Serialize.getInt64le eightBytes)----- | Consume a NAR string and assert that it matches an expectation-expectStr :: (IO.MonadIO m, Fail.MonadFail m) => Text -> NarParser m ()-expectStr expected = do-  actual <- parseStr-  when (actual /= expected) $-    Fail.fail $  "Expected " <> err expected <> ", got " <> err actual- where-  err t =-    show $-      bool-        t-        (Text.take 10 t <> "...")-        (Text.length t > 10)----- | Consume a raw string and assert that it equals some expectation.---   This is usually used when consuming padding 0's-expectRawString-  :: (IO.MonadIO m, Fail.MonadFail m) => ByteString -> NarParser m ()-expectRawString expected = do-  actual <- consume $ Bytes.length expected-  when (actual /= expected)-    $  Fail.fail-    $  "Expected "-    <> err expected-    <> ", got "-    <> err actual- where-  err bs =-    show $-      bool-        bs-        (Bytes.take 10 bs <> "...")-        (Bytes.length bs > 10)----- | Consume a NAR string, and dispatch to a parser depending on which string---   matched-matchStr-  :: (IO.MonadIO m, Fail.MonadFail m)-  => [(Text, NarParser m a)]-     -- ^ List of expected possible strings and the parsers they should run-  -> NarParser m a-matchStr parsers = do-  str <- parseStr-  case List.lookup str parsers of-    Just p -> p-    Nothing ->-      Fail.fail $ "Expected one of " <> show (fst <$> parsers) <> " found " <> show str----- | Wrap any parser in NAR formatted parentheses---   (a parenthesis is a NAR string, so it needs length encoding and padding)-parens :: (IO.MonadIO m, Fail.MonadFail m) => NarParser m a -> NarParser m a-parens act = do-  expectStr "("-  r <- act-  expectStr ")"-  pure r----- | Sort links in the symlink stack according to their connectivity---   (Targets must be created before the links that target them)-createLinks :: IO.MonadIO m => NarParser m ()-createLinks = do-  createLink  <- getNarEffect Nar.narCreateLink-  allLinks    <- State.gets links-  sortedLinks <- IO.liftIO $ sortLinksIO allLinks-  forM_ sortedLinks $ \li -> do-    pwd <- IO.liftIO Directory.getCurrentDirectory-    IO.liftIO $ Directory.setCurrentDirectory (linkPWD li)-    lift $ createLink (linkTarget li) (linkFile li)-    IO.liftIO $ Directory.setCurrentDirectory pwd-- where--  -- Convert every target and link file to a filepath relative-  -- to NAR root, then @Graph.topSort@ it, and map from the-  -- relative filepaths back to the original @LinkInfo@.-  -- Relative paths are needed for sorting, but @LinkInfo@s-  -- are needed for creating the link files-  sortLinksIO :: [LinkInfo] -> IO [LinkInfo]-  sortLinksIO ls = do-    linkLocations <- fmap Map.fromList $-      forM ls $ \li->-                  (,li) <$> Directory.canonicalizePath (linkFile li)-    canonicalLinks <- forM ls $ \l -> do-      targetAbsPath <- Directory.canonicalizePath-                        (linkPWD l </> linkTarget l)-      fileAbsPath   <- Directory.canonicalizePath-                        (linkFile l)-      pure (fileAbsPath, targetAbsPath)-    let linkGraph = Graph.edges canonicalLinks-    case Graph.topSort linkGraph of-      Left _            -> error "Symlinks form a loop"-      Right sortedNodes ->-        let-          sortedLinks = flip Map.lookup linkLocations <$> sortedNodes-        in-          pure $ catMaybes sortedLinks------------------------------------------------------------------------------------ * State manipulation---- | Pull n bytes from the underlying handle, failing if fewer bytes---   are available-consume-  :: (IO.MonadIO m, Fail.MonadFail m)-  => Int-  -> NarParser m ByteString-consume 0 = pure ""-consume n = do-  state0   <- State.get-  newBytes <- IO.liftIO $ Bytes.hGet (handle state0) (max 0 n)-  when (Bytes.length newBytes < n) $-    Fail.fail $-    "consume: Not enough bytes in handle. Wanted "-    <> show n <> " got " <> show (Bytes.length newBytes)-  pure newBytes----- | Pop a string off the token stack-popStr :: Monad m => NarParser m (Maybe Text)-popStr = do-  s <- State.get-  case uncons (tokenStack s) of-    Nothing      -> pure Nothing-    Just (x, xs) -> do-      State.put $ s { tokenStack = xs }-      pure $ Just x----- | Push a string onto the token stack-pushStr :: Monad m => Text -> NarParser m ()-pushStr str =-  State.modify $ \s -> -- s { loadedBytes = strBytes <> loadedBytes s }-    s { tokenStack = str : tokenStack s }----- | Push a level onto the directory stack-pushFileName :: Monad m => FilePath -> NarParser m ()-pushFileName fName =-  State.modify (\s -> s { directoryStack = fName : directoryStack s })----- | Go to the parent level in the directory stack-popFileName :: Monad m => NarParser m ()-popFileName =-  State.modify (\s -> s { directoryStack = List.drop 1 (directoryStack s )})----- | Convert the current directory stack into a filepath by interspersing---   the path components with "/"-currentFile :: Monad m => NarParser m FilePath-currentFile = do-  dirStack <- State.gets directoryStack-  pure $ List.foldr1 (</>) $ List.reverse dirStack----- | Add a link to the collection of encountered symlinks-pushLink :: Monad m => LinkInfo -> NarParser m ()-pushLink linkInfo = State.modify (\s -> s { links = linkInfo : links s })----- | Add a file path to the collection of encountered file paths-recordFilePath :: Monad m => FilePath -> NarParser m ()-recordFilePath fPath =-  State.modify (\s -> s { filePaths = HashMap.insertWith (\_ v -> v + 1) (CI.mk fPath) 0 (filePaths s) })--getFilePathConflictCount :: Monad m => FilePath -> NarParser m Int-getFilePathConflictCount fPath = do-  fileMap <- State.gets filePaths-  pure $ HashMap.findWithDefault 0 (CI.mk fPath) fileMap----------------------------------------------------------------------------------- * Utilities--testParser :: (m ~ IO) => NarParser m a -> ByteString -> m (Either String a)-testParser p b = do-  Bytes.writeFile tmpFileName b-  withFile tmpFileName ReadMode $ \h ->-    runParser Nar.narEffectsIO p h tmpFileName- where-  tmpFileName = "tmp"--testParser' :: (m ~ IO) => FilePath -> IO (Either String ())-testParser' fp =-  withFile fp ReadMode $ \h -> runParser Nar.narEffectsIO parseNar h "tmp"------- | Distance to the next multiple of 8-padLen :: Int -> Int-padLen n = (8 - n) `mod` 8---dbgState :: IO.MonadIO m => NarParser m ()-dbgState = do-  s <- State.get-  IO.liftIO $ print (tokenStack s, directoryStack s)
− src/System/Nix/Internal/Nar/Streamer.hs
@@ -1,150 +0,0 @@--- | Stream out a NAR file from a regular file--{-# LANGUAGE ScopedTypeVariables #-}--module System.Nix.Internal.Nar.Streamer-  ( NarSource-  , dumpString-  , dumpPath-  , streamNarIO-  , streamNarIOWithOptions-  , IsExecutable(..)-  )-where--import qualified Control.Monad.IO.Class          as IO-import qualified Data.ByteString                 as Bytes-import qualified Data.ByteString.Lazy            as Bytes.Lazy-import qualified Data.Serialize                  as Serial-import qualified Data.Text                       as T (pack, breakOn)-import qualified Data.Text.Encoding              as TE (encodeUtf8)-import qualified System.Directory                as Directory-import           System.FilePath                 ((</>))--import qualified System.Nix.Internal.Nar.Effects as Nar-import qualified System.Nix.Internal.Nar.Options as Nar----- | NarSource--- The source to provide nar to the handler `(ByteString -> m ())`.--- It is isomorphic to ByteString by Yoneda lemma--- if the result is meant to be m ().--- It is done in CPS style so IO can be chunks.-type NarSource m =  (ByteString -> m ()) -> m ()----- | dumpString--- dump a string to nar in CPS style. The function takes in a `ByteString`,--- and build a `NarSource m`.-dumpString-  :: forall m. IO.MonadIO m-  => ByteString -- ^ the string you want to dump-  -> NarSource m -- ^ The nar result in CPS style-dumpString text yield = traverse_ (yield . str)-  ["nix-archive-1", "(", "type" , "regular", "contents", text, ")"]----- | dumpPath--- shorthand--- build a Source that turn file path to nar using the default narEffectsIO.-dumpPath-  :: forall m . IO.MonadIO m-  => FilePath -- ^ path for the file you want to dump to nar-  -> NarSource m -- ^ the nar result in CPS style-dumpPath = streamNarIO Nar.narEffectsIO----- | This implementation of Nar encoding takes an arbitrary @yield@---   function from any streaming library, and repeatedly calls---   it while traversing the filesystem object to Nar encode-streamNarIO :: forall m . IO.MonadIO m => Nar.NarEffects IO -> FilePath -> NarSource m-streamNarIO effs basePath yield =-  streamNarIOWithOptions Nar.defaultNarOptions effs basePath yield--streamNarIOWithOptions :: forall m . IO.MonadIO m => Nar.NarOptions -> Nar.NarEffects IO -> FilePath -> NarSource m-streamNarIOWithOptions opts effs basePath yield = do-  yield $ str "nix-archive-1"-  parens $ go basePath- where-  go :: FilePath -> m ()-  go path = do-    isSymLink <- IO.liftIO $ Nar.narIsSymLink effs path-    if isSymLink then do-      target <- IO.liftIO $ Nar.narReadLink effs path-      yield $-        strs ["type", "symlink", "target", filePathToBS target]-      else do-        isDir <- IO.liftIO $ Nar.narIsDir effs path-        if isDir then do-          fs <- IO.liftIO (Nar.narListDir effs path)-          yield $ strs ["type", "directory"]-          forM_ (sort fs) $ \f -> do-            yield $ str "entry"-            parens $ do-              let fullName = path </> f-              let serializedPath =-                    if Nar.optUseCaseHack opts then-                      filePathToBSWithCaseHack f-                    else-                      filePathToBS f-              yield $ strs ["name", serializedPath, "node"]-              parens $ go fullName-        else do-          isExec <- IO.liftIO $ isExecutable effs path-          yield $ strs ["type", "regular"]-          when (isExec == Executable) $ yield $ strs ["executable", ""]-          fSize <- IO.liftIO $ Nar.narFileSize effs path-          yield $ str "contents"-          yield $ int fSize-          yieldFile path fSize--  parens act = do-    yield $ str "("-    r <- act-    yield $ str ")"-    pure r--  -- Read, yield, and pad the file-  yieldFile :: FilePath -> Int64 -> m ()-  yieldFile path fsize = do-    mapM_ yield . Bytes.Lazy.toChunks =<< IO.liftIO (Nar.narReadFile effs path)-    yield $ Bytes.replicate (padLen $ fromIntegral fsize) 0--data IsExecutable = NonExecutable | Executable-  deriving (Eq, Show)--isExecutable :: Functor m => Nar.NarEffects m -> FilePath -> m IsExecutable-isExecutable effs fp =-  bool-    NonExecutable-    Executable-    . Directory.executable <$> Nar.narGetPerms effs fp---- | Distance to the next multiple of 8-padLen :: Int -> Int-padLen n = (8 - n) `mod` 8--int :: Integral a => a -> ByteString-int n = Serial.runPut $ Serial.putInt64le $ fromIntegral n--str :: ByteString -> ByteString-str t =-  let-    len = Bytes.length t-  in-    int len <> padBS len t--padBS :: Int -> ByteString -> ByteString-padBS strSize bs = bs <> Bytes.replicate (padLen strSize) 0--strs :: [ByteString] -> ByteString-strs xs = Bytes.concat $ str <$> xs--filePathToBS :: FilePath -> ByteString-filePathToBS = TE.encodeUtf8 . T.pack--filePathToBSWithCaseHack :: FilePath -> ByteString-filePathToBSWithCaseHack = TE.encodeUtf8 . undoCaseHack . T.pack--undoCaseHack :: Text -> Text-undoCaseHack = fst . T.breakOn Nar.caseHackSuffix
− src/System/Nix/Internal/Signature.hs
@@ -1,45 +0,0 @@-{-|-Description : Nix-relevant interfaces to NaCl signatures.--}-{-# language CPP #-}--module System.Nix.Internal.Signature-  ( Signature-  , NarSignature(..)-  )-where---import qualified Data.ByteString                   as Bytes-import           Crypto.Saltine.Core.Sign           ( PublicKey )-import           Crypto.Saltine.Class               ( IsEncoding(..) )----  2021-05-30: NOTE: Please, clean-up these overloads in ~2022-#if MIN_VERSION_saltine(0,2,0)-import qualified Crypto.Saltine.Internal.Sign as NaClSizes-#else-import qualified Crypto.Saltine.Internal.ByteSizes as NaClSizes-#endif---- | A NaCl signature.-newtype Signature = Signature ByteString-  deriving (Eq, Ord)--instance IsEncoding Signature where-  decode s-#if MIN_VERSION_saltine(0,2,0)-    | Bytes.length s == NaClSizes.sign_bytes = Just $ Signature s-#else-    | Bytes.length s == NaClSizes.sign = Just $ Signature s-#endif-    | otherwise = Nothing-  encode = coerce---- | A detached NaCl signature attesting to a nix archive's validity.-data NarSignature = NarSignature-  { -- | The public key used to sign the archive.-    publicKey :: PublicKey-  , -- | The archive's signature.-    sig       :: Signature-  }-  deriving (Eq, Ord)
− src/System/Nix/Internal/StorePath.hs
@@ -1,239 +0,0 @@-{-|-Description : Representation of Nix store paths.--}-{-# language ConstraintKinds #-}-{-# language RecordWildCards #-}-{-# language GeneralizedNewtypeDeriving #-}-{-# language ScopedTypeVariables #-}-{-# language AllowAmbiguousTypes #-}-{-# language DataKinds #-}--module System.Nix.Internal.StorePath-  ( -- * Basic store path types-    StoreDir(..)-  , StorePath(..)-  , StorePathName(..)-  , StorePathSet-  , mkStorePathHashPart-  , StorePathHashPart(..)-  , ContentAddressableAddress(..)-  , NarHashMode(..)-  , -- * Manipulating 'StorePathName'-    makeStorePathName-  , validStorePathName-  , -- * Rendering out 'StorePath's-    storePathToFilePath-  , storePathToRawFilePath-  , storePathToText-  , storePathToNarInfo-  , -- * Parsing 'StorePath's-    parsePath-  , pathParser-  )-where--import qualified Relude.Unsafe as Unsafe-import           System.Nix.Internal.Hash-import           System.Nix.Internal.Base-import qualified System.Nix.Internal.Base32    as Nix.Base32--import qualified Data.ByteString.Char8         as Bytes.Char8-import qualified Data.Char                     as Char-import qualified Data.Text                     as Text-import           Data.Attoparsec.Text.Lazy      ( Parser-                                                , (<?>)-                                                )-import qualified Data.Attoparsec.Text.Lazy     as Parser.Text.Lazy-import qualified System.FilePath               as FilePath-import           Crypto.Hash                    ( SHA256-                                                , Digest-                                                )---- | A path in a Nix store.------ From the Nix thesis: A store path is the full path of a store--- object. It has the following anatomy: storeDir/hashPart-name.------ The store directory is *not* included, and must be known from the--- context. This matches modern C++ Nix, and also represents the fact--- that store paths for different store directories cannot be mixed.-data StorePath = StorePath-  { -- | The 160-bit hash digest reflecting the "address" of the name.-    -- Currently, this is a truncated SHA256 hash.-    storePathHash :: !StorePathHashPart-  , -- | The (typically human readable) name of the path. For packages-    -- this is typically the package name and version (e.g.-    -- hello-1.2.3).-    storePathName :: !StorePathName-  }-  deriving (Eq, Ord, Show)--instance Hashable StorePath where-  hashWithSalt s StorePath{..} =-    s `hashWithSalt` storePathHash `hashWithSalt` storePathName---- | The name portion of a Nix path.------ 'unStorePathName' must only contain a-zA-Z0-9+._?=-, can't start--- with a -, and must have at least one character (i.e. it must match--- 'storePathNameRegex').-newtype StorePathName = StorePathName-  { -- | Extract the contents of the name.-    unStorePathName :: Text-  } deriving (Eq, Hashable, Ord, Show)---- | The hash algorithm used for store path hashes.-newtype StorePathHashPart = StorePathHashPart ByteString-  deriving (Eq, Hashable, Ord, Show)--mkStorePathHashPart :: ByteString -> StorePathHashPart-mkStorePathHashPart = coerce . mkStorePathHash @SHA256---- | A set of 'StorePath's.-type StorePathSet = HashSet StorePath---- | 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--- paths that are derivation outputs, whose hashes are a function of--- the contents of the derivation file instead).------ For backwards-compatibility reasons, the same information is--- encodable in multiple ways, depending on the method used to add the--- path to the store. These unfortunately result in separate store--- paths.-data ContentAddressableAddress-  = -- | The path is a plain file added via makeTextPath or-    -- addTextToStore. It is addressed according to a sha256sum of the-    -- file contents.-    Text !(Digest SHA256)-  | -- | The path was added to the store via makeFixedOutputPath or-    -- addToStore. It is addressed according to some hash algorithm-    -- applied to the nar serialization via some 'NarHashMode'.-    Fixed !NarHashMode !SomeNamedDigest---- | Schemes for hashing a Nix archive.------ For backwards-compatibility reasons, there are two different modes--- here, even though 'Recursive' should be able to cover both.-data NarHashMode-  = -- | Require the nar to represent a non-executable regular file.-    RegularFile-  | -- | Hash an arbitrary nar, including a non-executable regular-    -- file if so desired.-    Recursive--makeStorePathName :: Text -> Either String StorePathName-makeStorePathName n =-  if validStorePathName n-    then pure $ StorePathName n-    else Left $ reasonInvalid n--reasonInvalid :: Text -> String-reasonInvalid n-  | n == ""          = "Empty name"-  | Text.length n > 211 = "Path too long"-  | Text.head n == '.'  = "Leading dot"-  | otherwise        = "Invalid character"--validStorePathName :: Text -> Bool-validStorePathName n =-  n /= ""-  && Text.length n <= 211-  && Text.head n /= '.'-  && Text.all validStorePathNameChar n--validStorePathNameChar :: Char -> Bool-validStorePathNameChar c =-  any ($ c)-    [ Char.isAsciiLower -- 'a'..'z', isAscii..er probably faster then putting it out-    , Char.isAsciiUpper -- 'A'..'Z'-    , Char.isDigit-    , (`elem` ("+-._?=" :: String))-    ]---- | Copied from @RawFilePath@ in the @unix@ package, duplicated here--- to avoid the dependency.-type RawFilePath = ByteString---- | The path to the store dir------ Many operations need to be parameterized with this, since store paths--- do not know their own store dir by design.-newtype StoreDir = StoreDir {-    unStoreDir :: RawFilePath-  } deriving (Eq, Hashable, Ord, Show)---- | Render a 'StorePath' as a 'RawFilePath'.-storePathToRawFilePath :: StoreDir -> StorePath -> RawFilePath-storePathToRawFilePath storeDir StorePath{..} =-  unStoreDir storeDir <> "/" <> hashPart <> "-" <> name- where-  hashPart = encodeUtf8 $ encodeWith NixBase32 $ coerce storePathHash-  name     = encodeUtf8 $ unStorePathName storePathName---- | Render a 'StorePath' as a 'FilePath'.-storePathToFilePath :: StoreDir -> StorePath -> FilePath-storePathToFilePath storeDir = Bytes.Char8.unpack . storePathToRawFilePath storeDir---- | Render a 'StorePath' as a 'Text'.-storePathToText :: StoreDir -> StorePath -> Text-storePathToText storeDir = toText . Bytes.Char8.unpack . storePathToRawFilePath storeDir---- | Build `narinfo` suffix from `StorePath` which--- can be used to query binary caches.-storePathToNarInfo :: StorePath -> Bytes.Char8.ByteString-storePathToNarInfo StorePath{..} =-  encodeUtf8 $ encodeWith NixBase32 (coerce storePathHash) <> ".narinfo"---- | Parse `StorePath` from `Bytes.Char8.ByteString`, checking--- that store directory matches `expectedRoot`.-parsePath :: StoreDir -> Bytes.Char8.ByteString -> Either String StorePath-parsePath expectedRoot x =-  let-    (rootDir, fname) = FilePath.splitFileName . Bytes.Char8.unpack $ x-    (storeBasedHashPart, namePart) = Text.breakOn "-" $ toText fname-    storeHash = decodeWith NixBase32 storeBasedHashPart-    name = makeStorePathName . Text.drop 1 $ namePart-    --rootDir' = dropTrailingPathSeparator rootDir-    -- cannot use ^^ as it drops multiple slashes /a/b/// -> /a/b-    rootDir' = Unsafe.init rootDir-    expectedRootS = Bytes.Char8.unpack (unStoreDir expectedRoot)-    storeDir =-      if expectedRootS == rootDir'-        then pure rootDir'-        else Left $ "Root store dir mismatch, expected" <> expectedRootS <> "got" <> rootDir'-  in-    either Left (pure $ StorePath <$> coerce storeHash <*> name) storeDir--pathParser :: StoreDir -> Parser StorePath-pathParser expectedRoot = do-  let expectedRootS = Bytes.Char8.unpack (unStoreDir expectedRoot)--  _ <--    Parser.Text.Lazy.string (toText expectedRootS)-      <?> "Store root mismatch" -- e.g. /nix/store--  _ <- Parser.Text.Lazy.char '/'-      <?> "Expecting path separator"--  digest <--    decodeWith NixBase32-    <$> Parser.Text.Lazy.takeWhile1 (`elem` Nix.Base32.digits32)-      <?> "Invalid Base32 part"--  _  <- Parser.Text.Lazy.char '-' <?> "Expecting dash (path name separator)"--  c0 <--    Parser.Text.Lazy.satisfy (\c -> c /= '.' && validStorePathNameChar c)-      <?> "Leading path name character is a dot or invalid character"--  rest <--    Parser.Text.Lazy.takeWhile validStorePathNameChar-      <?> "Path name contains invalid character"--  let name = makeStorePathName $ Text.cons c0 rest--  either-    fail-    pure-    (StorePath <$> coerce digest <*> name)
− src/System/Nix/Internal/Truncation.hs
@@ -1,39 +0,0 @@-{-# language ScopedTypeVariables #-}-{-# language DataKinds           #-}--module System.Nix.Internal.Truncation-  ( truncateInNixWay-  )-where--import qualified Data.ByteString        as Bytes---- | Bytewise truncation of a 'Digest'.------ When truncation length is greater than the length of the bytestring--- but less than twice the bytestring length, truncation splits the--- bytestring into a head part (truncation length) and tail part--- (leftover part), right-pads the leftovers with 0 to the truncation--- length, and combines the two strings bytewise with 'xor'.-truncateInNixWay-  :: Int -> Bytes.ByteString -> Bytes.ByteString---  2021-06-07: NOTE: Renamed function, since truncation can be done in a lot of ways, there is no practice of truncting hashes this way, moreover:--- 1. <https://crypto.stackexchange.com/questions/56337/strength-of-hash-obtained-by-xor-of-parts-of-sha3>--- 2. <https://www.reddit.com/r/crypto/comments/6olqfm/ways_to_truncated_hash/>-truncateInNixWay n c =-    Bytes.pack $ fmap truncOutputByte [0 .. n-1]-  where--    truncOutputByte :: Int -> Word8-    truncOutputByte i = foldl' (aux i) 0 [0 .. Bytes.length c - 1]--    inputByte :: Int -> Word8-    inputByte j = Bytes.index c j--    aux :: Int -> Word8 -> Int -> Word8-    aux i x j =-      bool-        id-        (`xor` inputByte j)-        (j `mod` n == i)-        x
− src/System/Nix/Nar.hs
@@ -1,78 +0,0 @@-{--Description : Generating and consuming NAR files-Maintainer  : Shea Levy <shea@shealevy.com>--}--{-# language ScopedTypeVariables #-}-{-# language TypeFamilies        #-}---module System.Nix.Nar-  (--  -- * Encoding and Decoding NAR archives-    buildNarIO-  , unpackNarIO--  -- * Experimental-  , Nar.parseNar-  , Nar.testParser-  , Nar.testParser'--  -- * Filesystem capabilities used by NAR encoder/decoder-  , Nar.NarEffects(..)-  , Nar.narEffectsIO--  , Nar.NarOptions(..)-  , Nar.defaultNarOptions--  -- * Internal-  , Nar.streamNarIO-  , Nar.streamNarIOWithOptions-  , Nar.runParser-  , Nar.runParserWithOptions-  , Nar.dumpString-  , Nar.dumpPath--  -- * Type-  , Nar.NarSource-  )-where--import qualified Control.Concurrent                as Concurrent-import qualified Data.ByteString                   as BS-import qualified System.IO                         as IO--import qualified System.Nix.Internal.Nar.Effects   as Nar-import qualified System.Nix.Internal.Nar.Options   as Nar-import qualified System.Nix.Internal.Nar.Parser    as Nar-import qualified System.Nix.Internal.Nar.Streamer  as Nar----- For a description of the NAR format, see Eelco's thesis--- https://nixos.org/%7Eeelco/pubs/phd-thesis.pdf----- | Pack the filesystem object at @FilePath@ into a NAR and stream it into the---   @IO.Handle@---   The handle should aleady be open and in @WriteMode@.-buildNarIO-  :: Nar.NarEffects IO-  -> FilePath-  -> IO.Handle-  -> IO ()-buildNarIO effs basePath outHandle =-  Nar.streamNarIO-    effs-    basePath-    (\chunk -> BS.hPut outHandle chunk >> Concurrent.threadDelay 10)----- | Read NAR formatted bytes from the @IO.Handle@ and unpack them into---   file system object(s) at the supplied @FilePath@-unpackNarIO-  :: Nar.NarEffects IO-  -> IO.Handle-  -> FilePath-  -> IO (Either String ())-unpackNarIO effs = Nar.runParser effs Nar.parseNar
+ src/System/Nix/OutputName.hs view
@@ -0,0 +1,29 @@+{-# LANGUAGE DeriveAnyClass #-}+{-|+Description : Derived path output names+-}++module System.Nix.OutputName+  ( OutputName(..)+  , mkOutputName+  -- * Re-exports+  , System.Nix.StorePath.InvalidNameError(..)+  , System.Nix.StorePath.parseNameText+  ) where++import Data.Hashable (Hashable)+import Data.Text (Text)+import GHC.Generics (Generic)+import System.Nix.StorePath (InvalidNameError)++import qualified System.Nix.StorePath++-- | Name of the derived path output+-- Typically used for "dev", "doc" sub-outputs+newtype OutputName = OutputName+  { -- | Extract the contents of the name.+    unOutputName :: Text+  } deriving (Eq, Generic, Hashable, Ord, Show)++mkOutputName :: Text -> Either InvalidNameError OutputName+mkOutputName = fmap OutputName . System.Nix.StorePath.parseNameText
− src/System/Nix/ReadonlyStore.hs
@@ -1,94 +0,0 @@-{-# language DataKinds #-}-{-# language ScopedTypeVariables #-}--module System.Nix.ReadonlyStore where---import qualified Data.ByteString.Char8         as Bytes.Char8-import qualified Data.ByteString               as BS-import qualified Data.HashSet                  as HS-import           System.Nix.Hash-import           System.Nix.Nar-import           System.Nix.StorePath-import           Crypto.Hash                    ( Context-                                                , Digest-                                                , hash-                                                , hashlazy-                                                , hashInit-                                                , hashUpdate-                                                , hashFinalize-                                                , SHA256-                                                )---makeStorePath-  :: forall h-   . (NamedAlgo h)-  => StoreDir-  -> ByteString-  -> Digest h-  -> StorePathName-  -> StorePath-makeStorePath storeDir ty h nm = StorePath (coerce storeHash) nm- where-  storeHash = mkStorePathHash @h s-  s =-    BS.intercalate ":" $-      ty:fmap encodeUtf8-        [ algoName @h-        , encodeDigestWith Base16 h-        , toText . Bytes.Char8.unpack $ unStoreDir storeDir-        , coerce nm-        ]--makeTextPath-  :: StoreDir -> StorePathName -> Digest SHA256 -> StorePathSet -> StorePath-makeTextPath storeDir nm h refs = makeStorePath storeDir ty h nm- where-  ty =-    BS.intercalate ":" $ "text" : sort (storePathToRawFilePath storeDir <$> HS.toList refs)--makeFixedOutputPath-  :: forall hashAlgo-  .  NamedAlgo hashAlgo-  => StoreDir-  -> Bool-  -> Digest hashAlgo-  -> StorePathName-  -> StorePath-makeFixedOutputPath storeDir recursive h =-  if recursive && (algoName @hashAlgo) == "sha256"-    then makeStorePath storeDir "source" h-    else makeStorePath storeDir "output:out" h'- where-  h' =-    hash @ByteString @SHA256-      $  "fixed:out:"-      <> encodeUtf8 (algoName @hashAlgo)-      <> (if recursive then ":r:" else ":")-      <> encodeUtf8 (encodeDigestWith Base16 h)-      <> ":"--computeStorePathForText-  :: StoreDir -> StorePathName -> ByteString -> (StorePathSet -> StorePath)-computeStorePathForText storeDir nm = makeTextPath storeDir nm . hash--computeStorePathForPath-  :: StoreDir-  -> StorePathName        -- ^ Name part of the newly created `StorePath`-  -> FilePath             -- ^ Local `FilePath` to add-  -> Bool                 -- ^ Add target directory recursively-  -> (FilePath -> Bool)   -- ^ Path filter function-  -> Bool                 -- ^ Only used by local store backend-  -> IO StorePath-computeStorePathForPath storeDir name pth recursive _pathFilter _repair = do-  selectedHash <- if recursive then recursiveContentHash else flatContentHash-  pure $ makeFixedOutputPath storeDir recursive selectedHash name- where-  recursiveContentHash :: IO (Digest SHA256)-  recursiveContentHash = hashFinalize <$> execStateT streamNarUpdate (hashInit @SHA256)-  streamNarUpdate :: StateT (Context SHA256) IO ()-  streamNarUpdate = streamNarIO narEffectsIO pth (modify . flip (hashUpdate @ByteString @SHA256))--  flatContentHash :: IO (Digest SHA256)-  flatContentHash = hashlazy <$> narReadFile narEffectsIO pth
+ src/System/Nix/Realisation.hs view
@@ -0,0 +1,103 @@+{-|+Description : Derivation realisations+-}++module System.Nix.Realisation (+    DerivationOutput(..)+  , DerivationOutputError(..)+  , derivationOutputBuilder+  , derivationOutputParser+  , Realisation(..)+  , RealisationWithId(..)+  ) where++import Crypto.Hash (Digest)+import Data.Map (Map)+import Data.Set (Set)+import Data.Text (Text)+import Data.Text.Lazy.Builder (Builder)+import Data.Dependent.Sum (DSum)+import GHC.Generics (Generic)+import System.Nix.Hash (HashAlgo)+import System.Nix.OutputName (OutputName, InvalidNameError)+import System.Nix.Signature (Signature)+import System.Nix.StorePath (StorePath)++import qualified Data.Bifunctor+import qualified Data.Text+import qualified Data.Text.Lazy.Builder+import qualified System.Nix.Hash++-- | Output of the derivation+data DerivationOutput a = DerivationOutput+  { derivationOutputHash :: DSum HashAlgo Digest+  -- ^ Hash modulo of the derivation+  , derivationOutputOutput :: a+  -- ^ Output (either a OutputName or StorePatH)+  } deriving (Eq, Generic, Ord, Show)++data DerivationOutputError+  = DerivationOutputError_Digest String+  | DerivationOutputError_Name InvalidNameError+  | DerivationOutputError_NoExclamationMark+  | DerivationOutputError_NoColon+  | DerivationOutputError_TooManyParts [Text]+  deriving (Eq, Ord, Show)++derivationOutputParser+  :: (Text -> Either InvalidNameError outputName)+  -> Text+  -> Either DerivationOutputError (DerivationOutput outputName)+derivationOutputParser outputName dOut =+  case Data.Text.splitOn (Data.Text.singleton '!') dOut of+    [] -> Left DerivationOutputError_NoColon+    [sriHash, oName] -> do+      hash <-+        case Data.Text.splitOn (Data.Text.singleton ':') sriHash of+          [] -> Left DerivationOutputError_NoColon+          [hashName, digest] ->+            Data.Bifunctor.first+              DerivationOutputError_Digest+              $ System.Nix.Hash.mkNamedDigest hashName digest+          x -> Left $ DerivationOutputError_TooManyParts x+      name <-+        Data.Bifunctor.first+          DerivationOutputError_Name+          $ outputName oName++      pure $ DerivationOutput hash name+    x -> Left $ DerivationOutputError_TooManyParts x++derivationOutputBuilder+  :: (outputName -> Text)+  -> DerivationOutput outputName+  -> Builder+derivationOutputBuilder outputName DerivationOutput{..} =+     System.Nix.Hash.algoDigestBuilder derivationOutputHash+  <> Data.Text.Lazy.Builder.singleton '!'+  <> Data.Text.Lazy.Builder.fromText (outputName derivationOutputOutput)++-- | Build realisation context+--+-- realisationId is ommited since it is a key+-- of type @DerivationOutput OutputName@ so+-- we will use @RealisationWithId@ newtype+data Realisation = Realisation+  { realisationOutPath :: StorePath+  -- ^ Output path+  , realisationSignatures :: Set Signature+  -- ^ Signatures+  , realisationDependencies :: Map (DerivationOutput OutputName) StorePath+  -- ^ Dependent realisations required for this one to be valid+  } deriving (Eq, Generic, Ord, Show)++-- | For wire protocol+--+-- We store this normalized in @Build.buildResultBuiltOutputs@+-- as @Map (DerivationOutput OutputName) Realisation@+-- but wire protocol needs it de-normalized so we+-- need a special (From|To)JSON instances for it+newtype RealisationWithId = RealisationWithId+  { unRealisationWithId :: (DerivationOutput OutputName, Realisation)+  }+  deriving (Eq, Generic, Ord, Show)
src/System/Nix/Signature.hs view
@@ -1,10 +1,85 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE NamedFieldPuns #-} {-| Description : Nix-relevant interfaces to NaCl signatures. -}+ module System.Nix.Signature-  ( Signature+  ( Signature(..)+  , signatureParser+  , parseSignature+  , signatureToText   , NarSignature(..)-  )-where+  , narSignatureParser+  , parseNarSignature+  , narSignatureToText+  ) where -import           System.Nix.Internal.Signature+import Crypto.Error (CryptoFailable(..))+import Data.Attoparsec.Text (Parser)+import Data.ByteString (ByteString)+import Data.Text (Text)+import GHC.Generics (Generic)+import System.Nix.Base (decodeWith, encodeWith, BaseEncoding(Base64))++import qualified Crypto.PubKey.Ed25519 as Ed25519+import qualified Data.Attoparsec.Text+import qualified Data.ByteArray+import qualified Data.Char+import qualified Data.Text++-- | An ed25519 signature.+newtype Signature = Signature Ed25519.Signature+  deriving (Eq, Generic, Show)++signatureParser :: Parser Signature+signatureParser = do+  encodedSig <-+    Data.Attoparsec.Text.takeWhile1+      (\c -> Data.Char.isAlphaNum c || c == '+' || c == '/' || c == '=')+  decodedSig <- case decodeWith Base64 encodedSig of+    Left e -> fail e+    Right decodedSig -> pure decodedSig+  sig <- case Ed25519.signature decodedSig of+    CryptoFailed e -> (fail . show) e+    CryptoPassed sig -> pure sig+  pure $ Signature sig++parseSignature :: Text -> Either String Signature+parseSignature = Data.Attoparsec.Text.parseOnly signatureParser++signatureToText :: Signature -> Text+signatureToText (Signature sig) =+  encodeWith Base64 (Data.ByteArray.convert sig :: ByteString)++-- | A detached signature attesting to a nix archive's validity.+data NarSignature = NarSignature+  { -- | The name of the public key used to sign the archive.+    publicKey :: !Text+  , -- | The archive's signature.+    sig :: !Signature+  }+  deriving (Eq, Generic, Ord)++instance Ord Signature where+  compare (Signature x) (Signature y) = let+    xBS = Data.ByteArray.convert x :: ByteString+    yBS = Data.ByteArray.convert y :: ByteString+    in compare xBS yBS++narSignatureParser :: Parser NarSignature+narSignatureParser = do+  publicKey <- Data.Attoparsec.Text.takeWhile1 (/= ':')+  _ <- Data.Attoparsec.Text.string ":"+  sig <- signatureParser+  pure $ NarSignature {..}++parseNarSignature :: Text -> Either String NarSignature+parseNarSignature = Data.Attoparsec.Text.parseOnly narSignatureParser++narSignatureToText :: NarSignature -> Text+narSignatureToText NarSignature {..} =+  mconcat [ publicKey, ":", signatureToText sig ]++instance Show NarSignature where+  show narSig = Data.Text.unpack (narSignatureToText narSig)
+ src/System/Nix/Store/Types.hs view
@@ -0,0 +1,24 @@+module System.Nix.Store.Types+  ( FileIngestionMethod(..)+  , PathFilter(..)+  , RepairMode(..)+  ) where++import GHC.Generics (Generic)++-- | Add path recursively or not+data FileIngestionMethod+  = FileIngestionMethod_Flat+  | FileIngestionMethod_FileRecursive+  deriving (Bounded, Eq, Generic, Enum, Ord, Show)++-- | Path filtering function+newtype PathFilter = PathFilter+  { pathFilterFunction :: FilePath -> Bool+  }++-- | Repair mode+data RepairMode+  = RepairMode_DoRepair+  | RepairMode_DontRepair+  deriving (Bounded, Eq, Generic, Enum, Ord, Show)
src/System/Nix/StorePath.hs view
@@ -1,28 +1,334 @@+{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE OverloadedStrings #-} {-| Description : Representation of Nix store paths. -}+ module System.Nix.StorePath   ( -- * Basic store path types     StoreDir(..)-  , StorePath(..)-  , StorePathName(..)-  , StorePathSet+  , HasStoreDir(..)+  , StorePath+  , storePathHash+  , storePathName+  , StorePathName+  , unStorePathName+  , StorePathHashPart   , mkStorePathHashPart-  , StorePathHashPart(..)-  , ContentAddressableAddress(..)-  , NarHashMode(..)-  , -- * Manipulating 'StorePathName'-    makeStorePathName-  , validStorePathName+  , unStorePathHashPart+    -- * Manipulating 'StorePathName'+  , InvalidNameError(..)+  , mkStorePathName+  , parseNameText+    -- * Reason why a path is not valid+  , InvalidPathError(..)   , -- * Rendering out 'StorePath's     storePathToFilePath   , storePathToRawFilePath   , storePathToText   , storePathToNarInfo+  , storePathHashPartToText   , -- * Parsing 'StorePath's     parsePath+  , parsePathFromText   , pathParser-  )-where+    -- * Utilities for tests+  , unsafeMakeStorePath+  , unsafeMakeStorePathHashPart+  ) where -import           System.Nix.Internal.StorePath+import Crypto.Hash (HashAlgorithm)+import Data.Attoparsec.Text.Lazy (Parser, (<?>))+import Data.ByteString (ByteString)+import Data.Default.Class (Default(def))+import Data.Hashable (Hashable(hashWithSalt))+import Data.Text (Text)+import GHC.Generics (Generic)+import System.Nix.Base (BaseEncoding(NixBase32))++import qualified Data.Bifunctor+import qualified Data.ByteString.Char8+import qualified Data.Char+import qualified Data.Text+import qualified Data.Text.Encoding+import qualified Data.Attoparsec.Text.Lazy+import qualified System.FilePath++import qualified System.Nix.Base+import qualified System.Nix.Hash+import qualified System.Nix.Base32++-- | A path in a Nix store.+--+-- From the Nix thesis: A store path is the full path of a store+-- object. It has the following anatomy: storeDir/hashPart-name.+--+-- The store directory is *not* included, and must be known from the+-- context. This matches modern C++ Nix, and also represents the fact+-- that store paths for different store directories cannot be mixed.+data StorePath = StorePath+  { -- | The 160-bit hash digest reflecting the "address" of the name.+    -- Currently, this is a truncated SHA256 hash.+    storePathHash :: !StorePathHashPart+  , -- | The (typically human readable) name of the path. For packages+    -- this is typically the package name and version (e.g.+    -- hello-1.2.3).+    storePathName :: !StorePathName+  }+  deriving (Eq, Generic, Ord)++instance Hashable StorePath where+  hashWithSalt s StorePath{..} =+    s `hashWithSalt` storePathHash `hashWithSalt` storePathName++instance Show StorePath where+  show s =+    "StorePath"+    <> " "+    <> storePathToFilePath (StoreDir mempty) s++-- | The name portion of a Nix path.+--+-- 'unStorePathName' must only contain a-zA-Z0-9+._?=-, can't start+-- with a -, and must have at least one character (i.e. it must match+-- 'storePathNameRegex').+newtype StorePathName = StorePathName+  { -- | Extract the contents of the name.+    unStorePathName :: Text+  } deriving (Eq, Generic, Hashable, Ord, Show)++-- | The hash algorithm used for store path hashes.+newtype StorePathHashPart = StorePathHashPart+  { -- | Extract the contents of the hash.+    unStorePathHashPart :: ByteString+  }+  deriving (Eq, Generic, Hashable, Ord, Show)++-- | Make @StorePathHashPart@ from @ByteString@ (hash part of the @StorePath@)+-- using specific @HashAlgorithm@+mkStorePathHashPart+  :: forall hashAlgo+   . HashAlgorithm hashAlgo+  => ByteString+  -> StorePathHashPart+mkStorePathHashPart =+  StorePathHashPart+  . System.Nix.Hash.mkStorePathHash @hashAlgo++-- | Reason why a path name or output name is not valid+data InvalidNameError+  = EmptyName+  | NameTooLong Int+  | LeadingDot+  | InvalidCharacters Text+  deriving (Eq, Generic, Hashable, Ord, Show)++-- | Reason why a path is not valid+data InvalidPathError+  = PathNameInvalid InvalidNameError+  | HashDecodingFailure String+  | RootDirMismatch+      { rdMismatchExpected :: StoreDir+      , rdMismatchGot      :: StoreDir+      }+  deriving (Eq, Generic, Hashable, Ord, Show)++-- | Make @StorePathName@ from @Text@ (name part of the @StorePath@)+-- or fail with @InvalidNameError@ if it isn't valid+mkStorePathName :: Text -> Either InvalidNameError StorePathName+mkStorePathName = fmap StorePathName . parseNameText++-- | Parse name (either @StorePathName@ or @OutputName@)+parseNameText :: Text -> Either InvalidNameError Text+parseNameText n+  | n == ""+    = Left EmptyName+  | Data.Text.length n > 211+    = Left $ NameTooLong (Data.Text.length n)+  | Data.Text.head n == '.'+    = Left $ LeadingDot+  | not+    $ Data.Text.null+    $ Data.Text.filter+        (not . validStorePathNameChar)+        n+    = Left+      $ InvalidCharacters+      $ Data.Text.filter (not . validStorePathNameChar) n+  | otherwise = pure n++validStorePathNameChar :: Char -> Bool+validStorePathNameChar c =+  any ($ c)+    [ Data.Char.isAsciiLower -- 'a'..'z', isAscii..er probably faster then putting it out+    , Data.Char.isAsciiUpper -- 'A'..'Z'+    , Data.Char.isDigit+    , (`elem` ("+-._?=" :: String))+    ]++-- | Copied from @RawFilePath@ in the @unix@ package, duplicated here+-- to avoid the dependency.+type RawFilePath = ByteString++-- | The path to the store dir+--+-- Many operations need to be parameterized with this, since store paths+-- do not know their own store dir by design.+newtype StoreDir = StoreDir {+    unStoreDir :: RawFilePath+  } deriving (Eq, Generic, Hashable, Ord, Show)++instance Default StoreDir where+  def = StoreDir "/nix/store"++class HasStoreDir r where+  hasStoreDir :: r -> StoreDir++-- | Render a 'StorePath' as a 'RawFilePath'.+storePathToRawFilePath :: StoreDir -> StorePath -> RawFilePath+storePathToRawFilePath storeDir StorePath{..} =+  unStoreDir storeDir <> "/" <> hashPart <> "-" <> name+ where+  hashPart = Data.Text.Encoding.encodeUtf8 $ storePathHashPartToText storePathHash+  name     = Data.Text.Encoding.encodeUtf8 $ unStorePathName storePathName++-- | Render a 'StorePath' as a 'FilePath'.+storePathToFilePath :: StoreDir -> StorePath -> FilePath+storePathToFilePath storeDir = Data.ByteString.Char8.unpack . storePathToRawFilePath storeDir++-- | Render a 'StorePath' as a 'Text'.+storePathToText :: StoreDir -> StorePath -> Text+storePathToText storeDir =+  Data.Text.pack+  . Data.ByteString.Char8.unpack+  . storePathToRawFilePath storeDir++-- | Build `narinfo` suffix from `StorePath` which+-- can be used to query binary caches.+storePathToNarInfo :: StorePath -> ByteString+storePathToNarInfo StorePath{..} =+  Data.Text.Encoding.encodeUtf8+  $ System.Nix.Base.encodeWith NixBase32+      (unStorePathHashPart storePathHash) <> ".narinfo"++-- | Render a 'StorePathHashPart' as a 'Text'.+-- This is used by remote store / database+-- via queryPathFromHashPart+storePathHashPartToText :: StorePathHashPart -> Text+storePathHashPartToText =+  System.Nix.Base.encodeWith NixBase32 . unStorePathHashPart++-- | Parse `StorePath` from `String`, internal+parsePath'+  :: StoreDir+  -> String+  -> Either InvalidPathError StorePath+parsePath' expectedRoot stringyPath =+  let+    (rootDir, fname) = System.FilePath.splitFileName stringyPath+    (storeBasedHashPart, namePart) = Data.Text.breakOn "-" $ Data.Text.pack fname+    hashPart =+      Data.Bifunctor.bimap+        HashDecodingFailure+        StorePathHashPart+        $ System.Nix.Base.decodeWith NixBase32 storeBasedHashPart+    name =+      Data.Bifunctor.first+        PathNameInvalid+        $ mkStorePathName . Data.Text.drop 1 $ namePart+    --rootDir' = dropTrailingPathSeparator rootDir+    -- cannot use ^^ as it drops multiple slashes /a/b/// -> /a/b+    rootDir' = init rootDir+    expectedRootS = Data.ByteString.Char8.unpack (unStoreDir expectedRoot)+    storeDir =+      if expectedRootS == rootDir'+        then pure rootDir'+        else Left $ RootDirMismatch+                      { rdMismatchExpected = expectedRoot+                      , rdMismatchGot = StoreDir $ Data.ByteString.Char8.pack rootDir+                      }+  in+    either Left (pure $ StorePath <$> hashPart <*> name) storeDir++-- | Parse `StorePath` from `ByteString`, checking+-- that store directory matches `expectedRoot`.+parsePath+  :: StoreDir -- ^ expected @StoreDir@+  -> ByteString+  -> Either InvalidPathError StorePath+parsePath sd = parsePath' sd . Data.ByteString.Char8.unpack++-- | Parse `StorePath` from `Text`, checking+-- that store directory matches `expectedRoot`.+parsePathFromText+  :: StoreDir -- ^ expected @StoreDir@+  -> Text+  -> Either InvalidPathError StorePath+parsePathFromText sd = parsePath' sd . Data.Text.unpack++-- | Attoparsec @StorePath@ @Parser@+pathParser :: StoreDir -> Parser StorePath+pathParser expectedRoot = do+  let expectedRootT =+          Data.Text.pack+        . Data.ByteString.Char8.unpack+        $ unStoreDir expectedRoot++  _ <- Data.Attoparsec.Text.Lazy.string expectedRootT+      <?> "Store root mismatch" -- e.g. /nix/store++  _ <- Data.Attoparsec.Text.Lazy.char '/'+      <?> "Expecting path separator"++  digest <-+    System.Nix.Base.decodeWith NixBase32+    <$> Data.Attoparsec.Text.Lazy.takeWhile1+      (`elem` System.Nix.Base32.digits32)+      <?> "Invalid Base32 part"++  _  <- Data.Attoparsec.Text.Lazy.char '-'+      <?> "Expecting dash (path name separator)"++  c0 <-+    Data.Attoparsec.Text.Lazy.satisfy+      (\c -> c /= '.' && validStorePathNameChar c)+      <?> "Leading path name character is a dot or invalid character"++  rest <-+    Data.Attoparsec.Text.Lazy.takeWhile+    validStorePathNameChar+      <?> "Path name contains invalid character"++  let name =+        Data.Bifunctor.first+          PathNameInvalid+          $ mkStorePathName $ Data.Text.cons c0 rest+      hashPart =+        Data.Bifunctor.bimap+          HashDecodingFailure+          StorePathHashPart+          digest++  either+    (fail . show)+    pure+    (StorePath <$> hashPart <*> name)++-- * Utilities for tests++-- | Paths rarely need to be constructed directly.+-- Prefer @parsePath@ or @parsePathFromText@+unsafeMakeStorePath+  :: StorePathHashPart+  -> StorePathName+  -> StorePath+unsafeMakeStorePath = StorePath++-- | Path hash parts rarely need to be constructed directly.+-- Prefer @mkStorePathHashPart@+-- Used by remote store in wire protocol+unsafeMakeStorePathHashPart+  :: ByteString+  -> StorePathHashPart+unsafeMakeStorePathHashPart = StorePathHashPart
+ src/System/Nix/StorePath/Metadata.hs view
@@ -0,0 +1,59 @@+{-|+Description : Metadata about Nix store paths.+-}+module System.Nix.StorePath.Metadata+  ( Metadata(..)+  , StorePathTrust(..)+  ) where++import Crypto.Hash (Digest)+import Data.Dependent.Sum (DSum)+import Data.HashSet (HashSet)+import Data.Set (Set)+import Data.Time (UTCTime)+import Data.Word (Word64)+import GHC.Generics (Generic)++import System.Nix.Hash (HashAlgo)+import System.Nix.Signature (NarSignature)+import System.Nix.ContentAddress (ContentAddress)++-- | How much do we trust the path, based on its provenance?+-- This is called `Ultimate` in Nix, where Ultimate = True+-- means that the path is ultimately trusted, which+-- corresponds to our @BuiltLocally@+data StorePathTrust+  = -- | It was built locally and thus ultimately trusted+    BuiltLocally+  | -- | It was built elsewhere (and substituted or similar) and so+    -- is less trusted+    BuiltElsewhere+  deriving (Eq, Enum, Generic, Ord, Show)++-- | Metadata (typically about a 'StorePath')+-- This type corresponds to Nix-es `ValidPathInfo`+data Metadata a = Metadata+  { -- | The path to the derivation file that built this path, if any+    -- and known.+    metadataDeriverPath :: !(Maybe a)+  , -- | The hash of the nar serialization of the path.+    metadataNarHash :: !(DSum HashAlgo Digest)+  , -- | The paths that this path directly references+    metadataReferences :: !(HashSet a)+  , -- | When was this path registered valid in the store?+    metadataRegistrationTime :: !UTCTime+  , -- | The size of the nar serialization of the path, in bytes.+    metadataNarBytes :: !(Maybe Word64)+  , -- | How much we trust this path. Nix-es ultimate+    metadataTrust :: !StorePathTrust+  , -- | A set of cryptographic attestations of this path's validity.+    --+    -- There is no guarantee from this type alone that these+    -- signatures are valid.+    metadataSigs :: !(Set NarSignature)+  , -- | Whether and how this store path is content-addressable.+    --+    -- There is no guarantee from this type alone that this address+    -- is actually correct for this store path.+    metadataContentAddress :: !(Maybe ContentAddress)+  } deriving (Eq, Generic, Ord, Show)
− src/System/Nix/StorePathMetadata.hs
@@ -1,51 +0,0 @@-{-|-Description : Metadata about Nix store paths.--}-module System.Nix.StorePathMetadata where--import           System.Nix.StorePath           ( StorePath-                                                , StorePathSet-                                                , ContentAddressableAddress-                                                )-import           System.Nix.Hash                ( SomeNamedDigest )-import           Data.Time                      ( UTCTime )-import           System.Nix.Signature           ( NarSignature )---- | Metadata about a 'StorePath'-data StorePathMetadata = StorePathMetadata-  { -- | The path this metadata is about-    path :: !StorePath-  , -- | The path to the derivation file that built this path, if any-    -- and known.-    deriverPath :: !(Maybe StorePath)-  , -- TODO should this be optional?-    -- | The hash of the nar serialization of the path.-    narHash :: !SomeNamedDigest-  , -- | The paths that this path directly references-    references :: !StorePathSet-  , -- | When was this path registered valid in the store?-    registrationTime :: !UTCTime-  , -- | The size of the nar serialization of the path, in bytes.-    narBytes :: !(Maybe Word64)-  , -- | How much we trust this path.-    trust :: !StorePathTrust-  , -- | A set of cryptographic attestations of this path's validity.-    ---    -- There is no guarantee from this type alone that these-    -- signatures are valid.-    sigs :: !(Set NarSignature)-  , -- | Whether and how this store path is content-addressable.-    ---    -- There is no guarantee from this type alone that this address-    -- is actually correct for this store path.-    contentAddressableAddress :: !(Maybe ContentAddressableAddress)-  }---- | How much do we trust the path, based on its provenance?-data StorePathTrust-  = -- | It was built locally and thus ultimately trusted-    BuiltLocally-  | -- | It was built elsewhere (and substituted or similar) and so-    -- is less trusted-    BuiltElsewhere-  deriving (Show, Eq, Ord)
− tests/Arbitrary.hs
@@ -1,58 +0,0 @@-{-# language DataKinds            #-}-{-# OPTIONS_GHC -Wno-orphans      #-}--module Arbitrary where--import qualified Data.ByteString.Char8         as BSC--import           Test.Tasty.QuickCheck--import           System.Nix.Internal.StorePath-import           Crypto.Hash                        ( SHA256-                                                    , Digest-                                                    , hash-                                                    )--genSafeChar :: Gen Char-genSafeChar = choose ('\1', '\127') -- ASCII without \NUL--nonEmptyString :: Gen String-nonEmptyString = listOf1 genSafeChar--dir :: Gen String-dir = ('/':) <$> listOf1 (elements $ '/':['a'..'z'])--instance Arbitrary StorePathName where-  arbitrary = StorePathName . toText <$> ((:) <$> s1 <*> listOf sn)-   where-    alphanum = ['a' .. 'z'] <> ['A' .. 'Z'] <> ['0' .. '9']-    s1       = elements $ alphanum <> "+-_?="-    sn       = elements $ alphanum <> "+-._?="--instance Arbitrary StorePathHashPart where-  arbitrary = mkStorePathHashPart . BSC.pack <$> arbitrary--instance Arbitrary (Digest SHA256) where-  arbitrary = hash . BSC.pack <$> arbitrary--instance Arbitrary StoreDir where-  arbitrary = StoreDir . ("/" <>) . BSC.pack <$> arbitrary--newtype NixLike = NixLike {getNixLike :: StorePath}- deriving (Eq, Ord, Show)--instance Arbitrary NixLike where-  arbitrary =-    NixLike <$>-      liftA2 StorePath-        arbitraryTruncatedDigest-        arbitrary-   where-    -- 160-bit hash, 20 bytes, 32 chars in base32-    arbitraryTruncatedDigest = coerce . BSC.pack <$> replicateM 20 genSafeChar--instance Arbitrary StorePath where-  arbitrary =-    liftA2 StorePath-      arbitrary-      arbitrary
tests/Derivation.hs view
@@ -1,33 +1,27 @@  module Derivation where -import           Test.Tasty                     ( TestTree-                                                , testGroup-                                                )-import           Test.Tasty.Golden              ( goldenVsFile )+import Test.Tasty (TestTree, testGroup)+import Test.Tasty.Golden (goldenVsFile) -import           System.Nix.StorePath           ( StoreDir(..) )-import           System.Nix.Derivation          ( parseDerivation-                                                , buildDerivation-                                                )+import System.Nix.Derivation (parseDerivation, buildDerivation) -import qualified Data.Attoparsec.Text-import qualified Data.Text.IO+import Data.Default.Class (Default(def))+import qualified Data.Attoparsec.Text.Lazy+import qualified Data.Text.Lazy.IO import qualified Data.Text.Lazy.Builder  processDerivation :: FilePath -> FilePath -> IO () processDerivation source dest = do-  contents <- Data.Text.IO.readFile source+  contents <- Data.Text.Lazy.IO.readFile source   either     fail-    -- It seems to be derivation.-    (Data.Text.IO.writeFile dest-      . toText+    (Data.Text.Lazy.IO.writeFile dest       . Data.Text.Lazy.Builder.toLazyText-      . buildDerivation (StoreDir "/nix/store")+      . buildDerivation def     )-    (Data.Attoparsec.Text.parseOnly-      (parseDerivation $ StoreDir "/nix/store")+    (Data.Attoparsec.Text.Lazy.parseOnly+      (parseDerivation def)       contents     ) 
+ tests/Fingerprint.hs view
@@ -0,0 +1,76 @@+{-# LANGUAGE OverloadedStrings #-}+-- Test case from https://code.tvl.fyi/commit/tvix/nix-compat/src/narinfo/fingerprint.rs?id=a834966efd64c1b2306241c3ef20f4258f6b9c4e++module Fingerprint where++import Crypto.Error (CryptoFailable(..))+import Data.Default.Class+import System.Nix.Base (decodeWith, BaseEncoding(Base64))+import System.Nix.Fingerprint+import System.Nix.Signature+import System.Nix.StorePath+import System.Nix.StorePath.Metadata+import System.Nix.Hash (mkNamedDigest)+import Data.Text (Text)+import Data.Time.Clock (UTCTime(..))+import Data.Time.Calendar.OrdinalDate (fromOrdinalDate)+import Test.Hspec++import qualified Crypto.PubKey.Ed25519 as Ed25519+import qualified Data.HashSet as HashSet+import qualified Data.Set as Set+import qualified Data.Text.Encoding as Text++spec_fingerprint :: Spec+spec_fingerprint = do++  describe "fingerprint" $ do++    it "is valid for example metadata" $+      metadataFingerprint def exampleStorePath exampleMetadata `shouldBe` exampleFingerprint++    it "allows a successful signature verification" $ do+      let msg = Text.encodeUtf8 $ metadataFingerprint def exampleStorePath exampleMetadata+          Signature sig' =+            case+              sig+              <$> filter (\(NarSignature publicKey _) -> publicKey == "cache.nixos.org-1")+              (Set.toList (metadataSigs exampleMetadata))+            of+              (x:_) -> x+              _ -> error "impossible"+      sig' `shouldSatisfy` Ed25519.verify pubkey msg++exampleFingerprint :: Text+exampleFingerprint = "1;/nix/store/syd87l2rxw8cbsxmxl853h0r6pdwhwjr-curl-7.82.0-bin;sha256:1b4sb93wp679q4zx9k1ignby1yna3z7c4c2ri3wphylbc2dwsys0;196040;/nix/store/0jqd0rlxzra1rs38rdxl43yh6rxchgc6-curl-7.82.0,/nix/store/6w8g7njm4mck5dmjxws0z1xnrxvl81xa-glibc-2.34-115,/nix/store/j5jxw3iy7bbz4a57fh9g2xm2gxmyal8h-zlib-1.2.12,/nix/store/yxvjs9drzsphm9pcf42a4byzj1kb9m7k-openssl-1.1.1n";++exampleStorePath :: StorePath+exampleStorePath = forceRight $ parsePath def "/nix/store/syd87l2rxw8cbsxmxl853h0r6pdwhwjr-curl-7.82.0-bin"++exampleMetadata :: Metadata StorePath+exampleMetadata = Metadata+  { metadataDeriverPath = Just $ forceRight $ parsePath def "/nix/store/5rwxzi7pal3qhpsyfc16gzkh939q1np6-curl-7.82.0.drv"+  , metadataNarHash = forceRight $ mkNamedDigest "sha256" "1b4sb93wp679q4zx9k1ignby1yna3z7c4c2ri3wphylbc2dwsys0"+  , metadataReferences = HashSet.fromList $ forceRight . parsePath def <$> ["/nix/store/0jqd0rlxzra1rs38rdxl43yh6rxchgc6-curl-7.82.0","/nix/store/6w8g7njm4mck5dmjxws0z1xnrxvl81xa-glibc-2.34-115","/nix/store/j5jxw3iy7bbz4a57fh9g2xm2gxmyal8h-zlib-1.2.12","/nix/store/yxvjs9drzsphm9pcf42a4byzj1kb9m7k-openssl-1.1.1n"]+  , metadataRegistrationTime = UTCTime (fromOrdinalDate 0 0) 0+  , metadataNarBytes = Just 196040+  , metadataTrust = BuiltElsewhere+  , metadataSigs = Set.fromList $ forceRight . parseNarSignature <$> ["cache.nixos.org-1:TsTTb3WGTZKphvYdBHXwo6weVILmTytUjLB+vcX89fOjjRicCHmKA4RCPMVLkj6TMJ4GMX3HPVWRdD1hkeKZBQ==", "test1:519iiVLx/c4Rdt5DNt6Y2Jm6hcWE9+XY69ygiWSZCNGVcmOcyL64uVAJ3cV8vaTusIZdbTnYo9Y7vDNeTmmMBQ=="]+  , metadataContentAddress = Nothing+  }++pubkey :: Ed25519.PublicKey+pubkey = forceDecodeB64Pubkey "6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY="++forceDecodeB64Pubkey :: Text -> Ed25519.PublicKey+forceDecodeB64Pubkey b64EncodedPubkey = let+  decoded = forceRight $ decodeWith Base64 b64EncodedPubkey+  in case Ed25519.publicKey decoded of+    CryptoFailed err -> (error . show) err+    CryptoPassed x -> x++forceRight :: Either a b -> b+forceRight = \case+  Right x -> x+  _ -> error "forceRight failed"+
tests/Hash.hs view
@@ -1,59 +1,44 @@-{-# language DataKinds           #-}-{-# language ScopedTypeVariables #-}-{-# language CPP #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE OverloadedStrings #-}  module Hash where +import Data.ByteString (ByteString)++import Control.Monad (forM_)+import Crypto.Hash (MD5, SHA1, SHA256, hash) import qualified Data.ByteString.Base16      as B16 import qualified System.Nix.Base32           as B32 import qualified Data.ByteString.Base64.Lazy as B64+import qualified Data.ByteString.Lazy        as BSL -import           Test.Hspec-import           Test.Tasty.QuickCheck -import           System.Nix.Hash-import           System.Nix.StorePath-import           Arbitrary-import           System.Nix.Internal.Base-import           Crypto.Hash                ( MD5-                                            , SHA1-                                            , SHA256-                                            , hash-                                            , Digest-                                            )+import System.Nix.Base+import System.Nix.Hash+import System.Nix.StorePath+import Test.Hspec  spec_hash :: Spec spec_hash = do    describe "hashing parity with nix-store" $ do -    cmp "produces (base32 . sha256) of \"nix-output:foo\" the same as Nix does at the moment for placeholder \"foo\""-      NixBase32 (hash @ByteString @SHA256) "nix-output:foo" "1x0ymrsy7yr7i9wdsqy9khmzc1yy7nvxw6rdp72yzn50285s67j5"-    cmp "produces (base16 . md5) of \"Hello World\" the same as the thesis"-      Base16 (hash @ByteString @MD5) "Hello World" "b10a8db164e0754105b7a99be72e3fe5"-    cmp "produces (base32 . sha1) of \"Hello World\" the same as the thesis"-      NixBase32 (hash @ByteString @SHA1) "Hello World" "s23c9fs0v32pf6bhmcph5rbqsyl5ak8a"+    it "produces (base32 . sha256) of \"nix-output:foo\" the same as Nix does at the moment for placeholder \"foo\"" $+      shouldBe (encodeDigestWith NixBase32 (hash @ByteString @SHA256 "nix-output:foo"))+               "1x0ymrsy7yr7i9wdsqy9khmzc1yy7nvxw6rdp72yzn50285s67j5"+    it "produces (base16 . md5) of \"Hello World\" the same as the thesis" $+      shouldBe (encodeDigestWith Base16 (hash @ByteString @MD5 "Hello World"))+               "b10a8db164e0754105b7a99be72e3fe5"+    it "produces (base32 . sha1) of \"Hello World\" the same as the thesis" $+      shouldBe (encodeDigestWith NixBase32 (hash @ByteString @SHA1 "Hello World"))+               "s23c9fs0v32pf6bhmcph5rbqsyl5ak8a"      -- The example in question:     -- https://nixos.org/nixos/nix-pills/nix-store-paths.html     it "produces same base32 as nix pill flat file example" $ do-      shouldBe (encodeWith NixBase32 $ coerce $ mkStorePathHashPart "source:sha256:2bfef67de873c54551d884fdab3055d84d573e654efa79db3c0d7b98883f9ee3:/nix/store:myfile")+      shouldBe (encodeWith NixBase32 $ unStorePathHashPart $ mkStorePathHashPart @SHA256 "source:sha256:2bfef67de873c54551d884fdab3055d84d573e654efa79db3c0d7b98883f9ee3:/nix/store:myfile")         "xv2iccirbrvklck36f1g7vldn5v58vck"- where-  cmp :: String -> BaseEncoding -> (ByteString -> Digest a) -> ByteString -> Text -> SpecWith ()-  cmp t b f s h =-    it t $-      shouldBe (encodeDigestWith b $ f s) h --- | Test that Nix-like base32 encoding roundtrips-prop_nixBase32Roundtrip :: Property-prop_nixBase32Roundtrip = forAllShrink nonEmptyString genericShrink $-  \x -> pure (encodeUtf8 x) === (B32.decode . B32.encode . encodeUtf8 $ x)---- | API variants-prop_nixBase16Roundtrip :: StorePathHashPart -> Property-prop_nixBase16Roundtrip x = pure (coerce x) === decodeWith Base16 (encodeWith Base16 $ coerce x)- -- | Hash encoding conversion ground-truth. -- Similiar to nix/tests/hash.sh spec_nixhash :: Spec@@ -61,68 +46,47 @@    describe "hashing parity with nix-nash" $ do -    cmp-      "b16 encoded . b32 decoded should equal original b16"-      B16.encode B32.decode b32s b16s+    let+      samples = [+          ( "800d59cfcd3c05e900cb4e214be48f6b886a08df"+          , "vw46m23bizj4n8afrc0fj19wrp7mj3c0"+          , "gA1Zz808BekAy04hS+SPa4hqCN8="+          )+        , ( "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"+          , "1b8m03r63zqhnjf7l5wnldhh7c134ap5vpj0850ymkq1iyzicy5s"+          , "ungWv48Bz+pBQUDeXa4iI7ADYaOWF3qctBD/YfIAFa0="+          )+        , ( "204a8fc6dda82f0a0ced7beb8e08a41657c16ef468b228a8279be331a703c33596fd15c13b1b07f9aa1d3bea57789ca031ad85c7a71dd70354ec631238ca3445"+          , "12k9jiq29iyqm03swfsgiw5mlqs173qazm3n7daz43infy12pyrcdf30fkk3qwv4yl2ick8yipc2mqnlh48xsvvxl60lbx8vp38yji0"+          , "IEqPxt2oLwoM7XvrjgikFlfBbvRosiioJ5vjMacDwzWW/RXBOxsH+aodO+pXeJygMa2Fx6cd1wNU7GMSOMo0RQ=="+          )+        ] -    cmp-      "b64 encoded . b32 decoded should equal original b64"-      (B64.encode . fromStrict) B32.decode b32s b64s+    it "b16 encoded . b32 decoded should equal original b16" $+      forM_ samples $ \(b16, b32, _b64) -> shouldBe (B16.encode <$> B32.decode b32) (pure b16) -    cmp-      "b32 encoded . b64 decoded should equal original b32"-      (B32.encode . toStrict) B64.decode b64s b32s+    it "b64 encoded . b32 decoded should equal original b64" $+      forM_ samples $ \(_b16, b32, b64) -> shouldBe (B64.encode . BSL.fromStrict <$> B32.decode b32) (pure b64) -    cmp-      "b16 encoded . b64 decoded should equal original b16"-      (B16.encode . toStrict) B64.decode b64s b16s+    it "b32 encoded . b64 decoded should equal original b32" $+      forM_ samples $ \(_b16, b32, b64) -> shouldBe (B32.encode . BSL.toStrict <$> B64.decode b64 ) (pure b32) -#if MIN_VERSION_base16_bytestring(1,0,0)-    cmp-      "b32 encoded . b16 decoded should equal original b32"-      B32.encode B16.decode b16s b32s+    it "b16 encoded . b64 decoded should equal original b16" $+      forM_ samples $ \(b16, _b32, b64) -> shouldBe (B16.encode . BSL.toStrict <$> B64.decode b64 ) (pure b16) -    cmp-      "b64 encoded . b16 decoded should equal original b64"-      (B64.encode . fromStrict) B16.decode b16s b64s-#else     it "b32 encoded . b16 decoded should equal original b32" $-      traverse_ (\ b -> shouldBe (B32.encode $ fst $ B16.decode $ fst b) (snd b)) $ zip b16s b32s+      forM_ samples $ \(b16, b32, _b64) -> shouldBe (B32.encode+#if MIN_VERSION_base16_bytestring(1,0,0)+        <$> B16.decode b16) (pure b32)+#else+        $ fst $ B16.decode b16) (b32)+#endif      it "b64 encoded . b16 decoded should equal original b64" $-      traverse_ (\ b -> shouldBe (B64.encode . fromStrict $ fst $ B16.decode $ fst b) (snd b)) $ zip b16s b64s+      forM_ samples $ \(b16, _b32, b64) -> shouldBe (B64.encode . BSL.fromStrict+#if MIN_VERSION_base16_bytestring(1,0,0)+        <$> B16.decode b16) (pure b64)+#else+        $ fst $ B16.decode b16 ) (b64) #endif - where-  cmp-    :: ( Eq b-       , Show b-       )-    => String-    -> (a -> b)-    -> (c -> Either String a)-    -> [c]-    -> [b]-    -> SpecWith ()-  cmp s f1 f2 b1 b2 = it s $ traverse_ (uncurry shouldBe . bimap (fmap f1 . f2) pure) $ zip b1 b2--  b16s = takeAxis (\(a,_,_) -> a)-  b32s = takeAxis (\(_,b,_) -> b)-  b64s = takeAxis (\(_,_,c) -> c)--  takeAxis f = fmap f samples--  samples =-    [ ( "800d59cfcd3c05e900cb4e214be48f6b886a08df"-      , "vw46m23bizj4n8afrc0fj19wrp7mj3c0"-      , "gA1Zz808BekAy04hS+SPa4hqCN8="-      )-    , ( "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"-      , "1b8m03r63zqhnjf7l5wnldhh7c134ap5vpj0850ymkq1iyzicy5s"-      , "ungWv48Bz+pBQUDeXa4iI7ADYaOWF3qctBD/YfIAFa0="-      )-    , ( "204a8fc6dda82f0a0ced7beb8e08a41657c16ef468b228a8279be331a703c33596fd15c13b1b07f9aa1d3bea57789ca031ad85c7a71dd70354ec631238ca3445"-      , "12k9jiq29iyqm03swfsgiw5mlqs173qazm3n7daz43infy12pyrcdf30fkk3qwv4yl2ick8yipc2mqnlh48xsvvxl60lbx8vp38yji0"-      , "IEqPxt2oLwoM7XvrjgikFlfBbvRosiioJ5vjMacDwzWW/RXBOxsH+aodO+pXeJygMa2Fx6cd1wNU7GMSOMo0RQ=="-      )-    ]
− tests/NarFormat.hs
@@ -1,758 +0,0 @@-{-# language CPP                 #-}-{-# language ScopedTypeVariables #-}--module NarFormat where--import qualified Control.Concurrent               as Concurrent-import           Control.Exception                (try)-import           Data.Binary                      (Binary(..), decodeFile)-import           Data.Binary.Get                  (Get, getByteString,-                                                   getInt64le,-                                                   getLazyByteString, runGet)-import           Data.Binary.Put                  (Put, putInt64le,-                                                   putLazyByteString, runPut)-import qualified Data.ByteString                  as BS-import qualified Data.ByteString.Base64.Lazy      as B64-import qualified Data.ByteString.Char8            as BSC-import qualified Data.ByteString.Lazy             as BSL-import qualified Data.ByteString.Lazy.Char8       as BSLC-import qualified Data.Map                         as Map-import qualified Data.Text                        as T-import           System.Directory                 ( doesDirectoryExist-                                                  , doesPathExist-                                                  , removeDirectoryRecursive-                                                  , removeFile-                                                  )-import qualified System.Directory                 as Directory-import           System.Environment               (getEnv)-import           System.FilePath                  ((<.>), (</>))-import qualified System.IO                        as IO-import qualified System.IO.Temp                   as Temp-import qualified System.Posix.Process             as Unix-import qualified System.Process                   as P-import           Test.Tasty                       as T-import           Test.Hspec-import qualified Test.Tasty.HUnit                 as HU-import           Test.Tasty.QuickCheck-import qualified Text.Printf                      as Printf--import qualified System.Nix.Internal.Nar.Streamer as Nar-import           System.Nix.Nar---- Without the import, `max_live_bytes` and `getRTSStats` are undefined on some setups.-#ifdef BOUNDED_MEMORY-import GHC.Stats-#endif---withBytesAsHandle :: BSLC.ByteString -> (IO.Handle -> IO a) -> IO a-withBytesAsHandle bytes act = do-  Temp.withSystemTempFile "nar-test-file-XXXXX" $ \tmpFile h -> do-    IO.hClose h-    BSL.writeFile tmpFile bytes-    withFile tmpFile ReadMode act--spec_narEncoding :: Spec-spec_narEncoding = do--  -- For a Haskell embedded Nar, check that (decode . encode === id)-  let-    withTempDir act = Temp.withSystemTempDirectory "nar-test" act---    roundTrip :: String -> Nar -> IO ()-    roundTrip narFileName n = withTempDir $ \tmpDir -> do-        let packageFilePath = tmpDir </> narFileName--        e <- doesPathExist packageFilePath-        e `shouldBe` False--        res <- withBytesAsHandle (runPut (putNar n)) $ \h -> do-          unpackNarIO narEffectsIO h packageFilePath-        res `shouldBe` pass--        e' <- doesPathExist packageFilePath-        e' `shouldBe` True--        res' <- Temp.withSystemTempFile "nar-test-file-hnix" $ \tmpFile h -> do-          buildNarIO narEffectsIO packageFilePath h-          IO.hClose h-          BSL.readFile tmpFile--        res' `shouldBe` runPut (putNar n)--  -- For a Haskell embedded Nar, check that encoding it gives-  -- the same bytestring as `nix-store --dump`-  let-    encEqualsNixStore :: Nar -> BSL.ByteString -> IO ()-    encEqualsNixStore n b = runPut (putNar n) `shouldBe` b---  describe "parser-roundtrip" $ do-    it "roundtrips regular" $ do-      roundTrip "sampleRegular" (Nar sampleRegular)--    it "roundtrips regular 2" $ do-      roundTrip "sampleRegular'" (Nar sampleRegular')--    it "roundtrips executable" $ do-      roundTrip "sampleExecutable" (Nar sampleExecutable)--    it "roundtrips directory" $ do-      roundTrip "sampleDirectory" (Nar sampleDirectory)--    it "roundtrips case conflicts" $ do-      nar <- decodeFile "tests/fixtures/case-conflict.nar"-      roundTrip "caseConflict" nar--  describe "matches-nix-store fixture" $ do-    it "matches regular" $ do-      encEqualsNixStore (Nar sampleRegular) sampleRegularBaseline--    it "matches regular'" $-      encEqualsNixStore (Nar sampleRegular') sampleRegular'Baseline--    it "matches executable" $-      encEqualsNixStore (Nar sampleExecutable) sampleExecutableBaseline--    it "matches symlink" $-      encEqualsNixStore (Nar sampleSymLink) sampleSymLinkBaseline--    it "matches directory" $ do-      encEqualsNixStore (Nar sampleDirectory) sampleDirectoryBaseline--    it "matches symlink to directory" $ do-      encEqualsNixStore (Nar sampleLinkToDirectory) sampleLinkToDirectoryBaseline---unit_nixStoreRegular :: HU.Assertion-unit_nixStoreRegular = filesystemNixStore "regular" (Nar sampleRegular)--unit_nixStoreDirectory :: HU.Assertion-unit_nixStoreDirectory = filesystemNixStore "directory" (Nar sampleDirectory)--unit_nixStoreDirectory' :: HU.Assertion-unit_nixStoreDirectory' = filesystemNixStore "directory'" (Nar sampleDirectory')--test_nixStoreBigFile :: TestTree-test_nixStoreBigFile = packThenExtract "bigfile" $ \baseDir -> do-  mkBigFile (baseDir </> "bigfile")---test_nixStoreBigDir :: TestTree-test_nixStoreBigDir = packThenExtract "bigdir" $ \baseDir -> do-  let testDir = baseDir </> "bigdir"-  Directory.createDirectory testDir-  mkBigFile (testDir </> "bf1")-  mkBigFile (testDir </> "bf2")-  -- flip mapM_ [1..100] $ \i ->-  --   mkBigFile (testDir </> ('f': show i))-  -- -- Directory.createDirectory (testDir </> "")---prop_narEncodingArbitrary :: Nar -> Property-prop_narEncodingArbitrary n = runGet getNar (runPut $ putNar n) === n--unit_packSelfSrcDir :: HU.Assertion-unit_packSelfSrcDir = Temp.withSystemTempDirectory "nar-test" $ \tmpDir -> do-  ver <- try (P.readProcess "nix-store" ["--version"] "")-  let narFilePath = tmpDir </> "src.nar"-  case ver of-    Left  (_ :: SomeException) -> print ("No nix-store on system" :: String)-    Right _ -> do-      let go dir = do-            srcHere <- doesDirectoryExist dir-            bool-              pass-              (do-                withFile narFilePath WriteMode $ \h ->-                  buildNarIO narEffectsIO "src" h-                hnixNar <- BSL.readFile narFilePath-                nixStoreNar <- getNixStoreDump "src"-                HU.assertEqual-                  "src dir serializes the same between hnix-store and nix-store"-                  hnixNar-                  nixStoreNar-              )-              srcHere-      go "src"-      go "hnix-store-core/src"--- ||||||| merged common ancestors---       hnixNar <- runPut . put <$> localPackNar narEffectsIO "src"---       nixStoreNar <- getNixStoreDump "src"---       HU.assertEqual---         "src dir serializes the same between hnix-store and nix-store"---         hnixNar---         nixStoreNar--- =======---       let narFile = tmpDir </> "src.nar"---       withFile narFile WriteMode $ \h ->---         buildNarIO narEffectsIO "src" h---       hnixNar <- BSL.readFile narFile---       nixStoreNar <- getNixStoreDump "src"---       HU.assertEqual---         "src dir serializes the same between hnix-store and nix-store"---         hnixNar---         nixStoreNar--- >>>>>>> Use streaming to consume and produce NARs---- passes-test_streamLargeFileToNar :: TestTree-test_streamLargeFileToNar = HU.testCaseSteps "streamLargeFileToNar" $ \step -> do--  step "create test file"-  mkBigFile bigFileName-  -- BSL.writeFile narFileName =<< buildNarIO narEffectsIO bigFileName-  ---  step "create nar file"-  withFile narFileName WriteMode $ \h ->-    buildNarIO narEffectsIO bigFileName h--  step "assert bounded memory"-  assertBoundedMemory-  rmFiles-    where-      bigFileName = "bigFile.bin"-      narFileName = "bigFile.nar"-      rmFiles     = removeFile bigFileName >> removeFile narFileName------------------------------------------------------------------------------------test_streamManyFilesToNar :: TestTree-test_streamManyFilesToNar = HU.testCaseSteps "streamManyFilesToNar" $ \step ->-  Temp.withSystemTempDirectory "hnix-store" $ \baseDir -> do-    let--      packagePath = baseDir </> "package_with_many_files"-      packagePath' = baseDir </> "package_with_many_files2"-      narFilePath = packagePath <.> "nar"--      -- unused, see `step "check file exists"` bellow-      _rmFiles = try @SomeException @() $ do-        e <- doesPathExist narFilePath-        when e $ removeDirectoryRecursive narFilePath--      _run =  do-        filesPrecount <- countProcessFiles-        withFile "hnar" WriteMode $ \h ->-          buildNarIO narEffectsIO narFilePath h-        filesPostcount <- countProcessFiles-        pure $ (-) <$> filesPostcount <*> filesPrecount--    step "create test files"-    Directory.createDirectory packagePath-    forM_ [0..1000] $ \i -> do-      BSL.writeFile (Printf.printf (packagePath </> "%08d") (i :: Int)) "hi\n"-      Concurrent.threadDelay 50--    filesPrecount <- countProcessFiles--    step "pack nar"-    withFile narFilePath WriteMode $ \h ->-      buildNarIO narEffectsIO packagePath h--    step "unpack nar"-    r <- withFile narFilePath ReadMode $ \h ->-      unpackNarIO narEffectsIO h packagePath'-    r `shouldBe` pass--    step "check constant file usage"-    filesPostcount <- countProcessFiles-    case (-) <$> filesPostcount <*> filesPrecount of-      Nothing -> pass-      Just c -> c `shouldSatisfy` (< 50)--    -- step "check file exists"-    -- e <- doesPathExist packagePath'-    -- e `shouldBe` True--    -- step "read the NAR back in"-    -- filesCreated <- run `finally` rmFiles-    -- filesCreated `shouldSatisfy` (< 50)------ ****************  Utilities  ************************---- | Generate the ground-truth encoding on the fly with---   `nix-store --dump`, rather than generating fixtures---   beforehand-filesystemNixStore :: String -> Nar -> IO ()-filesystemNixStore testErrorName n = do--  ver <- try (P.readProcess "nix-store" ["--version"] "")-  case ver of-    -- Left is not an error - testing machine simply doesn't have-    -- `nix-store` executable, so pass-    Left  (_ :: SomeException) -> print ("No nix-store on system" :: String)-    Right _ -> Temp.withSystemTempDirectory "hnix-store" $ \baseDir -> do--      let-        testFile    = baseDir </> "testfile"-        nixNarFile  = baseDir </> "nixstorenar.nar"-        hnixNarFile = baseDir </> "hnix.nar"--        assertExists f = do-          e <- doesPathExist f-          e `shouldBe` True--      -- stream nar contents to unpacked file(s)-      void $ withBytesAsHandle (runPut $ putNar n) $ \h ->-        unpackNarIO narEffectsIO h testFile--      assertExists testFile--      -- nix-store converts those files to nar-      getNixStoreDump testFile >>= BSL.writeFile nixNarFile-      assertExists nixNarFile--      -- hnix converts those files to nar-      withFile hnixNarFile WriteMode $ \h ->-        buildNarIO narEffectsIO testFile h-      assertExists hnixNarFile--      diffResult <- P.readProcess "diff" [nixNarFile, hnixNarFile] ""--      assertBoundedMemory-      HU.assertEqual testErrorName diffResult ""----- | Assert that GHC uses less than 100M memory at peak-assertBoundedMemory :: IO ()-assertBoundedMemory = do-#ifdef BOUNDED_MEMORY-      bytes <- max_live_bytes <$> getRTSStats-      bytes < 100 * 1000 * 1000 `shouldBe` True-#else-      pass-#endif---packThenExtract-  :: String-     -- ^ Test name (will also be used for file name)-  -> (String -> IO ())-     -- ^ Action to create some files that we will-     --   pack into a NAR-  -> TestTree-packThenExtract testName setup =-  HU.testCaseSteps testName $ \step ->-  Temp.withSystemTempDirectory "hnix-store" $ \baseDir -> do-    setup baseDir--    let narFilePath = baseDir </> testName--    ver <- try (P.readProcess "nix-store" ["--version"] "")-    case ver of-      Left (_ :: SomeException) -> print ("No nix-store on system" :: String)-      Right _ -> do-        let-          nixNarFile  = narFilePath <> ".nix"-          hnixNarFile = narFilePath <> ".hnix"-          outputFile  = narFilePath <> ".out"--        step $ "Produce nix-store nar to " <> nixNarFile-        (_,_,_,handle) <- P.createProcess (P.shell $ "nix-store --dump " <> narFilePath <> " > " <> nixNarFile)-        void $ P.waitForProcess handle--        step $ "Build NAR from " <> narFilePath <> " to " <> hnixNarFile-        -- narBS <- buildNarIO narEffectsIO narFile-        withFile hnixNarFile WriteMode $ \h ->-          buildNarIO narEffectsIO narFilePath h--        -- BSL.writeFile hnixNarFile narBS--        step $ "Unpack NAR to " <> outputFile-        _narHandle <- withFile nixNarFile ReadMode $ \h ->-          unpackNarIO narEffectsIO h outputFile--        pass---- | Count file descriptors owned by the current process-countProcessFiles :: IO (Maybe Int)-countProcessFiles = do-  pid <- Unix.getProcessID-  hasProc <- doesDirectoryExist "/proc"-  if not hasProc-    then pure Nothing-    else do-      let fdDir = "/proc/" <> show pid <> "/fd"-      fds  <- toText <$> P.readProcess "ls" [fdDir] ""-      pure $ pure $ length $ words fds----- | Read the binary output of `nix-store --dump` for a filepath-getNixStoreDump :: String -> IO BSL.ByteString-getNixStoreDump fp = do-  (_,Just h, _, _) <- P.createProcess-                      (P.proc "nix-store" ["--dump", fp])-                      {P.std_out = P.CreatePipe}-  BSL.hGetContents h----- * Several sample FSOs defined in Haskell, for use in encoding/decoding---- | Simple regular text file with contents 'hi'-sampleRegular :: FileSystemObject-sampleRegular = Regular Nar.NonExecutable 3 "hi\n"---- | Simple text file with some c code-sampleRegular' :: FileSystemObject-sampleRegular' = Regular Nar.NonExecutable (BSL.length str) str-  where str =-          "#include <stdio.h>\n\nint main(int argc, char *argv[]){ exit 0; }\n"---- | Executable file-sampleExecutable :: FileSystemObject-sampleExecutable = Regular Nar.Executable (BSL.length str) str-  where str = "#!/bin/bash\n\ngcc -o hello hello.c\n"---- | A simple symlink-sampleSymLink :: FileSystemObject-sampleSymLink = SymLink "hello.c"----- | A directory that includes some of the above sample files-sampleDirectory :: FileSystemObject-sampleDirectory = Directory $ Map.fromList-  [(FilePathPart "hello.c", sampleRegular')-  ,(FilePathPart "build.sh", sampleExecutable)-  ,(FilePathPart "hi.c", sampleSymLink)-  ]---- | A deeper directory tree with crossing links-sampleDirectory' :: FileSystemObject-sampleDirectory' = Directory $ Map.fromList [--    (FilePathPart "foo", Directory $ Map.fromList [-        (FilePathPart "foo.txt", Regular Nar.NonExecutable 8 "foo text")-      , (FilePathPart "tobar"  , SymLink "../bar/bar.txt")-      ])--  , (FilePathPart "bar", Directory $ Map.fromList [-        (FilePathPart "bar.txt", Regular Nar.NonExecutable 8 "bar text")-      , (FilePathPart "tofoo"  , SymLink "../foo/foo.txt")-      ])-  ]--sampleLargeFile :: Int64 -> FileSystemObject-sampleLargeFile fSize =-  Regular Nar.NonExecutable fSize (BSL.take fSize (BSL.cycle "Lorem ipsum "))---sampleLargeFile' :: Int64 -> FileSystemObject-sampleLargeFile' fSize =-  Regular Nar.NonExecutable fSize (BSL.take fSize (BSL.cycle "Lorems ipsums "))--sampleLargeDir :: Int64 -> FileSystemObject-sampleLargeDir fSize = Directory $ Map.fromList $ [-    (FilePathPart "bf1", sampleLargeFile  fSize)-  , (FilePathPart "bf2", sampleLargeFile' fSize)-  ]-  <> [ (FilePathPart (BSC.pack $ 'f' : show n),-        Regular Nar.NonExecutable 10000 (BSL.take 10000 (BSL.cycle "hi ")))-     | n <- [1..100 :: Int]]-  <> [-  (FilePathPart "d", Directory $ Map.fromList-      [ (FilePathPart (BSC.pack $ "df" <> show n)-        , Regular Nar.NonExecutable 10000 (BSL.take 10000 (BSL.cycle "subhi ")))-      | n <- [1..100 :: Int]]-     )-  ]--sampleLinkToDirectory :: FileSystemObject-sampleLinkToDirectory = Directory $ Map.fromList [-  (FilePathPart "foo", Directory $ Map.fromList [-        (FilePathPart "file", Regular Nar.NonExecutable 8 "foo text")-      ])-  , (FilePathPart "linkfoo"  , SymLink "foo")-  ]-----------------------------------------------------------------------------------sampleDirWithManyFiles :: Int -> FileSystemObject-sampleDirWithManyFiles nFiles =-  Directory $ Map.fromList $ mkFile <$> take nFiles [0..]-  where-    mkFile :: Int -> (FilePathPart, FileSystemObject)-    mkFile i = (FilePathPart (BSC.pack (Printf.printf "%08d" i)),-                sampleRegular)---- * For each sample above, feed it into `nix-store --dump`,--- and base64 encode the resulting NAR binary. This lets us--- check our Haskell NAR generator against `nix-store`---- "hi" file turned to a NAR with `nix-store --dump`, Base64 encoded-sampleRegularBaseline :: BSL.ByteString-sampleRegularBaseline = B64.decodeLenient $ BSL.concat-  ["DQAAAAAAAABuaXgtYXJjaGl2ZS0xAAAAAQAAAAAAAAAoAAAAAAA"-  ,"AAAQAAAAAAAAAdHlwZQAAAAAHAAAAAAAAAHJlZ3VsYXIACAAAAA"-  ,"AAAABjb250ZW50cwMAAAAAAAAAaGkKAAAAAAABAAAAAAAAACkAA"-  ,"AAAAAAA"-  ]--sampleRegular'Baseline :: BSL.ByteString-sampleRegular'Baseline = B64.decodeLenient $ BSL.concat-  ["DQAAAAAAAABuaXgtYXJjaGl2ZS0xAAAAAQAAAAAAAAAoAAAAAAA"-  ,"AAAQAAAAAAAAAdHlwZQAAAAAHAAAAAAAAAHJlZ3VsYXIACAAAAA"-  ,"AAAABjb250ZW50c0AAAAAAAAAAI2luY2x1ZGUgPHN0ZGlvLmg+C"-  ,"gppbnQgbWFpbihpbnQgYXJnYywgY2hhciAqYXJndltdKXsgZXhp"-  ,"dCAwOyB9CgEAAAAAAAAAKQAAAAAAAAA="-  ]--sampleExecutableBaseline :: BSL.ByteString-sampleExecutableBaseline = B64.decodeLenient $ BSL.concat-  ["DQAAAAAAAABuaXgtYXJjaGl2ZS0xAAAAAQAAAAAAAAAoAAAAAAA"-  ,"AAAQAAAAAAAAAdHlwZQAAAAAHAAAAAAAAAHJlZ3VsYXIACgAAAA"-  ,"AAAABleGVjdXRhYmxlAAAAAAAAAAAAAAAAAAAIAAAAAAAAAGNvb"-  ,"nRlbnRzIgAAAAAAAAAjIS9iaW4vYmFzaAoKZ2NjIC1vIGhlbGxv"-  ,"IGhlbGxvLmMKAAAAAAAAAQAAAAAAAAApAAAAAAAAAA=="-  ]--sampleSymLinkBaseline :: BSL.ByteString-sampleSymLinkBaseline = B64.decodeLenient $ BSL.concat-  ["DQAAAAAAAABuaXgtYXJjaGl2ZS0xAAAAAQAAAAAAAAAoAAAAAAA"-  ,"AAAQAAAAAAAAAdHlwZQAAAAAHAAAAAAAAAHN5bWxpbmsABgAAAA"-  ,"AAAAB0YXJnZXQAAAcAAAAAAAAAaGVsbG8uYwABAAAAAAAAACkAA"-  ,"AAAAAAA"-  ]--sampleDirectoryBaseline :: BSL.ByteString-sampleDirectoryBaseline = B64.decodeLenient $ BSL.concat-  ["DQAAAAAAAABuaXgtYXJjaGl2ZS0xAAAAAQAAAAAAAAAoAAAAAAA"-  ,"AAAQAAAAAAAAAdHlwZQAAAAAJAAAAAAAAAGRpcmVjdG9yeQAAAA"-  ,"AAAAAFAAAAAAAAAGVudHJ5AAAAAQAAAAAAAAAoAAAAAAAAAAQAA"-  ,"AAAAAAAbmFtZQAAAAAIAAAAAAAAAGJ1aWxkLnNoBAAAAAAAAABu"-  ,"b2RlAAAAAAEAAAAAAAAAKAAAAAAAAAAEAAAAAAAAAHR5cGUAAAA"-  ,"ABwAAAAAAAAByZWd1bGFyAAoAAAAAAAAAZXhlY3V0YWJsZQAAAA"-  ,"AAAAAAAAAAAAAACAAAAAAAAABjb250ZW50cyIAAAAAAAAAIyEvY"-  ,"mluL2Jhc2gKCmdjYyAtbyBoZWxsbyBoZWxsby5jCgAAAAAAAAEA"-  ,"AAAAAAAAKQAAAAAAAAABAAAAAAAAACkAAAAAAAAABQAAAAAAAAB"-  ,"lbnRyeQAAAAEAAAAAAAAAKAAAAAAAAAAEAAAAAAAAAG5hbWUAAA"-  ,"AABwAAAAAAAABoZWxsby5jAAQAAAAAAAAAbm9kZQAAAAABAAAAA"-  ,"AAAACgAAAAAAAAABAAAAAAAAAB0eXBlAAAAAAcAAAAAAAAAcmVn"-  ,"dWxhcgAIAAAAAAAAAGNvbnRlbnRzQAAAAAAAAAAjaW5jbHVkZSA"-  ,"8c3RkaW8uaD4KCmludCBtYWluKGludCBhcmdjLCBjaGFyICphcm"-  ,"d2W10peyBleGl0IDA7IH0KAQAAAAAAAAApAAAAAAAAAAEAAAAAA"-  ,"AAAKQAAAAAAAAAFAAAAAAAAAGVudHJ5AAAAAQAAAAAAAAAoAAAA"-  ,"AAAAAAQAAAAAAAAAbmFtZQAAAAAEAAAAAAAAAGhpLmMAAAAABAA"-  ,"AAAAAAABub2RlAAAAAAEAAAAAAAAAKAAAAAAAAAAEAAAAAAAAAH"-  ,"R5cGUAAAAABwAAAAAAAABzeW1saW5rAAYAAAAAAAAAdGFyZ2V0A"-  ,"AAHAAAAAAAAAGhlbGxvLmMAAQAAAAAAAAApAAAAAAAAAAEAAAAA"-  ,"AAAAKQAAAAAAAAABAAAAAAAAACkAAAAAAAAA"-  ]--sampleLinkToDirectoryBaseline :: BSL.ByteString-sampleLinkToDirectoryBaseline = B64.decodeLenient $ BSL.concat-  ["DQAAAAAAAABuaXgtYXJjaGl2ZS0xAAAAAQAAAAAAAAAoAAAAAAAAAAQAAAAAAAAAdHlwZQAAAAAJ"-  ,"AAAAAAAAAGRpcmVjdG9yeQAAAAAAAAAFAAAAAAAAAGVudHJ5AAAAAQAAAAAAAAAoAAAAAAAAAAQA"-  ,"AAAAAAAAbmFtZQAAAAADAAAAAAAAAGZvbwAAAAAABAAAAAAAAABub2RlAAAAAAEAAAAAAAAAKAAA"-  ,"AAAAAAAEAAAAAAAAAHR5cGUAAAAACQAAAAAAAABkaXJlY3RvcnkAAAAAAAAABQAAAAAAAABlbnRy"-  ,"eQAAAAEAAAAAAAAAKAAAAAAAAAAEAAAAAAAAAG5hbWUAAAAABAAAAAAAAABmaWxlAAAAAAQAAAAA"-  ,"AAAAbm9kZQAAAAABAAAAAAAAACgAAAAAAAAABAAAAAAAAAB0eXBlAAAAAAcAAAAAAAAAcmVndWxh"-  ,"cgAIAAAAAAAAAGNvbnRlbnRzCAAAAAAAAABmb28gdGV4dAEAAAAAAAAAKQAAAAAAAAABAAAAAAAA"-  ,"ACkAAAAAAAAAAQAAAAAAAAApAAAAAAAAAAEAAAAAAAAAKQAAAAAAAAAFAAAAAAAAAGVudHJ5AAAA"-  ,"AQAAAAAAAAAoAAAAAAAAAAQAAAAAAAAAbmFtZQAAAAAHAAAAAAAAAGxpbmtmb28ABAAAAAAAAABu"-  ,"b2RlAAAAAAEAAAAAAAAAKAAAAAAAAAAEAAAAAAAAAHR5cGUAAAAABwAAAAAAAABzeW1saW5rAAYA"-  ,"AAAAAAAAdGFyZ2V0AAADAAAAAAAAAGZvbwAAAAAAAQAAAAAAAAApAAAAAAAAAAEAAAAAAAAAKQAA"-  ,"AAAAAAABAAAAAAAAACkAAAAAAAAA"-  ]---- | Control testcase sizes (bytes) by env variable-getBigFileSize :: IO Int64-getBigFileSize = fromMaybe 5000000 . readMaybe <$> (getEnv "HNIX_BIG_FILE_SIZE" <|> pure "")----- | Add a link to a FileSystemObject. This is useful---   when creating Arbitrary FileSystemObjects. It---   isn't implemented yet-mkLink-  :: FilePath -- ^ Target-  -> FilePath -- ^ Link-  -> FileSystemObject -- ^ FileSystemObject to add link to-  -> FileSystemObject-mkLink = undefined -- TODO--mkBigFile :: FilePath -> IO ()-mkBigFile path = do-  fsize <- getBigFileSize-  BSL.writeFile path (BSL.take fsize $ BSL.cycle "Lorem ipsum")----- | Construct FilePathPart from Text by checking that there---   are no '/' or '\\NUL' characters-filePathPart :: BSC.ByteString -> Maybe FilePathPart-filePathPart p = if BSC.any (`elem` ['/', '\NUL']) p then Nothing else Just $ FilePathPart p--newtype Nar = Nar { narFile :: FileSystemObject }-    deriving (Eq, Show, Generic)---- | A FileSystemObject (FSO) is an anonymous entity that can be NAR archived-data FileSystemObject =-    Regular Nar.IsExecutable Int64 BSL.ByteString-    -- ^ Reguar file, with its executable state, size (bytes) and contents-  | Directory (Map.Map FilePathPart FileSystemObject)-    -- ^ Directory with mapping of filenames to sub-FSOs-  | SymLink T.Text-    -- ^ Symbolic link target-  deriving (Eq, Show)---- | A valid filename or directory name-newtype FilePathPart = FilePathPart { unFilePathPart :: BSC.ByteString }-  deriving (Eq, Ord, Show)--instance Binary Nar where-  get = getNar-  put = putNar--instance Arbitrary Nar where-  arbitrary = Nar <$> resize 10 arbitrary--instance Arbitrary FileSystemObject where-  -- To build an arbitrary Nar,-  arbitrary = do-    n <- getSize-    if n < 2-      then arbFile-      else arbDirectory n--      where--        arbFile :: Gen FileSystemObject-        arbFile = do-          Positive fSize <- arbitrary-          Regular-            <$> elements [Nar.NonExecutable, Nar.Executable]-            <*> pure (fromIntegral fSize)-            <*> oneof  [-                  fmap (BSL.take fSize . BSL.cycle . BSL.pack . getNonEmpty) arbitrary , -- Binary File-                  fmap (BSL.take fSize . BSL.cycle . BSLC.pack . getNonEmpty) arbitrary   -- ASCII  File-                  ]--        arbName :: Gen FilePathPart-        arbName = fmap (FilePathPart . BS.pack . fmap (fromIntegral . fromEnum)) $ do-          Positive n <- arbitrary-          replicateM n (elements $ ['a'..'z'] <> ['0'..'9'])--        arbDirectory :: Int -> Gen FileSystemObject-        arbDirectory n = fmap (Directory . Map.fromList) $ replicateM n $ do-          nm <- arbName-          f <- oneof [arbFile, arbDirectory (n `div` 2)]-          pure (nm,f)----------------------------------------------------------------------------------- | Serialize Nar to lazy ByteString-putNar :: Nar -> Put-putNar (Nar file) = header <> parens (putFile file)-    where--        header   = str "nix-archive-1"--        putFile (Regular isExec fSize contents) =-               strs ["type", "regular"]-            >> (if isExec == Nar.Executable-               then strs ["executable", ""]-               else pass)-            >> putContents fSize contents--        putFile (SymLink target) =-               strs ["type", "symlink", "target", fromStrict $ encodeUtf8 target]--        -- toList sorts the entries by FilePathPart before serializing-        putFile (Directory entries) =-               strs ["type", "directory"]-            <> mapM_ putEntry (Map.toList entries)--        putEntry (FilePathPart name, fso) = do-            str "entry"-            parens $ do-              str "name"-              str (fromStrict name)-              str "node"-              parens (putFile fso)--        parens m = str "(" >> m >> str ")"--        -- Do not use this for file contents-        str :: BSL.ByteString -> Put-        str t = let len = BSL.length t-            in int len <> pad len t--        putContents :: Int64 -> BSL.ByteString -> Put-        putContents fSize bs = str "contents" <> int fSize <> pad fSize bs--        int :: Integral a => a -> Put-        int n = putInt64le $ fromIntegral n--        pad :: Int64 -> BSL.ByteString -> Put-        pad strSize bs = do-          putLazyByteString bs-          putLazyByteString (BSL.replicate (padLen strSize) 0)--        strs :: [BSL.ByteString] -> Put-        strs = mapM_ str---- | Distance to the next multiple of 8-padLen :: Int64 -> Int64-padLen n = (8 - n) `mod` 8------------------------------------------------------------------------------------ | Deserialize a Nar from lazy ByteString-getNar :: Get Nar-getNar = fmap Nar $ header >> parens getFile-    where--      header   = assertStr "nix-archive-1"--      -- Fetch a FileSystemObject-      getFile = getRegularFile <|> getDirectory <|> getSymLink--      getRegularFile = do-          assertStr_ "type"-          assertStr_ "regular"-          mExecutable <- optional $ Nar.Executable <$ (assertStr "executable"-                                                       >> assertStr "")-          assertStr_ "contents"-          (fSize, contents) <- sizedStr-          pure $ Regular (fromMaybe Nar.NonExecutable mExecutable) fSize contents--      getDirectory = do-          assertStr_ "type"-          assertStr_ "directory"-          fs <- many getEntry-          pure $ Directory (Map.fromList fs)--      getSymLink = do-          assertStr_ "type"-          assertStr_ "symlink"-          assertStr_ "target"-          fmap (SymLink . decodeUtf8) str--      getEntry = do-          assertStr_ "entry"-          parens $ do-              assertStr_ "name"-              name <- str-              assertStr_ "node"-              file <- parens getFile-              maybe (fail $ "Bad FilePathPart: " <> show name)-                    (pure . (,file))-                    (filePathPart $ toStrict name)--      -- Fetch a length-prefixed, null-padded string-      str = fmap snd sizedStr--      sizedStr = do-          n <- getInt64le-          s <- getLazyByteString n-          _ <- getByteString . fromIntegral $ padLen n-          pure (n,s)--      parens m = assertStr "(" *> m <* assertStr ")"--      assertStr_ = void . assertStr-      assertStr s = do-          s' <- str-          if s == s'-              then pure s-              else fail "No"
+ tests/Signature.hs view
@@ -0,0 +1,78 @@+{-# LANGUAGE OverloadedStrings #-}+-- Inspired by https://cl.tvl.fyi/c/depot/+/10081/1/tvix/nix-compat/src/narinfo/signature.rs+-- and https://github.com/nix-community/go-nix/pull/93+-- by @flokli and @zimbatm++module Signature where++import qualified Data.ByteString as BS+import Test.Hspec+import Data.Text (Text)+import qualified Crypto.PubKey.Ed25519+import qualified System.Nix.Base+import System.Nix.Base (BaseEncoding(Base64))+import Crypto.Error (CryptoFailable(..))++import System.Nix.Signature++spec_signature :: Spec+spec_signature = do++  describe "signature parser" $ do++    it "parses names" $ do+      shouldParseName "cache.nixos.org-1:TsTTb3WGTZKphvYdBHXwo6weVILmTytUjLB+vcX89fOjjRicCHmKA4RCPMVLkj6TMJ4GMX3HPVWRdD1hkeKZBQ==" "cache.nixos.org-1"++    it "fails on invalid signatures" $ do+      shouldNotParse ""+      shouldNotParse "asdf"+      shouldNotParse "cache.nixos.org-1:"+      shouldNotParse ":TsTTb3WGTZKphvYdBHXwo6weVILmTytUjLB+vcX89fOjjRicCHmKA4RCPMVLkj6TMJ4GMX3HPVWRdD1hkeKZBQ=="+      shouldNotParse "cache.nixos.org-1TsTTb3WGTZKphvYdBHXwo6weVILmTytUjLB+vcX89fOjjRicCHmKA4RCPMVLkj6TMJ4GMX3HPVWRdD1hkeKZBQ=="+      shouldNotParse "cache.nixos.org-1:sTTb3WGTZKphvYdBHXwo6weVILmTytUjLB+vcX89fOjjRicCHmKA4RCPMVLkj6TMJ4GMX3HPVWRdD1hkeKZBQ=="++    it "parses verifying signatures" $ do+      shouldVerify "cache.nixos.org-1:TsTTb3WGTZKphvYdBHXwo6weVILmTytUjLB+vcX89fOjjRicCHmKA4RCPMVLkj6TMJ4GMX3HPVWRdD1hkeKZBQ==" pubkeyNixosOrg fingerprint+      shouldVerify "cache.nixos.org-2:TsTTb3WGTZKphvYdBHXwo6weVILmTytUjLB+vcX89fOjjRicCHmKA4RCPMVLkj6TMJ4GMX3HPVWRdD1hkeKZBQ==" pubkeyNixosOrg fingerprint++    it "parses non-verifying signatures" $ do+      shouldNotVerify "cache.nixos.org-1:TsTTb000000000000000000000000ytUjLB+vcX89fOjjRicCHmKA4RCPMVLkj6TMJ4GMX3HPVWRdD1hkeKZBQ==" pubkeyNixosOrg fingerprint++fingerprint :: BS.ByteString+fingerprint = "1;/nix/store/syd87l2rxw8cbsxmxl853h0r6pdwhwjr-curl-7.82.0-bin;sha256:1b4sb93wp679q4zx9k1ignby1yna3z7c4c2ri3wphylbc2dwsys0;196040;/nix/store/0jqd0rlxzra1rs38rdxl43yh6rxchgc6-curl-7.82.0,/nix/store/6w8g7njm4mck5dmjxws0z1xnrxvl81xa-glibc-2.34-115,/nix/store/j5jxw3iy7bbz4a57fh9g2xm2gxmyal8h-zlib-1.2.12,/nix/store/yxvjs9drzsphm9pcf42a4byzj1kb9m7k-openssl-1.1.1n";++forceDecodeB64Pubkey :: Text -> Crypto.PubKey.Ed25519.PublicKey+forceDecodeB64Pubkey b64EncodedPubkey = let+  decoded = case System.Nix.Base.decodeWith Base64 b64EncodedPubkey of+    Left err -> error err+    Right x -> x+  in case Crypto.PubKey.Ed25519.publicKey decoded of+    CryptoFailed err -> (error . show) err+    CryptoPassed x -> x++pubkeyNixosOrg :: Crypto.PubKey.Ed25519.PublicKey+pubkeyNixosOrg = forceDecodeB64Pubkey "6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY="++shouldNotParse :: Text -> Expectation+shouldNotParse encoded = case parseNarSignature encoded of+  Left _ -> pure ()+  Right _ -> expectationFailure "should not have parsed"++shouldParseName :: Text -> Text -> Expectation+shouldParseName encoded name = case parseNarSignature encoded of+  Left err -> expectationFailure err+  Right narSig -> shouldBe name (publicKey narSig)++shouldVerify :: Text -> Crypto.PubKey.Ed25519.PublicKey -> BS.ByteString -> Expectation+shouldVerify encoded pubkey msg = case parseNarSignature encoded of+  Left err -> expectationFailure err+  Right narSig -> let+    (Signature sig') = sig narSig+    in sig' `shouldSatisfy` Crypto.PubKey.Ed25519.verify pubkey msg++shouldNotVerify :: Text -> Crypto.PubKey.Ed25519.PublicKey -> BS.ByteString -> Expectation+shouldNotVerify encoded pubkey msg = case parseNarSignature encoded of+  Left err -> expectationFailure err+  Right narSig -> let+    (Signature sig') = sig narSig+    in sig' `shouldNotSatisfy` Crypto.PubKey.Ed25519.verify pubkey msg
tests/StorePath.hs view
@@ -1,30 +1,37 @@-{-# language DataKinds           #-}-{-# language ScopedTypeVariables #-}-{-# language OverloadedStrings   #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE OverloadedStrings #-}  module StorePath where -import qualified Data.Attoparsec.Text+import Test.Hspec (Spec, describe, it, shouldBe)+import qualified Data.Text -import           Test.Tasty.QuickCheck+import System.Nix.StorePath (parseNameText, InvalidNameError(..)) -import           System.Nix.StorePath-import           Arbitrary+spec_storePath :: Spec+spec_storePath = do+  describe "parseNameText" $ do+    it "parses valid name" $+      parseNameText "name-dev.dotok"+      `shouldBe`+      pure "name-dev.dotok" --- | Test that Nix(OS) like paths roundtrip-prop_storePathRoundtrip :: StoreDir -> NixLike -> NixLike -> Property-prop_storePathRoundtrip storeDir (_ :: NixLike) (NixLike x) =-  parsePath storeDir (storePathToRawFilePath storeDir x) === pure x+    it "fails on empty" $+      parseNameText mempty+      `shouldBe`+      Left EmptyName --- | Test that any `StorePath` roundtrips-prop_storePathRoundtrip' :: StoreDir -> StorePath -> Property-prop_storePathRoundtrip' storeDir x =-  parsePath storeDir (storePathToRawFilePath storeDir x) === pure x+    it "fails on too long" $+      parseNameText (Data.Text.replicate 256 "n")+      `shouldBe`+      Left (NameTooLong 256) -prop_storePathRoundtripParser :: StoreDir -> NixLike -> NixLike -> Property-prop_storePathRoundtripParser storeDir (_ :: NixLike) (NixLike x) =-  Data.Attoparsec.Text.parseOnly (pathParser storeDir) (storePathToText storeDir x) === pure x+    it "fails on leading dot" $+      parseNameText ".ab"+      `shouldBe`+      Left LeadingDot -prop_storePathRoundtripParser' :: StoreDir -> StorePath -> Property-prop_storePathRoundtripParser' storeDir x =-  Data.Attoparsec.Text.parseOnly (pathParser storeDir) (storePathToText storeDir x) === pure x+    it "fails on invalid characters" $+      parseNameText "ab!cd#@"+      `shouldBe`+      Left (InvalidCharacters "!#@")
− tests/fixtures/case-conflict.nar

binary file changed (1000 → absent bytes)