packages feed

turtle 1.3.6 → 1.4.0

raw patch · 5 files changed

+142/−59 lines, 5 files

Files

CHANGELOG.md view
@@ -1,3 +1,14 @@+1.4.0++* BREAKING CHANGE: Remove unnecessary `Maybe` from type of `single`+* BREAKING CHANGE: Consolidate `searchable` and `executable`+* `stream{,WithErr}` now throws an `ExitCode` on failure++1.3.6++* Build against `ghc-8.2`+* Relax upper bound on `optparse-applicative` and `foldl`+ 1.3.5  * Increase upper bound on `foldl`
src/Turtle/Bytes.hs view
@@ -485,9 +485,18 @@     -- ^ Chunks of bytes read from process output inshell cmd = stream (Process.shell (Data.Text.unpack cmd)) +waitForProcessThrows :: Process.ProcessHandle -> IO ()+waitForProcessThrows ph = do+    exitCode <- Process.waitForProcess ph+    case exitCode of+        ExitSuccess   -> return ()+        ExitFailure _ -> Exception.throwIO exitCode+ {-| `stream` generalizes `inproc` and `inshell` by allowing you to supply your     own custom `CreateProcess`.  This is for advanced users who feel comfortable     using the lower-level @process@ API++    Throws an `ExitCode` exception if the command returns a non-zero exit code -} stream     :: Process.CreateProcess@@ -527,11 +536,13 @@         (Managed.managed (\k ->             Exception.mask (\restore ->                 Async.withAsync (feedIn restore) k ) ))-    inhandle hOut <|> (liftIO (Process.waitForProcess ph *> halt a) *> empty)+    inhandle hOut <|> (liftIO (waitForProcessThrows ph *> halt a) *> empty)  {-| `streamWithErr` generalizes `inprocWithErr` and `inshellWithErr` by allowing     you to supply your own custom `CreateProcess`.  This is for advanced users     who feel comfortable using the lower-level @process@ API++    Throws an `ExitCode` exception if the command returns a non-zero exit code -} streamWithErr     :: Process.CreateProcess@@ -611,12 +622,13 @@             _ <- r             return ()     let waitAll = STM.atomically (Async.waitSTM a `also` (Async.waitSTM b `also` Async.waitSTM c))-    drain <|> (liftIO (Process.waitForProcess ph *> waitAll) *> empty)+    drain <|> (liftIO (waitForProcessThrows ph *> waitAll) *> empty)  {-| Run a command using the shell, streaming @stdout@ and @stderr@ as chunks of     `ByteString`.  Chunks from @stdout@ are wrapped in `Right` and chunks from-    @stderr@ are wrapped in `Left`.  This does /not/ throw an exception if the-    command returns a non-zero exit code+    @stderr@ are wrapped in `Left`.++    Throws an `ExitCode` exception if the command returns a non-zero exit code -} inprocWithErr     :: Text@@ -633,11 +645,12 @@  {-| Run a command line using the shell, streaming @stdout@ and @stderr@ as     chunks of `ByteString`.  Chunks from @stdout@ are wrapped in `Right` and-    chunks from @stderr@ are wrapped in `Left`.  This does /not/ throw an-    exception if the command returns a non-zero exit code+    chunks from @stderr@ are wrapped in `Left`.      This command is more powerful than `inprocWithErr`, but highly vulnerable to     code injection if you template the command line with untrusted input++    Throws an `ExitCode` exception if the command returns a non-zero exit code -} inshellWithErr     :: Text
src/Turtle/Prelude.hs view
@@ -214,7 +214,7 @@     , systemStrictWithErr      -- * Permissions-    , Permissions+    , Permissions(..)     , chmod     , getmod     , setmod@@ -222,8 +222,7 @@     , readable, nonreadable     , writable, nonwritable     , executable, nonexecutable-    , searchable, nonsearchable-    , ooo,roo,owo,oox,oos,rwo,rox,ros,owx,rwx,rws+    , ooo,roo,owo,oox,rwo,rox,owx,rwx      -- * File size     , du@@ -307,7 +306,7 @@     lookupEnv, #endif     getEnvironment )-import System.Directory (Permissions)+import qualified System.Directory import qualified System.Directory as Directory import System.Exit (ExitCode(..), exitWith) import System.IO (Handle, hClose)@@ -669,9 +668,18 @@     -- ^ Lines of standard output inshell cmd = stream (Process.shell (unpack cmd)) +waitForProcessThrows :: Process.ProcessHandle -> IO ()+waitForProcessThrows ph = do+    exitCode <- Process.waitForProcess ph+    case exitCode of+        ExitSuccess   -> return ()+        ExitFailure _ -> Control.Exception.throwIO exitCode+ {-| `stream` generalizes `inproc` and `inshell` by allowing you to supply your     own custom `CreateProcess`.  This is for advanced users who feel comfortable     using the lower-level @process@ API++    Throws an `ExitCode` exception if the command returns a non-zero exit code -} stream     :: Process.CreateProcess@@ -706,11 +714,13 @@     a <- using         (managed (\k ->             mask (\restore -> withAsync (feedIn restore) (restore . k))))-    inhandle hOut <|> (liftIO (Process.waitForProcess ph *> halt a) *> empty)+    inhandle hOut <|> (liftIO (waitForProcessThrows ph *> halt a) *> empty)  {-| `streamWithErr` generalizes `inprocWithErr` and `inshellWithErr` by allowing     you to supply your own custom `CreateProcess`.  This is for advanced users     who feel comfortable using the lower-level @process@ API++    Throws an `ExitCode` exception if the command returns a non-zero exit code -} streamWithErr     :: Process.CreateProcess@@ -783,12 +793,13 @@             _ <- r             return ()     let waitAll = STM.atomically (waitSTM a `also` (waitSTM b `also` waitSTM c))-    drain <|> (liftIO (Process.waitForProcess ph *> waitAll) *> empty)+    drain <|> (liftIO (waitForProcessThrows ph *> waitAll) *> empty)  {-| Run a command using the shell, streaming @stdout@ and @stderr@ as lines of     `Text`.  Lines from @stdout@ are wrapped in `Right` and lines from @stderr@-    are wrapped in `Left`.  This does /not/ throw an exception if the command-    returns a non-zero exit code+    are wrapped in `Left`.++    Throws an `ExitCode` exception if the command returns a non-zero exit code -} inprocWithErr     :: Text@@ -802,14 +813,14 @@ inprocWithErr cmd args =     streamWithErr (Process.proc (unpack cmd) (map unpack args)) - {-| Run a command line using the shell, streaming @stdout@ and @stderr@ as lines     of `Text`.  Lines from @stdout@ are wrapped in `Right` and lines from-    @stderr@ are wrapped in `Left`.  This does /not/ throw an exception if the-    command returns a non-zero exit code+    @stderr@ are wrapped in `Left`.      This command is more powerful than `inprocWithErr`, but highly vulnerable to     code injection if you template the command line with untrusted input++    Throws an `ExitCode` exception if the command returns a non-zero exit code -} inshellWithErr     :: Text@@ -1109,11 +1120,64 @@ #endif         else output file empty ) +{-| This type is the same as @"System.Directory".`System.Directory.Permissions`@+    type except combining the `System.Directory.executable` and+    `System.Directory.searchable` fields into a single `executable` field for+    consistency with the Unix @chmod@.  This simplification is still entirely+    consistent with the behavior of "System.Directory", which treats the two+    fields as interchangeable.+-}+data Permissions = Permissions+    { _readable   :: Bool+    , _writable   :: Bool+    , _executable :: Bool+    } deriving (Eq, Read, Ord, Show)++{-| Under the hood, "System.Directory" does not distinguish between+    `System.Directory.executable` and `System.Directory.searchable`.  They both+    translate to the same `System.Posix.ownerExecuteMode` permission.  That+    means that we can always safely just set the `System.Directory.executable`+    field and safely leave the `System.Directory.searchable` field as `False`+    because the two fields are combined with (`||`) to determine whether to set+    the executable bit.+-}+toSystemDirectoryPermissions :: Permissions -> System.Directory.Permissions+toSystemDirectoryPermissions p =+    ( System.Directory.setOwnerReadable   (_readable   p)+    . System.Directory.setOwnerWritable   (_writable   p)+    . System.Directory.setOwnerExecutable (_executable p)+    ) System.Directory.emptyPermissions++fromSystemDirectoryPermissions :: System.Directory.Permissions -> Permissions+fromSystemDirectoryPermissions p = Permissions+    { _readable   = System.Directory.readable p+    , _writable   = System.Directory.writable p+    , _executable =+        System.Directory.executable p || System.Directory.searchable p+    }+ {-| Update a file or directory's user permissions -> chmod rwo        "foo.txt"  -- chmod u=rw foo.txt-> chmod executable "foo.txt"  -- chmod u+x foo.txt-> chmod nonwritable "foo.txt" -- chmod u-w foo.txt+> chmod rwo         "foo.txt"  -- chmod u=rw foo.txt+> chmod executable  "foo.txt"  -- chmod u+x foo.txt+> chmod nonwritable "foo.txt"  -- chmod u-w foo.txt++    The meaning of each permission is:++    * `readable` (@+r@ for short): For files, determines whether you can read+      from that file (such as with `input`).  For directories, determines+      whether or not you can list the directory contents (such as with `ls`).+      Note: if a directory is not readable then `ls` will stream an empty list+      of contents++    * `writable` (@+w@ for short): For files, determines whether you can write+      to that file (such as with `output`).  For directories, determines whether+      you can create a new file underneath that directory.++    * `executable` (@+x@ for short): For files, determines whether or not that+      file is executable (such as with `proc`).  For directories, determines+      whether or not you can read or execute files underneath that directory+      (such as with `input` or `proc`) -} chmod     :: MonadIO io@@ -1126,22 +1190,25 @@ chmod modifyPermissions path = liftIO (do     let path' = deslash (Filesystem.encodeString path)     permissions <- Directory.getPermissions path'-    let permissions' = modifyPermissions permissions-        changed = permissions /= permissions'-    when changed (Directory.setPermissions path' permissions')+    let permissions' = fromSystemDirectoryPermissions permissions+    let permissions'' = modifyPermissions permissions'+        changed = permissions' /= permissions''+    let permissions''' = toSystemDirectoryPermissions permissions'+    when changed (Directory.setPermissions path' permissions''')     return permissions' )  -- | Get a file or directory's user permissions getmod :: MonadIO io => FilePath -> io Permissions getmod path = liftIO (do     let path' = deslash (Filesystem.encodeString path)-    Directory.getPermissions path' )+    permissions <- Directory.getPermissions path'+    return (fromSystemDirectoryPermissions permissions))  -- | Set a file or directory's user permissions setmod :: MonadIO io => Permissions -> FilePath -> io () setmod permissions path = liftIO (do     let path' = deslash (Filesystem.encodeString path)-    Directory.setPermissions path' permissions )+    Directory.setPermissions path' (toSystemDirectoryPermissions permissions) )  -- | Copy a file or directory's permissions (analogous to @chmod --reference@) copymod :: MonadIO io => FilePath -> FilePath -> io ()@@ -1152,39 +1219,35 @@  -- | @+r@ readable :: Permissions -> Permissions-readable = Directory.setOwnerReadable True+readable p = p { _readable = True }  -- | @-r@ nonreadable :: Permissions -> Permissions-nonreadable = Directory.setOwnerReadable False+nonreadable p = p { _readable = False }  -- | @+w@ writable :: Permissions -> Permissions-writable = Directory.setOwnerWritable True+writable p = p { _writable = True }  -- | @-w@ nonwritable :: Permissions -> Permissions-nonwritable = Directory.setOwnerWritable False+nonwritable p = p { _writable = False }  -- | @+x@ executable :: Permissions -> Permissions-executable = Directory.setOwnerExecutable True+executable p = p { _executable = True }  -- | @-x@ nonexecutable :: Permissions -> Permissions-nonexecutable = Directory.setOwnerExecutable False---- | @+s@-searchable :: Permissions -> Permissions-searchable = Directory.setOwnerSearchable True---- | @-s@-nonsearchable :: Permissions -> Permissions-nonsearchable = Directory.setOwnerSearchable False+nonexecutable p = p { _executable = False }  -- | @-r -w -x@ ooo :: Permissions -> Permissions-ooo = const Directory.emptyPermissions+ooo _ = Permissions+    { _readable   = False+    , _writable   = False+    , _executable = False+    }  -- | @+r -w -x@ roo :: Permissions -> Permissions@@ -1198,10 +1261,6 @@ oox :: Permissions -> Permissions oox = executable . ooo --- | @-r -w +s@-oos :: Permissions -> Permissions-oos = searchable . ooo- -- | @+r +w -x@ rwo :: Permissions -> Permissions rwo = readable . writable . ooo@@ -1210,10 +1269,6 @@ rox :: Permissions -> Permissions rox = readable . executable . ooo --- | @+r -w +s@-ros :: Permissions -> Permissions-ros = readable . searchable . ooo- -- | @-r +w +x@ owx :: Permissions -> Permissions owx = writable . executable . ooo@@ -1222,10 +1277,6 @@ rwx :: Permissions -> Permissions rwx = readable . writable . executable . ooo --- | @+r +w +s@-rws :: Permissions -> Permissions-rws = readable . writable . searchable . ooo- {-| Time how long a command takes in monotonic wall clock time      Returns the duration alongside the return value@@ -1256,15 +1307,16 @@    True <- testfile path' -  let handler :: IOError -> IO Permissions+  let handler :: IOError -> IO Bool       handler e =           if isPermissionError e || isDoesNotExistError e-              then return Directory.emptyPermissions+              then return False               else throwIO e -  perms <- liftIO (getmod path' `catchIOError` handler)+  let getIsExecutable = fmap _executable (getmod path')+  isExecutable <- liftIO (getIsExecutable `catchIOError` handler) -  guard (Directory.executable perms)+  guard isExecutable   return path'  {-| Sleep for the given duration@@ -1872,11 +1924,11 @@ -- > main = do -- >   Just directory <- single (inshell "pwd" empty) -- >   print directory-single :: MonadIO io => Shell a -> io (Maybe a)+single :: MonadIO io => Shell a -> io a single s = do     as <- fold s Control.Foldl.list     case as of-        [a] -> return (Just a)+        [a] -> return a         _   -> do             let msg = format ("single: expected 1 line of input but there were "%d%" lines of input") (length as)             die msg
src/Turtle/Tutorial.hs view
@@ -1818,3 +1818,10 @@ -- /Answer:/ Use `runManaged`, `sh`, or (`<|>`) (all resources acquired in the -- left stream will close before beginning the right stream).  Alternatively, -- use `with` to acquire a resource for a limited scope.+--+-- /Question:/ How do I use @turtle@ to run another shell as a subprocess?+--+-- /Answer:/ Use `Turtle.system` in conjunction with the `process` library,+-- like this:+--+-- > Turtle.system (System.Process.proc "/bin/sh" []) empty
turtle.cabal view
@@ -1,5 +1,5 @@ Name: turtle-Version: 1.3.6+Version: 1.4.0 Cabal-Version: >=1.10 Build-Type: Simple License: BSD3