packages feed

fbrnch-1.6.2: src/Cmd/ReviewPackage.hs

{-# LANGUAGE CPP, OverloadedStrings #-}

module Cmd.ReviewPackage (
  reviewPackage
  ) where

import Common
import Common.System

import Data.Char
import Data.Tuple.Extra (second)
import Safe (headDef, headMay, tailSafe)
import SelectRPMs (selectDefault)
import SimplePrompt (promptEnter, yesNoDefault)

import Branches
import Bugzilla
import Cmd.Import (downloadReviewSRPM, upstreamDir)
import Cmd.Install (installCmd)
import Cmd.Local (localCmd)
import Git (isGitRepo, git_, gitBool)
import Package
import RpmBuild

-- FIXME does not work with pkg dir/spec: 'fbrnch: No spec file found'
-- FIXME --user to download all user's review requests
reviewPackage :: Bool -> Maybe String -> IO ()
reviewPackage interactive Nothing = do
  -- FIXME catch no spec file
  spec <- findSpecfile
  srpm <- generateSrpm Nothing spec
  if interactive
    then doInteractiveReview False (Just spec) srpm
    else do
    cmd_ "fedora-review" ["-rn", srpm]
reviewPackage interactive (Just pkgbug) = do
  let epkgbid =
        if all isDigit pkgbug
        then Right $ read pkgbug
        else Left pkgbug
  (bugs,session) <- bugsSession $
          case epkgbid of
            Right bid -> packageReview .&&. statusNewAssigned .&&. bugIdIs bid
            Left pkg -> pkgReviews pkg .&&. statusNewAssigned
  case bugs of
    [bug] -> do
      putReviewBug False bug
      let bid = bugId bug
          pkg = reviewBugToPackage bug
      if interactive
        then reviewPackageInteractive bid pkg session bug
        else do
        promptEnter "Press Enter to run fedora-review"
        -- FIXME support copr build
        -- FIXME if toolbox set REVIEW_NO_MOCKGROUP_CHECK
        cmd_ "fedora-review" ["-b", show bid]
    [] -> error' $ "No package review found for" +-+ pkgbug
    _ -> error' $ "More than one review bug found for" +-+ pkgbug

reviewPackageInteractive :: Int -> String -> BugzillaSession -> Bug -> IO ()
reviewPackageInteractive bid pkg session bug = do
  let dir = show bid ++ '-' : pkg
  -- FIXME check if current directory
  exists <- doesDirectoryExist dir
  unless exists $
    createDirectory dir
  setCurrentDirectory dir
  srpm <- downloadReviewSRPM True False pkg bid session
  importsrpm <-
    if exists
    then do
    putStrLn $ "in" +-+ dir </> ":\n"
    cmd_ "ls" ["-F"]
    putNewLn
    -- FIXME default to no if nvr unchanged?
    yesNoDefault True "Press Enter to install/prep srpm"
    else return True
  -- review and package name may be different (eg ramalama)
  doInteractiveReview importsrpm Nothing srpm
  putNewLn
  putReviewBug False bug
  putNewLn
  putStrLn $ "Review dir is" +-+ dir

doInteractiveReview :: Bool -> Maybe FilePath -> FilePath -> IO ()
doInteractiveReview importsrpm mspec srpm = do
  when importsrpm $ do
    isgit <- isGitRepo
    unless isgit $ git_ "init" []
    -- FIXME override %_sourcedir so it doesn't put elsewhere?
    sourcediropt <- sourceDirCwdOpt
    putStrLn "installing srpm:"
    cmd_ "rpm" $ ["-i", srpm] ++ sourcediropt
    spec <- maybe findSpecfile return mspec
    git_ "add" [spec]
    allsrcs <- map sourceFieldFile <$> cmdLines "spectool" [spec]
    forM_ allsrcs $ \src ->
      unless (isArchiveFile src) $
      git_ "add" [src]
    putStrLn $ "# Diff with" +-+ upstreamDir
    cmd_ "diff" ["-u", spec, upstreamDir </> spec]
    withCurrentDirectory upstreamDir $
      void $ getSources spec
    diff <- lines <$> cmdIgnoreErr "diff" ["--brief", ".", upstreamDir] ""
    let filterdiff = filter (\d -> not (any (`isSuffixOf` d) ["SPEC","SRPMS","RPMS","BUILD","BUILDROOT","src.rpm",".log", ".git", upstreamDir])) diff
    if null filterdiff
      then putStrLn $ "no difference with" +-+ upstreamDir ++ "/"
      else mapM_ putStrLn filterdiff
    unlessM (gitBool "diff" ["--quiet", "--cached"]) $
      git_ "commit" ["-m", srpm]
  putNewLn
  putStrLn "# Build"
  -- FIXME or download rpms
  build <- yesNoDefault importsrpm "Build package locally"
  when build $
    localCmd False False False Nothing Nothing [] (Branches [],[])
  putNewLn
  putStrLn "# RpmLint"
  void $ cmdBool "rpmlint" ["."] -- FIXME $ spec:srpm:rpms
  spec <- maybe findSpecfile return mspec
  whenM (yesNoDefault importsrpm "Install packages locally") $ do
    installCmd False False Nothing Nothing Nothing [] False True True selectDefault Nothing (Nothing,[])
    rpms <- cmdLines "rpmspec" ["-q", "--rpms", "--qf", "%{name}\n", spec]
    whenM (yesNoDefault importsrpm "Rpmlint installed packages") $ do
       (_ok, out, err) <- cmdFull "rpmlint" ("-i" : rpms) ""
       let rpmlintout = "rpmlint.output"
       writeFile rpmlintout out
       let ls = lines out
           nolines = length ls
       if nolines > 20
         then do
         mapM_ putStrLn $ takeEnd 10 ls
         putNewLn
         putStrLn "RpmLint summary:"
         mapM_ (putStrLn . renderLintSummary) $ summarizeErrors ls
         putStrLn $ show nolines +-+ "lines saved to" +-+ rpmlintout
         else putStrLn out
       unless (null err) $ warning $ "rpmlint stderr:\n" ++ err
       putNewLn
  putStrLn "# Licensing"
  -- FIXME use build subdir
  -- FIXME filter out files not in tarball or prep
  cmdLines "licensecheck" ["-r", "BUILD"] >>=
    -- handle "FILEPATH: *No copyright* UNKNOWN [generated file]"
    mapM_ putStrLn . filter (not . (" UNKNOWN" `isInfixOf`))
  cmd_ "rpmspec" ["-q", "--srpm", "--qf", "Spec license: %{license}\n", spec]

summarizeErrors :: [String] -> [(String,Int)]
summarizeErrors =
  sortOn fst . map (second length) . groupOnKey (headDef "''") . map tailSafe . filter ((Just "E:" ==) . headMay) . map (tailSafe . words)

renderLintSummary :: (String,Int) -> String
renderLintSummary (err,n) = err ++ ":" +-+ show n

#if !MIN_VERSION_extra(1,7,11)
groupOnKey :: Eq k => (a -> k) -> [a] -> [(k, [a])]
groupOnKey _ []     = []
groupOnKey f (x:xs) = (fx, x:yes) : groupOnKey f no
    where
        fx = f x
        (yes, no) = span (\y -> fx == f y) xs
#endif