Skip to content

Commit

Permalink
Merge pull request #46 from expipiplus1/attrs
Browse files Browse the repository at this point in the history
Implement attribute filtering
  • Loading branch information
expipiplus1 authored Nov 6, 2020
2 parents 67df858 + aa41087 commit 25e1cb3
Show file tree
Hide file tree
Showing 10 changed files with 121 additions and 30 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
matrix:
os: [ubuntu-latest]
cabal: [latest]
ghc: [8.6.5, 8.8.4, 8.10.2]
ghc: [8.8.4, 8.10.2]
fail-fast: false

steps:
Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

## WIP

## [0.2.3] - 2020-11-06

- Implement filtering updates based on binding name
- Better error message output on parse failure
- Drop support for GHC 8.6

## [0.2.2] - 2020-11-03

- Require hnix version 0.11 with several important bugfixes
Expand Down
30 changes: 25 additions & 5 deletions app/Main.hs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
{-# OPTIONS_GHC -Wno-orphans #-}

module Main where
module Main
( main
) where

import Data.Foldable
import qualified Data.Text.IO as T
Expand All @@ -14,6 +17,7 @@ import Text.ParserCombinators.ReadP ( char
, readS_to_P
, sepBy
)
import Text.Regex.TDFA
import Update.Nix.FetchGit
import Update.Nix.FetchGit.Types

Expand Down Expand Up @@ -43,7 +47,8 @@ env Options {..} =
Normal -> sayErr
Quiet -> sayErr
updateLocations = [ (l, c) | Position l c <- location ]
in Env { .. }
attrPatterns = attribute
in Env { .. }

----------------------------------------------------------------
-- Options
Expand All @@ -52,7 +57,9 @@ env Options {..} =
data Options w = Options
{ verbose :: w ::: Bool <!> "False"
, quiet :: w ::: Bool <!> "False"
, location :: w ::: [Position] <?> "Source location to limit updates to"
, location :: w ::: [Position] <?> "Source location to limit updates to, Combined using inclusive or"
, attribute
:: w ::: [Regex] <?> "Pattern (POSIX regex) to limit updates to expressions under matching names in attrsets and let bindings. Combined using inclusing or, if this isn't specified then no expressions will be filtered by attribute name"
}
deriving stock Generic

Expand All @@ -70,7 +77,9 @@ optParser =
versionOption
<*> ( (,)
<$> (unwrap <$> parseRecordWithModifiers defaultModifiers
{ shortNameModifier = firstLetter
{ shortNameModifier = \case
"attribute" -> Just 'A'
n -> firstLetter n
}
)
<*> many
Expand All @@ -88,7 +97,6 @@ optParser =
(long "version" <> help ("print " <> versionString))

instance ParseRecord (Options Wrapped)
deriving instance Show (Options Unwrapped)

data Position = Position Int Int
deriving Show
Expand All @@ -101,3 +109,15 @@ instance Read Position where

instance ParseField Position where
metavar _ = "LINE:COL"

instance Read Regex where
readsPrec _ s = case makeRegexM s of
Nothing -> []
Just r -> [(r, "")]

instance ParseField Regex where
metavar _ = "REGEX"
readField = eitherReader makeRegexM

instance (e ~ String) => MonadFail (Either e) where
fail = Left
6 changes: 4 additions & 2 deletions package.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: update-nix-fetchgit
version: "0.2.2"
version: "0.2.3"
synopsis: A program to update fetchgit values in Nix expressions
description: |
This command-line utility is meant to be used by people maintaining Nix
Expand Down Expand Up @@ -29,9 +29,10 @@ executables:
source-dirs: app
ghc-options: -threaded -rtsopts -with-rtsopts=-N
dependencies:
- base
- base >= 4.13
- optparse-applicative
- optparse-generic >= 1.4.2
- regex-tdfa
- say
- text >= 1.2
- update-nix-fetchgit
Expand All @@ -49,6 +50,7 @@ library:
- monad-validate
- mtl
- process >= 1.2
- regex-tdfa
- syb
- template-haskell
- text >= 1.2
Expand Down
59 changes: 47 additions & 12 deletions src/Update/Nix/FetchGit.hs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@ module Update.Nix.FetchGit
) where

import Control.Monad ( when )
import Control.Monad.Reader ( MonadReader(ask) )
import Control.Monad.Validate ( MonadValidate(tolerate) )
import Data.Fix
import Data.Foldable
import Data.Functor
import Data.Maybe
import Data.Text ( Text
, pack
Expand All @@ -21,13 +24,11 @@ import Nix.Comments
import Nix.Expr
import Nix.Match.Typed
import System.Exit
import Text.Regex.TDFA
import Update.Nix.FetchGit.Types
import Update.Nix.FetchGit.Utils
import Update.Nix.Updater
import Update.Span
import Control.Monad.Validate ( MonadValidate(tolerate) )
import Data.Functor
import Control.Monad.Reader ( MonadReader(ask) )

--------------------------------------------------------------------------------
-- Tying it all together
Expand Down Expand Up @@ -57,7 +58,7 @@ updatesFromText t = do
tree <- do
expr <- fromEither $ ourParseNixText t
findUpdates (getComment nixLines) expr
us <- evalUpdates tree
us <- evalUpdates =<< filterUpdates tree
case us of
[] -> logVerbose "Made no updates"
[_] -> logVerbose "Made 1 update"
Expand All @@ -71,19 +72,53 @@ updatesFromText t = do
findUpdates :: (NExprLoc -> Maybe Comment) -> NExprLoc -> M FetchTree
findUpdates getComment e = do
Env {..} <- ask
-- First of all, if this expression doesn't enclose the requested position,
-- return an empty tree
-- Then check against all the updaters, if they match we have a leaf
if not (null updateLocations || any (containsPosition e) updateLocations)
then pure $ Node Nothing []
else
let updaters = ($ e) <$> fetchers getComment
let
updaters = ($ e) <$> fetchers getComment
bindingTrees = \case
NamedVar p e' _ | Just t <- pathText p ->
(: []) . (Just t, ) <$> findUpdates getComment e'
b ->
traverse (fmap (Nothing, ) . findUpdates getComment) . toList $ b
in
case asum updaters of
Just u -> UpdaterNode <$> u
Nothing -> case e of
[matchNixLoc|{ _version = ^version; }|] ->
Node version
<$> traverse (findUpdates getComment) (toList (unFix e))
_ -> Node Nothing
<$> traverse (findUpdates getComment) (toList (unFix e))
[matchNixLoc|{ _version = ^version; }|] | NSet_ _ _ bs <- unFix e ->
Node version . concat <$> traverse bindingTrees bs
[matchNixLoc|let _version = ^version; in ^x|]
| NLet_ _ bs _ <- unFix e -> do
bs' <- concat <$> traverse bindingTrees bs
x' <- findUpdates getComment x
pure $ Node version ((Nothing, x') : bs')
_ -> Node Nothing <$> traverse
(fmap (Nothing, ) . findUpdates getComment)
(toList (unFix e))

filterUpdates :: FetchTree -> M FetchTree
filterUpdates t = do
Env {..} <- ask
let matches s = any (`match` s) attrPatterns
-- If we're in a branch, include any bindings which match unconditionally,
-- otherwise recurse
-- If we reach a leaf, return empty because it hasn't been included by a
-- binding yet
let go = \case
Node v cs -> Node
v
[ (n, c')
| (n, c) <- cs
, let c' = if maybe False matches n then c else go c
]
UpdaterNode _ -> Node Nothing []
-- If there are no patterns, don't do any filtering
pure $ if null attrPatterns then t else go t


evalUpdates :: FetchTree -> M [SpanUpdate]
evalUpdates = fmap snd . go
Expand All @@ -92,7 +127,7 @@ evalUpdates = fmap snd . go
go = \case
UpdaterNode (Updater u) -> u
Node versionExpr cs -> do
(ds, ss) <- unzip . catMaybes <$> traverse (tolerate . go) cs
(ds, ss) <- unzip . catMaybes <$> traverse (tolerate . go . snd) cs
-- Update version string with the maximum of versions in the children
let latestDate = maximumMay (catMaybes ds)
pure
Expand All @@ -101,7 +136,7 @@ evalUpdates = fmap snd . go
| Just d <- pure latestDate
, Just v <- pure versionExpr
]
<> concat ss
<> concat ss
)

----------------------------------------------------------------
Expand Down
6 changes: 4 additions & 2 deletions src/Update/Nix/FetchGit/Types.hs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import Data.Monoid
import Data.Text ( Text )
import Data.Time ( Day )
import Nix.Expr ( NExprLoc )
import Text.Regex.TDFA ( Regex )
import Update.Nix.FetchGit.Warning
import Update.Span

Expand All @@ -28,8 +29,9 @@ asWarnings m = unValidateT MNothing m <&> \case
Right (MNothing, a) -> (mempty, Just a)

data Env = Env
{ sayLog :: Verbosity -> Text -> IO ()
{ sayLog :: Verbosity -> Text -> IO ()
, updateLocations :: [(Int, Int)]
, attrPatterns :: [Regex]
}

data Verbosity
Expand All @@ -45,7 +47,7 @@ newtype Updater = Updater
-- parsing, but which only contains the information we care about.
data FetchTree
= Node { nodeVersionExpr :: Maybe NExprLoc
, nodeChildren :: [FetchTree]
, nodeChildren :: [(Maybe Text, FetchTree)]
}
| UpdaterNode Updater

Expand Down
27 changes: 26 additions & 1 deletion src/Update/Nix/FetchGit/Utils.hs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ module Update.Nix.FetchGit.Utils
, prettyRepoLocation
, quoteString
, extractFuncName
, pathText
, exprText
, exprBool
, exprSpan
Expand Down Expand Up @@ -101,6 +102,30 @@ extractFuncName (AnnE _ (NSym name)) = Just name
extractFuncName (AnnE _ (NSelect _ (NE.last -> StaticKey name) _)) = Just name
extractFuncName _ = Nothing

pathText :: NAttrPath r -> Maybe Text
pathText = fmap (T.concat . toList) . traverse e
where
e :: NKeyName r -> Maybe Text
e = \case
StaticKey s -> Just s
DynamicKey (Plain s) -> t s
DynamicKey EscapedNewline -> Just "\n"
DynamicKey (Antiquoted _) -> Nothing
t :: NString r -> Maybe Text
t =
fmap T.concat
. traverse a
. (\case
DoubleQuoted as -> as
Indented _ as -> as
)
a :: Antiquoted Text r -> Maybe Text
a = \case
Plain s -> pure s
EscapedNewline -> pure "\n"
Antiquoted _ -> Nothing


-- Takes an ISO 8601 date and returns just the day portion.
parseISO8601DateToDay :: Text -> Either Warning Day
parseISO8601DateToDay t =
Expand All @@ -110,7 +135,7 @@ parseISO8601DateToDay t =
(parseTimeM False defaultTimeLocale "%Y-%m-%d" justDate)

formatWarning :: Warning -> Text
formatWarning (CouldNotParseInput doc) = tShow doc
formatWarning (CouldNotParseInput doc) = doc
formatWarning (MissingAttr attrName) =
"Error: The \"" <> attrName <> "\" attribute is missing."
formatWarning (DuplicateAttrs attrName) =
Expand Down
1 change: 0 additions & 1 deletion src/Update/Nix/FetchGit/Warning.hs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ module Update.Nix.FetchGit.Warning

import Data.Text
import Nix.Expr
import Data.Void

data Warning = CouldNotParseInput Text
| MissingAttr Text
Expand Down
8 changes: 4 additions & 4 deletions tests/Samples.hs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ module Samples where
import Test.Tasty (TestTree, testGroup)
import Test.Tasty.Golden (goldenVsFile)
import System.FilePath ((</>))
import Data.Bool (bool)
import Data.Maybe (mapMaybe)

import qualified Data.List
Expand All @@ -30,6 +29,7 @@ import Data.Text.IO (hPutStrLn)
-- * perform update
-- * adjust @url@ to point to the expected one
-- * copy file back to @tests/test_rec_sets.out.nix@ so it be compared to @expected.nix@
runTest :: String -> IO ()
runTest f =
System.IO.Temp.withSystemTempDirectory "test-update-nix-fetchgit" $ \dir ->
System.IO.Temp.withSystemTempDirectory "test-update-nix-fetchgit-store" $ \storeDir -> do
Expand Down Expand Up @@ -60,15 +60,15 @@ runTest f =
(System.Process.shell ("nix-store --init"))
mempty

let env = Env (const (Data.Text.IO.hPutStrLn System.IO.stderr)) []
let env = Env (const (Data.Text.IO.hPutStrLn System.IO.stderr)) [] []
Update.Nix.FetchGit.processFile env (dir </> inBase)

replaceFile (dir </> inBase) (Data.Text.pack dir) "/tmp/nix-update-fetchgit-test"

System.Directory.copyFile (dir </> inBase) f
where
replaceFile f what with =
Data.Text.IO.readFile f >>= Data.Text.IO.writeFile f . Data.Text.replace what with
replaceFile f' what with =
Data.Text.IO.readFile f' >>= Data.Text.IO.writeFile f' . Data.Text.replace what with

test_derivation :: IO TestTree
test_derivation = do
Expand Down
6 changes: 4 additions & 2 deletions update-nix-fetchgit.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ cabal-version: 1.12
-- see: https://github.com/sol/hpack

name: update-nix-fetchgit
version: 0.2.2
version: 0.2.3
synopsis: A program to update fetchgit values in Nix expressions
description: This command-line utility is meant to be used by people maintaining Nix
expressions that fetch files from Git repositories. It automates the process
Expand Down Expand Up @@ -87,6 +87,7 @@ library
, monad-validate
, mtl
, process >=1.2
, regex-tdfa
, syb
, template-haskell
, text >=1.2
Expand All @@ -104,9 +105,10 @@ executable update-nix-fetchgit
default-extensions: DataKinds DefaultSignatures DeriveAnyClass DeriveDataTypeable DeriveGeneric DerivingStrategies FlexibleContexts FlexibleInstances GADTs LambdaCase MultiParamTypeClasses OverloadedStrings PolyKinds RankNTypes RecordWildCards ScopedTypeVariables StandaloneDeriving TemplateHaskellQuotes TupleSections TypeApplications TypeFamilies TypeOperators ViewPatterns
ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N
build-depends:
base
base >=4.13
, optparse-applicative
, optparse-generic >=1.4.2
, regex-tdfa
, say
, text >=1.2
, update-nix-fetchgit
Expand Down

0 comments on commit 25e1cb3

Please sign in to comment.