spy 0.4 → 0.5
raw patch · 5 files changed
+62/−71 lines, 5 filesdep +fsnotifydep +system-filepathdep +timedep −hfsevents
Dependencies added: fsnotify, system-filepath, time
Dependencies removed: hfsevents
Files
- README.md +20/−9
- spy.cabal +11/−7
- src/Main.hs +1/−1
- src/Spy/Watcher.hs +28/−49
- tests/Tests.hs +2/−5
README.md view
@@ -1,7 +1,7 @@ Spy === -Spy is a compact file system watcher for Mac OS X using the [File System Events API](https://developer.apple.com/library/mac/#documentation/Darwin/Conceptual/FSEvents_ProgGuide/Introduction/Introduction.html) via [hfsevents](https://github.com/luite/hfsevents).+Spy is a compact file system watcher for Mac OS X and Linux (Note: Should work on Windows but is currently untested). Usage@@ -28,8 +28,11 @@ The default format is the full path to the modified file followed by a newline. To make it easier to parse the output, the `--format=json` changes the output to be printed formatted as a JSON object (again followed by a newline). $> spy watch --format=json .- {"path": "path/to/modified/file", "flags": ["ItemModified"], "id": 123143234}+ {"path":"/path/to/modified.file","flag":"Added","time":"2012-12-20 11:26:56.859456 UTC"} +Flag is one of "Added", "Modified", "Removed".++ For directories the following options apply: An optional second argument can be used to filter the files in the given directory using a glob pattern:@@ -54,17 +57,15 @@ Installation ============ -Spy only works on Mac OS X >= 10.7 (Lion and above)!- Binary distribution ------------------- -The binary distribution contains a 64bit binary compiled for Mac OS X > 10.7.+The binary distribution contains a 64bit binary compiled for Mac OS X or Linux. -Download the tarball and run "make install" to copy the binary and the man page into the correct target directories:+Download the tarball from https://bitbucket.org/ssaasen/spy/downloads and run "make install" to copy the binary and the man page into the correct target directories: - $> curl -OL https://bitbucket.org/ssaasen/spy/downloads/spy-osx-x86_64-v0.4.tar.gz- $> tar xfz spy-osx-x86_64-v0.4.tar.gz+ $> curl -OL https://bitbucket.org/ssaasen/spy/downloads/spy-PLATFORM-ARCH-VERSION.tar.gz+ $> tar xfz spy-PLATFORM-ARCH-VERSION.tar.gz $> cd spy $> make install @@ -74,8 +75,18 @@ Source distribution ------------------- -To install spy from source you need the Haskell platform installed and cabal-install available on your $PATH:+You need the Haskell platform installed and cabal-install available on your $PATH. +To install spy from hackage simply run:++ $> cabal install spy++Done!++To install spy from git you need the Haskell platform installed and cabal-install available on your $PATH:++ $> git@bitbucket.org:ssaasen/spy.git+ $> cd spy $> cabal install --only-dependencies $> cabal configure $> cabal build
spy.cabal view
@@ -1,11 +1,11 @@ name: spy-version: 0.4+version: 0.5 license: BSD3 license-file: LICENSE author: Stefan Saasen maintainer: stefan@saasen.me-synopsis: A compact file system watcher for Mac OS X-category: Apple, Development+synopsis: A compact file system watcher for Mac OS X, Linux and Windows+category: Development description: Spy can be used to watch for file changes and to either report the modified files or run a command if files change. It can be used to trigger compilation, to run tests or start a deployment. homepage: https://bitbucket.org/ssaasen/spy bug-reports: https://bitbucket.org/ssaasen/spy/issues@@ -30,26 +30,30 @@ test-framework-hunit, HUnit, QuickCheck >= 2.4.0.1,- hfsevents >= 0.1.3,+ fsnotify >= 0.0.4, cmdargs >= 0.10, filepath >= 1.3, filemanip >= 0.3.6.2, process >= 1.1, json >= 0.7,- directory >= 1.1+ directory >= 1.1,+ system-filepath >= 0.4.7,+ time >= 1.4 executable spy main-is: Main.hs other-modules: Spy.Watcher build-depends: base < 5 && >= 3,- hfsevents >= 0.1.3,+ fsnotify >= 0.0.4, cmdargs >= 0.10, filepath >= 1.3, filemanip >= 0.3.6.2, process >= 1.1, json >= 0.7,- directory >= 1.1+ directory >= 1.1,+ system-filepath >= 0.4.7,+ time >= 1.4 ghc-options: -Wall
src/Main.hs view
@@ -6,7 +6,7 @@ import Spy.Watcher version :: String-version = "spy v0.4, (C) Stefan Saasen"+version = "spy v0.5, (C) Stefan Saasen" hiddenOpts x = x &= help "Set to true if hidden files/directories should be included" &= name "i" &= typ "BOOL"
src/Spy/Watcher.hs view
@@ -11,18 +11,16 @@ ,containsHiddenPathElement ) where -import System.OSX.FSEvents+import System.FSNotify import System.Console.CmdArgs import System.Cmd import System.Exit import System.IO (stderr, hPrint) import System.FilePath.GlobPattern import System.FilePath (splitDirectories, takeFileName)-import Control.Monad (unless)-import Control.Exception (bracket)+import Filesystem.Path.CurrentOS (encodeString, decodeString)+import Data.Time.Clock(UTCTime) import Data.Maybe (fromMaybe, maybeToList)-import Data.Bits-import Data.Word import Text.JSON -- | The output format when Spy prints out changes to STDOUT@@ -48,15 +46,15 @@ -- | Register for FS events using the given Spy config. spy :: Spy -> IO String-spy config = bracket- (eventStreamCreate [dir config] 1.0 True True True $ handleEvent config)- eventStreamDestroy- (\_ -> getLine)+spy config = withManager $ \wm ->+ watchTree wm (decodeString $ dir config)+ (not . skipEvent config . eventPath)+ (handleEvent config) >>+ getLine -- | Handle the FS event based on the current Spy run configuration handleEvent :: Spy -> Event -> IO ()-handleEvent config@Run{..} event =- unless (skipEvent config event) $+handleEvent Run{..} event = runCommand command pathAsArg >>= \exit -> case exit of ExitSuccess -> return ()@@ -65,8 +63,7 @@ Nothing else Just (eventPath event) -handleEvent config@Watch{..} event =- unless (skipEvent config event) $+handleEvent Watch{..} event = putStrLn $ (outputHandler $ fromMaybe Plain format) event -- =================================================================================@@ -86,20 +83,33 @@ outputHandler :: Format -> Printer outputHandler Json = \event -> encode $ makeObj [ ("path", showJSON $ eventPath event),- ("id", showJSON $ eventId event),- ("flags", showJSONs $ showEventFlags $ eventFlags event)]+ ("flag", showJSON $ eventType event),+ ("time", showJSON . show $ eventTime event)] outputHandler Plain = eventPath -- | Skip events based on the configuration given-skipEvent :: Spy -> Event -> Bool-skipEvent config event = skipHidden || skipNonMatchingGlob+skipEvent :: Spy -> FilePath -> Bool+skipEvent config path = skipHidden || skipNonMatchingGlob where skipHidden = let includeHiddenfiles = hidden config in not includeHiddenfiles && containsHiddenPathElement path skipNonMatchingGlob = maybe False (not . matchesFile path) $ glob config- path = eventPath event +eventTime :: Event -> UTCTime+eventTime (Added _ t) = t+eventTime (Modified _ t) = t+eventTime (Removed _ t) = t +eventPath :: Event -> FilePath+eventPath (Added fp _) = encodeString fp+eventPath (Modified fp _) = encodeString fp+eventPath (Removed fp _) = encodeString fp++eventType :: Event -> FilePath+eventType (Added _ _) = "Added"+eventType (Modified _ _) = "Modified"+eventType (Removed _ _) = "Removed"+ matchesFile :: FilePath -> GlobPattern -> Bool matchesFile path glob' = takeFileName path ~~ glob' @@ -109,34 +119,3 @@ isHidden name' = case name' of (x:_) -> x == '.' _ -> False---- =================================================================================--- The following code is taken from:--- https://github.com/luite/hfsevents/blob/master/test/trace.hs--- Copyright (c) 2012, Luite Stegeman-showEventFlags :: Word64 -> [String]-showEventFlags fl = map fst . filter hasFlag $ flagList- where hasFlag (_,f) = fl .&. f /= 0---flagList :: [(String, Word64)]-flagList = [ ("MustScanSubDirs" , 0x00000001)- , ("UserDropped" , 0x00000002)- , ("KernelDropped" , 0x00000004)- , ("EventIdsWrapped" , 0x00000008)- , ("HistoryDone" , 0x00000010)- , ("RootChanged" , 0x00000020)- , ("Mount" , 0x00000040)- , ("Unmount" , 0x00000080)- , ("ItemCreated" , 0x00000100)- , ("ItemRemoved" , 0x00000200)- , ("ItemInodeMetaMod" , 0x00000400)- , ("ItemRenamed" , 0x00000800)- , ("ItemModified" , 0x00001000)- , ("ItemFinderInfoMod" , 0x00002000)- , ("ItemChangeOwner" , 0x00004000)- , ("ItemXattrMod" , 0x00008000)- , ("ItemIsFile" , 0x00010000)- , ("ItemIsDir" , 0x00020000)- , ("ItemIsSymlink" , 0x00040000)- ]
tests/Tests.hs view
@@ -4,7 +4,6 @@ import qualified Test.HUnit as H import Data.Maybe-import System.OSX.FSEvents import Spy.Watcher import Test.QuickCheck hiding ((.&.)) import Test.Framework (Test, defaultMain, testGroup)@@ -22,7 +21,7 @@ test_skipEventHidden = H.assertBool "Skip path if hidden directory and showing hidden files is not enabled"- (skipEvent (mockWatch {hidden = False}) (mockEvent "/a/b/.git/refs"))+ (skipEvent (mockWatch {hidden = False}) "/a/b/.git/refs") test_containsHiddenPathElement = H.assertBool "Should identify hidden directory"@@ -32,9 +31,7 @@ mockWatch :: Spy mockWatch = Watch { dir = ".", glob = Nothing, format = Nothing, hidden = False } -mockEvent :: FilePath -> Event-mockEvent path = Event path 0x00000001 0x00000001-------------------------------------------------------------------------+-- =========================================================== -- Test harness main :: IO ()