Skip to content

Commit 3b75f8c

Browse files
authored
Add test log display (#1100)
* Add test log
1 parent 021eea0 commit 3b75f8c

File tree

9 files changed

+193
-72
lines changed

9 files changed

+193
-72
lines changed

datafiles/templates/Html/report.html.st

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,5 +100,16 @@ $else$
100100
<p>No log was submitted for this report.</p>
101101
$endif$
102102

103+
<h3>Test log</h3>
104+
105+
$if(test)$
106+
<p style="font-size: small">[<a href="/package/$pkgid$/reports/$report.0$/test">view raw</a>]</p>
107+
<pre>
108+
$test$</pre>
109+
$else$
110+
<p>No test log was submitted for this report.</p>
111+
$endif$
112+
113+
103114
</div>
104115
</body></html>

exes/BuildClient.hs

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ import System.Exit(exitFailure, ExitCode(..))
3838
import System.FilePath
3939
import System.Directory (canonicalizePath, createDirectoryIfMissing,
4040
doesFileExist, doesDirectoryExist, getDirectoryContents,
41-
renameFile, removeFile, getAppUserDataDirectory,
41+
renameFile, removeFile,
4242
createDirectory, removeDirectoryRecursive,
4343
createDirectoryIfMissing, makeAbsolute)
4444
import System.Console.GetOpt
@@ -156,13 +156,24 @@ initialise opts uri auxUris
156156
readMissingOpt prompt = maybe (putStrLn prompt >> getLine) return
157157

158158
-- | Parse the @00-index.cache@ file of the available package repositories.
159-
parseRepositoryIndices :: Verbosity -> IO (M.Map PackageIdentifier Tar.EpochTime)
160-
parseRepositoryIndices verbosity = do
161-
cabalDir <- getAppUserDataDirectory "cabal/packages"
159+
parseRepositoryIndices :: BuildOpts -> Verbosity -> IO (M.Map PackageIdentifier Tar.EpochTime)
160+
parseRepositoryIndices opts verbosity = do
162161
cacheDirs <- listDirectory cabalDir
163-
indexFiles <- filterM doesFileExist $ map (\dir -> cabalDir </> dir </> "01-index.tar") cacheDirs
162+
indexFiles <- catMaybes <$> mapM findIdx cacheDirs
164163
M.unions <$> mapM readIndex indexFiles
165164
where
165+
cabalDir = bo_stateDir opts </> "cached-tarballs"
166+
findIdx dir = do
167+
let index01 = cabalDir </> dir </> "01-index.tar"
168+
index00 = cabalDir </> dir </> "00-index.tar"
169+
b <- doesFileExist index01
170+
if b
171+
then return (Just index01)
172+
else do
173+
b2 <- doesFileExist index00
174+
if b2
175+
then return (Just index00)
176+
else return Nothing
166177
readIndex fname = do
167178
bs <- BS.readFile fname
168179
let mkPkg pkg entry = (pkg, Tar.entryTime entry)
@@ -364,6 +375,7 @@ data DocInfo = DocInfo {
364375
, docInfoIsCandidate :: Bool
365376
, docInfoRunTests :: Bool
366377
}
378+
deriving Show
367379

368380
docInfoPackageName :: DocInfo -> PackageName
369381
docInfoPackageName = pkgName . docInfoPackage
@@ -485,7 +497,7 @@ buildOnce opts pkgs = keepGoing $ do
485497
-- documentation index. Consequently, we make sure that the packages we are
486498
-- going to build actually appear in the repository before building. See
487499
-- #543.
488-
repoIndex <- parseRepositoryIndices verbosity
500+
repoIndex <- parseRepositoryIndices opts verbosity
489501

490502
pkgIdsHaveDocs <- getDocumentationStats verbosity opts config (Just pkgs)
491503
infoStats verbosity Nothing pkgIdsHaveDocs
@@ -576,9 +588,9 @@ processPkg verbosity opts config docInfo = do
576588
let installOk = fmap ("install-outcome: InstallOk" `isInfixOf`) buildReport == Just True
577589

578590
-- Run Tests if installOk, Run coverage is Tests runs
579-
(testOutcome, hpcLoc) <- case installOk && docInfoRunTests docInfo of
591+
(testOutcome, hpcLoc, testfile) <- case installOk && docInfoRunTests docInfo of
580592
True -> testPackage verbosity opts docInfo
581-
False -> return (Nothing, Nothing)
593+
False -> return (Nothing, Nothing, Nothing)
582594
coverageFile <- mapM (coveragePackage verbosity opts docInfo) hpcLoc
583595

584596
-- Modify test-outcome and rewrite report file.
@@ -587,7 +599,7 @@ processPkg verbosity opts config docInfo = do
587599
case bo_dryRun opts of
588600
True -> return ()
589601
False -> uploadResults verbosity config docInfo
590-
mTgz mRpt logfile coverageFile installOk
602+
mTgz mRpt logfile testfile coverageFile installOk
591603
where
592604
prepareTempBuildDir :: IO ()
593605
prepareTempBuildDir = do
@@ -637,7 +649,7 @@ coveragePackage verbosity opts docInfo loc = do
637649
return coverageFile
638650

639651

640-
testPackage :: Verbosity -> BuildOpts -> DocInfo -> IO (Maybe String, Maybe FilePath)
652+
testPackage :: Verbosity -> BuildOpts -> DocInfo -> IO (Maybe String, Maybe FilePath, Maybe FilePath)
641653
testPackage verbosity opts docInfo = do
642654
let pkgid = docInfoPackage docInfo
643655
testLogFile = (installDirectory opts) </> display pkgid <.> "test"
@@ -670,7 +682,7 @@ testPackage verbosity opts docInfo = do
670682
[ "Test results for " ++ display pkgid ++ ":"
671683
, testResultFile
672684
]
673-
return (testOutcome, hpcLoc)
685+
return (testOutcome, hpcLoc, Just testResultFile)
674686

675687

676688
-- | Build documentation and return @(Just tgz)@ for the built tgz file
@@ -862,9 +874,9 @@ tarGzDirectory dir = do
862874
where (containing_dir, nested_dir) = splitFileName dir
863875

864876
uploadResults :: Verbosity -> BuildConfig -> DocInfo -> Maybe FilePath
865-
-> Maybe FilePath -> FilePath -> Maybe FilePath -> Bool -> IO ()
877+
-> Maybe FilePath -> FilePath -> Maybe FilePath -> Maybe FilePath -> Bool -> IO ()
866878
uploadResults verbosity config docInfo
867-
mdocsTarballFile buildReportFile buildLogFile coverageFile installOk =
879+
mdocsTarballFile buildReportFile buildLogFile testLogFile coverageFile installOk =
868880
httpSession verbosity "hackage-build" version $ do
869881
-- Make sure we authenticate to Hackage
870882
setAuthorityGen (provideAuthInfo (bc_srcURI config)
@@ -874,21 +886,22 @@ uploadResults verbosity config docInfo
874886
Just docsTarballFile ->
875887
putDocsTarball config docInfo docsTarballFile
876888

877-
putBuildFiles config docInfo buildReportFile buildLogFile coverageFile installOk
889+
putBuildFiles config docInfo buildReportFile buildLogFile testLogFile coverageFile installOk
878890

879891
putDocsTarball :: BuildConfig -> DocInfo -> FilePath -> HttpSession ()
880892
putDocsTarball config docInfo docsTarballFile =
881893
requestPUTFile (docInfoDocsURI config docInfo)
882894
"application/x-tar" (Just "gzip") docsTarballFile
883895

884896
putBuildFiles :: BuildConfig -> DocInfo -> Maybe FilePath
885-
-> FilePath -> Maybe FilePath -> Bool -> HttpSession ()
886-
putBuildFiles config docInfo reportFile buildLogFile coverageFile installOk = do
897+
-> FilePath -> Maybe FilePath -> Maybe FilePath -> Bool -> HttpSession ()
898+
putBuildFiles config docInfo reportFile buildLogFile testLogFile coverageFile installOk = do
887899
reportContent <- liftIO $ traverse readFile reportFile
888900
logContent <- liftIO $ readFile buildLogFile
901+
testContent <- liftIO $ traverse readFile testLogFile
889902
coverageContent <- liftIO $ traverse readFile coverageFile
890903
let uri = docInfoReports config docInfo
891-
body = encode $ BR.BuildFiles reportContent (Just logContent) coverageContent (not installOk)
904+
body = encode $ BR.BuildFiles reportContent (Just logContent) testContent coverageContent (not installOk)
892905
setAllowRedirects False
893906
(_, response) <- request Request {
894907
rqURI = uri,

src/Distribution/Server/Features/BuildReports.hs

Lines changed: 70 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ module Distribution.Server.Features.BuildReports (
66
initBuildReportsFeature
77
) where
88

9-
import Distribution.Server.Framework hiding (BuildLog, BuildCovg)
9+
import Distribution.Server.Framework hiding (BuildLog, TestLog, BuildCovg)
1010

1111
import Distribution.Server.Features.Users
1212
import Distribution.Server.Features.Upload
@@ -16,7 +16,7 @@ import Distribution.Server.Features.BuildReports.Backup
1616
import Distribution.Server.Features.BuildReports.State
1717
import qualified Distribution.Server.Features.BuildReports.BuildReport as BuildReport
1818
import Distribution.Server.Features.BuildReports.BuildReport (BuildReport(..))
19-
import Distribution.Server.Features.BuildReports.BuildReports (BuildReports, BuildReportId(..), BuildCovg(..), BuildLog(..))
19+
import Distribution.Server.Features.BuildReports.BuildReports (BuildReports, BuildReportId(..), BuildCovg(..), BuildLog(..), TestLog(..))
2020
import qualified Distribution.Server.Framework.ResponseContentTypes as Resource
2121

2222
import Distribution.Server.Packages.Types
@@ -42,10 +42,11 @@ data ReportsFeature = ReportsFeature {
4242
reportsFeatureInterface :: HackageFeature,
4343

4444
packageReports :: DynamicPath -> ([(BuildReportId, BuildReport)] -> ServerPartE Response) -> ServerPartE Response,
45-
packageReport :: DynamicPath -> ServerPartE (BuildReportId, BuildReport, Maybe BuildLog, Maybe BuildCovg),
45+
packageReport :: DynamicPath -> ServerPartE (BuildReportId, BuildReport, Maybe BuildLog, Maybe TestLog, Maybe BuildCovg),
4646

4747
queryPackageReports :: forall m. MonadIO m => PackageId -> m [(BuildReportId, BuildReport)],
4848
queryBuildLog :: forall m. MonadIO m => BuildLog -> m Resource.BuildLog,
49+
queryTestLog :: forall m. MonadIO m => TestLog -> m Resource.TestLog,
4950
pkgReportDetails :: forall m. MonadIO m => (PackageIdentifier, Bool) -> m BuildReport.PkgDetails,
5051
queryLastReportStats:: forall m. MonadIO m => PackageIdentifier -> m (Maybe (BuildReportId, BuildReport, Maybe BuildCovg)),
5152
queryRunTests :: forall m. MonadIO m => PackageId -> m Bool,
@@ -60,8 +61,9 @@ data ReportsResource = ReportsResource {
6061
reportsList :: Resource,
6162
reportsPage :: Resource,
6263
reportsLog :: Resource,
63-
reportsReset:: Resource,
6464
reportsTest :: Resource,
65+
reportsReset:: Resource,
66+
reportsTestsEnabled :: Resource,
6567
reportsListUri :: String -> PackageId -> String,
6668
reportsPageUri :: String -> PackageId -> BuildReportId -> String,
6769
reportsLogUri :: PackageId -> BuildReportId -> String
@@ -121,8 +123,9 @@ buildReportsFeature name
121123
reportsList
122124
, reportsPage
123125
, reportsLog
124-
, reportsReset
125126
, reportsTest
127+
, reportsReset
128+
, reportsTestsEnabled
126129
]
127130
, featureState = [abstractAcidStateComponent reportsState]
128131
}
@@ -144,12 +147,12 @@ buildReportsFeature name
144147
]
145148
, resourceGet = [ ("", resetBuildFails) ]
146149
}
147-
, reportsTest = (extendResourcePath "/reports/test/" corePackagePage) {
150+
, reportsTestsEnabled = (extendResourcePath "/reports/testsEnabled/" corePackagePage) {
148151
resourceDesc = [ (GET, "Get reports test settings")
149152
, (POST, "Set reports test settings")
150153
]
151-
, resourceGet = [ ("json", getReportsTest) ]
152-
, resourcePost = [ ("", postReportsTest) ]
154+
, resourceGet = [ ("json", getReportsTestsEnabled) ]
155+
, resourcePost = [ ("", postReportsTestsEnabled) ]
153156
}
154157
, reportsPage = (extendResourcePath "/reports/:id.:format" corePackagePage) {
155158
resourceDesc = [ (GET, "Get a specific build report")
@@ -167,6 +170,15 @@ buildReportsFeature name
167170
, resourceDelete = [ ("", deleteBuildLog )]
168171
, resourcePut = [ ("", putBuildLog) ]
169172
}
173+
, reportsTest = (extendResourcePath "/reports/:id/test" corePackagePage) {
174+
resourceDesc = [ (GET, "Get the test log associated with a build report")
175+
, (DELETE, "Delete a test log")
176+
, (PUT, "Upload a test log for a build report")
177+
]
178+
, resourceGet = [ ("txt", serveTestLog) ]
179+
, resourceDelete = [ ("", deleteTestLog )]
180+
, resourcePut = [ ("", putTestLog) ]
181+
}
170182
, reportsListUri = \format pkgid -> renderResource (reportsList reportsResource) [display pkgid, format]
171183
, reportsPageUri = \format pkgid repid -> renderResource (reportsPage reportsResource) [display pkgid, display repid, format]
172184
, reportsLogUri = \pkgid repid -> renderResource (reportsLog reportsResource) [display pkgid, display repid]
@@ -187,26 +199,30 @@ buildReportsFeature name
187199
guardValidPackageId pkgid
188200
queryPackageReports pkgid >>= continue
189201

190-
packageReport :: DynamicPath -> ServerPartE (BuildReportId, BuildReport, Maybe BuildLog, Maybe BuildCovg)
202+
packageReport :: DynamicPath -> ServerPartE (BuildReportId, BuildReport, Maybe BuildLog, Maybe TestLog, Maybe BuildCovg)
191203
packageReport dpath = do
192204
pkgid <- packageInPath dpath
193205
guardValidPackageId pkgid
194206
reportId <- reportIdInPath dpath
195207
mreport <- queryState reportsState $ LookupReportCovg pkgid reportId
196208
case mreport of
197209
Nothing -> errNotFound "Report not found" [MText "Build report does not exist"]
198-
Just (report, mlog, covg) -> return (reportId, report, mlog, covg)
210+
Just (report, mlog, mtest, covg) -> return (reportId, report, mlog, mtest, covg)
199211

200212
queryPackageReports :: MonadIO m => PackageId -> m [(BuildReportId, BuildReport)]
201213
queryPackageReports pkgid = do
202214
reports <- queryState reportsState $ LookupPackageReports pkgid
203-
return $ map (second fst) reports
215+
return $ map (second (\(a, _, _) -> a)) reports
204216

205217
queryBuildLog :: MonadIO m => BuildLog -> m Resource.BuildLog
206218
queryBuildLog (BuildLog blobId) = do
207219
file <- liftIO $ BlobStorage.fetch store blobId
208220
return $ Resource.BuildLog file
209221

222+
queryTestLog :: MonadIO m => TestLog -> m Resource.TestLog
223+
queryTestLog (TestLog blobId) = do
224+
file <- liftIO $ BlobStorage.fetch store blobId
225+
return $ Resource.TestLog file
210226

211227
pkgReportDetails :: MonadIO m => (PackageIdentifier, Bool) -> m BuildReport.PkgDetails--(PackageIdentifier, Bool, Maybe (BuildStatus, Maybe UTCTime, Maybe Version))
212228
pkgReportDetails (pkgid, docs) = do
@@ -215,7 +231,7 @@ buildReportsFeature name
215231
runTests <- fmap Just . queryState reportsState $ LookupRunTests pkgid
216232
(time, ghcId) <- case latestRpt of
217233
Nothing -> return (Nothing,Nothing)
218-
Just (_, brp, _, _) -> do
234+
Just (_, brp, _, _, _) -> do
219235
let (CompilerId _ vrsn) = compiler brp
220236
return (time brp, Just vrsn)
221237
return (BuildReport.PkgDetails pkgid docs failCnt time ghcId runTests)
@@ -225,7 +241,7 @@ buildReportsFeature name
225241
lookupRes <- queryState reportsState $ LookupLatestReport pkgid
226242
case lookupRes of
227243
Nothing -> return Nothing
228-
Just (rptId, rpt, _, covg) -> return (Just (rptId, rpt, covg))
244+
Just (rptId, rpt, _, _, covg) -> return (Just (rptId, rpt, covg))
229245

230246
queryRunTests :: MonadIO m => PackageId -> m Bool
231247
queryRunTests pkgid = queryState reportsState $ LookupRunTests pkgid
@@ -235,19 +251,30 @@ buildReportsFeature name
235251
textPackageReports dpath = packageReports dpath $ return . toResponse . show
236252

237253
textPackageReport dpath = do
238-
(_, report, _, _) <- packageReport dpath
254+
(_, report, _, _, _) <- packageReport dpath
239255
return . toResponse $ BuildReport.show report
240256

241257
-- result: not-found error or text file
242258
serveBuildLog :: DynamicPath -> ServerPartE Response
243259
serveBuildLog dpath = do
244-
(repid, _, mlog, _) <- packageReport dpath
260+
(repid, _, mlog, _, _) <- packageReport dpath
245261
case mlog of
246262
Nothing -> errNotFound "Log not found" [MText $ "Build log for report " ++ display repid ++ " not found"]
247263
Just logId -> do
248264
cacheControlWithoutETag [Public, maxAgeDays 30]
249265
toResponse <$> queryBuildLog logId
250266

267+
-- result: not-found error or text file
268+
serveTestLog :: DynamicPath -> ServerPartE Response
269+
serveTestLog dpath = do
270+
(repid, _, _, mtest, _) <- packageReport dpath
271+
case mtest of
272+
Nothing -> errNotFound "Test log not found" [MText $ "Test log for report " ++ display repid ++ " not found"]
273+
Just logId -> do
274+
cacheControlWithoutETag [Public, maxAgeDays 30]
275+
toResponse <$> queryTestLog logId
276+
277+
251278
-- result: auth error, not-found error, parse error, or redirect
252279
submitBuildReport :: DynamicPath -> ServerPartE Response
253280
submitBuildReport dpath = do
@@ -300,6 +327,18 @@ buildReportsFeature name
300327
void $ updateState reportsState $ SetBuildLog pkgid reportId (Just $ BuildLog buildLog)
301328
noContent (toResponse ())
302329

330+
putTestLog :: DynamicPath -> ServerPartE Response
331+
putTestLog dpath = do
332+
pkgid <- packageInPath dpath
333+
guardValidPackageId pkgid
334+
reportId <- reportIdInPath dpath
335+
-- logged in users
336+
guardAuthorised_ [AnyKnownUser]
337+
blogbody <- expectTextPlain
338+
testLog <- liftIO $ BlobStorage.add store blogbody
339+
void $ updateState reportsState $ SetTestLog pkgid reportId (Just $ TestLog testLog)
340+
noContent (toResponse ())
341+
303342
{-
304343
Example using curl: (TODO: why is this PUT, while logs are POST?)
305344
@@ -319,6 +358,15 @@ buildReportsFeature name
319358
void $ updateState reportsState $ SetBuildLog pkgid reportId Nothing
320359
noContent (toResponse ())
321360

361+
deleteTestLog :: DynamicPath -> ServerPartE Response
362+
deleteTestLog dpath = do
363+
pkgid <- packageInPath dpath
364+
guardValidPackageId pkgid
365+
reportId <- reportIdInPath dpath
366+
guardAuthorised_ [InGroup trusteesGroup]
367+
void $ updateState reportsState $ SetTestLog pkgid reportId Nothing
368+
noContent (toResponse ())
369+
322370
guardAuthorisedAsMaintainerOrTrustee pkgname =
323371
guardAuthorised_ [InGroup (maintainersGroup pkgname), InGroup trusteesGroup]
324372

@@ -332,16 +380,16 @@ buildReportsFeature name
332380
then seeOther (reportsListUri reportsResource "" pkgid) $ toResponse ()
333381
else errNotFound "Report not found" [MText "Build report does not exist"]
334382

335-
getReportsTest :: DynamicPath -> ServerPartE Response
336-
getReportsTest dpath = do
383+
getReportsTestsEnabled :: DynamicPath -> ServerPartE Response
384+
getReportsTestsEnabled dpath = do
337385
pkgid <- packageInPath dpath
338386
guardValidPackageId pkgid
339387
guardAuthorisedAsMaintainerOrTrustee (packageName pkgid)
340388
runTest <- queryRunTests pkgid
341389
pure $ toResponse $ toJSON runTest
342390

343-
postReportsTest :: DynamicPath -> ServerPartE Response
344-
postReportsTest dpath = do
391+
postReportsTestsEnabled :: DynamicPath -> ServerPartE Response
392+
postReportsTestsEnabled dpath = do
345393
pkgid <- packageInPath dpath
346394
runTests <- body $ looks "runTests"
347395
guardValidPackageId pkgid
@@ -360,6 +408,7 @@ buildReportsFeature name
360408
buildFiles <- expectAesonContent::ServerPartE BuildReport.BuildFiles
361409
let reportBody = BuildReport.reportContent buildFiles
362410
logBody = BuildReport.logContent buildFiles
411+
testBody = BuildReport.testContent buildFiles
363412
covgBody = BuildReport.coverageContent buildFiles
364413
failStatus = BuildReport.buildFail buildFiles
365414

@@ -374,8 +423,9 @@ buildReportsFeature name
374423
guardAuthorisedAsMaintainerOrTrustee (packageName pkgid)
375424
report' <- liftIO $ BuildReport.affixTimestamp report
376425
logBlob <- liftIO $ traverse (\x -> BlobStorage.add store $ fromString x) logBody
426+
testBlob <- liftIO $ traverse (\x -> BlobStorage.add store $ fromString x) testBody
377427
reportId <- updateState reportsState $
378-
AddRptLogCovg pkgid (report', (fmap BuildLog logBlob), (fmap BuildReport.parseCovg covgBody))
428+
AddRptLogTestCovg pkgid (report', (fmap BuildLog logBlob), (fmap TestLog testBlob), (fmap BuildReport.parseCovg covgBody))
379429
-- redirect to new reports page
380430
seeOther (reportsPageUri reportsResource "" pkgid reportId) $ toResponse ()
381431

0 commit comments

Comments
 (0)