{-# LANGUAGE CPP, LambdaCase #-} -- SPDX-License-Identifier: BSD-3-Clause module Main (main) where import Control.Monad.Extra import Data.Bifunctor (bimap) import Data.List.Extra 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 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 'l' "list" "List all repos" <*> switchWith 's' "save" "Save the repo enable/disable state" <*> optional (flagWith' True 'w' "weak-deps" "Use weak dependencies" <|> flagWith' False 'W' "no-weak-deps" "Disable weak dependencies") <*> switchLongWith "exact" "Match repo names exactly" <*> many modeOpt <*> many (strArg "DNFARGS") where repoOptionWith = (fmap . fmap . fmap . fmap) (replace "/" ":") . strOptionWith modeOpt = DisableRepo <$> repoOptionWith 'd' "disable" "REPOPAT" "Disable repos" <|> EnableRepo <$> repoOptionWith 'e' "enable" "REPOPAT" "Enable repos" <|> ExpireRepo <$> repoOptionWith 'x' "expire" "REPOPAT" "Expire repo cache" <|> flagWith' ClearExpires 'X' "clear-expires" "Undo cache expirations" <|> DeleteRepo <$> repoOptionWith 'E' "delete-repofile" "REPOPAT" "Remove unwanted .repo file" <|> flagWith' (Specific EnableTesting) 't' "enable-testing" "Enable testing repos" <|> flagWith' (Specific DisableTesting) 'T' "disable-testing" "Disable testing repos" <|> flagWith' (Specific EnableModular) 'm' "enable-modular" "Enable modular repos" <|> flagWith' (Specific DisableModular) 'M' "disable-modular" "Disable modular repos" <|> flagLongWith' (Specific EnableDebuginfo) "enable-debuginfo" "Enable debuginfo repos" <|> flagLongWith' (Specific DisableDebuginfo) "disable-debuginfo" "Disable debuginfo repos" <|> flagLongWith' (Specific EnableSource) "enable-source" "Enable source repos" <|> flagLongWith' (Specific DisableSource) "disable-source" "Disable source repos" <|> AddCopr <$> repoOptionWith 'c' "add-copr" "COPR" "Create repo file for copr repo" <|> AddKoji <$> repoOptionWith 'k' "add-koji" "REPO" "Create repo file for koji repo (f37-build, rawhide, epel9-build, etc)" 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 -> Bool -> Maybe Bool -> Bool -> [Mode] -> [String] -> IO () runMain dryrun debug listrepos save mweakdeps exact modes args = do hSetBuffering stdout NoBuffering withCurrentDirectory "/etc/yum.repos.d" $ do forM_ modes $ \case AddCopr copr -> addCoprRepo dryrun debug copr AddKoji repo -> addKojiRepo dryrun debug repo _ -> return () repofiles <- filesWithExtension "." "repo" -- when debug $ print repofiles nameStates <- sort <$> concatMapM readRepos repofiles let actions = selectRepo exact nameStates modes moreoutput = not (null args) || null actions || listrepos unless (null actions) $ do mapM_ (printAction save) actions when moreoutput $ putStrLn "" outputs <- forM actions $ \case Expire repo -> do expireRepo dryrun debug repo return True UnExpire -> do clearExpired dryrun debug return True Delete repofile True -> do deleteRepo dryrun debug repofile return True _ -> return False when (or outputs && (save || moreoutput)) $ putStrLn "" when save $ do if null actions then putStrLn "no changes to save" else do prompt_ "Press Enter to save repo enabled state" doSudo dryrun debug "dnf" $ "config-manager" : concatMap saveRepo actions putStrLn "" if null args then when (null actions || listrepos) $ listRepos $ map (updateState actions) nameStates else do sleep 1 let repoargs = concatMap changeRepo actions weakdeps = maybe [] (\w -> ["--setopt=install_weak_deps=" ++ show w]) mweakdeps in doSudo dryrun debug "dnf" $ repoargs ++ weakdeps ++ args -- FIXME pull non-fedora copr repo file -- FIXME delete created copr repo file if repo doesn't exist addCoprRepo :: Bool -> Bool -> String -> IO () addCoprRepo dryrun debug 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 debug "cp" [tmpfile, repofile] putStrLn "" -- FIXME check url exists! addKojiRepo :: Bool -> Bool -> String -> IO () addKojiRepo dryrun debug 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 debug "cp" [tmpfile, repofile] putStrLn "" listRepos :: [RepoState] -> IO () listRepos repoStates = do let (on,off) = -- can't this be simplified? bimap (map fst) (map fst) $ partition (fst . snd) repoStates putStrLn "Enabled:" mapM_ putStrLn on putStrLn "" putStrLn "Disabled:" mapM_ putStrLn off deleteRepo :: Bool -> Bool -> FilePath -> IO () deleteRepo dryrun debug repofile = do mowned <- cmdMaybe "rpm" ["-qf", "/etc/yum.repos.d" repofile] case mowned of Just owner -> warning $ repofile +-+ "owned by" +-+ owner Nothing -> do ok <- yesno $ "Remove " ++ takeFileName repofile when ok $ do doSudo dryrun debug "rm" [repofile] #if !MIN_VERSION_simple_cmd(0,2,4) filesWithExtension :: FilePath -> String -> IO [FilePath] filesWithExtension dir ext = filter (ext `isExtensionOf`) <$> listDirectory dir #endif