packages feed

nix-tree 0.1.9 → 0.2.0

raw patch · 5 files changed

+151/−88 lines, 5 filesdep +optparse-applicative

Dependencies added: optparse-applicative

Files

CHANGELOG.md view
@@ -1,5 +1,15 @@ # Changelog +## 0.2.0 - 2022-01-02:++* feat: Support passing flake references (issue: [#27][])+  * Try `nix-tree nixpkgs#hello'+* change: `nix-tree` now requires an explicit `--derivation` flag to work on store derivations rather than their outputs.+  * This is to be more consistent `nix path-info`.+* fix: Fix timeout when running external command for the 'yank' functionality (issue: [#25][])++[#27]: https://github.com/utdemir/nix-tree/issues/27+ ## 0.1.9 - 2021-11-08:  * fix: Automatically enable the required 'nix-command' experimental feature on Nix >= 2.4@@ -7,20 +17,28 @@  ## 0.1.8 - 2021-09-06: -* fix: Reduce idle CPU use-* fix: Put a timeout on yank command+* fix: Reduce idle CPU use (issue: [#22][])+* fix: Put a timeout on yank command (issue: [#25][]) * fix: Various performance improvements +[#22]: https://github.com/utdemir/nix-tree/issues/22+[#25]: https://github.com/utdemir/nix-tree/issues/25+ ## 0.1.7 - 2021-03-28  * feat: Ability to yank selected store path to clipboard (shortcut: 'y')-* fix: Try to respect terminals color scheme (thanks @kanashimia)+* fix: Try to respect terminals color scheme (thanks @kanashimia, PR: [#20][]) +[#20]: https://github.com/utdemir/nix-tree/issues/20+ ## 0.1.6 - 2021-03-12 -* feat: Support non standard Nix store locations-* feat: Horizontal scrolling on why-depends window+* feat: Support non standard Nix store locations (issue: [#17][])+* feat: Horizontal scrolling on why-depends window (issue: [#18][]) +[#17]: https://github.com/utdemir/nix-tree/issues/17+[#18]: https://github.com/utdemir/nix-tree/issues/18+ ## 0.1.5 - 2021-03-03  * feat: Add sort order toggle.@@ -42,7 +60,9 @@ ## 0.1.2.0 - 2020-09-15  * feat: Add --version flag-* fix: Can search the letter 'q' without closing the search modal.+* fix: Can search the letter 'q' without closing the search modal. (issue: [#21][])++[#21]: https://github.com/utdemir/nix-tree/issues/21  ## 0.1.1.0 - 2020-08-23 
nix-tree.cabal view
@@ -3,7 +3,7 @@ name:                nix-tree synopsis:            Interactively browse a Nix store paths dependencies description:         A terminal curses application to browse a Nix store paths dependencies-version:             0.1.9+version:             0.2.0 homepage:            https://github.com/utdemir/nix-tree license:             BSD-3-Clause license-file:        LICENSE@@ -36,6 +36,7 @@                       RankNTypes                       ScopedTypeVariables                       NumericUnderscores+                      MultiWayIf   other-modules:      NixTree.PathStats                       NixTree.StorePath                       NixTree.App@@ -60,6 +61,7 @@                     , unordered-containers                     , vty                     , directory+                    , optparse-applicative  executable nix-tree   import:             common-options
src/NixTree/Clipboard.hs view
@@ -20,21 +20,13 @@ runCmd txt (cmd, args) =   P.proc (toString cmd) (map toString args)     & P.setStdin (P.byteStringInput $ encodeUtf8 txt)-    & P.readProcess+    & P.runProcess     & timeout 1_000_000     & try     <&> \case-      Right (Just (ExitSuccess, _, _)) -> Right ()-      Right (Just (ExitFailure e, out, err)) ->-        Left $-          "failed with exit code "-            <> show e-            <> ", "-            <> "stdout: "-            <> decodeUtf8 (toStrict out)-            <> ", "-            <> "stderr: "-            <> decodeUtf8 (toStrict err)+      Right (Just ExitSuccess) -> Right ()+      Right (Just (ExitFailure e)) ->+        Left $ "failed with exit code " <> show e       Right Nothing ->         Left $ "timed out"       Left (ex :: SomeException) ->
src/NixTree/Main.hs view
@@ -6,8 +6,8 @@ import Control.Exception (evaluate) import NixTree.App import NixTree.PathStats-import System.Directory (canonicalizePath, doesDirectoryExist, getHomeDirectory)-import System.Environment (getArgs)+import qualified Options.Applicative as Opts+import System.Directory (doesDirectoryExist, getHomeDirectory) import System.Exit (ExitCode (..)) import System.FilePath ((</>)) import System.IO (hPutStr, hPutStrLn)@@ -16,6 +16,27 @@ version :: Text version = VERSION_nix_tree +data Opts = Opts+  { oVersion :: Bool,+    oDerivation :: Bool,+    oInstallables :: [Installable]+  }++optsParser :: Opts.ParserInfo Opts+optsParser =+  Opts.info (parser <**> Opts.helper) $+    mconcat+      [ Opts.progDesc "Interactively browse dependency graphs of Nix derivations.",+        Opts.fullDesc+      ]+  where+    parser :: Opts.Parser Opts+    parser =+      Opts+        <$> Opts.switch (Opts.long "version" <> Opts.help "Show the nix-tree version.")+        <*> Opts.switch (Opts.long "derivation" <> Opts.help "Operate on the store derivation rather than its outputs.")+        <*> many (Opts.strArgument @Text (Opts.metavar "INSTALLABLE" <> Opts.help "A store path or a flake reference.") <&> Installable)+ usage :: Text usage =   unlines@@ -33,16 +54,13 @@  main :: IO () main = do-  args <- getArgs-  when (any (`elem` ["-h", "--help"]) args) $ do-    putText usage-    exitWith ExitSuccess+  opts <- Opts.execParser optsParser -  when ("--version" `elem` args) $ do+  when (opts & oVersion) $ do     putTextLn $ "nix-tree " <> version     exitWith ExitSuccess -  paths <- case args of+  installables <- case opts & oInstallables of     p : ps ->       return $ p :| ps     [] -> do@@ -55,9 +73,9 @@           ]       case roots of         [] -> usageAndFail "No store path given."-        p : ps -> return $ p :| ps-  storePaths <- mapM canonicalizePath paths-  ret <- withStoreEnv storePaths $ \env' -> do+        p : ps -> return . fmap (Installable . toText) $ p :| ps++  withStoreEnv (opts & oDerivation) installables $ \env' -> do     let env = calculatePathStats env'         allPaths = seAll env @@ -68,11 +86,6 @@       & mapConcurrently_ (mapM_ (\p -> evaluate (rnf p) >> incProgress bar 1))      run env--  case ret of-    Right () -> return ()-    Left err ->-      usageAndFail $ "Not a store path: " <> show err  chunks :: Int -> [a] -> [[a]] chunks _ [] = []
src/NixTree/StorePath.hs view
@@ -5,6 +5,7 @@     storeNameToShortText,     storeNameToSplitShortText,     StorePath (..),+    Installable (..),     StoreEnv (..),     withStoreEnv,     seLookup,@@ -21,10 +22,9 @@ import qualified Data.ByteString.Lazy as BL import qualified Data.HashMap.Strict as HM import qualified Data.HashSet as HS-import Data.List (partition)-import qualified Data.List.NonEmpty as NE import qualified Data.Text as T import System.FilePath.Posix (addTrailingPathSeparator, splitDirectories, (</>))+import System.IO (hPutStrLn) import System.Process.Typed (proc, readProcessStdout_)  -- Technically these both are filepaths. However, most people use the default "/nix/store",@@ -86,6 +86,42 @@  -------------------------------------------------------------------------------- +data NixVersion+  = NixOlder+  | Nix2_4+  | NixNewer+  | NixUnknown+  deriving (Show, Eq, Ord)++getNixVersion :: IO NixVersion+getNixVersion = do+  out <- decodeUtf8 . BL.toStrict <$> readProcessStdout_ (proc "nix" ["--version"])++  -- Parses strings like:+  --  nix (Nix) 2.6.0pre20211217_6e6e998+  --  nix (Nix) 2.5.1+  return . fromMaybe NixUnknown $ do+    -- get the last space delimited part+    ver <-+      out+        & T.splitOn " "+        & viaNonEmpty last++    -- split by ".", take the first two, and convert them to numbers+    (major, minor) <- do+      let maT : miT : _ = T.splitOn "." ver+      ma <- readMaybe @Natural (toString maT)+      mi <- readMaybe @Natural (toString miT)+      return (ma, mi)++    -- map it to the sum+    return $ case compare (major, minor) (2, 4) of+      LT -> NixOlder+      EQ -> Nix2_4+      GT -> NixNewer++--------------------------------------------------------------------------------+ data StorePath s ref payload = StorePath   { spName :: StoreName s,     spSize :: Int,@@ -96,45 +132,33 @@  instance (NFData a, NFData b) => NFData (StorePath s a b) -mkStorePaths :: NonEmpty (StoreName s) -> IO [StorePath s (StoreName s) ()]-mkStorePaths names = do-  nixStore <- getNixStore-  -- See: https://github.com/utdemir/nix-tree/issues/12-  ---  -- > In Nix < 2.4, when you pass a .drv to path-info, it returns information about the store-  -- > derivation. However, when you do the same in 2.4, it "resolves" it and works on-  -- > the output of given derivation; to actually work on the derivation you need to pass-  -- > --derivation.-  isAtLeastNix24 <- (>= Just "2.4") <$> getNixVersion+newtype Installable = Installable {installableToText :: Text} -  let (derivations, outputs) =-        partition-          (\i -> ".drv" `T.isSuffixOf` storeNameToText i)-          (NE.toList names)-  (++)-    <$> maybe (return []) (getPathInfo nixStore isAtLeastNix24 False) (NE.nonEmpty outputs)-    <*> maybe (return []) (getPathInfo nixStore isAtLeastNix24 True) (NE.nonEmpty derivations)-  where-    getNixVersion :: IO (Maybe Text)-    getNixVersion = do-      out <- decodeUtf8 . BL.toStrict <$> readProcessStdout_ (proc "nix" ["--version"])-      return . viaNonEmpty last $ T.splitOn " " out+-------------------------------------------------------------------------------- -getPathInfo :: NixStore -> Bool -> Bool -> NonEmpty (StoreName s) -> IO [StorePath s (StoreName s) ()]-getPathInfo nixStore isAtLeastNix24 isDrv names = do+data PathInfoOptions = PathInfoOptions+  { pioIsRecursive :: Bool,+    pioIsDerivation :: Bool+  }++getPathInfo :: NixStore -> NixVersion -> PathInfoOptions -> NonEmpty Installable -> IO (NonEmpty (StorePath s (StoreName s) ()))+getPathInfo nixStore nixVersion options names = do   infos <-     decode @[NixPathInfoResult]       <$> readProcessStdout_         ( proc             "nix"-            ( ["path-info", "--recursive", "--json"]-                ++ (if isAtLeastNix24 then ["--extra-experimental-features", "nix-command"] else [])-                ++ (if isDrv && isAtLeastNix24 then ["--derivation"] else [])-                ++ map storeNameToPath (toList names)+            ( ["path-info", "--json"]+                ++ (if options & pioIsRecursive then ["--recursive"] else [])+                ++ (if (options & pioIsDerivation) && nixVersion >= Nix2_4 then ["--derivation"] else [])+                ++ (if nixVersion >= Nix2_4 then ["--extra-experimental-features", "nix-command flakes"] else [])+                ++ map (toString . installableToText) (toList names)             )         )       >>= maybe (fail "Failed parsing nix path-info output.") return       >>= mapM assertValidInfo+      >>= maybe (fail "invariant violation: getPathInfo returned []") return . nonEmpty+   mapM infoToStorePath infos   where     infoToStorePath NixPathInfo {npiPath, npiNarSize, npiReferences} = do@@ -155,7 +179,7 @@      assertValidInfo (NixPathInfoValid pathinfo) = return pathinfo     assertValidInfo (NixPathInfoInvalid path) =-      fail $ "Invalid path: " ++ path ++ ". Inconsistent NIX_STORE or ongoing GC."+      fail $ "Invalid path: " ++ path ++ ". Make sure that it is built, or pass '--derivation' if you want to work on the derivation."  -------------------------------------------------------------------------------- @@ -168,32 +192,44 @@ withStoreEnv ::   forall m a.   MonadIO m =>-  NonEmpty FilePath ->+  Bool ->+  NonEmpty Installable ->   (forall s. StoreEnv s () -> m a) ->-  m (Either [FilePath] a)-withStoreEnv fnames cb = do+  m a+withStoreEnv isDerivation names cb = do   nixStore <- liftIO getNixStore -  let names' =-        fnames-          & toList-          & map (\f -> maybe (Left f) Right (mkStoreName nixStore f))-          & partitionEithers+  -- See: https://github.com/utdemir/nix-tree/issues/12+  nixVersion <- liftIO getNixVersion -  case names' of-    (errs@(_ : _), _) -> return (Left errs)-    ([], xs) -> case nonEmpty xs of-      Nothing -> error "invariant violation"-      Just names -> do-        paths <- liftIO $ mkStorePaths names-        let env =-              StoreEnv-                ( paths-                    & map (\p@StorePath {spName} -> (spName, p))-                    & HM.fromList-                )-                names-        Right <$> cb env+  when (isDerivation && nixVersion < Nix2_4) $+    liftIO $ hPutStrLn stderr "Warning: --derivation flag is ignored on Nix versions older than 2.4."++  roots <-+    liftIO $+      getPathInfo+        nixStore+        nixVersion+        (PathInfoOptions {pioIsDerivation = isDerivation, pioIsRecursive = False})+        names++  paths <-+    liftIO $+      getPathInfo+        nixStore+        nixVersion+        (PathInfoOptions {pioIsDerivation = isDerivation, pioIsRecursive = True})+        (Installable . toText . storeNameToPath . spName <$> roots)++  let env =+        StoreEnv+          ( paths+              & toList+              & map (\p@StorePath {spName} -> (spName, p))+              & HM.fromList+          )+          (roots <&> spName)+  cb env  seLookup :: StoreEnv s a -> StoreName s -> StorePath s (StoreName s) a seLookup StoreEnv {sePaths} name =