diff --git a/Annex/Hook.hs b/Annex/Hook.hs index e4264ce9d5..0496094be8 100644 --- a/Annex/Hook.hs +++ b/Annex/Hook.hs @@ -48,6 +48,9 @@ preCommitAnnexHook = Git.Hook "pre-commit-annex" "" [] postUpdateAnnexHook :: Git.Hook postUpdateAnnexHook = Git.Hook "post-update-annex" "" [] +preInitAnnexHook :: Git.Hook +preInitAnnexHook = Git.Hook "pre-init-annex" "" [] + freezeContentAnnexHook :: Git.Hook freezeContentAnnexHook = Git.Hook "freezecontent-annex" "" [] @@ -98,20 +101,33 @@ doesAnnexHookExist hook = do return exists runAnnexHook :: Git.Hook -> (GitConfig -> Maybe String) -> Annex () -runAnnexHook hook commandcfg = ifM (doesAnnexHookExist hook) +runAnnexHook hook commandcfg = runAnnexHook' hook commandcfg >>= \case + Nothing -> noop + Just failedcommanddesc -> + warning $ UnquotedString $ failedcommanddesc ++ " failed" + +-- Returns Nothing if the hook or GitConfig command succeeded, or a +-- description of what failed. +runAnnexHook' :: Git.Hook -> (GitConfig -> Maybe String) -> Annex (Maybe String) +runAnnexHook' hook commandcfg = ifM (doesAnnexHookExist hook) ( runhook , runcommandcfg ) where - runhook = unlessM (inRepo $ Git.runHook boolSystem hook []) $ do - h <- fromRepo $ Git.hookFile hook - commandfailed h + runhook = ifM (inRepo $ Git.runHook boolSystem hook []) + ( return Nothing + , do + h <- fromRepo (Git.hookFile hook) + commandfailed h + ) runcommandcfg = commandcfg <$> Annex.getGitConfig >>= \case + Nothing -> return Nothing Just command -> - unlessM (liftIO $ boolSystem "sh" [Param "-c", Param command]) $ - commandfailed command - Nothing -> noop - commandfailed c = warning $ UnquotedString $ c ++ " failed" + ifM (liftIO $ boolSystem "sh" [Param "-c", Param command]) + ( return Nothing + , commandfailed $ "git configured command '" ++ command ++ "'" + ) + commandfailed c = return $ Just c runAnnexPathHook :: String -> Git.Hook -> (GitConfig -> Maybe String) -> RawFilePath -> Annex Bool runAnnexPathHook pathtoken hook commandcfg p = ifM (doesAnnexHookExist hook) @@ -121,9 +137,9 @@ runAnnexPathHook pathtoken hook commandcfg p = ifM (doesAnnexHookExist hook) where runhook = inRepo $ Git.runHook boolSystem hook [ File (fromRawFilePath p) ] runcommandcfg = commandcfg <$> Annex.getGitConfig >>= \case + Nothing -> return True Just basecmd -> liftIO $ boolSystem "sh" [Param "-c", Param $ gencmd basecmd] - Nothing -> return True gencmd = massReplace [ (pathtoken, shellEscape (fromRawFilePath p)) ] outputOfAnnexHook :: Git.Hook -> (GitConfig -> Maybe String) -> Annex (Maybe String) @@ -135,6 +151,6 @@ outputOfAnnexHook hook commandcfg = ifM (doesAnnexHookExist hook) runhook = inRepo (Git.runHook runhook' hook []) runhook' c ps = Just <$> readProcess c (toCommand ps) runcommandcfg = commandcfg <$> Annex.getGitConfig >>= \case + Nothing -> return Nothing Just command -> liftIO $ Just <$> readProcess "sh" ["-c", command] - Nothing -> return Nothing diff --git a/Annex/Init.hs b/Annex/Init.hs index 0cb2e09019..ea7cd09765 100644 --- a/Annex/Init.hs +++ b/Annex/Init.hs @@ -1,6 +1,6 @@ {- git-annex repository initialization - - - Copyright 2011-2024 Joey Hess + - Copyright 2011-2025 Joey Hess - - Licensed under the GNU AGPL version 3 or higher. -} @@ -74,17 +74,29 @@ data InitializeAllowed = InitializeAllowed checkInitializeAllowed :: (InitializeAllowed -> Annex a) -> Annex a checkInitializeAllowed a = guardSafeToUseRepo $ noAnnexFileContent' >>= \case - Nothing -> do - checkSqliteWorks - a InitializeAllowed + Nothing -> runAnnexHook' preInitAnnexHook annexPreInitCommand >>= \case + Nothing -> do + checkSqliteWorks + a InitializeAllowed + Just failedcommanddesc -> do + initpreventedby failedcommanddesc + notinitialized Just noannexmsg -> do - warning "Initialization prevented by .noannex file (remove the file to override)" + initpreventedby ".noannex file (remove the file to override)" unless (null noannexmsg) $ warning (UnquotedString noannexmsg) - giveup "Not initialized." + notinitialized + where + initpreventedby r = warning $ UnquotedString $ + "Initialization prevented by " ++ r + notinitialized = giveup "Not initialized." initializeAllowed :: Annex Bool -initializeAllowed = isNothing <$> noAnnexFileContent' +initializeAllowed = noAnnexFileContent' >>= \case + Nothing -> runAnnexHook' preInitAnnexHook annexPreInitCommand >>= \case + Nothing -> return True + Just _ -> return False + Just _ -> return False noAnnexFileContent' :: Annex (Maybe String) noAnnexFileContent' = inRepo $ @@ -268,7 +280,7 @@ autoInitialize' check startupannex remotelist = getInitializedVersion >>= maybe needsinit checkUpgrade where needsinit = - whenM (initializeAllowed <&&> check) $ do + whenM (check <&&> initializeAllowed) $ do initialize startupannex Nothing Nothing autoEnableSpecialRemotes remotelist diff --git a/CHANGELOG b/CHANGELOG index 101fb7d0e2..d7ca6e3d15 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -22,6 +22,8 @@ git-annex (10.20250103) UNRELEASED; urgency=medium annex.http-headers-command. * Added git configs annex.post-update-command and annex.pre-commit-command that correspond to the post-update-annex and pre-commit-annex hooks. + * Added annex.pre-init-command git config and pre-init-annex hook + that is run before git-annex repository initialization. -- Joey Hess Fri, 03 Jan 2025 14:30:38 -0400 diff --git a/Types/GitConfig.hs b/Types/GitConfig.hs index 81904653f8..053a9c8c66 100644 --- a/Types/GitConfig.hs +++ b/Types/GitConfig.hs @@ -97,6 +97,7 @@ data GitConfig = GitConfig , annexAlwaysCompact :: Bool , annexCommitMessage :: Maybe String , annexCommitMessageCommand :: Maybe String + , annexPreInitCommand :: Maybe String , annexPreCommitCommand :: Maybe String , annexPostUpdateCommand :: Maybe String , annexMergeAnnexBranches :: Bool @@ -192,6 +193,7 @@ extractGitConfig configsource r = GitConfig , annexAlwaysCompact = getbool (annexConfig "alwayscompact") True , annexCommitMessage = getmaybe (annexConfig "commitmessage") , annexCommitMessageCommand = getmaybe (annexConfig "commitmessage-command") + , annexPreInitCommand = getmaybe (annexConfig "pre-init-command") , annexPreCommitCommand = getmaybe (annexConfig "pre-commit-command") , annexPostUpdateCommand = getmaybe (annexConfig "post-update-command") , annexMergeAnnexBranches = getbool (annexConfig "merge-annex-branches") True diff --git a/doc/git-annex-init.mdwn b/doc/git-annex-init.mdwn index aa0e418639..dcbfd64102 100644 --- a/doc/git-annex-init.mdwn +++ b/doc/git-annex-init.mdwn @@ -27,7 +27,11 @@ already initialized git-annex repository. A top-level `.noannex` file will prevent git-annex init from being used in a repository. This is useful for repositories that have a policy reason not to use git-annex. The content of the file will be displayed -to the user who tries to run git-annex init. +to the user who tries to run git-annex init. + +The annex.pre-init-command git configuration or pre-init-annex hook +is run before initialization and can exit nonzero as well to prevent +initialization, as well as doing other setup. # EXAMPLES diff --git a/doc/git-annex.mdwn b/doc/git-annex.mdwn index db305956f3..869686883a 100644 --- a/doc/git-annex.mdwn +++ b/doc/git-annex.mdwn @@ -1176,6 +1176,16 @@ repository, using [[git-annex-config]]. See its man page for a list.) Alternatively, a hook script can be installed in `.git/hooks/pre-commit-annex` +* `annex.pre-init-command` + + This command is run before the repository is initialized, either by + `git-annex init`, or automatic initialization. It can configure + the repository in any way needed. If it exits nonzero, the repository + initialization will fail. + + Alternatively, a hook script can be installed in + `.git/hooks/pre-init-annex` + * `annex.alwayscompact` By default, git-annex compacts data it records in the git-annex branch. diff --git a/doc/todo/Support___40__globally_configured__41_____34__annex_init__34___hooks.mdwn b/doc/todo/Support___40__globally_configured__41_____34__annex_init__34___hooks.mdwn index 95cd81a9c1..3d6d7c97f2 100644 --- a/doc/todo/Support___40__globally_configured__41_____34__annex_init__34___hooks.mdwn +++ b/doc/todo/Support___40__globally_configured__41_____34__annex_init__34___hooks.mdwn @@ -6,3 +6,5 @@ The idea is stemmed from discussions/problems with using freeze/thaw hooks, and [[!meta author=yoh]] [[!tag projects/openneuro]] + +> [[done]] --[[Joey]] diff --git a/doc/todo/Support___40__globally_configured__41_____34__annex_init__34___hooks/comment_5_c1687c1d853159debff18ed5c75268ad._comment b/doc/todo/Support___40__globally_configured__41_____34__annex_init__34___hooks/comment_5_c1687c1d853159debff18ed5c75268ad._comment new file mode 100644 index 0000000000..554fb73a21 --- /dev/null +++ b/doc/todo/Support___40__globally_configured__41_____34__annex_init__34___hooks/comment_5_c1687c1d853159debff18ed5c75268ad._comment @@ -0,0 +1,10 @@ +[[!comment format=mdwn + username="joey" + subject="""comment 5""" + date="2025-01-13T17:33:02Z" + content=""" +Note that the `.noannex` file that prevents init has some overlap with +a pre-init hook that exits nonzero. I guess the .noannex file has the +benefit of working in every clone of a repository without additional +configuration. +"""]] diff --git a/doc/todo/Support___40__globally_configured__41_____34__annex_init__34___hooks/comment_6_e2e93b900f018d276029389405a09c28._comment b/doc/todo/Support___40__globally_configured__41_____34__annex_init__34___hooks/comment_6_e2e93b900f018d276029389405a09c28._comment new file mode 100644 index 0000000000..a40031affc --- /dev/null +++ b/doc/todo/Support___40__globally_configured__41_____34__annex_init__34___hooks/comment_6_e2e93b900f018d276029389405a09c28._comment @@ -0,0 +1,10 @@ +[[!comment format=mdwn + username="joey" + subject="""comment 6""" + date="2025-01-13T18:17:42Z" + content=""" +Implemented .git/hooks/pre-init-annex +(and alternatively git config annex.pre-init-command) + +Note that this is also run before automatic initialization. +"""]]