Install darcs post-apply hooks in darcs repos, no-op hook for now
In Darcs, any command can have a post hook (and a pre hook), and the hook command can be set using a command-line option to the darcs command that you run. So, in the Vervis SSH server, if we add a --posthook option when running `darcs apply` to apply remotely received patches, we get a chance to process the patch data much like in the git post-receive hook. The setup this patch creates is similar to the git one: It writes a _darcs/prefs/defaults file to all Darcs repos, and that defaults file sets the posthook line for `darcs apply`. The posthook line simply executes the actual hook program written in Haskell. The current hook program is a one-liner that prints a line to stdout, so every time you `darcs push` you can tell the hook got executed. The next step is to implement the actual hook logic, by reading patch data from the environment variable in which Darcs puts it.
This commit is contained in:
parent
c529722b5a
commit
6cb86ebbf1
10 changed files with 94 additions and 8 deletions
|
@ -80,6 +80,7 @@ max-actor-keys: 2
|
||||||
repo-dir: repos
|
repo-dir: repos
|
||||||
diff-context-lines: 5
|
diff-context-lines: 5
|
||||||
#post-receive-hook: /home/joe/.local/bin/vervis-post-receive
|
#post-receive-hook: /home/joe/.local/bin/vervis-post-receive
|
||||||
|
#post-apply-hook: /home/joe/.local/bin/vervis-post-apply
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
# SSH server
|
# SSH server
|
||||||
|
|
17
hook-darcs/main.hs
Normal file
17
hook-darcs/main.hs
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
{- This file is part of Vervis.
|
||||||
|
-
|
||||||
|
- Written in 2019 by fr33domlover <fr33domlover@riseup.net>.
|
||||||
|
-
|
||||||
|
- ♡ Copying is an act of love. Please copy, reuse and share.
|
||||||
|
-
|
||||||
|
- The author(s) have dedicated all copyright and related and neighboring
|
||||||
|
- rights to this software to the public domain worldwide. This software is
|
||||||
|
- distributed without any warranty.
|
||||||
|
-
|
||||||
|
- You should have received a copy of the CC0 Public Domain Dedication along
|
||||||
|
- with this software. If not, see
|
||||||
|
- <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||||
|
-}
|
||||||
|
|
||||||
|
main :: IO ()
|
||||||
|
main = putStrLn "Hello, I'm the posthook!"
|
|
@ -1,6 +1,6 @@
|
||||||
{- This file is part of Vervis.
|
{- This file is part of Vervis.
|
||||||
-
|
-
|
||||||
- Written in 2016 by fr33domlover <fr33domlover@riseup.net>.
|
- Written in 2016, 2019 by fr33domlover <fr33domlover@riseup.net>.
|
||||||
-
|
-
|
||||||
- ♡ Copying is an act of love. Please copy, reuse and share.
|
- ♡ Copying is an act of love. Please copy, reuse and share.
|
||||||
-
|
-
|
||||||
|
@ -14,19 +14,35 @@
|
||||||
-}
|
-}
|
||||||
|
|
||||||
module Darcs.Local.Repository
|
module Darcs.Local.Repository
|
||||||
( createRepo
|
( writeDefaultsFile
|
||||||
|
, createRepo
|
||||||
, readPristineRoot
|
, readPristineRoot
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
|
||||||
import Darcs.Util.Hash
|
import Darcs.Util.Hash
|
||||||
|
import Data.Bits
|
||||||
|
import Data.Text (Text)
|
||||||
import System.Directory (createDirectory)
|
import System.Directory (createDirectory)
|
||||||
import System.Exit (ExitCode (..))
|
import System.Exit (ExitCode (..))
|
||||||
import System.FilePath ((</>))
|
import System.FilePath ((</>))
|
||||||
import System.IO (withBinaryFile, IOMode (ReadMode))
|
import System.IO (withBinaryFile, IOMode (ReadMode))
|
||||||
|
import System.Posix.Files
|
||||||
import System.Process (createProcess, proc, waitForProcess)
|
import System.Process (createProcess, proc, waitForProcess)
|
||||||
|
|
||||||
import qualified Data.ByteString as B
|
import qualified Data.ByteString as B
|
||||||
|
import qualified Data.Text as T
|
||||||
|
import qualified Data.Text.IO as TIO
|
||||||
|
|
||||||
|
writeDefaultsFile :: FilePath -> FilePath -> Text -> Text -> IO ()
|
||||||
|
writeDefaultsFile path cmd sharer repo = do
|
||||||
|
let file = path </> "_darcs" </> "prefs" </> "defaults"
|
||||||
|
TIO.writeFile file $ defaultsContent cmd sharer repo
|
||||||
|
setFileMode file $ ownerReadMode .|. ownerWriteMode
|
||||||
|
where
|
||||||
|
defaultsContent :: FilePath -> Text -> Text -> Text
|
||||||
|
defaultsContent hook sharer repo =
|
||||||
|
T.concat ["apply posthook ", T.pack hook, " ", sharer, " ", repo]
|
||||||
|
|
||||||
{-
|
{-
|
||||||
initialRepoTree :: FileName -> DirTree B.ByteString
|
initialRepoTree :: FileName -> DirTree B.ByteString
|
||||||
|
@ -56,15 +72,21 @@ createRepo
|
||||||
-- ^ Parent directory which already exists
|
-- ^ Parent directory which already exists
|
||||||
-> String
|
-> String
|
||||||
-- ^ Name of new repo, i.e. new directory to create under the parent
|
-- ^ Name of new repo, i.e. new directory to create under the parent
|
||||||
|
-> FilePath
|
||||||
|
-- ^ Path of Vervis hook program
|
||||||
|
-> Text
|
||||||
|
-- ^ Repo sharer textual ID
|
||||||
|
-> Text
|
||||||
|
-- ^ Repo textual ID
|
||||||
-> IO ()
|
-> IO ()
|
||||||
createRepo parent name = do
|
createRepo parent name cmd sharer repo = do
|
||||||
let path = parent </> name
|
let path = parent </> name
|
||||||
createDirectory path
|
createDirectory path
|
||||||
let settings = proc "darcs" ["init", "--no-working-dir", "--repodir", path]
|
let settings = proc "darcs" ["init", "--no-working-dir", "--repodir", path]
|
||||||
(_, _, _, ph) <- createProcess settings
|
(_, _, _, ph) <- createProcess settings
|
||||||
ec <- waitForProcess ph
|
ec <- waitForProcess ph
|
||||||
case ec of
|
case ec of
|
||||||
ExitSuccess -> return ()
|
ExitSuccess -> writeDefaultsFile path cmd sharer repo
|
||||||
ExitFailure n -> error $ "darcs init failed with exit code " ++ show n
|
ExitFailure n -> error $ "darcs init failed with exit code " ++ show n
|
||||||
|
|
||||||
readPristineRoot :: FilePath -> IO (Maybe Int, Hash)
|
readPristineRoot :: FilePath -> IO (Maybe Int, Hash)
|
||||||
|
|
|
@ -74,6 +74,7 @@ import Control.Concurrent.Local
|
||||||
import Web.Hashids.Local
|
import Web.Hashids.Local
|
||||||
|
|
||||||
import Vervis.ActorKey (generateActorKey, actorKeyRotator)
|
import Vervis.ActorKey (generateActorKey, actorKeyRotator)
|
||||||
|
import Vervis.Darcs
|
||||||
import Vervis.Federation
|
import Vervis.Federation
|
||||||
import Vervis.Foundation
|
import Vervis.Foundation
|
||||||
import Vervis.Git
|
import Vervis.Git
|
||||||
|
@ -199,6 +200,7 @@ makeFoundation appSettings = do
|
||||||
fixRunningDeliveries
|
fixRunningDeliveries
|
||||||
deleteUnusedURAs
|
deleteUnusedURAs
|
||||||
writePostReceiveHooks
|
writePostReceiveHooks
|
||||||
|
writePostApplyHooks
|
||||||
|
|
||||||
writeHookConfig Config
|
writeHookConfig Config
|
||||||
{ configSecret = hookSecretText appHookSecret
|
{ configSecret = hookSecretText appHookSecret
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{- This file is part of Vervis.
|
{- This file is part of Vervis.
|
||||||
-
|
-
|
||||||
- Written in 2016, 2018 by fr33domlover <fr33domlover@riseup.net>.
|
- Written in 2016, 2018, 2019 by fr33domlover <fr33domlover@riseup.net>.
|
||||||
-
|
-
|
||||||
- ♡ Copying is an act of love. Please copy, reuse and share.
|
- ♡ Copying is an act of love. Please copy, reuse and share.
|
||||||
-
|
-
|
||||||
|
@ -19,6 +19,7 @@ module Vervis.Darcs
|
||||||
, readChangesView
|
, readChangesView
|
||||||
, lastChange
|
, lastChange
|
||||||
, readPatch
|
, readPatch
|
||||||
|
, writePostApplyHooks
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
|
||||||
|
@ -26,6 +27,7 @@ import Prelude hiding (lookup)
|
||||||
|
|
||||||
import Control.Applicative ((<|>))
|
import Control.Applicative ((<|>))
|
||||||
import Control.Arrow (second)
|
import Control.Arrow (second)
|
||||||
|
import Control.Monad.IO.Class
|
||||||
import Control.Monad.Trans.Class (lift)
|
import Control.Monad.Trans.Class (lift)
|
||||||
import Control.Monad.Trans.Except (ExceptT (ExceptT), runExceptT)
|
import Control.Monad.Trans.Except (ExceptT (ExceptT), runExceptT)
|
||||||
import Darcs.Util.Path
|
import Darcs.Util.Path
|
||||||
|
@ -33,6 +35,7 @@ import Darcs.Util.Tree
|
||||||
import Darcs.Util.Tree.Hashed
|
import Darcs.Util.Tree.Hashed
|
||||||
import Data.Bool (bool)
|
import Data.Bool (bool)
|
||||||
import Data.ByteString (ByteString)
|
import Data.ByteString (ByteString)
|
||||||
|
import Data.Foldable hiding (find)
|
||||||
import Data.List.NonEmpty (NonEmpty (..))
|
import Data.List.NonEmpty (NonEmpty (..))
|
||||||
import Data.Maybe (listToMaybe, mapMaybe, fromMaybe)
|
import Data.Maybe (listToMaybe, mapMaybe, fromMaybe)
|
||||||
import Data.Text (Text)
|
import Data.Text (Text)
|
||||||
|
@ -56,8 +59,12 @@ import qualified Data.Foldable as F (find)
|
||||||
import qualified Data.List.NonEmpty as N
|
import qualified Data.List.NonEmpty as N
|
||||||
import qualified Data.Text as T (unpack, takeWhile, stripEnd)
|
import qualified Data.Text as T (unpack, takeWhile, stripEnd)
|
||||||
import qualified Data.Vector as V (empty)
|
import qualified Data.Vector as V (empty)
|
||||||
|
import qualified Database.Esqueleto as E
|
||||||
|
|
||||||
import qualified Development.Darcs.Internal.Patch.Parser as P
|
import qualified Development.Darcs.Internal.Patch.Parser as P
|
||||||
|
|
||||||
|
import Yesod.MonadSite
|
||||||
|
|
||||||
import Darcs.Local.Repository
|
import Darcs.Local.Repository
|
||||||
import Data.Either.Local (maybeRight)
|
import Data.Either.Local (maybeRight)
|
||||||
import Data.EventTime.Local
|
import Data.EventTime.Local
|
||||||
|
@ -66,9 +73,14 @@ import Data.Text.UTF8.Local (decodeStrict)
|
||||||
import Data.Time.Clock.Local ()
|
import Data.Time.Clock.Local ()
|
||||||
|
|
||||||
import Vervis.Changes
|
import Vervis.Changes
|
||||||
import Vervis.Foundation (Widget)
|
import Vervis.Foundation
|
||||||
|
import Vervis.Model
|
||||||
|
import Vervis.Model.Ident
|
||||||
|
import Vervis.Model.Repo
|
||||||
import Vervis.Patch
|
import Vervis.Patch
|
||||||
|
import Vervis.Path
|
||||||
import Vervis.Readme
|
import Vervis.Readme
|
||||||
|
import Vervis.Settings
|
||||||
import Vervis.SourceTree
|
import Vervis.SourceTree
|
||||||
import Vervis.Wiki (WikiView (..))
|
import Vervis.Wiki (WikiView (..))
|
||||||
|
|
||||||
|
@ -314,3 +326,14 @@ readPatch path hash = do
|
||||||
<* A.skip (== '>')
|
<* A.skip (== '>')
|
||||||
mkedit (file, hunks) =
|
mkedit (file, hunks) =
|
||||||
EditTextFile (T.unpack $ decodeUtf8 file) V.empty hunks 0 0
|
EditTextFile (T.unpack $ decodeUtf8 file) V.empty hunks 0 0
|
||||||
|
|
||||||
|
writePostApplyHooks :: WorkerDB ()
|
||||||
|
writePostApplyHooks = do
|
||||||
|
repos <- E.select $ E.from $ \ (r `E.InnerJoin` s) -> do
|
||||||
|
E.on $ r E.^. RepoSharer E.==. s E.^. SharerId
|
||||||
|
E.where_ $ r E.^. RepoVcs E.==. E.val VCSDarcs
|
||||||
|
return (s E.^. SharerIdent, r E.^. RepoIdent)
|
||||||
|
hook <- asksSite $ appPostApplyHookFile . appSettings
|
||||||
|
for_ repos $ \ (E.Value shr, E.Value rp) -> do
|
||||||
|
path <- askRepoDir shr rp
|
||||||
|
liftIO $ writeDefaultsFile path hook (shr2text shr) (rp2text rp)
|
||||||
|
|
|
@ -79,6 +79,7 @@ import Vervis.Changes
|
||||||
import Vervis.Foundation
|
import Vervis.Foundation
|
||||||
import Vervis.Model
|
import Vervis.Model
|
||||||
import Vervis.Model.Ident
|
import Vervis.Model.Ident
|
||||||
|
import Vervis.Model.Repo
|
||||||
import Vervis.Patch
|
import Vervis.Patch
|
||||||
import Vervis.Path
|
import Vervis.Path
|
||||||
import Vervis.Readme
|
import Vervis.Readme
|
||||||
|
@ -334,6 +335,7 @@ writePostReceiveHooks :: WorkerDB ()
|
||||||
writePostReceiveHooks = do
|
writePostReceiveHooks = do
|
||||||
repos <- E.select $ E.from $ \ (r `E.InnerJoin` s) -> do
|
repos <- E.select $ E.from $ \ (r `E.InnerJoin` s) -> do
|
||||||
E.on $ r E.^. RepoSharer E.==. s E.^. SharerId
|
E.on $ r E.^. RepoSharer E.==. s E.^. SharerId
|
||||||
|
E.where_ $ r E.^. RepoVcs E.==. E.val VCSGit
|
||||||
return (s E.^. SharerIdent, r E.^. RepoIdent)
|
return (s E.^. SharerIdent, r E.^. RepoIdent)
|
||||||
hook <- asksSite $ appPostReceiveHookFile . appSettings
|
hook <- asksSite $ appPostReceiveHookFile . appSettings
|
||||||
for_ repos $ \ (E.Value shr, E.Value rp) -> do
|
for_ repos $ \ (E.Value shr, E.Value rp) -> do
|
||||||
|
|
|
@ -146,7 +146,15 @@ postReposR user = do
|
||||||
let repoName =
|
let repoName =
|
||||||
unpack $ CI.foldedCase $ unRpIdent $ nrpIdent nrp
|
unpack $ CI.foldedCase $ unRpIdent $ nrpIdent nrp
|
||||||
case nrpVcs nrp of
|
case nrpVcs nrp of
|
||||||
VCSDarcs -> liftIO $ D.createRepo parent repoName
|
VCSDarcs -> do
|
||||||
|
hook <- getsYesod $ appPostApplyHookFile . appSettings
|
||||||
|
liftIO $
|
||||||
|
D.createRepo
|
||||||
|
parent
|
||||||
|
repoName
|
||||||
|
hook
|
||||||
|
(shr2text user)
|
||||||
|
(rp2text $ nrpIdent nrp)
|
||||||
VCSGit -> do
|
VCSGit -> do
|
||||||
hook <- getsYesod $ appPostReceiveHookFile . appSettings
|
hook <- getsYesod $ appPostReceiveHookFile . appSettings
|
||||||
liftIO $
|
liftIO $
|
||||||
|
|
|
@ -135,6 +135,8 @@ data AppSettings = AppSettings
|
||||||
, appDiffContextLines :: Int
|
, appDiffContextLines :: Int
|
||||||
-- | Path of the Vervis post-receive hook executable
|
-- | Path of the Vervis post-receive hook executable
|
||||||
, appPostReceiveHookFile :: FilePath
|
, appPostReceiveHookFile :: FilePath
|
||||||
|
-- | Path of the Vervis darcs posthook executable
|
||||||
|
, appPostApplyHookFile :: FilePath
|
||||||
-- | Port for the SSH server component to listen on
|
-- | Port for the SSH server component to listen on
|
||||||
, appSshPort :: Int
|
, appSshPort :: Int
|
||||||
-- | Path to the server's SSH private key file
|
-- | Path to the server's SSH private key file
|
||||||
|
@ -229,6 +231,7 @@ instance FromJSON AppSettings where
|
||||||
appRepoDir <- o .: "repo-dir"
|
appRepoDir <- o .: "repo-dir"
|
||||||
appDiffContextLines <- o .: "diff-context-lines"
|
appDiffContextLines <- o .: "diff-context-lines"
|
||||||
appPostReceiveHookFile <- o .:? "post-receive-hook" .!= detectedHookFile
|
appPostReceiveHookFile <- o .:? "post-receive-hook" .!= detectedHookFile
|
||||||
|
appPostApplyHookFile <- o .:? "post-apply-hook" .!= detectedDarcsHookFile
|
||||||
appSshPort <- o .: "ssh-port"
|
appSshPort <- o .: "ssh-port"
|
||||||
appSshKeyFile <- o .: "ssh-key-file"
|
appSshKeyFile <- o .: "ssh-key-file"
|
||||||
appRegister <- o .: "registration"
|
appRegister <- o .: "registration"
|
||||||
|
@ -257,6 +260,7 @@ instance FromJSON AppSettings where
|
||||||
toSeconds = toTimeUnit
|
toSeconds = toTimeUnit
|
||||||
ndt = fromIntegral . toSeconds . interval
|
ndt = fromIntegral . toSeconds . interval
|
||||||
detectedHookFile = $localInstallRoot </> "bin" </> "vervis-post-receive"
|
detectedHookFile = $localInstallRoot </> "bin" </> "vervis-post-receive"
|
||||||
|
detectedDarcsHookFile = $localInstallRoot </> "bin" </> "vervis-post-apply"
|
||||||
|
|
||||||
-- | Settings for 'widgetFile', such as which template languages to support and
|
-- | Settings for 'widgetFile', such as which template languages to support and
|
||||||
-- default Hamlet settings.
|
-- default Hamlet settings.
|
||||||
|
|
|
@ -403,7 +403,14 @@ executable vervis
|
||||||
executable vervis-post-receive
|
executable vervis-post-receive
|
||||||
main-is: main.hs
|
main-is: main.hs
|
||||||
build-depends: base, vervis
|
build-depends: base, vervis
|
||||||
hs-source-dirs: hook
|
hs-source-dirs: hook-git
|
||||||
|
default-language: Haskell2010
|
||||||
|
ghc-options: -Wall
|
||||||
|
|
||||||
|
executable vervis-post-apply
|
||||||
|
main-is: main.hs
|
||||||
|
build-depends: base, vervis
|
||||||
|
hs-source-dirs: hook-darcs
|
||||||
default-language: Haskell2010
|
default-language: Haskell2010
|
||||||
ghc-options: -Wall
|
ghc-options: -Wall
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue