{-# LANGUAGE CPP #-} -- SPDX-License-Identifier: BSD-3-Clause module Main (main) where import Control.Monad.Extra import Data.Bifunctor (bimap) import Data.List.Extra import Data.Maybe (mapMaybe) import Data.Tuple.Extra (fst3,snd3) import SimpleCmd import SimpleCmdArgs import System.Directory import System.FilePath import System.IO (hSetBuffering, stdout, BufferMode(NoBuffering)) import System.IO.Extra (withTempDir) import System.Time.Extra (sleep) import Paths_dnf_repo (getDataFileName, version) import ExpireRepos (expireRepos) import Sudo import YumRepoFile main :: IO () main = do simpleCmdArgs' (Just version) "DNF wrapper repo tool" "see https://github.com/juhp/dnf-repo#readme" $ runMain <$> switchWith 'n' "dryrun" "Dry run" <*> switchWith 'D' "debug" "Debug output" <*> switchWith 's' "save" "Save the repo enable/disable state" <*> modeOpt <*> optional testingOpt <*> optional modularOpt <*> many (strArg "DNFARGS") where modeOpt = AddCopr . replace "/" ":" <$> strOptionWith 'c' "add-copr" "COPR" "Create repo file for copr repo" <|> AddKoji <$> strOptionWith 'k' "add-koji" "REPO" "Create repo file for koji repo" <|> DisableRepo <$> strOptionWith 'd' "disable" "REPOPAT" "Disable repos" <|> EnableRepo <$> strOptionWith 'e' "enable" "REPOPAT" "Enable repos" <|> ExpireRepo <$> strOptionWith 'x' "expire" "REPOPAT" "Expire repo cache" <|> DeleteRepo <$> strOptionWith 'E' "delete-repofile" "REPOPAT" "Remove unwanted .repo file" <|> pure Default testingOpt = flagWith' EnableTesting 't' "enable-testing" "Enable testing repos" <|> flagWith' DisableTesting 'T' "disable-testing" "Disable testing repos" modularOpt = flagWith' EnableModular 'm' "enable-modular" "Enable modular repos" <|> flagWith' DisableModular 'M' "disable-modular" "Disable modular repos" coprRepoTemplate :: FilePath coprRepoTemplate = "copr.fedorainfracloud.orgCOLONOWNERCOLONREPO.repo" kojiRepoTemplate :: FilePath kojiRepoTemplate = "koji-REPO.repo" -- FIXME both enabling and disabled at the same time -- FIXME --enable-all-coprs (for updating etc) -- FIXME confirm repos if many -- FIXME --disable-non-cores (modular,testing,cisco, etc) runMain :: Bool -> Bool -> Bool -> Mode -> Maybe Testing -> Maybe Modular -> [String] -> IO () runMain dryrun debug save mode mtesting mmodular args = do hSetBuffering stdout NoBuffering withCurrentDirectory "/etc/yum.repos.d" $ do case mode of AddCopr copr -> addCoprRepo copr AddKoji repo -> addKojiRepo repo _ -> return () repofiles <- filesWithExtension "." "repo" -- when debug $ print repofiles nameStates <- sort <$> mapM readRepo repofiles let repoActs = mapMaybe (selectRepo debug mode mtesting mmodular) nameStates unless (null repoActs) $ do mapM_ print repoActs putStrLn "" case mode of ExpireRepo _ -> do putStrLn "" expireRepos dryrun $ mapMaybe expiring repoActs DeleteRepo _ -> mapM_ deleteRepos $ mapMaybe deleting repoActs _ -> return () when save $ if null repoActs then putStrLn "no changes to save\n" else do prompt_ "Press Enter to save repo enabled state" doSudo dryrun "dnf" $ "config-manager" : concatMap saveRepo repoActs if null args then do listRepos $ map (updateState repoActs) nameStates else do sleep 1 putStrLn "" let repoargs = concatMap changeRepo repoActs in doSudo dryrun "dnf" $ repoargs ++ args where -- FIXME pull non-fedora copr repo file -- FIXME delete created copr repo file if repo doesn't exist addCoprRepo :: String -> IO () addCoprRepo repo = do case stripInfix ":" repo of Nothing -> error' $ "invalid copr: " ++ repo Just (copr_owner,copr_repo) -> do template <- getDataFileName coprRepoTemplate repodef <- cmd "sed" ["-e", "s/@COPR_OWNER@/" ++ copr_owner ++ "/g", "-e", "s/@COPR_REPO@/" ++ copr_repo ++ "/g", template] let repofile = ("_copr:" ++) $ replace "COLON" ":" $ replace "OWNER" copr_owner $ replace "REPO" copr_repo coprRepoTemplate exists <- doesFileExist repofile if exists then error' $ "repo already defined: " ++ repofile else putStrLn $ "Setting up copr repo " ++ repo withTempDir $ \ tmpdir -> do let tmpfile = tmpdir repofile unless dryrun $ writeFile tmpfile repodef doSudo dryrun "cp" [tmpfile, repofile] addKojiRepo :: String -> IO () addKojiRepo repo = do template <- getDataFileName kojiRepoTemplate repodef <- cmd "sed" ["-e", "s/@REPO@/" ++ repo ++ "/g", template] let repofile = replace "REPO" repo kojiRepoTemplate exists <- doesFileExist repofile if exists then error' $ "repo already defined: " ++ repofile else putStrLn $ "Setting up koji repo " ++ repo withTempDir $ \ tmpdir -> do let tmpfile = tmpdir repofile unless dryrun $ writeFile tmpfile repodef doSudo dryrun "cp" [tmpfile, repofile] listRepos :: [RepoState] -> IO () listRepos repoStates = do let (on,off) = -- can't this be simplified? bimap (map fst3) (map fst3) $ partition snd3 repoStates putStrLn "Enabled:" mapM_ putStrLn on putStrLn "" putStrLn "Disabled:" mapM_ putStrLn off deleteRepos :: FilePath -> IO () deleteRepos repofile = do mowned <- cmdMaybe "rpm" ["-qf", "/etc/yum.repos.d" repofile] case mowned of Just owner -> error' $ repofile +-+ "owned by" +-+ owner Nothing -> do ok <- yesno $ "Remove " ++ takeFileName repofile when ok $ doSudo dryrun "rm" [repofile] #if !MIN_VERSION_simple_cmd(0,2,4) filesWithExtension :: FilePath -> String -> IO [FilePath] filesWithExtension dir ext = filter (ext `isExtensionOf`) <$> listDirectory dir #endif prompt :: String -> IO String prompt desc = do putStr $ desc ++ ": " getLine prompt_ :: String -> IO () prompt_ desc = do void $ prompt desc yesno :: String -> IO Bool yesno desc = do inp <- prompt $ desc ++ "? [y/n]" case lower inp of "y" -> return True "yes" -> return True "n" -> return False "no" -> return False _ -> yesno desc