ddc-war-0.2.1.1: DDC/War/Task/Nightly.hs
{-# OPTIONS -fno-warn-unused-matches #-}
-- | Run the nightly DDC build.
module DDC.War.Task.Nightly
( Spec (..)
, Result (..)
, build)
where
import BuildBox.Command.Mail
import BuildBox.Command.System
import BuildBox.Command.File
import BuildBox.Command.Environment
import BuildBox.Control.Cron
import BuildBox.Pretty
import BuildBox.Build
import BuildBox.Time
import BuildBox as BuildBox
import Control.Monad
import Data.Char
import System.FilePath.Posix as Remote
import System.FilePath as Local
-- Spec -----------------------------------------------------------------------
data Spec
= Spec
{ -- | Use this scratch directory to perform the build.
specLocalBuildDir :: Maybe FilePath
-- | URL of DDC snapshot.tgz
, specRemoteSnapshotURL :: String
-- | URL of DDC repository, used to update the snapshot.
, specRemoteRepoURL :: String
-- | Number of threads to use when building.
, specBuildThreads :: Int
-- | Cleanup after build
, specCleanup :: Bool
-- | If set build continuously
, specContinuous :: Maybe When
-- | If we're doing a continuous build, also build once on startup
, specNow :: Bool
-- | Mailer to use to send build results,
-- or Nothing if to not send mail.
, specMailer :: Maybe Mailer
-- | Send mail from this address.
, specMailFrom :: Maybe String
-- | Send mail to this address.
, specMailTo :: Maybe String
-- | User and host name to copy logs to eg 'overlord@deluge.ouroborus.net'
, specLogUserHost :: Maybe String
-- | Copy logs into this directory on the server
, specLogRemoteDir :: Maybe String
-- | HTTP address of where the above logs appear
, specLogRemoteURL :: Maybe String }
deriving Show
-- Result ---------------------------------------------------------------------
data Result
= ResultSuccess
| ResultFailure
instance Pretty Result where
ppr result
= case result of
ResultSuccess -> text "success"
ResultFailure -> text "failure"
-- Build ----------------------------------------------------------------------
-- | Run the nightly DDC build.
build :: Spec -> Build Result
build spec
= case specContinuous spec of
-- Do a one-shot build.
Nothing
-> buildCatch spec
-- Continuous build
Just whence
-> do outLn $ "* Starting cron loop"
cronLoop
$ makeSchedule
[ ("nightly"
, whence
, if specNow spec
then Just Immediate
else Nothing
, buildCatch spec >> return ()) ]
-- The cron loop will quit if the build died and we couldn't
-- send a failure message. If that happens then give up completely.
return ResultFailure
buildCatch :: Spec -> Build Result
buildCatch spec
= BuildBox.catch
(buildProject spec)
(\err -> do
postFailure spec err
return ResultFailure)
buildProject :: Spec -> Build Result
buildProject spec
= do
strTime <- io $ getStampyTime
let Just localBuildDir = specLocalBuildDir spec
let buildDir = localBuildDir Local.</> strTime
let urlSnapshot = specRemoteSnapshotURL spec
let urlRepo = specRemoteRepoURL spec
let buildThreads = specBuildThreads spec
ensureDir buildDir
inDir buildDir
$ do
outLn "* Creating log directory"
ensureDir "log"
outLn "* Downloading snapshot"
(getOut, getErr) <- ssystem $ "wget --progress=bar " ++ urlSnapshot
io $ writeFile "log/01-wget.stdout" getOut
io $ writeFile "log/01-wget.stderr" getErr
needs (takeFileName urlSnapshot)
outLn "* Unpacking snapshot"
ssystem $ "tar zxf " ++ takeFileName urlSnapshot
inDir "ddc-head"
$ do
outLn "* Updating shapshot"
(darcsOut, darcsErr) <- ssystem $ "darcs pull -av " ++ urlRepo
io $ writeFile "../log/02-darcs.stdout" darcsOut
io $ writeFile "../log/02-darcs.stderr" darcsErr
outLn "* Writing build config"
needs "make"
io $ writeFile "make/config-override.mk"
$ unlines ["THREADS = " ++ show buildThreads]
outLn "* Building project"
needs "Makefile"
(makeOut, makeErr) <- ssystem $ "make nightly"
io $ writeFile "../log/03-make.stdout" makeOut
io $ writeFile "../log/03-make.stderr" makeErr
outLn "* Copying results"
needs "war.results"
needs "war.failed"
ssystem "cp war.results ../log"
ssystem "cp war.failed ../log"
return ()
-- Copy logs to remote host.
copyLogs spec strTime
-- Send mail reporting build success including failed tests.
strFailed <- io $ readFile "log/war.failed"
postSuccess spec strTime strFailed
when (specCleanup spec)
$ do outLn "* Cleaning up"
clobberDir buildDir
return ResultSuccess
-- | Copy logs to the remote host
copyLogs :: Spec -> String -> Build ()
copyLogs spec strTime
| Just userHost <- specLogUserHost spec
, Just logDir <- specLogRemoteDir spec
= do
outLn $ "* Copying logs to " ++ userHost
let dir = logDir Remote.</> strTime
-- Make the destination directory.
sesystemq $ "ssh " ++ userHost ++ " mkdir -p " ++ dir
-- Copy the files.
sesystemq $ "scp -r log/* " ++ userHost ++ ":" ++ dir
return ()
| otherwise = return ()
-- | Send mail reporting build success.
postSuccess :: Spec -> String -> String -> Build ()
postSuccess spec strTime strFailed
| Just mailer <- specMailer spec
, Just addrFrom <- specMailFrom spec
, Just addrTo <- specMailTo spec
= do
outLn "* Reporting build success"
-- The overall build succeeded, but some tests might have failed.
let nFailed = length $ lines strFailed
-- Create message subject.
hostId <- getHostId
let subject
| nFailed == 0
= "DDC Build Success (" ++ hostId ++ ")"
| otherwise
= "DDC Build Success (" ++ hostId ++ ") with " ++ show nFailed ++ " failed tests"
-- Create message body.
let strLog
= case specLogRemoteURL spec of
Nothing -> ""
Just url -> "Logs are at: " ++ url ++ "/" ++ strTime ++ "\n\n"
let body = strLog ++ strFailed
-- Send the mail
mail <- createMailWithCurrentTime addrFrom addrTo subject body
sendMailWithMailer mail mailer
-- don't send mail.
postSuccess _ _ _ = return ()
-- | Send mail reporting build failure.
postFailure :: Spec -> BuildError -> Build ()
postFailure spec err
| Just mailer <- specMailer spec
, Just addrFrom <- specMailFrom spec
, Just addrTo <- specMailTo spec
= do
outLn "* Reporting build failure"
-- Create message subject.
hostId <- getHostId
let subject = "DDC Build FAILURE (" ++ hostId ++ ")"
-- Create message body.
let body = render $ ppr err
-- Send the mail
mail <- createMailWithCurrentTime addrFrom addrTo subject body
sendMailWithMailer mail mailer
-- don't send mail
postFailure spec strFailed = return ()
-- | Build a string identifying this host
getHostId :: Build String
getHostId
= do
hostName <- liftM (takeWhile (not . (==) '.')) $ getHostName
machName <- liftM (takeWhile (not . isSpace)) $ sesystemq "uname -m"
osName <- liftM (takeWhile (not . isSpace)) $ sesystemq "uname"
return $ hostName ++ "." ++ machName ++ "." ++ osName