koji-tool-0.8.3: src/Builds.hs
{-# LANGUAGE CPP #-}
-- SPDX-License-Identifier: BSD-3-Clause
module Builds (
BuildReq(..),
buildsCmd,
parseBuildState,
fedoraKojiHub,
kojiBuildTypes,
latestCmd
)
where
import Control.Monad.Extra
import Data.Char (isDigit, toUpper)
import Data.List.Extra
import Data.Maybe
import Data.RPM.NVR
import Data.Time.Clock
import Data.Time.Format
import Data.Time.LocalTime
import Distribution.Koji
import Distribution.Koji.API
import SimpleCmd
import Text.Pretty.Simple
import Common
import qualified Tasks
import Time
import User
data BuildReq = BuildBuild String | BuildPackage String
| BuildQuery | BuildPattern String
deriving Eq
getTimedate :: Tasks.BeforeAfter -> String
getTimedate (Tasks.Before s) = s
getTimedate (Tasks.After s) = s
capitalize :: String -> String
capitalize "" = ""
capitalize (h:t) = toUpper h : t
buildsCmd :: Maybe String -> Maybe UserOpt -> Int -> [BuildState]
-> Maybe Tasks.BeforeAfter -> Maybe String -> Bool -> Bool
-> BuildReq -> IO ()
buildsCmd mhub museropt limit states mdate mtype details debug buildreq = do
let server = maybe fedoraKojiHub hubURL mhub
when (server /= fedoraKojiHub && museropt == Just UserSelf) $
error' "--mine currently only works with Fedora Koji: use --user instead"
tz <- getCurrentTimeZone
case buildreq of
BuildBuild bld -> do
when (isJust mdate) $
error' "cannot use buildinfo together with timedate"
let bldinfo = if all isDigit bld
then InfoID (read bld)
else InfoString bld
mbld <- getBuild server bldinfo
whenJust (mbld >>= maybeBuildResult) $ printBuild server tz
BuildPackage pkg -> do
when (head pkg == '-') $
error' $ "bad combination: not a package: " ++ pkg
when (isJust mdate) $
error' "cannot use --package together with timedate"
mpkgid <- getPackageID server pkg
case mpkgid of
Nothing -> error' $ "no package id found for " ++ pkg
Just pkgid -> do
let fullquery = [("packageID", ValueInt pkgid),
commonBuildQueryOptions limit]
when debug $ print fullquery
builds <- listBuilds server fullquery
when debug $ mapM_ pPrintCompact builds
if details || length builds == 1
then mapM_ (printBuild server tz) $ mapMaybe maybeBuildResult builds
else mapM_ putStrLn $ mapMaybe (shortBuildResult tz) builds
_ -> do
query <- setupQuery server
let fullquery = query ++ [commonBuildQueryOptions limit]
when debug $ print fullquery
builds <- listBuilds server fullquery
when debug $ mapM_ pPrintCompact builds
if details || length builds == 1
then mapM_ (printBuild server tz) $ mapMaybe maybeBuildResult builds
else mapM_ putStrLn $ mapMaybe (shortBuildResult tz) builds
where
shortBuildResult :: TimeZone -> Struct -> Maybe String
shortBuildResult tz bld = do
nvr <- lookupStruct "nvr" bld
state <- readBuildState <$> lookupStruct "state" bld
let date =
case lookupTime "completion" bld of
Just t -> compactZonedTime tz t
Nothing ->
case lookupTime "start" bld of
Just t -> compactZonedTime tz t
Nothing -> ""
return $ nvr +-+ show state +-+ date
setupQuery server = do
mdatestring <-
case mdate of
Nothing -> return Nothing
Just date -> Just <$> cmd "date" ["+%F %T%z", "--date=" ++ dateString date]
-- FIXME better output including user
whenJust mdatestring $ \date ->
putStrLn $ maybe "" show mdate +-+ date
mowner <- maybeGetKojiUser server museropt
return $
[("complete" ++ (capitalize . show) date, ValueString datestring) | Just date <- [mdate], Just datestring <- [mdatestring]]
++ [("userID", ValueInt (getID owner)) | Just owner <- [mowner]]
++ [("state", ValueArray (map buildStateToValue states)) | notNull states]
++ [("type", ValueString typ) | Just typ <- [mtype]]
++ case buildreq of
BuildPattern pat -> [("pattern", ValueString pat)]
_ -> []
dateString :: Tasks.BeforeAfter -> String
-- make time refer to past not future
dateString beforeAfter =
let timedate = getTimedate beforeAfter
in case words timedate of
[t] | t `elem` ["hour", "day", "week", "month", "year"] ->
"last " ++ t
[t] | t `elem` ["today", "yesterday"] ->
t ++ " 00:00"
[t] | any (lower t `isPrefixOf`) ["monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"] ->
"last " ++ t ++ " 00:00"
[n,_unit] | all isDigit n -> timedate ++ " ago"
_ -> timedate
pPrintCompact =
#if MIN_VERSION_pretty_simple(4,0,0)
pPrintOpt CheckColorTty
(defaultOutputOptionsDarkBg {outputOptionsCompact = True})
#else
pPrint
#endif
-- FIXME
data BuildResult =
BuildResult {_buildNVR :: NVR,
_buildState :: BuildState,
_buildId :: Int,
_mtaskId :: Maybe Int,
_buildStartTime :: UTCTime,
mbuildEndTime :: Maybe UTCTime
}
maybeBuildResult :: Struct -> Maybe BuildResult
maybeBuildResult st = do
start_time <- lookupTime "start" st
let mend_time = lookupTime "completion" st
buildid <- lookupStruct "build_id" st
-- buildContainer has no task_id
let mtaskid = lookupStruct "task_id" st
state <- getBuildState st
nvr <- lookupStruct "nvr" st >>= maybeNVR
return $
BuildResult nvr state buildid mtaskid start_time mend_time
printBuild :: String -> TimeZone -> BuildResult -> IO ()
printBuild server tz task = do
putStrLn ""
let mendtime = mbuildEndTime task
time <- maybe getCurrentTime return mendtime
(mapM_ putStrLn . formatBuildResult server (isJust mendtime) tz) (task {mbuildEndTime = Just time})
formatBuildResult :: String -> Bool -> TimeZone -> BuildResult -> [String]
formatBuildResult server ended tz (BuildResult nvr state buildid mtaskid start mendtime) =
-- FIXME any better way?
let weburl = dropSuffix "hub" server
in
[ showNVR nvr +-+ show state
, weburl ++ "/buildinfo?buildID=" ++ show buildid]
++ [weburl ++ "/taskinfo?taskID=" ++ show taskid | Just taskid <- [mtaskid]]
++ [formatTime defaultTimeLocale "Start: %c" (utcToZonedTime tz start)]
++
case mendtime of
Nothing -> []
Just end ->
[formatTime defaultTimeLocale "End: %c" (utcToZonedTime tz end) | ended]
#if MIN_VERSION_time(1,9,1)
++
let dur = diffUTCTime end start
in [(if not ended then "current " else "") ++ "duration: " ++ formatTime defaultTimeLocale "%Hh %Mm %Ss" dur]
#endif
#if !MIN_VERSION_koji(0,0,3)
buildStateToValue :: BuildState -> Value
buildStateToValue = ValueInt . fromEnum
parseBuildState :: String -> BuildState
parseBuildState s =
case lower s of
"building" -> BuildBuilding
"complete" -> BuildComplete
"deleted" -> BuildDeleted
"fail" -> BuildFailed
"failed" -> BuildFailed
"cancel" -> BuildCanceled
"canceled" -> BuildCanceled
_ -> error' $! "unknown task state: " ++ s
#endif
getBuildState :: Struct -> Maybe BuildState
getBuildState st = readBuildState <$> lookup "state" st
kojiBuildTypes :: [String]
kojiBuildTypes = ["all", "image", "maven", "module", "rpm", "win"]
latestCmd :: Maybe String -> Bool -> String -> String -> IO ()
latestCmd mhub debug tag pkg = do
let server = maybe fedoraKojiHub hubURL mhub
mbld <- kojiLatestBuild server tag pkg
when debug $ print mbld
tz <- getCurrentTimeZone
whenJust (mbld >>= maybeBuildResult) $ printBuild server tz