packages feed

sync-mht (empty) → 0.2.0.0

raw patch · 4 files changed

+255/−0 lines, 4 filesdep +arraydep +basedep +base16-bytestringsetup-changed

Dependencies added: array, base, base16-bytestring, byteable, bytestring, cereal, containers, cryptohash, directory, filepath, io-streams, mtl, process, text, transformers, unix

Files

+ LICENSE view
@@ -0,0 +1,22 @@+The MIT License (MIT)++Copyright (c) 2015 Emin Karayel++Permission is hereby granted, free of charge, to any person obtaining a copy+of this software and associated documentation files (the "Software"), to deal+in the Software without restriction, including without limitation the rights+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+copies of the Software, and to permit persons to whom the Software is+furnished to do so, subject to the following conditions:++The above copyright notice and this permission notice shall be included in all+copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+SOFTWARE.+
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ src/main/hs/Main.hs view
@@ -0,0 +1,164 @@+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE OverloadedStrings #-}+import System.Environment+import System.Process+import System.IO+import System.IO.Error+import System.Console.GetOpt+import Paths_sync_mht (version)+import Data.List+import Data.Version (showVersion)++import Sync.MerkleTree.CommTypes+import Sync.MerkleTree.Sync++data SyncOptions+    = SyncOptions+      { so_source :: Maybe FilePath+      , so_destination :: Maybe FilePath+      , so_remote :: Maybe String+      , so_ignore :: [FilePath]+      , so_add :: Bool+      , so_update :: Bool+      , so_delete :: Bool+      , so_help :: Bool+      , so_nonOptions :: [String]+      }++defaultSyncOptions :: SyncOptions+defaultSyncOptions =+    SyncOptions+    { so_source = Nothing+    , so_destination = Nothing+    , so_remote = Nothing+    , so_ignore = []+    , so_add = False+    , so_update = False+    , so_delete = False+    , so_help = False+    , so_nonOptions = []+    }+++toClientServerOptions :: SyncOptions -> ClientServerOptions+toClientServerOptions so =+     ClientServerOptions+     { cs_add = so_add so+     , cs_update = so_update so+     , cs_delete = so_delete so+     , cs_ignore = so_ignore so+     }++optDescriptions :: [OptDescr (SyncOptions -> SyncOptions)]+optDescriptions =+    [ Option ['s'] ["source"] (ReqArg (\fp so -> so { so_source = Just fp }) "DIR")+        "directory to copy files from (source) (this option is required)"+    , Option ['d'] ["destination"] (ReqArg (\fp so -> so { so_destination = Just fp }) "DIR")+        "directory to copy files to (destination) (this option is required)"+    , Option ['r'] ["remote-shell"] (ReqArg (\s so -> so { so_remote = Just s }) "CMD")+        "synchroize with a remote-site using a remote command execution tool (like ssh or docker)"+    , Option ['i'] ["ignore"] (ReqArg (\fp so -> so { so_ignore = fp:(so_ignore so) }) "PATH") ( concat+        [ "files or directories (relative to the source and destination directories) that are to "+        , "be ignored during synchroization (this option can be given multiple times)"+        ])+    , Option ['a'] ["add"] (NoArg (\so -> so { so_add = True })) ( concat+        [ "copy files from the source directory if there is corresponding file inside "+        , "the destination directory"+        ])+    , Option ['u'] ["update"] (NoArg (\so -> so { so_update = True })) ( concat+        [ "overwrite existing files inside the destination directory if their content does not "+        , "match the respective files inside the source directory"+        ])+    , Option [] ["delete"] (NoArg (\so -> so { so_delete = True })) ( concat+        [ "delete files inside the destination directory if there is no corresponding file inside "+        , "the source directory"+        ])+    , Option ['h'] ["help"] (NoArg (\so -> so { so_help = True })) "shows usage information"+    ]++parseNonOption :: String -> (SyncOptions -> SyncOptions)+parseNonOption s so = so { so_nonOptions = s:(so_nonOptions so) }++toSyncOptions :: [(SyncOptions -> SyncOptions)] -> SyncOptions+toSyncOptions = foldl (flip id) defaultSyncOptions++putError :: String -> IO ()+putError = hPutStrLn stderr++printUsageInfo :: [String] -> IO ()+printUsageInfo prefix = mapM_ putError (prefix ++ [usageInfo header optDescriptions] ++ [details])+    where+      header = unlines+          [ "sync-mht version " ++ showVersion version+          , ""+          , "Usage: sync-mht [OPTIONS..]"+          ]+      details = concat+          [ "Note: If the --remote-shell option has been provided, exactly one of the directories "+          , "must be prepended with 'remote:' - indicating a folder on the site, accessible with "+          , "the provided remote shell command."+          ]++_HIDDENT_CLIENT_MODE_OPTION_ :: String+_HIDDENT_CLIENT_MODE_OPTION_ = "--hidden-client-mode-option"++main :: IO ()+main = flip catchIOError (putError . show) $+    do args <- getArgs+       let parsedOpts = getOpt (ReturnInOrder parseNonOption) optDescriptions args+       case () of+         () | [_HIDDENT_CLIENT_MODE_OPTION_] == args -> child+            | (options,[],[]) <- parsedOpts -> run $ toSyncOptions options+            | (_,_,errs) <- parsedOpts -> printUsageInfo errs++data Side+    = Remote FilePath+    | Local FilePath++parseFilePath :: FilePath -> Side+parseFilePath fp+    | Just rest <- stripPrefix "remote:" fp = Remote rest+    | otherwise = Local fp++run :: SyncOptions -> IO ()+run so+    | so_help so =+        printUsageInfo []+    | not (null (so_nonOptions so)) =+        printUsageInfo ["Could not understand the following options: " ++ show (so_nonOptions so)]+    | Just source <- so_source so, Just destination <- so_destination so =+        case (parseFilePath source, parseFilePath destination) of+          (Remote _, Remote _) -> printUsageInfo [doubleRemote]+          (Local source', Local destination')+              | Just _ <- so_remote so -> printUsageInfo [missingRemote]+              | otherwise -> local cs source' destination'+          (Remote source', Local destination')+              | Just remoteCmd <- so_remote so -> runParent cs remoteCmd source' destination' FromRemote+              | otherwise -> printUsageInfo [missingRemoteCmd]+          (Local source', Remote destination')+              | Just remoteCmd <- so_remote so -> runParent cs remoteCmd source' destination' ToRemote+              | otherwise -> printUsageInfo [missingRemoteCmd]+    | otherwise =+        do let missingOpts =+                intercalate ", " $ map snd $ filter ((== Nothing) . ($ so) . fst)+                [(so_source, "--source"),(so_destination, "--destination")]+           printUsageInfo ["The follwing required options: " ++ missingOpts ++ " were missing."]+    where+      cs = toClientServerOptions so+      doubleRemote = "Either the directory given in --source or --destination must be local."+      missingRemote = concat+          [ "The --remote-shell options requires that either the directory given at "+          , "--source or --destination is at remote site. (Indicated by the prefix: 'remote:')"+          ]+      missingRemoteCmd = "The --remote-shell is required when the prefix 'remote:' is used."++runParent :: ClientServerOptions -> String -> FilePath -> FilePath -> Direction -> IO ()+runParent clientServerOpts remoteCmd source destination dir =+    do let remoteCmd' = remoteCmd ++ " " ++ _HIDDENT_CLIENT_MODE_OPTION_+       handles <- createProcess ((shell remoteCmd') { std_in = CreatePipe, std_out = CreatePipe })+       case handles of+         (Just hIn, Just hOut, Nothing, _ph) ->+             do streams <- openStreams hOut hIn+                parent streams source destination dir clientServerOpts+         _ -> fail "createProcess did return the correct set of handles."+
+ sync-mht.cabal view
@@ -0,0 +1,67 @@+-- This .cabal file was generated by src/build/hs/CabalTemplate.hs+name: sync-mht+version: 0.2.0.0+synopsis: +   Fast incremental file transfer using Merke-Hash-Trees+   +description: +   A command line tool that can be used to incrementally synchronize a directory hierarchy with a+   second one. It is using a Merkle-Hash-Tree to compare the folders, such that the synchronization+   time and communication (round) complexity grows only logarithmically with the actual size of the+   directories (assuming the actual difference of the directories is small).+   +   The communication happens through standard streams between parent and child processes, which can+   easily be routed through remote command execution tools, e.g.+   +       sync-mht -s foo/ -d bar+   +   will synchronize the local folder bar/ with the local folder foo/, but+   +       sync-mht -s foo/ -d remote:/bar -r "ssh fred@example.org sync-mht"+   +   will synchronize the folder bar/ in the home directory of the user fred on the host machine+   example.org with the local folder foo/.+   +   It is also possible to use it with docker, e.g.+   +       sync-mht -s foo/ -d remote:/bar -r "docker run -i --volumes-from bar ekarayel/sync-mht sync-mht"+   +   to synchronize the folder /bar (of the container named bar) with the local folder foo/.+   +license: MIT+license-file: LICENSE+author: Emin Karayel <me@eminkarayel.de>+maintainer: Emin Karayel <me@eminkarayel.de>+package-url: https://github.com/ekarayel/sync-mht+category: Utility+build-type: Simple+cabal-version: >=1.10+source-repository head+    type: git+    location: https://github.com/ekarayel/sync-mht+source-repository this+    type: git+    tag: 0.2.0.0+    location: https://github.com/ekarayel/sync-mht+executable sync-mht+    main-is: Main.hs+    ghc-options: -Wall+    build-depends: +        base >=4.7 && <4.8+        , unix >=2.7 && <2.8+        , directory >=1.2 && <1.3+        , filepath >=1.3 && <1.4+        , process >=1.2 &&  <1.3+        , cryptohash >=0.11 && <0.12+        , byteable >=0.1 && <0.2+        , array >=0.5 && <0.6+        , containers >=0.5 && <0.6+        , text >=1.2 && <1.3+        , bytestring >=0.10 && <0.11+        , base16-bytestring >=0.1 && <0.2+        , cereal >= 0.4 && < 0.5+        , io-streams >= 1.2 && <1.3+        , transformers >= 0.4 && < 0.5+        , mtl >= 2.2 && < 2.3+    hs-source-dirs: src/main/hs+    default-language: Haskell2010