hook special remote: Added combined hook program support.

This commit is contained in:
Joey Hess 2013-05-21 19:19:03 -04:00
parent 08c03b2af3
commit 2dce874c77
3 changed files with 68 additions and 22 deletions

View file

@ -23,6 +23,9 @@ import Remote.Helper.Encryptable
import Crypto import Crypto
import Utility.Metered import Utility.Metered
type Action = String
type HookName = String
remote :: RemoteType remote :: RemoteType
remote = RemoteType { remote = RemoteType {
typename = "hook", typename = "hook",
@ -67,14 +70,15 @@ hookSetup u c = do
gitConfigSpecialRemote u c' "hooktype" hooktype gitConfigSpecialRemote u c' "hooktype" hooktype
return c' return c'
hookEnv :: Key -> Maybe FilePath -> IO (Maybe [(String, String)]) hookEnv :: Action -> Key -> Maybe FilePath -> IO (Maybe [(String, String)])
hookEnv k f = Just <$> mergeenv (fileenv f ++ keyenv) hookEnv action k f = Just <$> mergeenv (fileenv f ++ keyenv)
where where
mergeenv l = M.toList . M.union (M.fromList l) mergeenv l = M.toList . M.union (M.fromList l)
<$> M.fromList <$> getEnvironment <$> M.fromList <$> getEnvironment
env s v = ("ANNEX_" ++ s, v) env s v = ("ANNEX_" ++ s, v)
keyenv = catMaybes keyenv = catMaybes
[ Just $ env "KEY" (key2file k) [ Just $ env "KEY" (key2file k)
, Just $ env "ACTION" action
, env "HASH_1" <$> headMaybe hashbits , env "HASH_1" <$> headMaybe hashbits
, env "HASH_2" <$> headMaybe (drop 1 hashbits) , env "HASH_2" <$> headMaybe (drop 1 hashbits)
] ]
@ -82,64 +86,70 @@ hookEnv k f = Just <$> mergeenv (fileenv f ++ keyenv)
fileenv (Just file) = [env "FILE" file] fileenv (Just file) = [env "FILE" file]
hashbits = map takeDirectory $ splitPath $ hashDirMixed k hashbits = map takeDirectory $ splitPath $ hashDirMixed k
lookupHook :: String -> String -> Annex (Maybe String) lookupHook :: HookName -> Action -> Annex (Maybe String)
lookupHook hooktype hook =do lookupHook hookname action = do
command <- getConfig (annexConfig hookname) "" command <- getConfig (annexConfig hook) ""
if null command if null command
then do then do
warning $ "missing configuration for " ++ hookname fallback <- getConfig (annexConfig $ hookfallback) ""
if null fallback
then do
warning $ "missing configuration for " ++ hook ++ " or " ++ hookfallback
return Nothing return Nothing
else return $ Just fallback
else return $ Just command else return $ Just command
where where
hookname = hooktype ++ "-" ++ hook ++ "-hook" hook = hookname ++ "-" ++ action ++ "-hook"
hookfallback = hookname ++ "-hook"
runHook :: String -> String -> Key -> Maybe FilePath -> Annex Bool -> Annex Bool runHook :: HookName -> Action -> Key -> Maybe FilePath -> Annex Bool -> Annex Bool
runHook hooktype hook k f a = maybe (return False) run =<< lookupHook hooktype hook runHook hook action k f a = maybe (return False) run =<< lookupHook hook action
where where
run command = do run command = do
showOutput -- make way for hook output showOutput -- make way for hook output
ifM (liftIO $ boolSystemEnv "sh" [Param "-c", Param command] =<< hookEnv k f) ifM (liftIO $ boolSystemEnv "sh" [Param "-c", Param command] =<< hookEnv action k f)
( a ( a
, do , do
warning $ hook ++ " hook exited nonzero!" warning $ hook ++ " hook exited nonzero!"
return False return False
) )
store :: String -> Key -> AssociatedFile -> MeterUpdate -> Annex Bool store :: HookName -> Key -> AssociatedFile -> MeterUpdate -> Annex Bool
store h k _f _p = sendAnnex k (void $ remove h k) $ \src -> store h k _f _p = sendAnnex k (void $ remove h k) $ \src ->
runHook h "store" k (Just src) $ return True runHook h "store" k (Just src) $ return True
storeEncrypted :: String -> GpgOpts -> (Cipher, Key) -> Key -> MeterUpdate -> Annex Bool storeEncrypted :: HookName -> GpgOpts -> (Cipher, Key) -> Key -> MeterUpdate -> Annex Bool
storeEncrypted h gpgOpts (cipher, enck) k _p = withTmp enck $ \tmp -> storeEncrypted h gpgOpts (cipher, enck) k _p = withTmp enck $ \tmp ->
sendAnnex k (void $ remove h enck) $ \src -> do sendAnnex k (void $ remove h enck) $ \src -> do
liftIO $ encrypt gpgOpts cipher (feedFile src) $ liftIO $ encrypt gpgOpts cipher (feedFile src) $
readBytes $ L.writeFile tmp readBytes $ L.writeFile tmp
runHook h "store" enck (Just tmp) $ return True runHook h "store" enck (Just tmp) $ return True
retrieve :: String -> Key -> AssociatedFile -> FilePath -> MeterUpdate -> Annex Bool retrieve :: HookName -> Key -> AssociatedFile -> FilePath -> MeterUpdate -> Annex Bool
retrieve h k _f d _p = runHook h "retrieve" k (Just d) $ return True retrieve h k _f d _p = runHook h "retrieve" k (Just d) $ return True
retrieveCheap :: String -> Key -> FilePath -> Annex Bool retrieveCheap :: HookName -> Key -> FilePath -> Annex Bool
retrieveCheap _ _ _ = return False retrieveCheap _ _ _ = return False
retrieveEncrypted :: String -> (Cipher, Key) -> Key -> FilePath -> MeterUpdate -> Annex Bool retrieveEncrypted :: HookName -> (Cipher, Key) -> Key -> FilePath -> MeterUpdate -> Annex Bool
retrieveEncrypted h (cipher, enck) _ f _p = withTmp enck $ \tmp -> retrieveEncrypted h (cipher, enck) _ f _p = withTmp enck $ \tmp ->
runHook h "retrieve" enck (Just tmp) $ liftIO $ catchBoolIO $ do runHook h "retrieve" enck (Just tmp) $ liftIO $ catchBoolIO $ do
decrypt cipher (feedFile tmp) $ decrypt cipher (feedFile tmp) $
readBytes $ L.writeFile f readBytes $ L.writeFile f
return True return True
remove :: String -> Key -> Annex Bool remove :: HookName -> Key -> Annex Bool
remove h k = runHook h "remove" k Nothing $ return True remove h k = runHook h "remove" k Nothing $ return True
checkPresent :: Git.Repo -> String -> Key -> Annex (Either String Bool) checkPresent :: Git.Repo -> HookName -> Key -> Annex (Either String Bool)
checkPresent r h k = do checkPresent r h k = do
showAction $ "checking " ++ Git.repoDescribe r showAction $ "checking " ++ Git.repoDescribe r
v <- lookupHook h "checkpresent" v <- lookupHook h action
liftIO $ catchMsgIO $ check v liftIO $ catchMsgIO $ check v
where where
action = "checkpresent"
findkey s = key2file k `elem` lines s findkey s = key2file k `elem` lines s
check Nothing = error "checkpresent hook misconfigured" check Nothing = error $ action ++ " hook misconfigured"
check (Just hook) = do check (Just hook) = do
env <- hookEnv k Nothing env <- hookEnv action k Nothing
findkey <$> readProcessEnv "sh" ["-c", hook] env findkey <$> readProcessEnv "sh" ["-c", hook] env

1
debian/changelog vendored
View file

@ -4,6 +4,7 @@ git-annex (4.20130522) UNRELEASED; urgency=low
* XMPP: Avoid redundant and unncessary pushes. Note that this breaks * XMPP: Avoid redundant and unncessary pushes. Note that this breaks
compatibility with previous versions of git-annex, which will refuse compatibility with previous versions of git-annex, which will refuse
to accept any XMPP pushes from this version. to accept any XMPP pushes from this version.
* hook special remote: Added combined hook program support.
-- Joey Hess <joeyh@debian.org> Tue, 21 May 2013 18:22:46 -0400 -- Joey Hess <joeyh@debian.org> Tue, 21 May 2013 18:22:46 -0400

View file

@ -50,7 +50,7 @@ These environment variables are used to communicate with the hook commands:
into 1024 buckets. into 1024 buckets.
* `ANNEX_HASH_2` - another hash value, can be used for a second level of hashing * `ANNEX_HASH_2` - another hash value, can be used for a second level of hashing
The setting to use in git config for the hook commands are as follows: The settings to use in git config for the hook commands are as follows:
* `annex.$hooktype-store-hook` - Command run to store a key in the special remote. * `annex.$hooktype-store-hook` - Command run to store a key in the special remote.
`ANNEX_FILE` contains the content to be stored. `ANNEX_FILE` contains the content to be stored.
@ -68,3 +68,38 @@ The setting to use in git config for the hook commands are as follows:
if and only if the key has been actively verified to be present in the if and only if the key has been actively verified to be present in the
special remote (caching presence information is a very bad idea); special remote (caching presence information is a very bad idea);
all other output to stdout will be ignored. all other output to stdout will be ignored.
## combined hook program
Rather than setting all of the above hooks, you can write a single
program that handles everything, and set a single hook to make it be used.
# git config annex.demo-hook /usr/local/bin/annexdemo
# git annex initremote mydemorepo type=hook hooktype=demo encryption=none
The program just needs to look at the `ANNEX_ACTION` environment variable
to see what it's being asked to do For example:
[[!format sh """
#!/bin/sh
set -e
case "$ANNEX_ACTION" in
store)
demo-upload "$ANNEX_HASH_1/$ANNEX_HASH_2/$ANNEX_KEY" < "$ANNEX_FILE"
;;
retrieve)
demo-download "$ANNEX_HASH_1/$ANNEX_HASH_2/$ANNEX_KEY" > "$ANNEX_FILE"
;;
remove)
demo-nuke "$ANNEX_HASH_1/$ANNEX_HASH_2/$ANNEX_KEY"
;;
checkpresent)
if demo-exists "$ANNEX_HASH_1/$ANNEX_HASH_2/$ANNEX_KEY"; then
echo "$ANNEX_KEY"
fi
*)
echo "unkown ANNEX_ACTION: $ANNEX_ACTION" >&2
exit 1
;;
esac
"""]]