nix-diff 1.0.20 → 1.0.21
raw patch · 12 files changed
+336/−157 lines, 12 filesdep ~QuickCheckdep ~filepathdep ~optparse-applicativesetup-changedPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependency ranges changed: QuickCheck, filepath, optparse-applicative
API changes (from Hackage documentation)
- Nix.Diff: [environment] :: DiffContext -> Bool
- Nix.Diff: [leftDerivation] :: Diffed -> FilePath
- Nix.Diff: [leftOutput] :: Diffed -> Set Text
- Nix.Diff: [orientation] :: DiffContext -> Orientation
- Nix.Diff: [rightDerivation] :: Diffed -> FilePath
- Nix.Diff: [rightOutput] :: Diffed -> Set Text
- Nix.Diff: [unDiff] :: Diff a -> ReaderT DiffContext (StateT Status IO) a
- Nix.Diff: [visited] :: Status -> Set Diffed
- Nix.Diff.Types: [argumentsDiff] :: DerivationDiff -> Maybe ArgumentsDiff
- Nix.Diff.Types: [before] :: Changed a -> a
- Nix.Diff.Types: [builderDiff] :: DerivationDiff -> Maybe (Changed Builder)
- Nix.Diff.Types: [derivationOutputs] :: OutputStructure -> Set Text
- Nix.Diff.Types: [derivationPath] :: OutputStructure -> FilePath
- Nix.Diff.Types: [drvDiff] :: InputDerivationsDiff -> DerivationDiff
- Nix.Diff.Types: [drvName] :: InputDerivationsDiff -> Text
- Nix.Diff.Types: [envContentDiff] :: EnvironmentDiff -> [EnvVarDiff]
- Nix.Diff.Types: [envDiff] :: DerivationDiff -> Maybe EnvironmentDiff
- Nix.Diff.Types: [envKey] :: EnvVarDiff -> Text
- Nix.Diff.Types: [envValueDiff] :: EnvVarDiff -> TextDiff
- Nix.Diff.Types: [extraEnvDiff] :: EnvironmentDiff -> Changed (Map Text Text)
- Nix.Diff.Types: [extraOutputs] :: OutputsDiff -> Maybe (Changed (Map Text (DerivationOutput FilePath Text)))
- Nix.Diff.Types: [extraPartsDiff] :: InputDerivationsDiff -> Changed (Map FilePath (Set Text))
- Nix.Diff.Types: [extraSrcNames] :: SourcesDiff -> Maybe (Changed (Set Text))
- Nix.Diff.Types: [hashDifference] :: OutputDiff -> Changed OutputHash
- Nix.Diff.Types: [inputDerivationDiffs] :: InputsDiff -> [InputDerivationsDiff]
- Nix.Diff.Types: [inputExtraNames] :: InputsDiff -> Maybe (Changed (Set Text))
- Nix.Diff.Types: [inputsDiff] :: DerivationDiff -> InputsDiff
- Nix.Diff.Types: [now] :: Changed a -> a
- Nix.Diff.Types: [outputHashDiff] :: OutputsDiff -> [OutputDiff]
- Nix.Diff.Types: [outputName] :: OutputDiff -> Text
- Nix.Diff.Types: [outputStructure] :: DerivationDiff -> Changed OutputStructure
- Nix.Diff.Types: [outputsDiff] :: DerivationDiff -> OutputsDiff
- Nix.Diff.Types: [platformDiff] :: DerivationDiff -> Maybe (Changed Platform)
- Nix.Diff.Types: [sourcesDiff] :: DerivationDiff -> SourcesDiff
- Nix.Diff.Types: [srcContentDiff] :: SourceFileDiff -> Maybe TextDiff
- Nix.Diff.Types: [srcFileDiff] :: SourceFileDiff -> Changed [FilePath]
- Nix.Diff.Types: [srcFilesDiff] :: SourcesDiff -> [SourceFileDiff]
- Nix.Diff.Types: [srcName] :: SourceFileDiff -> Text
- Nix.Diff.Types: [unArgumetsDiff] :: ArgumentsDiff -> NonEmpty (Item Argument)
- Nix.Diff.Types: [unTextDiff] :: TextDiff -> [Item Text]
- Nix.Diff.Types: instance Data.Data.Data (Nix.Derivation.Types.DerivationOutput GHC.IO.FilePath Data.Text.Internal.Text)
+ Nix.Diff: [$sel:environment:DiffContext] :: DiffContext -> Bool
+ Nix.Diff: [$sel:leftDerivation:Diffed] :: Diffed -> StorePath
+ Nix.Diff: [$sel:leftOutput:Diffed] :: Diffed -> OutputNames
+ Nix.Diff: [$sel:orientation:DiffContext] :: DiffContext -> Orientation
+ Nix.Diff: [$sel:rightDerivation:Diffed] :: Diffed -> StorePath
+ Nix.Diff: [$sel:rightOutput:Diffed] :: Diffed -> OutputNames
+ Nix.Diff: [$sel:unDiff:Diff] :: Diff a -> ReaderT DiffContext (StateT Status IO) a
+ Nix.Diff: [$sel:visited:Status] :: Status -> Set Diffed
+ Nix.Diff: storepathParser :: Parser StorePath
+ Nix.Diff: unzip :: Functor f => f (a, b) -> (f a, f b)
+ Nix.Diff.Render.HumanReadable: escape :: Text -> Text -> Text
+ Nix.Diff.Store: StorePath :: FilePath -> StorePath
+ Nix.Diff.Store: [$sel:unsafeStorePathFile:StorePath] :: StorePath -> FilePath
+ Nix.Diff.Store: doesFileExist :: StorePath -> IO Bool
+ Nix.Diff.Store: instance Data.Aeson.Types.FromJSON.FromJSON Nix.Diff.Store.StorePath
+ Nix.Diff.Store: instance Data.Aeson.Types.FromJSON.FromJSONKey Nix.Diff.Store.StorePath
+ Nix.Diff.Store: instance Data.Aeson.Types.ToJSON.ToJSON Nix.Diff.Store.StorePath
+ Nix.Diff.Store: instance Data.Aeson.Types.ToJSON.ToJSONKey Nix.Diff.Store.StorePath
+ Nix.Diff.Store: instance Data.Data.Data Nix.Diff.Store.StorePath
+ Nix.Diff.Store: instance GHC.Classes.Eq Nix.Diff.Store.StorePath
+ Nix.Diff.Store: instance GHC.Classes.Ord Nix.Diff.Store.StorePath
+ Nix.Diff.Store: instance GHC.Show.Show Nix.Diff.Store.StorePath
+ Nix.Diff.Store: instance Test.QuickCheck.Arbitrary.Arbitrary Nix.Diff.Store.StorePath
+ Nix.Diff.Store: newtype StorePath
+ Nix.Diff.Store: readFileUtf8Lenient :: StorePath -> IO Text
+ Nix.Diff.Store: toPhysicalPath :: StorePath -> IO FilePath
+ Nix.Diff.Store: toText :: StorePath -> Text
+ Nix.Diff.Transformations: foldManyInputDerivationsAlreadyCompared :: DerivationDiff -> DerivationDiff
+ Nix.Diff.Types: ManyDerivationsAlreadyComparedDiff :: Set Text -> InputDerivationsDiff
+ Nix.Diff.Types: OutputNames :: Set Text -> OutputNames
+ Nix.Diff.Types: [$sel:argumentsDiff:DerivationsAreTheSame] :: DerivationDiff -> Maybe ArgumentsDiff
+ Nix.Diff.Types: [$sel:before:Changed] :: Changed a -> a
+ Nix.Diff.Types: [$sel:builderDiff:DerivationsAreTheSame] :: DerivationDiff -> Maybe (Changed Builder)
+ Nix.Diff.Types: [$sel:derivationOutputs:OutputStructure] :: OutputStructure -> OutputNames
+ Nix.Diff.Types: [$sel:derivationPath:OutputStructure] :: OutputStructure -> StorePath
+ Nix.Diff.Types: [$sel:drvDiff:OneDerivationDiff] :: InputDerivationsDiff -> DerivationDiff
+ Nix.Diff.Types: [$sel:drvName:OneDerivationDiff] :: InputDerivationsDiff -> Text
+ Nix.Diff.Types: [$sel:drvNames:OneDerivationDiff] :: InputDerivationsDiff -> Set Text
+ Nix.Diff.Types: [$sel:envContentDiff:EnvironmentsAreEqual] :: EnvironmentDiff -> [EnvVarDiff]
+ Nix.Diff.Types: [$sel:envDiff:DerivationsAreTheSame] :: DerivationDiff -> Maybe EnvironmentDiff
+ Nix.Diff.Types: [$sel:envKey:EnvVarDiff] :: EnvVarDiff -> Text
+ Nix.Diff.Types: [$sel:envValueDiff:EnvVarDiff] :: EnvVarDiff -> TextDiff
+ Nix.Diff.Types: [$sel:extraEnvDiff:EnvironmentsAreEqual] :: EnvironmentDiff -> Changed (Map Text Text)
+ Nix.Diff.Types: [$sel:extraOutputs:OutputsDiff] :: OutputsDiff -> Maybe (Changed (Map Text (DerivationOutput StorePath Text)))
+ Nix.Diff.Types: [$sel:extraPartsDiff:OneDerivationDiff] :: InputDerivationsDiff -> Changed (Map StorePath OutputNames)
+ Nix.Diff.Types: [$sel:extraSrcNames:SourcesDiff] :: SourcesDiff -> Maybe (Changed (Set Text))
+ Nix.Diff.Types: [$sel:hashDifference:OutputDiff] :: OutputDiff -> Changed OutputHash
+ Nix.Diff.Types: [$sel:inputDerivationDiffs:InputsDiff] :: InputsDiff -> [InputDerivationsDiff]
+ Nix.Diff.Types: [$sel:inputExtraNames:InputsDiff] :: InputsDiff -> Maybe (Changed (Set Text))
+ Nix.Diff.Types: [$sel:inputsDiff:DerivationsAreTheSame] :: DerivationDiff -> InputsDiff
+ Nix.Diff.Types: [$sel:now:Changed] :: Changed a -> a
+ Nix.Diff.Types: [$sel:outputHashDiff:OutputsDiff] :: OutputsDiff -> [OutputDiff]
+ Nix.Diff.Types: [$sel:outputName:OutputDiff] :: OutputDiff -> Text
+ Nix.Diff.Types: [$sel:outputStructure:DerivationsAreTheSame] :: DerivationDiff -> Changed OutputStructure
+ Nix.Diff.Types: [$sel:outputsDiff:DerivationsAreTheSame] :: DerivationDiff -> OutputsDiff
+ Nix.Diff.Types: [$sel:platformDiff:DerivationsAreTheSame] :: DerivationDiff -> Maybe (Changed Platform)
+ Nix.Diff.Types: [$sel:sourcesDiff:DerivationsAreTheSame] :: DerivationDiff -> SourcesDiff
+ Nix.Diff.Types: [$sel:srcContentDiff:OneSourceFileDiff] :: SourceFileDiff -> Maybe TextDiff
+ Nix.Diff.Types: [$sel:srcFileDiff:OneSourceFileDiff] :: SourceFileDiff -> Changed [StorePath]
+ Nix.Diff.Types: [$sel:srcFilesDiff:SourcesDiff] :: SourcesDiff -> [SourceFileDiff]
+ Nix.Diff.Types: [$sel:srcName:OneSourceFileDiff] :: SourceFileDiff -> Text
+ Nix.Diff.Types: [$sel:unArgumetsDiff:ArgumentsDiff] :: ArgumentsDiff -> NonEmpty (Item Argument)
+ Nix.Diff.Types: [$sel:unOutputNames:OutputNames] :: OutputNames -> Set Text
+ Nix.Diff.Types: [$sel:unTextDiff:TextDiff] :: TextDiff -> [Item Text]
+ Nix.Diff.Types: instance Data.Aeson.Types.FromJSON.FromJSON Nix.Diff.Types.OutputNames
+ Nix.Diff.Types: instance Data.Aeson.Types.ToJSON.ToJSON Nix.Diff.Types.OutputNames
+ Nix.Diff.Types: instance Data.Data.Data (Nix.Derivation.Types.DerivationOutput Nix.Diff.Store.StorePath Data.Text.Internal.Text)
+ Nix.Diff.Types: instance Data.Data.Data Nix.Diff.Types.OutputNames
+ Nix.Diff.Types: instance GHC.Classes.Eq Nix.Diff.Types.OutputNames
+ Nix.Diff.Types: instance GHC.Classes.Ord Nix.Diff.Types.OutputNames
+ Nix.Diff.Types: instance GHC.Generics.Generic Nix.Diff.Types.OutputNames
+ Nix.Diff.Types: instance GHC.Show.Show Nix.Diff.Types.OutputNames
+ Nix.Diff.Types: instance Test.QuickCheck.Arbitrary.Arbitrary Nix.Diff.Types.OutputNames
+ Nix.Diff.Types: newtype OutputNames
- Nix.Diff: Diffed :: FilePath -> Set Text -> FilePath -> Set Text -> Diffed
+ Nix.Diff: Diffed :: StorePath -> OutputNames -> StorePath -> OutputNames -> Diffed
- Nix.Diff: buildProductName :: FilePath -> Text
+ Nix.Diff: buildProductName :: StorePath -> Text
- Nix.Diff: derivationName :: FilePath -> Text
+ Nix.Diff: derivationName :: StorePath -> Text
- Nix.Diff: diff :: Bool -> FilePath -> Set Text -> FilePath -> Set Text -> Diff DerivationDiff
+ Nix.Diff: diff :: Bool -> StorePath -> OutputNames -> StorePath -> OutputNames -> Diff DerivationDiff
- Nix.Diff: diffEnv :: Set Text -> Set Text -> Map Text Text -> Map Text Text -> Diff EnvironmentDiff
+ Nix.Diff: diffEnv :: OutputNames -> OutputNames -> Map Text Text -> Map Text Text -> Diff EnvironmentDiff
- Nix.Diff: diffOutput :: Text -> DerivationOutput FilePath Text -> DerivationOutput FilePath Text -> Maybe OutputDiff
+ Nix.Diff: diffOutput :: Text -> DerivationOutput StorePath Text -> DerivationOutput StorePath Text -> Maybe OutputDiff
- Nix.Diff: diffOutputs :: Map Text (DerivationOutput FilePath Text) -> Map Text (DerivationOutput FilePath Text) -> OutputsDiff
+ Nix.Diff: diffOutputs :: Map Text (DerivationOutput StorePath Text) -> Map Text (DerivationOutput StorePath Text) -> OutputsDiff
- Nix.Diff: diffSrcs :: Set FilePath -> Set FilePath -> Diff SourcesDiff
+ Nix.Diff: diffSrcs :: Set StorePath -> Set StorePath -> Diff SourcesDiff
- Nix.Diff: groupByName :: Map FilePath a -> Map Text (Map FilePath a)
+ Nix.Diff: groupByName :: Map StorePath a -> Map Text (Map StorePath a)
- Nix.Diff: groupSetsByName :: Set FilePath -> Map Text (Set FilePath)
+ Nix.Diff: groupSetsByName :: Set StorePath -> Map Text (Set StorePath)
- Nix.Diff: readDerivation :: FilePath -> Diff (Derivation FilePath Text)
+ Nix.Diff: readDerivation :: StorePath -> Diff (Derivation StorePath Text)
- Nix.Diff: readInput :: FilePath -> Diff (Derivation FilePath Text)
+ Nix.Diff: readInput :: StorePath -> Diff (Derivation StorePath Text)
- Nix.Diff.Render.HumanReadable: renderOutputs :: Set Text -> Text
+ Nix.Diff.Render.HumanReadable: renderOutputs :: OutputNames -> Text
- Nix.Diff.Types: OutputStructure :: FilePath -> Set Text -> OutputStructure
+ Nix.Diff.Types: OutputStructure :: StorePath -> OutputNames -> OutputStructure
- Nix.Diff.Types: OutputsDiff :: Maybe (Changed (Map Text (DerivationOutput FilePath Text))) -> [OutputDiff] -> OutputsDiff
+ Nix.Diff.Types: OutputsDiff :: Maybe (Changed (Map Text (DerivationOutput StorePath Text))) -> [OutputDiff] -> OutputsDiff
- Nix.Diff.Types: SomeDerivationsDiff :: Text -> Changed (Map FilePath (Set Text)) -> InputDerivationsDiff
+ Nix.Diff.Types: SomeDerivationsDiff :: Text -> Changed (Map StorePath OutputNames) -> InputDerivationsDiff
- Nix.Diff.Types: SomeSourceFileDiff :: Text -> Changed [FilePath] -> SourceFileDiff
+ Nix.Diff.Types: SomeSourceFileDiff :: Text -> Changed [StorePath] -> SourceFileDiff
Files
- CHANGELOG.md +7/−0
- Setup.hs +0/−2
- app/Main.hs +6/−12
- nix-diff.cabal +37/−17
- src/Nix/Diff.hs +77/−50
- src/Nix/Diff/Render/HumanReadable.hs +40/−28
- src/Nix/Diff/Store.hs +66/−0
- src/Nix/Diff/Transformations.hs +59/−7
- src/Nix/Diff/Types.hs +27/−24
- test/Golden/Tests.hs +0/−1
- test/Golden/Utils.hs +12/−14
- test/Main.hs +5/−2
CHANGELOG.md view
@@ -1,3 +1,10 @@+1.0.21++* [Support relocated Nix store](https://github.com/Gabriella439/nix-diff/pull/82)+ * If configured through `NIX_REMOTE` environment variable.+* [Compact input derivation diffs](https://github.com/Gabriella439/nix-diff/pull/86)+* [Fix support for `less -R` when coloring multiple lines](https://github.com/Gabriella439/nix-diff/pull/96)+ 1.0.20 * [Add `--context` flag](https://github.com/Gabriella439/nix-diff/pull/81)
− Setup.hs
@@ -1,2 +0,0 @@-import Distribution.Simple-main = defaultMain
app/Main.hs view
@@ -1,12 +1,4 @@-{-# LANGUAGE ApplicativeDo #-}-{-# LANGUAGE BlockArguments #-}-{-# LANGUAGE CPP #-}-{-# LANGUAGE DuplicateRecordFields #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE LambdaCase #-}-{-# LANGUAGE NamedFieldPuns #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE CPP #-} module Main where @@ -27,6 +19,7 @@ import qualified System.Posix.Terminal import Nix.Diff+import Nix.Diff.Store (StorePath(StorePath)) import Nix.Diff.Types import Nix.Diff.Render.HumanReadable import Nix.Diff.Transformations@@ -46,7 +39,7 @@ Options.Applicative.option reader ( Options.Applicative.long "color"- <> Options.Applicative.help ("display colors always, automatically (if terminal detected), or never")+ <> Options.Applicative.help "display colors always, automatically (if terminal detected), or never" <> Options.Applicative.value Auto <> Options.Applicative.metavar "(always|auto|never)" )@@ -160,6 +153,7 @@ transformDiff TransformOptions{..} = transformIf foldAlreadyCompared foldAlreadyComparedSubTrees . transformIf squashTextDiff squashSourcesAndEnvsDiff+ . foldManyInputDerivationsAlreadyCompared renderDiff :: RenderRunner -> RenderContext -> DerivationDiff -> IO () renderDiff HumanReadable context derivation@@ -185,8 +179,8 @@ let diffContext = DiffContext {..} let renderContext = RenderContext {..} let status = Status Data.Set.empty- let action = diff True left (Data.Set.singleton "out") right (Data.Set.singleton "out")- diffTree <- Control.Monad.State.evalStateT (Control.Monad.Reader.runReaderT (unDiff action) diffContext) status+ let action = diff True (StorePath left) (OutputNames (Data.Set.singleton "out")) (StorePath right) (OutputNames (Data.Set.singleton "out"))+ diffTree <- Control.Monad.State.evalStateT (Control.Monad.Reader.runReaderT action.unDiff diffContext) status let diffTree' = transformDiff transformOptions diffTree renderDiff renderRunner renderContext diffTree'
nix-diff.cabal view
@@ -1,28 +1,53 @@+cabal-version: 3.0 name: nix-diff-version: 1.0.20+version: 1.0.21 synopsis: Explain why two Nix derivations differ description: This package provides a @nix-diff@ executable which explains why two Nix derivations (i.e. @*.drv@ files) differ homepage: https://github.com/Gabriella439/nix-diff-license: BSD3+license: BSD-3-Clause license-file: LICENSE author: Gabriella Gonzalez maintainer: GenuineGabriella@gmail.com-copyright: 2017 Gabriella Gonzalez+copyright: 2024 Gabriella Gonzalez category: System build-type: Simple-tested-with: GHC == 8.10.7, GHC == 9.0.2, GHC == 9.2.5, GHC == 9.4.4-cabal-version: >= 1.10+tested-with: GHC == 9.2.8,+ GHC == 9.4.7,+ GHC == 9.6.3 extra-source-files: README.md CHANGELOG.md +common language+ default-language: GHC2021+ default-extensions:+ ApplicativeDo,+ BlockArguments,+ DeriveAnyClass,+ DerivingStrategies,+ DerivingVia,+ DuplicateRecordFields,+ LambdaCase,+ MultiWayIf,+ NamedFieldPuns,+ NoFieldSelectors,+ OverloadedRecordDot,+ OverloadedStrings,+ RecordWildCards,+ ghc-options: -Wall+ library+ import: language+ hs-source-dirs: src+ exposed-modules: Nix.Diff+ Nix.Diff.Store Nix.Diff.Types Nix.Diff.Transformations Nix.Diff.Render.HumanReadable+ build-depends: base >= 4.9 && < 5 , attoparsec >= 0.13 && < 0.15 , aeson@@ -41,27 +66,27 @@ , quickcheck-instances < 3.29 , generic-arbitrary < 1.1 , uniplate < 1.17- hs-source-dirs: src- default-language: Haskell2010- ghc-options: -Wall executable nix-diff+ import: language+ hs-source-dirs: app main-is: Main.hs build-depends: base , nix-diff , aeson , bytestring- , optparse-applicative >= 0.14.0.0 && < 0.18+ , optparse-applicative , text , unix < 2.9 , containers , mtl- hs-source-dirs: app- default-language: Haskell2010- ghc-options: -Wall test-suite nix-diff-test+ import: language+ ghc-options: -Wall++ hs-source-dirs: test main-is: Main.hs other-modules:@@ -82,8 +107,3 @@ , tasty < 1.6 , tasty-quickcheck < 0.11 , tasty-silver < 3.4-- hs-source-dirs: test-- default-language: Haskell2010- ghc-options: -Wall
src/Nix/Diff.hs view
@@ -1,11 +1,4 @@-{-# LANGUAGE ApplicativeDo #-}-{-# LANGUAGE BlockArguments #-}-{-# LANGUAGE CPP #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE LambdaCase #-}-{-# LANGUAGE NamedFieldPuns #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE CPP #-} module Nix.Diff where @@ -13,7 +6,7 @@ import Control.Monad.IO.Class (MonadIO, liftIO) import Control.Monad.Reader (MonadReader, ReaderT, ask) import Control.Monad.State (MonadState, StateT, get, put)-import Data.Attoparsec.Text (IResult(..))+import Data.Attoparsec.Text (IResult(..), Parser) import Data.List.NonEmpty (NonEmpty(..)) import Data.Map (Map) import Data.Maybe (catMaybes)@@ -21,8 +14,8 @@ import Data.Text (Text) import Data.Vector (Vector) import Nix.Derivation (Derivation, DerivationOutput)+import Prelude hiding (unzip) -import qualified Control.Monad.Reader import qualified Data.Attoparsec.Text import qualified Data.ByteString import qualified Data.Char as Char@@ -37,7 +30,6 @@ import qualified Data.Vector import qualified Nix.Derivation import qualified Patience-import qualified System.Directory as Directory import qualified System.FilePath as FilePath import qualified System.Process as Process @@ -46,18 +38,27 @@ #endif import Nix.Diff.Types+import Nix.Diff.Store (StorePath (StorePath, unsafeStorePathFile))+import qualified Nix.Diff.Store as Store +#if MIN_VERSION_base(4,19,0)+import Data.Functor (unzip)+#else+unzip :: Functor f => f (a, b) -> (f a, f b)+unzip xs = (fst <$> xs, snd <$> xs)+#endif+ newtype Status = Status { visited :: Set Diffed } data Diffed = Diffed- { leftDerivation :: FilePath- , leftOutput :: Set Text- , rightDerivation :: FilePath- , rightOutput :: Set Text+ { leftDerivation :: StorePath+ , leftOutput :: OutputNames+ , rightDerivation :: StorePath+ , rightOutput :: OutputNames } deriving (Eq, Ord) newtype Diff a = Diff { unDiff :: ReaderT DiffContext (StateT Status IO) a }- deriving+ deriving newtype ( Functor , Applicative , Monad@@ -88,11 +89,11 @@ Nix technically does not require that the Nix store is actually stored underneath `/nix/store`, but this is the overwhelmingly common use case -}-derivationName :: FilePath -> Text-derivationName = Text.dropEnd 4 . Text.drop 44 . Text.pack+derivationName :: StorePath -> Text+derivationName storePath = Text.dropEnd 4 (Text.drop 44 (Text.pack storePath.unsafeStorePathFile)) -- | Group paths by their name-groupByName :: Map FilePath a -> Map Text (Map FilePath a)+groupByName :: Map StorePath a -> Map Text (Map StorePath a) groupByName m = Data.Map.fromList assocs where toAssoc key = (derivationName key, Data.Map.filterWithKey predicate m)@@ -107,11 +108,11 @@ > /nix/store/${32_CHARACTER_HASH}-${NAME}.drv -}-buildProductName :: FilePath -> Text-buildProductName = Text.drop 44 . Text.pack+buildProductName :: StorePath -> Text+buildProductName storePath = Text.drop 44 (Text.pack storePath.unsafeStorePathFile) -- | Like `groupByName`, but for `Set`s-groupSetsByName :: Set FilePath -> Map Text (Set FilePath)+groupSetsByName :: Set StorePath -> Map Text (Set StorePath) groupSetsByName s = Data.Map.fromList (fmap toAssoc (Data.Set.toList s)) where toAssoc key = (buildProductName key, Data.Set.filter predicate s)@@ -127,12 +128,25 @@ Data.Text.Encoding.decodeUtf8With Data.Text.Encoding.Error.lenientDecode <$> Data.ByteString.readFile file +storepathParser :: Parser StorePath+storepathParser = do+ text <- Nix.Derivation.textParser+ let str = Text.unpack text+ case (Text.uncons text, FilePath.isValid str) of+ (Just ('/', _), True) -> do+ return (StorePath str)+ _ -> do+ fail ("bad path ‘" <> Text.unpack text <> "’ in derivation")++ -- | Read and parse a derivation from a file-readDerivation :: FilePath -> Diff (Derivation FilePath Text)-readDerivation path = do+readDerivation :: StorePath -> Diff (Derivation StorePath Text)+readDerivation sp = do+ path <- liftIO (Store.toPhysicalPath sp) let string = path text <- liftIO (readFileUtf8Lenient string)- case Data.Attoparsec.Text.parse Nix.Derivation.parseDerivation text of+ let parser = Nix.Derivation.parseDerivationWith storepathParser Nix.Derivation.textParser+ case Data.Attoparsec.Text.parse parser text of Done _ derivation -> do return derivation _ -> do@@ -141,11 +155,11 @@ -- | Read and parse a derivation from a store path that can be a derivation -- (.drv) or a realized path, in which case the corresponding derivation is -- queried.-readInput :: FilePath -> Diff (Derivation FilePath Text)+readInput :: StorePath -> Diff (Derivation StorePath Text) readInput pathAndMaybeOutput = do- let (path, _) = List.break (== '!') pathAndMaybeOutput+ let (path, _) = List.break (== '!') pathAndMaybeOutput.unsafeStorePathFile if FilePath.isExtensionOf ".drv" path- then readDerivation path+ then readDerivation (StorePath path) else do let string = path result <- liftIO (Process.readProcess "nix-store" [ "--query", "--deriver", string ] [])@@ -153,7 +167,7 @@ [] -> fail ("Could not obtain the derivation of " ++ string) l : ls -> do let drv_path = Data.List.NonEmpty.last (l :| ls)- readDerivation drv_path+ readDerivation (StorePath drv_path) {-| Join two `Map`s on shared keys, discarding keys which are not present in both `Map`s@@ -198,11 +212,11 @@ diffOutput :: Text -- ^ Output name- -> (DerivationOutput FilePath Text)+ -> DerivationOutput StorePath Text -- ^ Left derivation outputs- -> (DerivationOutput FilePath Text)+ -> DerivationOutput StorePath Text -- ^ Right derivation outputs- -> (Maybe OutputDiff)+ -> Maybe OutputDiff diffOutput outputName leftOutput rightOutput = do -- We deliberately do not include output paths or hashes in the diff since -- we already expect them to differ if the inputs differ. Instead, we focus@@ -215,9 +229,9 @@ -- | Diff two sets of outputs diffOutputs- :: Map Text (DerivationOutput FilePath Text)+ :: Map Text (DerivationOutput StorePath Text) -- ^ Left derivation outputs- -> Map Text (DerivationOutput FilePath Text)+ -> Map Text (DerivationOutput StorePath Text) -- ^ Right derivation outputs -> OutputsDiff diffOutputs leftOutputs rightOutputs = do@@ -314,16 +328,16 @@ -- | Diff two environments diffEnv- :: Set Text+ :: OutputNames -- ^ Left derivation outputs- -> Set Text+ -> OutputNames -- ^ Right derivation outputs -> Map Text Text -- ^ Left environment to compare -> Map Text Text -- ^ Right environment to compare -> Diff EnvironmentDiff-diffEnv leftOutputs rightOutputs leftEnv rightEnv = do+diffEnv (OutputNames leftOutputs) (OutputNames rightOutputs) leftEnv rightEnv = do let leftExtraEnv = Data.Map.difference leftEnv rightEnv let rightExtraEnv = Data.Map.difference rightEnv leftEnv @@ -355,9 +369,9 @@ -- | Diff input sources diffSrcs- :: Set FilePath+ :: Set StorePath -- ^ Left input sources- -> Set FilePath+ -> Set StorePath -- ^ Right inputSources -> Diff SourcesDiff diffSrcs leftSrcs rightSrcs = do@@ -370,7 +384,7 @@ let leftExtraNames = Data.Set.difference leftNames rightNames let rightExtraNames = Data.Set.difference rightNames leftNames - let extraSrcNames = if (leftNames /= rightNames)+ let extraSrcNames = if leftNames /= rightNames then Just (Changed leftExtraNames rightExtraNames) else Nothing @@ -382,12 +396,12 @@ case (Data.Set.toList leftExtraPaths, Data.Set.toList rightExtraPaths) of ([], []) -> return Nothing ([leftPath], [rightPath]) -> do- leftExists <- liftIO (Directory.doesFileExist leftPath)- rightExists <- liftIO (Directory.doesFileExist rightPath)+ leftExists <- liftIO (Store.doesFileExist leftPath)+ rightExists <- liftIO (Store.doesFileExist rightPath) srcContentDiff <- if leftExists && rightExists then do- leftText <- liftIO (readFileUtf8Lenient leftPath)- rightText <- liftIO (readFileUtf8Lenient rightPath)+ leftText <- liftIO (Store.readFileUtf8Lenient leftPath)+ rightText <- liftIO (Store.readFileUtf8Lenient rightPath) text <- diffText leftText rightText return (Just text)@@ -419,7 +433,20 @@ let rightList = Data.Vector.toList rightArgs Data.List.NonEmpty.nonEmpty (Patience.diff leftList rightList) -diff :: Bool -> FilePath -> Set Text -> FilePath -> Set Text -> Diff DerivationDiff+diff :: Bool+ -- ^ Is this the top-level call for a comparison?+ --+ -- If so, the diff will be more detailed.+ -> StorePath+ -- ^ Store path of left derivation.+ -> OutputNames+ -- ^ Output names of left derivation.+ -> StorePath+ -- ^ Store path of right derivation.+ -> OutputNames+ -- ^ Output names of right derivation.+ -> Diff DerivationDiff+ -- ^ Description of how the two derivations differ. diff topLevel leftPath leftOutputs rightPath rightOutputs = do Status { visited } <- get let diffed = Diffed leftPath leftOutputs rightPath rightOutputs@@ -465,15 +492,15 @@ let rightSrcs = Nix.Derivation.inputSrcs rightDerivation sourcesDiff <- diffSrcs leftSrcs rightSrcs - let leftInputs = groupByName (Nix.Derivation.inputDrvs leftDerivation)- let rightInputs = groupByName (Nix.Derivation.inputDrvs rightDerivation)+ let leftInputs = groupByName (Data.Map.map OutputNames (Nix.Derivation.inputDrvs leftDerivation))+ let rightInputs = groupByName (Data.Map.map OutputNames (Nix.Derivation.inputDrvs rightDerivation)) let leftNames = Data.Map.keysSet leftInputs let rightNames = Data.Map.keysSet rightInputs let leftExtraNames = Data.Set.difference leftNames rightNames let rightExtraNames = Data.Set.difference rightNames leftNames - let inputExtraNames = if (leftNames /= rightNames)+ let inputExtraNames = if leftNames /= rightNames then Just (Changed leftExtraNames rightExtraNames) else Nothing @@ -504,7 +531,7 @@ else do let leftEnv = Nix.Derivation.env leftDerivation let rightEnv = Nix.Derivation.env rightDerivation- let leftOutNames = Data.Map.keysSet leftOuts- let rightOutNames = Data.Map.keysSet rightOuts+ let leftOutNames = OutputNames (Data.Map.keysSet leftOuts)+ let rightOutNames = OutputNames (Data.Map.keysSet rightOuts) Just <$> diffEnv leftOutNames rightOutNames leftEnv rightEnv pure DerivationDiff{..}
src/Nix/Diff/Render/HumanReadable.hs view
@@ -1,19 +1,10 @@-{-# LANGUAGE ApplicativeDo #-}-{-# LANGUAGE BlockArguments #-}-{-# LANGUAGE CPP #-}-{-# LANGUAGE DuplicateRecordFields #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE LambdaCase #-}-{-# LANGUAGE NamedFieldPuns #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE CPP #-} module Nix.Diff.Render.HumanReadable where import Control.Monad (forM_) import Control.Monad.Reader (MonadReader, ReaderT (runReaderT), ask, local) import Control.Monad.Writer(MonadWriter, Writer, tell, runWriter)-import Data.Set (Set) import Data.Text (Text) import Numeric.Natural (Natural) @@ -29,6 +20,7 @@ import Nix.Diff import Nix.Diff.Types+import qualified Nix.Diff.Store as Store data RenderContext = RenderContext@@ -39,7 +31,7 @@ } newtype Render a = Render { unRender :: ReaderT RenderContext (Writer Text) a}- deriving+ deriving newtype ( Functor , Applicative , Monad@@ -48,7 +40,7 @@ ) runRender :: Render a -> RenderContext -> (a, Text)-runRender render rc = runWriter $ runReaderT (unRender render) rc+runRender render rc = runWriter $ runReaderT render.unRender rc runRender' :: Render () -> RenderContext -> Text runRender' render = snd . runRender render@@ -64,47 +56,63 @@ indented :: Natural -> Render a -> Render a indented n = local adapt where- adapt context = context { indent = indent context + n }+ adapt context = context { indent = context.indent + n } data TTY = IsTTY | NotTTY +-- | This exists to improve compatibility with @less -R@. See:+--+-- https://github.com/Gabriella439/nix-diff/issues/95+escape+ :: Text+ -- ^ Beginning of escape sequence+ -> Text+ -- ^ Text to escape+ -> Text+escape begin text =+ begin <> Text.intercalate middle (Text.splitOn "\n" text) <> end+ where+ end = "\ESC[0m"++ middle = end <> "\n" <> begin+ -- | Color text red red :: TTY -> Text -> Text-red IsTTY text = "\ESC[1;31m" <> text <> "\ESC[0m"+red IsTTY text = escape "\ESC[1;31m" text red NotTTY text = text -- | Color text background red redBackground :: Orientation -> TTY -> Text -> Text-redBackground Line IsTTY text = "\ESC[41m" <> prefix <> "\ESC[0m" <> suffix+redBackground Line IsTTY text = escape "\ESC[41m" prefix <> suffix where (prefix, suffix) = Text.break lineBoundary text-redBackground Word IsTTY text = "\ESC[41m" <> prefix <> "\ESC[0m" <> suffix+redBackground Word IsTTY text = escape "\ESC[41m" prefix <> suffix where (prefix, suffix) = Text.break wordBoundary text-redBackground Character IsTTY text = "\ESC[41m" <> text <> "\ESC[0m"+redBackground Character IsTTY text = escape "\ESC[41m" text redBackground Line NotTTY text = "- " <> text redBackground _ NotTTY text = "←" <> text <> "←" -- | Color text green green :: TTY -> Text -> Text-green IsTTY text = "\ESC[1;32m" <> text <> "\ESC[0m"+green IsTTY text = escape "\ESC[1;32m" text green NotTTY text = text -- | Color text background green greenBackground :: Orientation -> TTY -> Text -> Text-greenBackground Line IsTTY text = "\ESC[42m" <> prefix <> "\ESC[0m" <> suffix+greenBackground Line IsTTY text = escape "\ESC[42m" prefix <> suffix where (prefix, suffix) = Text.break lineBoundary text-greenBackground Word IsTTY text = "\ESC[42m" <> prefix <> "\ESC[0m" <> suffix+greenBackground Word IsTTY text = escape "\ESC[42m" prefix <> suffix where (prefix, suffix) = Text.break wordBoundary text-greenBackground Character IsTTY text = "\ESC[42m" <> text <> "\ESC[0m"+greenBackground Character IsTTY text = escape "\ESC[42m" text greenBackground Line NotTTY text = "+ " <> text greenBackground _ NotTTY text = "→" <> text <> "→" -- | Color text grey grey :: Orientation -> TTY -> Text -> Text-grey _ IsTTY text = "\ESC[1;2m" <> text <> "\ESC[0m"+grey _ IsTTY text = escape "\ESC[1;2m" text grey Line NotTTY text = " " <> text grey _ NotTTY text = text @@ -130,8 +138,8 @@ k (plus tty, now) -- | Format the derivation outputs-renderOutputs :: Set Text -> Text-renderOutputs outputs =+renderOutputs :: OutputNames -> Text+renderOutputs (OutputNames outputs) = ":{" <> Text.intercalate "," (Data.Set.toList outputs) <> "}" renderDiffHumanReadable :: DerivationDiff -> Render ()@@ -159,8 +167,8 @@ where renderOutputStructure os =- renderWith os \(sign, (OutputStructure path outputs)) -> do- echo (sign (Text.pack path <> renderOutputs outputs))+ renderWith os \(sign, OutputStructure path outputs) -> do+ echo (sign (Store.toText path <> renderOutputs outputs)) renderOutputsDiff OutputsDiff{..} = do ifExist extraOutputs \eo -> do@@ -218,7 +226,7 @@ echo (explain ("The input sources named `" <> srcName <> "` differ")) renderWith srcFileDiff \(sign, paths) -> do forM_ paths \path -> do- echo (" " <> sign (Text.pack path))+ echo (" " <> sign (Store.toText path)) renderInputsDiff InputsDiff{..} = do renderInputExtraNames inputExtraNames@@ -238,7 +246,11 @@ echo (explain ("The set of input derivations named `" <> drvName <> "` do not match")) renderWith extraPartsDiff \(sign, extraPaths) -> do forM_ (Data.Map.toList extraPaths) \(extraPath, outputs) -> do- echo (" " <> sign (Text.pack extraPath <> renderOutputs outputs))+ echo (" " <> sign (Store.toText extraPath <> renderOutputs outputs))+ renderInputDerivationsDiff ManyDerivationsAlreadyComparedDiff{..} = do+ echo (explain "Input derivations differ but have already been compared")+ forM_ (Data.Set.toList drvNames) \name -> do+ echo (" " <> name) renderEnvDiff Nothing = echo (explain "Skipping environment comparison")
+ src/Nix/Diff/Store.hs view
@@ -0,0 +1,66 @@+-- | A crude implementation of the Nix store concept.+--+-- For anything fancier than this, it would be best to use FFI bindings instead,+-- such as hercules-ci-cnix-store.+module Nix.Diff.Store+ ( StorePath (..),+ toPhysicalPath,+ toText,+ doesFileExist,+ readFileUtf8Lenient,+ )+where++import Control.Monad ((<=<))+import Data.Aeson (FromJSON, FromJSONKey, ToJSON, ToJSONKey)+import qualified Data.ByteString+import Data.Data (Data)+import Data.Functor ((<&>))+import qualified Data.List as L+import Data.Text (Text)+import qualified Data.Text.Encoding+import qualified Data.Text.Encoding.Error+import qualified System.Directory as Directory+import System.Environment (lookupEnv)+import Test.QuickCheck (Arbitrary)+import qualified Data.Text as T++-- | A file path that may not exist on the true file system;+-- needs to be looked up in a store, which may be relocated.+--+-- Unlike the (C++) Nix StorePath type, subpaths are allowed.+newtype StorePath = StorePath+ { -- | If the store is relocated, its physical location is elsewhere, and this 'FilePath' won't resolve.+ -- Use 'toPhysicalPath'.+ unsafeStorePathFile :: FilePath+ }+ deriving (Data)+ deriving newtype (Show, Eq, Ord, ToJSON, FromJSON, ToJSONKey, FromJSONKey, Arbitrary)++doesFileExist :: StorePath -> IO Bool+doesFileExist =+ Directory.doesFileExist <=< toPhysicalPath++readFileUtf8Lenient :: StorePath -> IO Text+readFileUtf8Lenient sp = do+ file <- toPhysicalPath sp+ Data.Text.Encoding.decodeUtf8With Data.Text.Encoding.Error.lenientDecode+ <$> Data.ByteString.readFile file++toPhysicalPath :: StorePath -> IO FilePath+toPhysicalPath (StorePath p) = do+ nixStoreDir <- lookupEnv "NIX_STORE_DIR" <&> maybe "/nix/store" stripSlash+ nixRemoteMaybe <- lookupEnv "NIX_REMOTE" <&> fmap stripSlash+ case nixRemoteMaybe of+ Just nixRemote | nixStoreDir `L.isPrefixOf` p -> do+ pure $ nixRemote <> "/" <> L.dropWhile (== '/') p+ _ -> pure p++-- | Convert a 'StorePath' to a 'Text' for display purposes. The path may not exist at this physical location.+toText :: StorePath -> Text+toText (StorePath p) = T.pack p++stripSlash :: FilePath -> FilePath+stripSlash s | not ("/" `L.isSuffixOf` s) = s+stripSlash s = doIt s+ where doIt = T.unpack . T.dropWhileEnd (== '/') . T.pack
src/Nix/Diff/Transformations.hs view
@@ -1,15 +1,11 @@-{-# LANGUAGE RecordWildCards #-}-{-# LANGUAGE LambdaCase #-}-{-# LANGUAGE NamedFieldPuns #-}-{-# LANGUAGE MultiWayIf #-}-{-# LANGUAGE BlockArguments #-}-{-# LANGUAGE FlexibleContexts #-} module Nix.Diff.Transformations where import qualified Patience import Data.Generics.Uniplate.Data ( transformBi ) import Nix.Diff.Types+import qualified Data.Set as Set+import Data.Text (Text) {-| In large diffs there may be a lot of derivations that doesn't change at all, but changed some of@@ -26,7 +22,7 @@ • These two derivations have already been compared ``` This transformation will fold all these subtrees of diff- into one OnlyAlreadComparedBelow.+ into one OnlyAlreadyComparedBelow. -} foldAlreadyComparedSubTrees :: DerivationDiff -> DerivationDiff foldAlreadyComparedSubTrees dd = case dd of@@ -61,6 +57,61 @@ foldAlreadyComparedSubTrees inputsDiff +{-| If packages deep in the dependency graph have been changed, many other+ derivations will also change in an uninteresting manner. This can lead to+ hundreds or thousands of lines of output like this:++ ```+ • The input derivation named `bash-5.2p32` differs+ • These two derivations have already been compared+ • The input derivation named `ensure-newer-sources-hook` differs+ • These two derivations have already been compared+ • The input derivation named `pypa-install-hook` differs+ • These two derivations have already been compared+ ```++ This transformation will fold sequences of `OneDerivationDiff` like this+ into a single `ManyDerivationsAlreadyComparedDiff`.+-}+foldManyInputDerivationsAlreadyCompared :: DerivationDiff -> DerivationDiff+foldManyInputDerivationsAlreadyCompared dd = case dd of+ DerivationsAreTheSame -> dd+ AlreadyCompared -> dd+ OnlyAlreadyComparedBelow{} -> dd+ NamesDontMatch{} -> dd+ OutputsDontMatch{} -> dd+ DerivationDiff{..} ->+ let inputsDiff' = transformNestedDerivationDiffs+ foldManyInputDerivationsAlreadyCompared+ inputsDiff++ helper :: [Text] -> [InputDerivationsDiff] -> [InputDerivationsDiff]+ helper [] [] = []+ helper names [] =+ [ManyDerivationsAlreadyComparedDiff+ { drvNames = Set.fromList names+ }]+ helper names (input:inputs) =+ case input of+ OneDerivationDiff+ { drvName+ , drvDiff = AlreadyCompared+ } -> helper+ (drvName:names)+ inputs+ OneDerivationDiff{} -> input : helper names inputs+ SomeDerivationsDiff{} -> input : helper names inputs+ ManyDerivationsAlreadyComparedDiff{} -> error "unreachable"++ in DerivationDiff+ { inputsDiff =+ inputsDiff'+ { inputDerivationDiffs =+ helper [] inputsDiff'.inputDerivationDiffs+ }+ , ..+ }+ {-| This transformation is most useful for --json output, because it will sqash a lot of `{"content":" ","type":"Both"},{"content":"When","type":"Both"},{"content":" ","type":"Both"},{"content":"in","type":"Both"},{"content":" ","type":"Both"}`@@ -102,6 +153,7 @@ OneDerivationDiff name dd -> OneDerivationDiff name (f dd) SomeDerivationsDiff {} -> idd+ ManyDerivationsAlreadyComparedDiff {} -> idd envSkippedOrUnchanged :: Maybe EnvironmentDiff -> Bool envSkippedOrUnchanged = \case
src/Nix/Diff/Types.hs view
@@ -1,15 +1,7 @@-{-# LANGUAGE RecordWildCards #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE DerivingStrategies #-}-{-# LANGUAGE DeriveAnyClass #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE DeriveTraversable #-}-{-# LANGUAGE BlockArguments #-}-{-# LANGUAGE DerivingVia #-}-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE StandaloneDeriving #-}-{-# LANGUAGE FlexibleInstances #-}-{-# OPTIONS_GHC -Wno-orphans -fconstraint-solver-iterations=0 #-}+-- Needed for `deriving Arbitrary via GenericArbitrary DerivationDiff`:+{-# OPTIONS_GHC -fconstraint-solver-iterations=0 #-}+-- Needed for `deriving instance Data (DerivationOutput StorePath Text)`:+{-# OPTIONS_GHC -Wno-orphans #-} module Nix.Diff.Types where @@ -20,6 +12,7 @@ import Data.Set (Set) import Data.Text (Text) import Nix.Derivation (DerivationOutput (..))+import Nix.Diff.Store ( StorePath ) import qualified Patience import GHC.Generics (Generic)@@ -61,12 +54,12 @@ arbitrary = TextDiff <$> listOf arbitraryItem instance ToJSON TextDiff where- toJSON = listValue itemToJSON . unTextDiff+ toJSON textDiff = listValue itemToJSON textDiff.unTextDiff instance FromJSON TextDiff where parseJSON v = TextDiff <$> (traverse itemFromJSON =<< parseJSON v) --- Helpfull aliases+-- Helpful aliases type OutputHash = Text @@ -76,6 +69,12 @@ type Argument = Text +-- | A set of Nix derivation output names.+newtype OutputNames = OutputNames {unOutputNames :: Set Text}+ deriving newtype (Eq, Ord, ToJSON, FromJSON)+ deriving stock (Show, Generic, Data)+ deriving Arbitrary via GenericArbitrary OutputNames+ -- Derivation diff data DerivationDiff@@ -105,8 +104,8 @@ -- Output structure data OutputStructure = OutputStructure- { derivationPath :: FilePath- , derivationOutputs :: Set Text+ { derivationPath :: StorePath+ , derivationOutputs :: OutputNames } deriving stock (Eq, Show, Generic, Data) deriving anyclass (ToJSON, FromJSON)@@ -115,7 +114,7 @@ -- ** Outputs diff data OutputsDiff = OutputsDiff- { extraOutputs :: Maybe (Changed (Map Text (DerivationOutput FilePath Text)))+ { extraOutputs :: Maybe (Changed (Map Text (DerivationOutput StorePath Text))) -- ^ Map from derivation name to its outputs. -- Will be Nothing, if `Data.Map.difference` gives -- empty Maps for both new and old outputs@@ -125,7 +124,7 @@ } deriving stock (Eq, Show, Data) -deriving instance Data (DerivationOutput FilePath Text)+deriving instance Data (DerivationOutput StorePath Text) instance Arbitrary OutputsDiff where arbitrary = OutputsDiff <$> arbitraryExtraOutputs <*> arbitrary@@ -145,10 +144,10 @@ , "outputHashDiff" .= outputHashDiff ] where- extraOutputsToJSON :: Map Text (DerivationOutput FilePath Text) -> Value+ extraOutputsToJSON :: Map Text (DerivationOutput StorePath Text) -> Value extraOutputsToJSON = toJSON . fmap derivationOutputToJSON - derivationOutputToJSON :: DerivationOutput FilePath Text -> Value+ derivationOutputToJSON :: DerivationOutput StorePath Text -> Value derivationOutputToJSON DerivationOutput{..} = object [ "path" .= path , "hashAlgo" .= hashAlgo@@ -165,7 +164,7 @@ pure $ OutputsDiff eo ohd where - derivationOutputFromJSON :: Value -> Parser (DerivationOutput FilePath Text)+ derivationOutputFromJSON :: Value -> Parser (DerivationOutput StorePath Text) derivationOutputFromJSON = withObject "DerivationOutput" \o -> DerivationOutput <$> o .: "path" <*> o .: "hashAlgo" <*> o .: "hash" @@ -188,7 +187,7 @@ arbitrary = ArgumentsDiff . NonEmpty.fromList <$> listOf1 arbitraryItem instance ToJSON ArgumentsDiff where- toJSON = listValue itemToJSON . toList . unArgumetsDiff+ toJSON argumentsDiff = listValue itemToJSON (toList argumentsDiff.unArgumetsDiff) instance FromJSON ArgumentsDiff where parseJSON v = ArgumentsDiff <$> (traverse itemFromJSON =<< parseJSON v)@@ -212,7 +211,7 @@ } | SomeSourceFileDiff { srcName :: Text- , srcFileDiff :: Changed [FilePath]+ , srcFileDiff :: Changed [StorePath] } deriving stock (Eq, Show, Generic, Data) deriving anyclass (ToJSON, FromJSON)@@ -236,7 +235,11 @@ } | SomeDerivationsDiff { drvName :: Text- , extraPartsDiff :: Changed (Map FilePath (Set Text))+ , extraPartsDiff :: Changed (Map StorePath OutputNames)+ }+ | -- | Many input derivations differ, but they've all already been compared.+ ManyDerivationsAlreadyComparedDiff+ { drvNames :: Set Text } deriving stock (Eq, Show, Generic, Data) deriving anyclass (ToJSON, FromJSON)
test/Golden/Tests.hs view
@@ -1,4 +1,3 @@-{-# LANGUAGE OverloadedStrings #-} module Golden.Tests where import qualified Data.Aeson
test/Golden/Utils.hs view
@@ -1,6 +1,3 @@-{-# LANGUAGE RecordWildCards #-}-{-# LANGUAGE OverloadedStrings #-}- module Golden.Utils where import qualified Data.ByteString.Lazy.Char8 as BS@@ -12,11 +9,12 @@ import System.Process.Typed import Nix.Diff ( diff, Diff(unDiff), DiffContext, Status(Status) )-import Nix.Diff.Types ( DerivationDiff )+import Nix.Diff.Types ( DerivationDiff, OutputNames(OutputNames) )+import Nix.Diff.Store ( StorePath(StorePath) ) data TestableDerivations = TestableDerivations- { oldDerivation :: FilePath- , newDerivation :: FilePath+ { oldDerivation :: StorePath+ , newDerivation :: StorePath } deriving Show @@ -26,9 +24,7 @@ (nExit, n) <- readProcessStdout (nixInstantiate "./golden-tests/derivations/simple/new/drv.nix") unless (oExit == ExitSuccess || nExit == ExitSuccess) (throwIO (ErrorCall "Can't instantiate simple derivations"))- pure (TestableDerivations (toFilePath o) (toFilePath n))- where- toFilePath = BS.unpack . BS.init -- drop new-line char+ pure (TestableDerivations (storePathFromCLI o) (storePathFromCLI n)) initComplexDerivations :: IO TestableDerivations initComplexDerivations = do@@ -36,9 +32,7 @@ (nExit, n) <- readProcessStdout (nixPathInfo "./golden-tests/derivations/complex/new/") unless (oExit == ExitSuccess || nExit == ExitSuccess) (throwIO (ErrorCall "Can't instantiate complex derivations"))- pure (TestableDerivations (toFilePath o) (toFilePath n))- where- toFilePath = BS.unpack . BS.init -- drop new-line char+ pure (TestableDerivations (storePathFromCLI o) (storePathFromCLI n)) nixInstantiate :: FilePath -> ProcessConfig () () () nixInstantiate fp = shell ("nix-instantiate " <> fp)@@ -49,5 +43,9 @@ makeDiffTree :: TestableDerivations -> DiffContext -> IO DerivationDiff makeDiffTree TestableDerivations{..} diffContext = do let status = Status Data.Set.empty- let action = diff True oldDerivation (Data.Set.singleton "out") newDerivation (Data.Set.singleton "out")- Control.Monad.State.evalStateT (Control.Monad.Reader.runReaderT (unDiff action) diffContext) status+ let action = diff True oldDerivation (OutputNames (Data.Set.singleton "out")) newDerivation (OutputNames (Data.Set.singleton "out"))+ Control.Monad.State.evalStateT (Control.Monad.Reader.runReaderT action.unDiff diffContext) status++-- | Drop conventional stdout newline, convert to StorePath+storePathFromCLI :: BS.ByteString -> StorePath+storePathFromCLI = StorePath . BS.unpack . BS.init -- drop new-line char
test/Main.hs view
@@ -1,6 +1,7 @@ module Main where import Test.Tasty+import Test.Tasty.Silver.Interactive (interactiveTests) import Golden.Utils (initSimpleDerivations, initComplexDerivations) import Golden.Tests (goldenTests)@@ -10,5 +11,7 @@ main = do simpleTd <- initSimpleDerivations complexTd <- initComplexDerivations- defaultMain $- testGroup "Tests" [goldenTests simpleTd complexTd, properties]+ defaultMainWithIngredients+ [ interactiveTests (const False)+ ]+ $ testGroup "Tests" [goldenTests simpleTd complexTd, properties]