fedora-haskell-tools 0.8 → 0.9
raw patch · 12 files changed
+304/−196 lines, 12 filesdep ~simple-cmddep ~simple-cmd-argsdep ~time
Dependency ranges changed: simple-cmd, simple-cmd-args, time
Files
- ChangeLog +19/−6
- README.md +3/−1
- TODO +9/−9
- fedora-haskell-tools.cabal +9/−9
- fhbz.hs +5/−4
- fhmock.hs +4/−1
- fhpkg.hs +154/−104
- src/Build.hs +12/−12
- src/Dist.hs +14/−4
- src/Koji.hs +12/−8
- src/RPM.hs +49/−36
- src/Utils.hs +14/−2
ChangeLog view
@@ -1,4 +1,17 @@-* 0.8 (2019-04-26)+# 0.9 (2019-10-30)+- changes for Fedora 31+- ghc now lives in ../+- 'list' now repoqueries for Haskell packages+- new commands: 'cblrpm', 'depends', 'diffstat', 'fetch'+- 'refresh' gained -n+- rename 'diff-stackage' to 'stackage-compare' and improve output+- setTermTitle for Koji builds+- some commands now only print header if there's output+- fhbz: no longer assumes haskell-devel CC, and sets Fixed In: nvr+- hackageRelease moved from fedora-dists+- no longer cache overrides in .fhbuilt++# 0.8 (2019-04-26) - remaining --counting option - make kojiWatchTask safe by checking taskinfo status - move distTag and distTarget from fedora-dists to Dist@@ -9,7 +22,7 @@ - use simple-cmd-opts-0.1.1 option wrappers - fix Hackage data generation repoqueries -* 0.7 (2019-02-28)+# 0.7 (2019-02-28) - merge fhbuild into fhpkg (please delete fhbuild) - use simple-cmd and fedora-dists - fhpkg:@@ -23,7 +36,7 @@ - new fhmock program - use Dist type -* 0.6 (2018-08-07)+# 0.6 (2018-08-07) - fhpkg - new commands: hackage-compare, merge, missing, count, leaf, unpushed, head-origin - command options including arguments and global options@@ -43,7 +56,7 @@ - ghc-8.4 - lot refactoring for utils etc -* 0.5 (2018-02-16)+# 0.5 (2018-02-16) - fhpkg - add commands for prep, refresh, diff-origin, push, and commit - only update hackages@@ -62,14 +75,14 @@ - repoquery koji build repo - add bump command -* 0.4 (2017-12-05)+# 0.4 (2017-12-05) - new commands: clone-new, diff-stackage, diff-branch - fix hackage distro data output - check spec file exists - fix cloning of missing branch dirs - allow no dist arg before pkg arg -* 0.3 (2017-10-24)+# 0.3 (2017-10-24) - initial release with fhbuild, fhbz, and fhpkg # Local Variables:
README.md view
@@ -6,13 +6,15 @@ # Fedora Haskell Tools +Tools for Fedora Haskell package maintainence.+ ## fhpkg Git clones and pull, etc Fedora Haskell package repos. Can also generate Hackage distro meta data. Builds set of packages locally or in Koji or Mock. (fhbuild has been merged into fhpkg.) -For local builds unbuilt dependent packages are also built recursive.+For local builds unbuilt dependent packages are also built recursively. For updates built in Koji, buildroot overrides are created and waited for. Chain builds packages with wait-repo.
TODO view
@@ -1,16 +1,19 @@-split into separate packages or merge??+- warn about unpushed commit(s) without any bump+- filter out ghc-prof & ghc-doc deps, ghc-rpm-macros removal?, ghc-srpm-macros+- filter xmonad and pandoc-pdf from install +- bodhi overrides not happening for chain deps+- check update deps in candidate tag+ # fhpkg-- use optparse-applicative-- only print header if output - license-check command-* use pagure-cli to get new ghc-* packages+- -B dist is kind of nonsense+- library+- allow pkg/ or filter out '/' (from fhpkg.hs): - cache dist package lists?-- push subpackages to Hackage - compare branch versions-- compare with LTS - query-format string deps query tool@@ -21,8 +24,6 @@ allow master instead of rawhide -(`fhpkg clone -B branch pkg` doesn't really make sense)- check commands has all commands count: option to query without updates@@ -32,7 +33,6 @@ # support chain-build # git-- branch merging * merge a/next koji build - build new packages across branches
fedora-haskell-tools.cabal view
@@ -1,5 +1,5 @@ Name: fedora-haskell-tools-Version: 0.8+Version: 0.9 Synopsis: Building and maintenance tools for Fedora Haskell Description: Tools for building and managing Fedora Haskell packages.@@ -15,7 +15,7 @@ Extra-source-files: README.md TODO ChangeLog Cabal-version: >=1.6 Tested-with: GHC == 7.6.3, GHC == 7.8.4, GHC == 7.10.3, GHC == 8.0.2,- GHC == 8.2.2, GHC == 8.4.4, GHC == 8.6.4+ GHC == 8.2.2, GHC == 8.4.4, GHC == 8.6.5 source-repository head type: git@@ -29,11 +29,12 @@ fedora-dists, filepath, HTTP,+ optparse-applicative, process,- simple-cmd >= 0.1.4,- simple-cmd-args >= 0.1.1,+ simple-cmd >= 0.2.0,+ simple-cmd-args >= 0.1.2, split,- time < 1.9,+ time, unix Hs-Source-Dirs: src, . Other-modules: Build, Dist, Koji, Paths_fedora_haskell_tools, Utils@@ -47,7 +48,7 @@ fedora-dists, time Hs-Source-Dirs: src, .- Other-modules: Dist, Koji, RPM+ Other-modules: Dist, Koji, RPM, Utils GHC-options: -fwarn-missing-signatures -Wall Extensions: CPP @@ -55,9 +56,8 @@ Main-is: fhmock.hs Build-depends: base < 5, fedora-dists,- optparse-applicative,- simple-cmd >= 0.1.1,- simple-cmd-args+ simple-cmd >= 0.2.0,+ simple-cmd-args >= 0.1.2 if impl(ghc<8.0) Build-depends: semigroups Hs-Source-Dirs: src, .
fhbz.hs view
@@ -66,13 +66,14 @@ (_,_,errs) -> do prog <- getProgName error $ concat errs ++ usageInfo (header prog) options- where header prog = "Usage:" +-+ prog +-+ "[OPTION...] [package]..."+ where header prog = "Usage:" +-+ prog +-+ "[OPTION...] [PACKAGE...]" main :: IO () main = do (opts, args) <- getArgs >>= parseOpts+ when (null args) $ error "must give one or more packages" let state = fromMaybe "NEW" $ listToMaybe $ map (\ (State s) -> s) $ filter isState opts- bugs <- parseLines . lines <$> bugzillaQuery (["--cc=haskell-devel@lists.fedoraproject.org", "--bug_status=" ++ state, "--short_desc=is available", "--outputformat=%{id}\n%{component}\n%{bug_status}\n%{summary}\n%{status_whiteboard}"] ++ if null args then [] else ["--component=" ++ intercalate "," args])+ bugs <- parseLines . lines <$> bugzillaQuery (["--bug_status=" ++ state, "--short_desc=is available", "--outputformat=%{id}\n%{component}\n%{bug_status}\n%{summary}\n%{status_whiteboard}"] ++ ["--component=" ++ intercalate "," args]) mapM_ (checkBug opts) bugs bugzillaQuery :: [String] -> IO String@@ -148,7 +149,7 @@ when (nv == pkgver) $ do putStrLn $ "closing" +-+ bid ++ ":" +-+ nv +-+ "in rawhide" unless (DryRun `elem` opts) $- bugzillaModify ["--close=RAWHIDE", "--comment=" ++ nvr, bid]+ bugzillaModify ["--close=RAWHIDE", "--fixed_in=" ++ nvr, "--comment=Closed with fhbz from fedora-haskell-tools", bid] where removeRelease = init . dropWhileEnd (/= '-') @@ -161,7 +162,7 @@ then "No missing dependencies for" +-+ hkgver +-+ "\naccording to cblrpm missingdeps" {-++ (if null rebuilds then "\nwithout any other package rebuilds." else ".\n\nIt would require also rebuilding:\n" +-+ unwords rebuilds)-} else "cblrpm missingdeps output for" +-+ hkgver ++ ":\n\n" ++ missing bugzillaModify $ ["--whiteboard==" ++ hkgver ++ ":" ++ state] ++- (if nocomment then [] else ["--comment=" ++ comment]) ++ [bid]+ ["--comment=" ++ comment | not nocomment] ++ [bid] cabalUpdate :: IO () cabalUpdate = do
fhmock.hs view
@@ -11,6 +11,10 @@ -- the Free Software Foundation, either version 3 of the License, or -- (at your option) any later version. +#if (defined(MIN_VERSION_base) && MIN_VERSION_base(4,13,0))+#else+import Control.Applicative (many, pure, some)+#endif import Data.List (sort) import System.Environment (getArgs) @@ -20,7 +24,6 @@ import FedoraDists (Dist, distRepo, releaseVersion) import SimpleCmd (cmd_) import SimpleCmdArgs-import Options.Applicative #if (defined(MIN_VERSION_optparse_applicative) && MIN_VERSION_optparse_applicative(0,13,0)) --import Data.Semigroup ((<>)) #endif
fhpkg.hs view
@@ -14,19 +14,21 @@ module Main where +#if (defined(MIN_VERSION_base) && MIN_VERSION_base(4,13,0))+#else import Control.Applicative (optional, some, (<|>) #if (defined(MIN_VERSION_base) && MIN_VERSION_base(4,8,0)) #else , pure, (<$>), (<*>) #endif )+#endif import Control.Monad (filterM, unless, when, (>=>)) import Data.Maybe import Data.List (intercalate, isInfixOf, isPrefixOf, nub, sort, (\\)) import Data.List.Split (splitOn) import Network.HTTP (getRequest, getResponseBody, simpleHTTP)-import Options.Applicative (Parser, auto, flag') import System.Directory (doesDirectoryExist, doesFileExist, getCurrentDirectory, getHomeDirectory, #if (defined(MIN_VERSION_directory) && MIN_VERSION_directory(1,2,5))@@ -36,20 +38,19 @@ #endif setCurrentDirectory) import System.FilePath ((</>), (<.>), takeFileName)-import System.IO (BufferMode(..), hSetBuffering, stdout)+import System.IO (BufferMode(..), hIsTerminalDevice, hSetBuffering, stdout) --import System.Posix.Env (getEnv) import Text.CSV (parseCSV) -import FedoraDists (Dist(..), distBranch, distRepo, distUpdates,- hackageRelease, rawhide)+import FedoraDists (Dist(..), distBranch, distRepo, distUpdates, rawhide) -import SimpleCmd ((+-+), cmd, cmd_, cmdBool, cmdLines, cmdMaybe, cmdSilent,- grep_, removePrefix, shell_)-import SimpleCmd.Git (git, git_, gitBranch, isGitDir)+import SimpleCmd ((+-+), cmd, cmd_, cmdBool, cmdLines, cmdMaybe, cmdQuiet,+ cmdSilent, grep_, removePrefix, shell_, warning)+import SimpleCmd.Git (git, git_, gitBranch, gitDiffQuiet, isGitDir) import SimpleCmdArgs import Build (build, readBuildCmd)-import Dist (distArg, distRemote)+import Dist (distArg, distRemote, hackageRelease, ltsStream) import Koji (kojiListPkgs, rpkg) import Paths_fedora_haskell_tools (version) import RPM (buildRequires, haskellSrcPkgs, Package, pkgDir,@@ -71,18 +72,20 @@ simpleCmdArgs (Just version) "Fedora Haskell packages tool" "Fedora packages maintenance tool" $ subcommands $- [ Subcommand "bump" "bump package release" $- bump <$> strOptionWith 'm' "message" "CHANGELOG" "changelog message" <*> distArg <*> pkgArgs- , Subcommand "checkout" "fedpkg switch-branch" $+ [ Subcommand "checkout" "fedpkg switch-branch" $ repoAction_ True False (return ()) <$> distArg <*> pkgArgs , Subcommand "clone" "clone repos" $ clone <$> branching <*> distArg <*> pkgArgs , Subcommand "clone-new" "clone new packages" $ cloneNew <$> branching <*> distArg+ , Subcommand "cblrpm" "Run cblrpm command" $+ cblrpm <$> strOptionWith 'c' "cmd" "CMD" "command to execute" <*> distArg <*> pkgArgs , Subcommand "cmd" "arbitrary command (with args)" $ execCmd <$> strOptionWith 'c' "cmd" "CMD" "command to execute" <*> distArg <*> pkgArgs , Subcommand "count" "count number of packages" $ (repoqueryHaskellPkgs False >=> (print . length)) <$> distArg+ , Subcommand "depends" "cabal-depends" $+ repoAction False (Output cabalDepends) <$> distArg <*> pkgArgs , Subcommand "diff" "git diff" $ gitDiff <$> optional gitFormat <*> optional (strOptionWith 'w' "with-branch" "BRANCH" "Branch to compare")@@ -90,46 +93,51 @@ , Subcommand "diff-origin" "git diff origin" $ gitDiffOrigin <$> distArg <*> pkgArgs , Subcommand "diff-branch" "compare branch with master" $- repoAction False True compareRawhide <$> distArg <*> pkgArgs- , Subcommand "diff-stackage" "compare with stackage" $- diffStackage <$> switchWith 'm' "missing" "only list missing packages" <*> distArg <*> pkgArgs+ repoAction True (Header False compareRawhide) <$> distArg <*> pkgArgs+ , Subcommand "diffstat" "Show diffstat output" $+ repoAction False (Output (const diffStat)) <$> distArg <*> pkgArgs , Subcommand "hackage" "generate Hackage distro data" $ repoqueryHackageCSV hackageRelease <$> switchRefresh , Subcommand "hackage-compare" "compare with Hackage distro data" $ hackageCompare <$> switchRefresh- , Subcommand "head-origin" "head in sync with origin" $+ , Subcommand "head-origin" "packages with head in sync with origin" $ headOrigin <$> distArg <*> pkgArgs- , Subcommand "leaf" "list leaf packages" $+ , Subcommand "leaf" "list leaf packages (slow!)" $ leaves <$> switchWith 'v' "deps" "show also deps" <*> distArg <*> pkgArgs- , Subcommand "list" "list packages" $ mapM_ putStrLn <$> pkgArgs+ , Subcommand "list" "list packages that BR ghc-Cabal-devel" $+ (repoqueryHaskellPkgs False >=> putStrList) <$> distArg , Subcommand "merge" "git merge" $ merge <$> strOptionWith 'f' "from" "BRANCH" "specify branch to merge from" <*> distArg <*> pkgArgs , Subcommand "missing" "missing dependency source packages" $ missingDeps <$> distArg <*> pkgArgs- , Subcommand "new" "new unbuilt packages" $- (newPackages >=> mapM_ putStrLn) <$> distArg+ , Subcommand "new" "unbuilt packages" $+ (newPackages >=> putStrList) <$> distArg , Subcommand "old-packages" "packages not in repoquery" $ oldPackages <$> distArg <*> pkgArgs , Subcommand "prep" "fedpkg prep" $ prep <$> distArg <*> pkgArgs+ , Subcommand "stackage-compare" "compare with stackage" $+ stackageCompare <$> streamOpt <*> switchWith 'm' "missing" "only list missing packages" <*> distArg <*> pkgArgs , Subcommand "commit" "fedpkg commit" $ commit <$> strOptionWith 'm' "message" "COMMITMSG" "commit message" <*> distArg <*> pkgArgs+ , Subcommand "fetch" "git fetch repos" $+ repoAction_ True False (git_ "fetch" []) <$> distArg <*> pkgArgs , Subcommand "pull" "git pull repos" $ repoAction_ True False (git_ "pull" ["--rebase"]) <$> distArg <*> pkgArgs , Subcommand "push" "git push repos" $ repoAction_ True False (git_ "push" []) <$> distArg <*> pkgArgs , Subcommand "refresh" "cabal-rpm refresh" $- repoAction True True refreshPkg <$> distArg <*> pkgArgs+ refresh <$> switchWith 'n' "dry-run" "Show patch but don't apply" <*> distArg <*> pkgArgs , Subcommand "remaining" "remaining packages to be built in TAG" $ remaining <$> switchWith 'c' "count" "show many packages left" <*> strArg "TAG" <*> pkgArgs- , Subcommand "unpushed" "show unpushed commits" $- unpushed <$> switchWith 's' "short" "no log" <*> distArg <*> pkgArgs- , Subcommand "update" "cabal-rpm update" $- update <$> strOptionWith 's' "stream" "STACKAGESTREAM" "Stackage stream (lts-X)" <*> distArg <*> pkgArgs , Subcommand "subpkgs" "list subpackages" $- repoAction True True (\ p -> rpmspec [] (Just "%{name}-%{version}") (p <.> "spec") >>= mapM_ putStrLn) <$> distArg <*> pkgArgs+ repoAction True (Header True (\ p -> rpmspec [] (Just "%{name}-%{version}") (p <.> "spec") >>= putStrList)) <$> distArg <*> pkgArgs , Subcommand "tagged" "list koji DIST tagged builds" $ listTagged_ <$> switchWith 's' "short" "list packages not builds" <*> strArg "TAG"+ , Subcommand "unpushed" "show unpushed commits" $+ unpushed <$> switchWith 's' "short" "no log" <*> distArg <*> pkgArgs+ , Subcommand "update" "cabal-rpm update" $+ update <$> streamOpt <*> distArg <*> pkgArgs , Subcommand "verrel" "show nvr of packages" $ verrel <$> distArg <*> pkgArgs] ++ map (buildCmd cwd) [ ("install", "build locally and install")@@ -149,7 +157,7 @@ gitFormat :: Parser DiffFormat gitFormat =- flag' DiffShort (switchMods 's' "short" "Just output package name") <|>+ flagWith' DiffShort 's' "short" "Just output package name" <|> DiffContext <$> optionWith auto 'u' "unified" "CONTEXT" "Lines of context" buildCmd cwd (c, desc) =@@ -158,17 +166,17 @@ switchRefresh = switchWith 'r' "refresh" "repoquery --refresh" + streamOpt = strOptionalWith 's' "stream" "STACKAGESTREAM" ("Stackage stream [" ++ ltsStream ++ "]") ltsStream+ data DiffFormat = DiffShort | DiffContext Int deriving (Eq) -bump :: String -> Dist -> [Package] -> IO ()-bump msg =- repoAction True False bumpspec- where- bumpspec pkg =- cmd_ "rpmdev-bumpspec" ["-c", msg, pkg <.> "spec"]+putStrList :: [String] -> IO ()+putStrList =+ putStr . unlines +-- should make separate rhel client so -B does not need dist clone :: Bool -> Dist -> [Package] -> IO () clone True dist pkgs = cloneAllBranches dist pkgs clone False dist pkgs =@@ -187,7 +195,7 @@ gitDiff :: Maybe DiffFormat -> Maybe String -> Dist -> [Package] -> IO () gitDiff fmt mbrnch =- repoAction False False doGitDiff+ repoAction False (Output doGitDiff) where doGitDiff pkg = do let branch = maybeToList mbrnch@@ -196,32 +204,37 @@ _ -> [] short = fmt == Just DiffShort out <- git "diff" $ branch ++ contxt- unless (null out) $ do- putStrLn $ if short then pkg else "==" +-+ pkg +-+ "=="- unless short $ putStrLn out+ return $ if null out then ""+ else if short then pkg else out gitDiffOrigin :: Dist -> [Package] -> IO () gitDiffOrigin dist =- repoAction_ True False (git_ "diff" [distRemote dist]) dist+ repoAction False (Output (const (git "diff" [distRemote dist]))) dist -diffStackage :: Bool -> Dist -> [Package] -> IO ()-diffStackage missingOnly dist =- repoAction False True compareStackage dist+stackageCompare :: String -> Bool -> Dist -> [Package] -> IO ()+stackageCompare stream missingOnly dist =+ repoAction True (Header False compareStackage) dist where compareStackage :: Package -> IO () compareStackage p = do nvr <- cmd (rpkg dist) ["verrel"]- let stream = "lts-12" stkg <- cmdMaybe "stackage" ["package", stream, removePrefix "ghc-" p]- let same = isJust stkg && fromJust stkg `isInfixOf` nvr- unless missingOnly $- putStrLn $ nvr +-+ "(fedora)"- if missingOnly then when (isNothing stkg) $ putStrLn p+ let same = isJust stkg && (fromJust stkg ++ "-") `isInfixOf` nvr+ unless same $+ if missingOnly+ then when (isNothing stkg) $ putStrLn p else- putStrLn $ (if same then "same" else fromMaybe "none" stkg) +-+ "(" ++ stream ++ ")"+ if isNothing stkg+ then putStrLn $ stream ++ " missing: " ++ removePrefix "ghc-" p+ else do+ putStrLn nvr+ putStrLn $ replicate (length (dropVerrel nvr) + 1) ' ' ++ fromJust stkg +-+ "(" ++ stream ++ ")" +diffStat :: IO String+diffStat = git "diff" ["--stat"]+ hackageCompare :: Bool -> IO ()-hackageCompare refresh =+hackageCompare refreshData = repoqueryHackages hackageRelease >>= compareHackage hackageRelease where@@ -229,7 +242,7 @@ compareHackage dist pkgs' = do hck <- simpleHTTP (getRequest "http://hackage.haskell.org/distro/Fedora/packages.csv") >>= getResponseBody let hackage = sort . either (error "Malformed Hackage csv") (map mungeHackage) $ parseCSV "packages.csv" hck- sort . map mungeRepo . lines <$> repoquery dist (["--repo=fedora", "--repo=updates", "--latest-limit=1", "--qf=%{name},%{version}"] ++ ["--refresh" | refresh] ++ pkgs') >>= + sort . map mungeRepo . lines <$> repoquery dist (["--repo=fedora", "--repo=updates", "--latest-limit=1", "--qf=%{name},%{version}"] ++ ["--refresh" | refreshData] ++ pkgs') >>= compareSets True hackage mungeHackage :: [String] -> PkgVer@@ -263,7 +276,7 @@ headOrigin :: Dist -> [Package] -> IO () headOrigin dist =- repoAction False False gitHeadAtOrigin dist+ repoAction False (Header False gitHeadAtOrigin) dist where gitHeadAtOrigin :: Package -> IO () gitHeadAtOrigin pkg = do@@ -273,7 +286,7 @@ leaves :: Bool -> Dist -> [Package] -> IO () leaves verb =- repoAction verb True checkLeafPkg+ repoAction True (Header verb checkLeafPkg) where -- FIXME: make a dependency cache checkLeafPkg :: Package -> IO ()@@ -288,7 +301,7 @@ found <- filterM (dependsOn subpkgs) other if null found then putStrLn pkg- else when verb $ mapM_ putStrLn found+ else when verb $ putStrList found where dependsOn :: [Package] -> Package -> IO Bool dependsOn subpkgs p = do@@ -305,9 +318,9 @@ missingDeps :: Dist -> [Package] -> IO () missingDeps dist =- repoAction True True checkForMissingDeps dist+ repoAction True (Output checkForMissingDeps) dist where- checkForMissingDeps :: Package -> IO ()+ checkForMissingDeps :: Package -> IO String checkForMissingDeps pkg = do dir <- takeFileName <$> getCurrentDirectory let top = if dir == pkg then ".." else "../.."@@ -316,18 +329,17 @@ if hasSpec then do deps <- buildRequires (pkg <.> "spec") >>= haskellSrcPkgs top dist- mapM_ (checkMissing top) deps- else putStrLn "no spec file found!"+ unlines <$> filterM (noPkgDir top) deps+ else putStrLn ("no " ++ pkg ++ ".spec file found!") >> return "" where- checkMissing :: FilePath -> Package -> IO ()- checkMissing top dep = do- exists <- doesDirectoryExist $ top </> dep- unless exists $ putStrLn $ "Missing" +-+ dep+ noPkgDir :: FilePath -> Package -> IO Bool+ noPkgDir top dep =+ not <$> doesDirectoryExist (top </> dep) oldPackages :: Dist -> [Package] -> IO () oldPackages dist pkgs = do repopkgs <- repoqueryHaskellPkgs True dist- mapM_ putStrLn (pkgs \\ repopkgs)+ putStrList (pkgs \\ repopkgs) prep :: Dist -> [Package] -> IO () prep dist =@@ -335,19 +347,18 @@ commit :: String -> Dist -> [Package] -> IO () commit logmsg dist =- repoAction_ True True commitChanges dist+ repoAction False (Output (const commitChanges)) dist where- commitChanges :: IO ()+ commitChanges :: IO String commitChanges = do- -- use gitDiffQuiet- nochgs <- cmdBool "git" ["diff", "--quiet"]+ nochgs <- gitDiffQuiet [] if nochgs- then putStrLn "no changes"- else cmd_ (rpkg dist) ["commit", "-m", logmsg]+ then return ""+ else cmd (rpkg dist) ["commit", "-m", logmsg] unpushed :: Bool -> Dist -> [Package] -> IO () unpushed nolog dist =- repoAction False True gitLogOneLine dist+ repoAction True (Header False gitLogOneLine) dist where gitLogOneLine :: Package -> IO () gitLogOneLine pkg = do@@ -362,10 +373,10 @@ repoAction_ False True (cmd_ (rpkg dist) ["verrel"]) dist repoqueryHackageCSV :: Dist -> Bool -> IO ()-repoqueryHackageCSV dist refresh = do+repoqueryHackageCSV dist refreshData = do pkgs <- repoqueryHackages dist -- Hackage csv chokes on final newline so remove it- init . unlines . sort . map (replace "\"ghc-" "\"") . lines <$> repoquery dist (["--repo=fedora", "--repo=updates", "--latest-limit=1", "--qf=\"%{name}\",\"%{version}\",\"https://apps.fedoraproject.org/packages/%{source_name}\""] ++ ["--refresh" | refresh] ++ pkgs) >>= putStr+ init . unlines . sort . map (replace "\"ghc-" "\"") . lines <$> repoquery dist (["--repo=fedora", "--repo=updates", "--latest-limit=1", "--qf=\"%{name}\",\"%{version}\",\"https://apps.fedoraproject.org/packages/%{source_name}\""] ++ ["--refresh" | refreshData] ++ pkgs) >>= putStr data PkgVer = PV { pvPkg :: String, pvVer :: String} deriving (Eq)@@ -387,10 +398,12 @@ repoqueryHaskellPkgs :: Bool -> Dist -> IO [Package] repoqueryHaskellPkgs verbose dist = do- when verbose $ putStrLn "Getting packages from repoquery"+ when verbose $ do+ tty <- hIsTerminalDevice stdout+ when tty $ warning "Getting packages from repoquery" let repo = distRepo dist updates = maybeToList $ distUpdates dist- bin <- words <$> repoquery dist (["--repo=" ++ repo ++ "-source"] ++ ["--repo=" ++ u ++ "-source" | u <- updates] ++ ["--qf=%{name}", "--whatrequires", "ghc-Cabal-devel"])+ bin <- words <$> repoquery dist (["--repo=" ++ repo ++ "-source"] ++ ["--repo=" ++ u ++ "-source" | u <- updates] ++ ["--qf=%{name}", "--whatrequires", "ghc-Cabal-*"]) when (null bin) $ error "No packages using ghc-Cabal-devel found!" return $ sort $ nub bin @@ -414,25 +427,28 @@ newPackages :: Dist -> IO [Package] newPackages dist = do ps <- repoqueryHaskellPkgs True dist- kps <- kojiListHaskell True dist- return $ kps \\ ps+ pps <- cmdLines "pagure" ["list", "ghc*"]+ filterM (\ d -> not <$> doesFileExist (d </> "dead.package")) $ pps \\ ps kojiListHaskell :: Bool -> Dist -> IO [Package] kojiListHaskell verbose dist = do when verbose $ putStrLn "Getting package list from Koji"- libs <- filter (\ p -> "ghc-" `isPrefixOf` p && p `notElem` ["ghc-rpm-macros", "ghc-srpm-macros"]) <$> kojiListPkgs dist+ libs <- filter (\ p -> "ghc" `isPrefixOf` p && p `notElem` ["ghc-rpm-macros", "ghc-srpm-macros"]) <$> kojiListPkgs dist when (null libs) $ error "No library packages found" return $ sort $ nub libs +haveSshKey :: IO Bool+haveSshKey = do+ home <- getHomeDirectory+ doesFileExist $ home </> ".ssh/id_rsa"+ cloneAllBranches :: Dist -> [Package] -> IO () cloneAllBranches _ [] = return () cloneAllBranches dist (pkg:rest) = do withCurrentDirectory "." $ do putStrLn $ "\n==" +-+ pkg +-+ "==" -- muser <- getEnv "USER"- haveSSH <- do- home <- getHomeDirectory- doesFileExist $ home </> ".ssh/id_rsa"+ haveSSH <- haveSshKey dirExists <- doesDirectoryExist pkg unless dirExists $ cmd_ (rpkg dist) $ ["clone"] ++ ["-a" | not haveSSH] ++ ["-B", pkg]@@ -441,22 +457,26 @@ error "branch checkout already exists!" cloneAllBranches dist rest -repoAction :: Bool -> Bool -> (Package -> IO ()) -> Dist -> [Package] -> IO ()-repoAction _ _ _ _ [] = return ()-repoAction header needsSpec action dist (pkg:rest) = do+data Action = Output (Package -> IO String) | Header Bool (Package -> IO ())++showHeader :: Action -> Bool+showHeader (Header b _) = b+showHeader (Output _) = False++repoAction :: Bool -> Action -> Dist -> [Package] -> IO ()+repoAction _ _ _ [] = return ()+repoAction needsSpec action dist (pkg:rest) = do withCurrentDirectory "." $ do let branch = distBranch dist- when header $+ when (showHeader action) $ putStrLn $ "\n==" +-+ pkg ++ ":" ++ branch +-+ "==" -- muser <- getEnv "USER"- haveSSH <- do- home <- getHomeDirectory- doesFileExist $ home </> ".ssh/id_rsa"+ haveSSH <- haveSshKey fileExists <- doesFileExist pkg- dirExists <- doesDirectoryExist pkg if fileExists then error $ pkg +-+ "is a file" else do+ dirExists <- doesDirectoryExist pkg unless dirExists $ cmd_ (rpkg dist) $ ["clone"] ++ ["-a" | not haveSSH] ++ ["-b", branch, pkg] singleDir <- isGitDir pkg@@ -483,15 +503,21 @@ let spec = pkg <.> "spec" hasSpec <- doesFileExist spec -- FIXME: silence for cmds that only output package names (eg unpushed -s)- unless hasSpec $ putStrLn $ (if header then "" else pkg ++ ": ") ++ "No spec file!"+ unless hasSpec $ putStrLn $ (if showHeader action then "" else pkg ++ ": ") ++ "No spec file!" unless (needsSpec && not hasSpec) $- action pkg- repoAction header needsSpec action dist rest+ case action of+ Header _ act -> act pkg+ Output act -> do+ out <- act pkg+ unless (null out) $ do+ putStrLn $ "\n==" +-+ pkg ++ ":" ++ branch +-+ "=="+ putStrLn out+ repoAction needsSpec action dist rest +-- io independent of package repoAction_ :: Bool -> Bool -> IO () -> Dist -> [Package] -> IO () repoAction_ header needsSpec action =- repoAction header needsSpec (const action)-+ repoAction needsSpec (Header header (const action)) compareRawhide :: Package -> IO () compareRawhide p = do@@ -514,10 +540,9 @@ isFromHackage pkg = grep_ "hackage.haskell.org/package/" $ pkg <.> "spec" - update :: String -> Dist -> [Package] -> IO () update stream =- repoAction True True doUpdate+ repoAction True (Header True doUpdate) where doUpdate :: Package -> IO () doUpdate pkg = do@@ -526,27 +551,31 @@ then cmd_ "cabal-rpm" ["update", "-s", stream] else putStrLn "skipping since not hackage" -refreshPkg :: Package -> IO ()-refreshPkg pkg = do- hckg <- isFromHackage pkg- if hckg- then cmd_ "cabal-rpm" ["refresh"]- else putStrLn "skipping since not hackage"+refresh :: Bool -> Dist -> [Package] -> IO ()+refresh dryrun =+ repoAction True (Header True refreshPkg)+ where+ refreshPkg :: Package -> IO ()+ refreshPkg pkg = do+ hckg <- isFromHackage pkg+ if hckg+ then cmd_ "cabal-rpm" $ "refresh" : ["--dry-run" | dryrun]+ else putStrLn "skipping since not hackage" listTagged_ :: Bool -> String -> IO () listTagged_ short tag =- listTagged short tag >>= mapM_ putStrLn+ listTagged short tag >>= putStrList listTagged :: Bool -> String -> IO [String] listTagged short tag = do builds <- map (head . words) <$> cmdLines "koji" ["list-tagged", "--quiet", tag] return $ nub $ map (if short then dropVerrel else id) builds- where- dropVerrel :: String -> String- dropVerrel nvr =- let parts = splitOn "-" nvr in- intercalate "-" $ take (length parts - 2) parts +dropVerrel :: String -> String+dropVerrel nvr =+ let parts = splitOn "-" nvr in+ intercalate "-" $ take (length parts - 2) parts+ remaining :: Bool -> String -> [Package] -> IO () remaining count tag pkgs = do built <- listTagged True tag@@ -554,3 +583,24 @@ if count then print $ length left else cmd_ "rpmbuild-order" $ ["sort", "-p"] ++ left++cabalDepends :: Package -> IO String+cabalDepends p = do+ hckg <- isFromHackage p+ if hckg then do+ vr <- removePrefix "ghc-" . head <$>+ rpmspec ["--srpm"] (Just "%{name}-%{version}") (p <.> "spec")+ setCurrentDirectory vr+ cmdQuiet "cabal-depends" ["--not-build", "--unique"]+ else return ""++cblrpm :: String -> Dist -> [Package] -> IO ()+cblrpm "" = error "CMD string must be given"+cblrpm cs =+ repoAction True (Header True doCblRpm)+ where+ doCblRpm :: Package -> IO ()+ doCblRpm p = do+ hckg <- isFromHackage p+ when hckg $+ cmd_ "cblrpm" [cs]
src/Build.hs view
@@ -32,12 +32,13 @@ import System.FilePath ((</>), dropExtension) import FedoraDists (Dist, distBranch, distOverride, rpmDistTag)+import Dist (distRemote) import Koji (kojiBuilding, kojiCheckFHBuilt, kojiLatestPkg, kojiWaitPkg, notInKoji, rpkg, rpkgBuild) import RPM (buildRequires, derefSrcPkg, haskellSrcPkgs, Package, packageManager, pkgDir, rpmInstall, rpmspec) import SimpleCmd ((+-+), cmd, cmd_, cmdBool, cmdLines, cmdLog, cmdMaybe,- cmdSilent, grep_, removeStrictPrefix, sudo)+ cmdSilent, grep_, removeStrictPrefix, sudo_) import SimpleCmd.Git (git_, gitBranch, isGitDir) import Utils (checkPkgsGit) @@ -54,7 +55,7 @@ readBuildCmd "changed" = Changed readBuildCmd "built" = Built readBuildCmd "bump" = Bump-readBuildCmd "notinstalled" = NotInstalled+readBuildCmd "not-installed" = NotInstalled readBuildCmd _ = error "Unknown command" -- commands :: [String]@@ -153,8 +154,8 @@ then rpmInstall rpms else do pkgmgr <- packageManager- sudo pkgmgr ("--setopt=clean_requirements_on_remove=no":"remove":"-y":instpkgs)- sudo pkgmgr ("install":"-y":rpms)+ sudo_ pkgmgr ("--setopt=clean_requirements_on_remove=no":"remove":"-y":instpkgs)+ sudo_ pkgmgr ("install":"-y":rpms) putStrLn "" putStrLn $ show (length rest) +-+ "packages left" build topdir Nothing Nothing False Install dist rest@@ -215,9 +216,10 @@ Bump -> do latest <- kojiLatestPkg dist pkg when (eqNVR nvr latest) $ do- putStrLn pkg- cmd_ "rpmdev-bumpspec" ["-c", "rebuild", spec]- cmd_ (rpkg dist) ["commit", "-m", "bump release"]+ git_ "log" [distRemote dist ++ "..HEAD", "--pretty=oneline"]+ cmd_ "rpmdev-bumpspec" ["-c", "add doc and prof subpackages (cabal-rpm-1.0.0)", spec]+-- cmd_ (rpkg dist) ["commit", "-m", "cabal-rpm-1.0.0: add doc and prof subpkgs"]+ git_ "commit" ["-a", "--amend", "-m", "cabal-rpm-1.0.0: add doc and prof subpkgs"] build topdir Nothing Nothing False Bump dist rest NotInstalled -> do opkg <- head <$> rpmspec ["--builtrpms"] (Just "%{name}") spec@@ -232,8 +234,7 @@ if eqNVR nvr latest then do putStrLn $ fromJust latest +-+ "already built!"- unless (null rest) $ --do- --kojiWaitPkg topdir dist nvr+ unless (null rest) $ build topdir Nothing Nothing False Chain dist rest else do git_ "diff" []@@ -248,7 +249,6 @@ showChange pkg latest nvr putStrLn "" srcs <- buildRequires spec >>= haskellSrcPkgs topdir dist- --print srcs hmissing <- nub <$> filterM (notInKoji branch topdir dist) srcs putStrLn "" unless (null hmissing) $ do@@ -269,7 +269,7 @@ notInstalled :: String -> IO Bool notInstalled pkg =- not <$> cmdBool "rpm" ["--quiet", "-q", pkg]+ not <$> cmdBool "rpm" ["--quiet", "-q", "--whatprovides", pkg] showChange :: Package -> Maybe String -> String -> IO () showChange pkg mlatest nvr = do@@ -290,7 +290,7 @@ bodhiOverride dist nvr = when (distOverride dist) $ -- FIXME: improve Notes with recursive info- cmd_ "bodhi" ["overrides", "save", "--notes", "Haskell stack", nvr]+ cmdLog "bodhi" ["overrides", "save", "--notes", "Haskell stack", nvr] eqNVR :: String -> Maybe String -> Bool eqNVR p1 p2 =
src/Dist.hs view
@@ -2,17 +2,19 @@ (distArg, distRemote, distTag,- distTarget)+ distTarget,+ hackageRelease,+ ltsStream) where import Data.Maybe (fromMaybe) import FedoraDists (Dist(..), distBranch)-import Options.Applicative+import SimpleCmdArgs (Parser, argumentWith, auto) -- | Used for Koji sidetag when needed. sidetag :: Dist -> Maybe String--- sidetag (Fedora n) | n == 31 = Just "ghc"+--sidetag (Fedora n) | n == 31 = Just "ghc" sidetag _ = Nothing -- | Maps `Dist` to build tag@@ -25,8 +27,16 @@ -- | optparse-application DIST arg distArg :: Parser Dist-distArg = argument auto (metavar "DIST")+distArg = argumentWith auto "DIST" -- | Maps `Dist` to remote branch: eg "origin/master" distRemote :: Dist -> String distRemote d = "origin/" ++ distBranch d++-- | Fedora release being tracked in Hackage Distro data+hackageRelease :: Dist+hackageRelease = Fedora 31++-- | Stackage LTS stream major version+ltsStream :: String+ltsStream = "lts-13"
src/Koji.hs view
@@ -35,15 +35,14 @@ import System.Directory (doesFileExist) import System.Exit (ExitCode (..)) import System.FilePath ((</>), (<.>))-import System.IO (hPutStrLn, stderr) import System.Process (readProcessWithExitCode, rawSystem) import FedoraDists (Dist, kojicmd, rpkg) import SimpleCmd (cmd, cmd_, cmdBool, cmdLines, grep_, logMsg,- removeStrictPrefix, (+-+))+ removePrefix, removeStrictPrefix, warning, (+-+)) import Dist (distTag, distTarget) import RPM (pkgDir)-+import Utils (setTermTitle) kojisafe :: Dist -> String -> [String] -> IO String kojisafe dist c as =@@ -60,9 +59,13 @@ tag = distTag dist already <- kojiCheckFHBuilt topdir nvr unless already $ do- putStrLn $ "Waiting for" +-+ nvr +-+ "in" +-+ tag+ logMsg $ "Waiting for" +-+ nvr +-+ "in" +-+ tag+ setTermTitle $ '*' : removePrefix "ghc-" nvr cmdFragile_ (kojicmd dist) ["wait-repo", tag, "--build=" ++ nvr]- appendFile fhbuilt $ nvr ++ "\n"+ tags <- cmdLines (kojicmd dist) ["list-tags", "--build=" ++ nvr]+ -- do not cache overrides+ when (tag `elem` tags) $+ appendFile fhbuilt $ nvr ++ "\n" kojiCheckFHBuilt :: FilePath -> String -> IO Bool kojiCheckFHBuilt topdir nvr = do@@ -100,6 +103,7 @@ rpkgBuild :: FilePath -> Dist -> String -> Bool -> IO () rpkgBuild topdir dist nvr waitrepo = do giturl <- cmd (rpkg dist) ["giturl"]+ setTermTitle $ removePrefix "ghc-" nvr out <- cmd (kojicmd dist) ["build", "--nowait", "--fail-fast", distTarget dist, giturl] putStrLn out let task = last . words . head $ lines out@@ -149,8 +153,8 @@ ExitFailure n -> do unless (null out) $ putStrLn out when (null (out ++ err)) $- hPutStrLn stderr $ "\"" ++ c +-+ unwords as ++ "\"" +-+ "failed with status" +-+ show n- unless (null err) $ hPutStrLn stderr err+ warning $ "\"" ++ c +-+ unwords as ++ "\"" +-+ "failed with status" +-+ show n+ unless (null err) $ warning err threadDelay 2000000 cmdFragile c as @@ -160,7 +164,7 @@ case ret of ExitSuccess -> return () ExitFailure _ -> do- hPutStrLn stderr $ "retrying \"" ++ c +-+ unwords as ++ "\""+ warning $ "retrying \"" ++ c +-+ unwords as ++ "\"" threadDelay 2000000 cmdFragile_ c as
src/RPM.hs view
@@ -32,16 +32,15 @@ import System.FilePath ((</>)) -- die is available in ghc-7.10 base-4.8 import System.Exit (ExitCode (..), exitFailure, exitWith)-import System.IO (hPutStrLn, stderr) import Dist (distTag) import FedoraDists (Dist, distBranch, dists, releaseVersion)-import SimpleCmd (cmd, removeStrictPrefix, removeSuffix, sudo, (+-+))+import SimpleCmd (cmd, removeStrictPrefix, removeSuffix, sudo_, warning, (+-+)) import qualified SimpleCmd.Rpm as S -- @since base 4.8.0.0 die :: String -> IO a-die err = hPutStrLn stderr err >> exitFailure+die err = warning err >> exitFailure requireProgram :: String -> IO () requireProgram c = do@@ -62,7 +61,7 @@ rpmInstall rpms = do pkginstaller <- packageManager let (inst, arg) = if pkginstaller == "dnf" then ("dnf", "install") else ("yum", "localinstall")- sudo inst $ ["-y", "--nogpgcheck", arg] ++ rpms+ sudo_ inst $ ["-y", "--nogpgcheck", arg] ++ rpms repoquery :: Dist -> [String] -> IO String repoquery dist args = do@@ -76,7 +75,7 @@ havednf <- optionalProgram "dnf" let srcflag = if havednf then ["--qf=%{source_name}"] else ["--qf", "%{base_package_name}"] repo = if dist `elem` dists- then ["--repo=koji-buildroot", "--repofrompath", "koji-buildroot,http://kojipkgs.fedoraproject.org/repos" </> distTag dist </> "latest/x86_64/"]+ then ["--repo=koji-buildroot", "--repofrompath", "koji-buildroot,https://kojipkgs.fedoraproject.org/repos" </> distTag dist </> "latest/x86_64/"] else [] res <- words <$> repoquery dist (srcflag ++ repo ++ ["--whatprovides", key]) return $ case res of@@ -97,46 +96,60 @@ type Package = String derefSrcPkg :: FilePath -> Dist -> Bool -> Package -> IO Package-derefSrcPkg topdir dist verb pkg =- if isHaskellDevelPkg pkg- then- do let lib = removeSuffix "-devel" pkg- -- fixme: should check branch (dir)- libExists <- doesDirectoryExist $ topdir </> lib- if libExists- then return lib- else do- -- todo: check bin has lib- let bin = removeStrictPrefix "ghc-" lib- binExists <- doesDirectoryExist $ topdir </> bin- if binExists- then return bin- else derefSrcPkg topdir dist verb lib- else- do putStrLn $ "Repoquerying" +-+ pkg- res <- repoquerySrc dist pkg- case res of- Nothing ->- do putStrLn $ "Unknown package" +-+ removeSuffix "-devel" pkg- exitWith (ExitFailure 1)- Just s -> do- when (pkg /= s && verb) $ putStrLn $ pkg +-+ "->" +-+ s- return s+derefSrcPkg topdir dist verb pkg = do+ res <- maybeDerefSrcPkg topdir dist verb pkg+ case res of+ Nothing -> do+ putStrLn $ "Unknown package" +-+ removeSuffix "-devel" pkg+ exitWith (ExitFailure 1)+ Just s -> do+ when (pkg /= s && verb) $ putStrLn $ pkg +-+ "->" +-+ s+ return s +derefSrcPkgRelax :: FilePath -> Dist -> Package -> IO Package+derefSrcPkgRelax topdir dist pkg = do+ res <- maybeDerefSrcPkg topdir dist False pkg+ case res of+ Nothing -> return pkg+ Just s -> return s++maybeDerefSrcPkg :: FilePath -> Dist -> Bool -> Package -> IO (Maybe Package)+maybeDerefSrcPkg topdir dist verb pkg = do+ let lib = removeLibSuffix pkg+ -- fixme: should check branch (dir)+ libExists <- doesDirectoryExist $ topdir </> lib+ if libExists+ then return $ Just lib+ else do+ -- todo: check bin has lib+ let bin = removeStrictPrefix "ghc-" lib+ binExists <- doesDirectoryExist $ topdir </> bin+ if binExists+ then return $ Just bin+ else do+ when verb $ putStrLn $ "Repoquerying" +-+ pkg+ repoquerySrc dist pkg+ isHaskellDevelPkg :: Package -> Bool-isHaskellDevelPkg pkg = "ghc-" `isPrefixOf` pkg && ("-devel" `isSuffixOf` pkg) || pkg `elem`haskellTools+isHaskellDevelPkg pkg = "ghc-" `isPrefixOf` pkg && ("-devel" `isSuffixOf` pkg || "-prof" `isSuffixOf` pkg || "-static" `isSuffixOf` pkg) || pkg `elem`haskellTools +removeLibSuffix :: String -> String+removeLibSuffix p | "-devel" `isSuffixOf` p = removeSuffix "-devel" p+ | "-prof" `isSuffixOf` p = removeSuffix "-prof" p+ | "-static" `isSuffixOf` p = removeSuffix "-static" p+ | otherwise = p+ haskellTools :: [Package]-haskellTools = ["alex", "cabal-install", "gtk2hs-buildtools", "happy"]+haskellTools = ["alex", "cabal-install", "gtk2hs-buildtools", "happy", "hspec-discover"] haskellSrcPkgs :: FilePath -> Dist -> [Package] -> IO [Package] haskellSrcPkgs topdir dist brs = do ghcLibs <- do let branch = distBranch dist- ghcDir <- pkgDir "ghc" branch topdir- filter isHaskellDevelPkg <$> rpmspec [] (Just "%{name}") (ghcDir </> "ghc.spec")- let hdeps = filter (\ dp -> "ghc-" `isPrefixOf` dp || dp `elem` haskellTools) (brs \\ (["ghc-rpm-macros", "ghc-rpm-macros-extra"] ++ ghcLibs))- nub <$> mapM (derefSrcPkg topdir dist False) hdeps+ ghcDir <- pkgDir "ghc" branch (topdir </> "..")+ map removeLibSuffix . filter isHaskellDevelPkg <$> rpmspec [] (Just "%{name}") (ghcDir </> "ghc.spec")+ let hdeps = filter (\ dp -> "ghc-" `isPrefixOf` dp || dp `elem` haskellTools) (map removeLibSuffix brs \\ (["ghc-rpm-macros", "ghc-rpm-macros-extra"] ++ ghcLibs))+ nub <$> mapM (derefSrcPkgRelax topdir dist) hdeps pkgDir :: String -> String -> FilePath -> IO FilePath pkgDir dir branch top = do
src/Utils.hs view
@@ -14,18 +14,21 @@ -- (at your option) any later version. module Utils (checkPkgsGit,+ setTermTitle, withCurrentDirectory) where +import Control.Monad (when) #if (defined(MIN_VERSION_directory) && MIN_VERSION_directory(1,2,3)) import System.Directory (withCurrentDirectory) #else import Control.Exception (bracket) import System.Directory (getCurrentDirectory, setCurrentDirectory) #endif+import System.Environment (getEnv)+import System.IO (BufferMode(..), hGetBuffering, hSetBuffering, stdout) import SimpleCmd.Git (grepGitConfig) - -- singleLine :: String -> String -- singleLine "" = "" -- singleLine s = (head . lines) s@@ -35,7 +38,7 @@ -- (ret, out, err) <- readProcessWithExitCode c as "" -- case ret of -- ExitSuccess -> return (True, out)--- ExitFailure n -> hPutStrLn stderr ("\"" ++ c +-+ unwords as ++ "\"" +-+ "failed with status" +-+ show n ++ "\n" ++ err) >> return (False, out)+-- ExitFailure n -> warning ("\"" ++ c +-+ unwords as ++ "\"" +-+ "failed with status" +-+ show n ++ "\n" ++ err) >> return (False, out) #if (defined(MIN_VERSION_directory) && MIN_VERSION_directory(1,2,3)) #else@@ -57,3 +60,12 @@ -- git_ :: String -> [String] -> IO () -- git_ c as = -- cmd_ "git" ("--no-pager":c:as)++setTermTitle :: String -> IO ()+setTermTitle ts = do+ term <- getEnv "TERM"+ when (term == "xterm-256color") $ do+ buf <- hGetBuffering stdout+ hSetBuffering stdout NoBuffering+ putStr $ "\ESC]0;" ++ ts ++ "\a"+ hSetBuffering stdout buf