module Development.Bake.Git(
SHA1, ovenGit,
) where
import Development.Bake.Type
import Development.Bake.Util
import Development.Shake.Command
import Control.Monad.Extra
import Data.List.Extra
import Development.Bake.Format
import System.Directory.Extra
import System.FilePath
import Data.Maybe
newtype SHA1 = SHA1 {fromSHA1 :: String} deriving (Show,Eq)
sha1 :: String -> SHA1
sha1 x | length x /= 40 = error $ "SHA1 for Git must be 40 characters long, got " ++ show x
| not $ all (`elem` "0123456789abcdef") x = error $ "SHA1 for Git must be all lower case hex, got " ++ show x
| otherwise = SHA1 x
stringySHA1 :: Stringy SHA1
stringySHA1 = Stringy
{stringyTo = fromSHA1
,stringyFrom = sha1
,stringyPretty = take 7 . fromSHA1
}
ovenGit :: String -> String -> Maybe FilePath -> Oven () () test -> Oven SHA1 SHA1 test
ovenGit repo branch (fromMaybe "." -> path) o = o
{ovenUpdateState = gitUpdateState
,ovenPrepare = \s ps -> do gitCheckout s ps; ovenPrepare o () $ map (const ()) ps
,ovenPatchExtra = gitPatchExtra
,ovenStringyState = stringySHA1
,ovenStringyPatch = stringySHA1
}
where
traced msg act = do
putStrLn $ "% GIT: Begin " ++ msg
res <- act
putStrLn $ "% GIT: Finish " ++ msg
return res
gitSafe dir = do
unit $ cmd (Cwd dir) "git config user.email" ["https://github.com/ndmitchell/bake"]
unit $ cmd (Cwd dir) "git config user.name" ["Bake Continuous Integration"]
gitInitMirror = traced "gitInitMirror" $ do
mirror <- createDir "../bake-git" [repo]
ready <- doesFileExist $ mirror </> "HEAD"
if ready then
unit $ cmd (Cwd mirror) "git fetch --prune"
else do
unit $ cmd (Cwd mirror) "git clone --mirror" [repo] "."
gitSafe mirror
return mirror
gitUpdateState Nothing = traced "gitUpdateState Nothing" $ do
mirror <- gitInitMirror
Stdout hash <- cmd (Cwd mirror) "git rev-parse" [branch]
case words hash of
[] -> error "Couldn't find branch"
x:xs -> return $ sha1 $ trim x
gitUpdateState (Just (s, ps)) = traced "gitUpdateState Just" $ do
gitCheckout s ps
Stdout x <- cmd (Cwd path) "git rev-parse" [branch]
unit $ cmd (Cwd path) "git push" [repo] [branch ++ ":" ++ branch]
return $ sha1 $ trim x
gitCheckout s ps = traced "gitCheckout" $ do
createDirectoryIfMissing True path
mirror <- gitInitMirror
b <- doesDirectoryExist $ path </>".git"
if b then
unit $ cmd (Cwd path) "git pull origin"
else do
unit $ cmd (Cwd path) "git clone" [(if path == "." then "" else "../") ++ mirror] "." ["--branch",branch]
gitSafe path
unit $ cmd (Cwd path) "git checkout" [branch]
unit $ cmd (Cwd path) "git reset --hard" ["origin/" ++ branch]
Stdout x <- cmd (Cwd path) "git rev-parse HEAD"
when (trim x /= fromSHA1 s) $ error "Branch changed while running"
forM_ ps $ \p ->
unit $ cmd (Cwd path) "git merge" (fromSHA1 p)
gitPatchExtra s Nothing = traced "gitPatchExtra Nothing" $ do
mirror <- gitInitMirror
Stdout full <- cmd (Cwd mirror) "git log -n3" [fromSHA1 s]
return (concat $ take 1 $ lines full, tag_ "pre" full)
gitPatchExtra s (Just p) = traced "gitPatchExtra Just" $ do
mirror <- gitInitMirror
Stdout full <- cmd (Cwd mirror) "git diff" (fromSHA1 s ++ ".." ++ fromSHA1 p)
Stdout numstat <- cmd (Cwd mirror) "git diff --numstat" (fromSHA1 s ++ ".." ++ fromSHA1 p)
let xs = [x | [_,_,x] <- map words $ lines numstat]
return (unwordsLimit 3 xs, tag_ "pre" full)