packages feed

dnf-repo 0.1 → 0.2

raw patch · 7 files changed

+142/−110 lines, 7 filesdep ~simple-cmd-args

Dependency ranges changed: simple-cmd-args

Files

ChangeLog.md view
@@ -1,5 +1,11 @@ # dnf-repo releases +## 0.2 (2022-08-12)+- support multiple repos per repo file (eg needed for eln)+- support multiple repo actions (eg "-d rawhide -e fedora")+- --exact repo match option (eg for 'fedora')+- --debug now shows what sudo does, like --dryrun+ ## 0.1 (2022-06-20) - initial version with basic functionality: --add-copr, --add-koji,   --disable repo, --enable repo, --enable/disable-{testing,modular}
README.md view
@@ -16,14 +16,14 @@  ```shellsession $ dnf-repo --version-0.1+0.2 $ dnf-repo --help DNF wrapper repo tool -Usage: dnf-repo [--version] [-n|--dryrun] [-D|--debug] [-s|--save]+Usage: dnf-repo [--version] [-n|--dryrun] [-D|--debug] [--exact] [-s|--save]                 [(-c|--add-copr COPR) | (-k|--add-koji REPO) |                   (-d|--disable REPOPAT) | (-e|--enable REPOPAT) |-                  (-x|--expire REPOPAT) | (-E|--delete-repo REPOPAT)]+                  (-x|--expire REPOPAT) | (-E|--delete-repofile REPOPAT)]                 [(-t|--enable-testing) | (-T|--disable-testing)]                 [(-m|--enable-modular) | (-M|--disable-modular)] [DNFARGS]   see https://github.com/juhp/dnf-repo#readme@@ -33,41 +33,61 @@   --version                Show version   -n,--dryrun              Dry run   -D,--debug               Debug output+  --exact                  Match repo names exactly   -s,--save                Save the repo enable/disable state   -c,--add-copr COPR       Create repo file for copr repo   -k,--add-koji REPO       Create repo file for koji repo   -d,--disable REPOPAT     Disable repos   -e,--enable REPOPAT      Enable repos   -x,--expire REPOPAT      Expire repo cache-  -E,--delete-repo REPOPAT Remove unwanted .repo file+  -E,--delete-repofile REPOPAT+                           Remove unwanted .repo file   -t,--enable-testing      Enable testing repos   -T,--disable-testing     Disable testing repos   -m,--enable-modular      Enable modular repos   -M,--disable-modular     Disable modular repos ``` -## Usage+## Usage examples List repos: ```shellsession $ dnf-repo ``` -Update with --enable-testing:+Update with testing repos: ```shellsession-$ sudo dnf-repo -t update+$ dnf-repo -t update ``` -List disabled copr repos:+List disabled copr repos (actually shows "copr" repos that would be enabled): ```shellsession $ dnf-repo -e copr ``` -Disable copr repos for update:+Disable all copr repos for update: ```shellsession-$ sudo dnf-repo -d copr update+$ dnf-repo -d copr update ```  Disable modular repos permanently: ```shellsession-$ sudo dnf-repo --disable-modular --save+$ dnf-repo --disable-modular --save ```+(or equivalently `dnf-repo -M -s`).++Switch a system from Rawhide to F37:+```shellsession+$ dnf-repo --exact -d rawhide -e fedora distrosync --releasever 37 fedora\*+```++Note that sudo is used implicitly when needed:+there is no need to run dnf-repo with sudo.++## Installation++A copr repo is available:+<https://copr.fedorainfracloud.org/coprs/petersen/dnf-repo/>++## Building++Use {cabal,stack,cabal-rpm} install.
dnf-repo.cabal view
@@ -1,5 +1,5 @@ name:                dnf-repo-version:             0.1+version:             0.2 synopsis:            DNF wrapper tool to control repos description:         A command-line wrapper of the dnf package manager to@@ -20,7 +20,7 @@                      ||  == 8.8.4                      ||  == 8.10.7                      ||  == 9.0.2-                     ||  == 9.2.3+                     ||  == 9.2.4 data-dir:            data data-files:          copr.fedorainfracloud.orgCOLONOWNERCOLONREPO.repo                      koji-REPO.repo@@ -42,7 +42,7 @@                        extra,                        filepath,                        simple-cmd,-                       simple-cmd-args+                       simple-cmd-args >= 0.1.8   default-language:    Haskell2010   ghc-options:         -Wall   if impl(ghc >= 8.0)
src/ExpireRepos.hs view
@@ -8,12 +8,12 @@ expiredFile :: FilePath expiredFile = "/var/cache/dnf/expired_repos.json" -expireRepos :: Bool -> [String] -> IO ()-expireRepos _ [] = error' "no repos to expire given"-expireRepos dryrun repos = do+expireRepos :: Bool -> Bool -> [String] -> IO ()+expireRepos _ _ [] = error' "no repos to expire given"+expireRepos dryrun debug repos = do   old <- read <$> readFile expiredFile :: IO [String]   let expired = nub $ old ++ repos-  doSudo dryrun "sed" ["-i", "-e",+  doSudo dryrun debug "sed" ["-i", "-e",                        "s/" ++ renderShow old ++ "/" ++ renderShow expired ++ "/",                        expiredFile]   putStrLn $ "expired now: " ++ show expired
src/Main.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE CPP #-}+{-# LANGUAGE CPP, LambdaCase #-}  -- SPDX-License-Identifier: BSD-3-Clause @@ -30,8 +30,9 @@     runMain     <$> switchWith 'n' "dryrun" "Dry run"     <*> switchWith 'D' "debug" "Debug output"+    <*> switchLongWith "exact" "Match repo names exactly"     <*> switchWith 's' "save" "Save the repo enable/disable state"-    <*> modeOpt+    <*> many modeOpt     <*> optional testingOpt     <*> optional modularOpt     <*> many (strArg "DNFARGS")@@ -42,8 +43,7 @@       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+      DeleteRepo <$> strOptionWith 'E' "delete-repofile" "REPOPAT" "Remove unwanted .repo file"      testingOpt =       flagWith' EnableTesting 't' "enable-testing" "Enable testing repos" <|>@@ -63,35 +63,38 @@ -- 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+runMain :: Bool -> Bool -> Bool -> Bool -> [Mode]+        -> Maybe Testing -> Maybe Modular         -> [String] -> IO ()-runMain dryrun debug save mode mtesting mmodular args = do+runMain dryrun debug exact save modes 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 ()+    forM_ modes $+      \case+        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+    nameStates <- sort <$> concatMapM readRepos repofiles+    let repoActs = concatMap (selectRepo exact modes 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 ()+    forM_ modes $+      \case+        ExpireRepo _ -> do+          putStrLn ""+          expireRepos dryrun debug $ 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" $+        doSudo dryrun debug "dnf" $           "config-manager" :           concatMap saveRepo repoActs     if null args@@ -101,7 +104,7 @@       sleep 1       putStrLn ""       let repoargs = concatMap changeRepo repoActs-        in doSudo dryrun "dnf" $ repoargs ++ args+        in doSudo dryrun debug "dnf" $ repoargs ++ args     where       -- FIXME pull non-fedora copr repo file       -- FIXME delete created copr repo file if repo doesn't exist@@ -123,7 +126,7 @@             withTempDir $ \ tmpdir -> do               let tmpfile = tmpdir </> repofile               unless dryrun $ writeFile tmpfile repodef-              doSudo dryrun "cp" [tmpfile, repofile]+              doSudo dryrun debug "cp" [tmpfile, repofile]        addKojiRepo :: String -> IO ()       addKojiRepo repo = do@@ -137,7 +140,7 @@         withTempDir $ \ tmpdir -> do           let tmpfile = tmpdir </> repofile           unless dryrun $ writeFile tmpfile repodef-          doSudo dryrun "cp" [tmpfile, repofile]+          doSudo dryrun debug "cp" [tmpfile, repofile]        listRepos :: [RepoState] -> IO ()       listRepos repoStates = do@@ -157,7 +160,7 @@           Just owner -> error' $ repofile +-+ "owned by" +-+ owner           Nothing -> do             ok <- yesno $ "Remove " ++ takeFileName repofile-            when ok $ doSudo dryrun "rm" [repofile]+            when ok $ doSudo dryrun debug "rm" [repofile]  #if !MIN_VERSION_simple_cmd(0,2,4) filesWithExtension :: FilePath -> String -> IO [FilePath]
src/Sudo.hs view
@@ -1,9 +1,14 @@ module Sudo (doSudo) where +import Control.Monad (when) import SimpleCmd (cmdN, sudo_)  -- FIXME make this silent (simple-cmd-0.2.7) unless debug-doSudo :: Bool -> String -> [String] -> IO ()-doSudo dryrun c args = do-  (if dryrun then cmdN else sudo_) c args+doSudo :: Bool -> Bool -> String -> [String] -> IO ()+doSudo dryrun debug c args = do+  if dryrun+    then cmdN c args+    else do+    when debug $ cmdN c args+    sudo_ c args   putStrLn ""
src/YumRepoFile.hs view
@@ -2,7 +2,7 @@  module YumRepoFile (   Mode(..),-  readRepo,+  readRepos,   RepoState,   selectRepo,   Modular(..),@@ -22,7 +22,7 @@  data Mode = AddCopr String | AddKoji String           | EnableRepo String | DisableRepo String-          | ExpireRepo String | DeleteRepo String | Default+          | ExpireRepo String | DeleteRepo String   deriving Eq  data ChangeEnable = Disable String | Enable String | Expire String@@ -61,87 +61,85 @@ data Testing = EnableTesting | DisableTesting   deriving Eq -selectRepo :: Bool -> Mode -> Maybe Testing -> Maybe Modular-           -> RepoState -> Maybe ChangeEnable-selectRepo _debug mode mtesting mmodular (name,enabled,file) =-  case mode of-    AddCopr repo -> if repo `isSuffixOf` name && not enabled-                    then Just (Enable name)-                    else selectOther-    AddKoji repo -> if repo `isSuffixOf` name && not enabled-                    then Just (Enable name)-                    else selectOther-    EnableRepo pat -> if pat `isInfixOf` name && not enabled-                  then Just (Enable name)-                  else selectOther-    DisableRepo pat -> if pat `isInfixOf` name && enabled-                   then Just (Disable name)-                   else selectOther-    ExpireRepo pat -> if pat `isInfixOf` name-                  then Just (Expire name)-                  else Nothing-    DeleteRepo pat -> if pat `isInfixOf` name-                  then if enabled-                       then error' $ "disable repo before deleting: " ++ name-                       else Just (Delete file)-                  else Nothing-    _ -> selectOther+selectRepo :: Bool -> [Mode] -> Maybe Testing -> Maybe Modular+           -> RepoState -> [ChangeEnable]+selectRepo exact modes mtesting mmodular (name,enabled,file) =+  case modes of+    [] -> selectOther+    (mode:modes') ->+      case mode of+        AddCopr repo -> if repo `isSuffixOf` name && not enabled+                        then [Enable name]+                        else selectOther+        AddKoji repo -> if repo `isSuffixOf` name && not enabled+                        then [Enable name]+                        else selectOther+        EnableRepo pat -> if pat `matchesRepo` name && not enabled+                      then [Enable name]+                      else selectOther+        DisableRepo pat -> if pat `matchesRepo` name && enabled+                       then [Disable name]+                       else selectOther+        ExpireRepo pat -> [Expire name | pat `matchesRepo` name]+        DeleteRepo pat -> if pat `matchesRepo` name+                      then if enabled+                           then error' $ "disable repo before deleting: " ++ name+                           else [Delete file]+                      else []+      ++ selectRepo exact modes' mtesting mmodular (name,enabled,file)   where-    selectOther :: Maybe ChangeEnable+    matchesRepo = if exact then (==) else isInfixOf++    selectOther :: [ChangeEnable]     selectOther       | "modular" `isSuffixOf` name =         case mmodular of-          Nothing -> Nothing+          Nothing -> []           Just include ->             case include of               EnableModular | not enabled ->                                if "testing" `isInfixOf` name-                               then if mtesting == Just EnableTesting-                                    then Just (Enable name)-                                    else Nothing-                               else Just (Enable name)-              DisableModular | enabled -> Just (Disable name)-              _ -> Nothing+                               then+                                 [Enable name | mtesting == Just EnableTesting]+                               else [Enable name]+              DisableModular | enabled -> [Disable name]+              _ -> []       | "testing" `isInfixOf` name =           case mtesting of-            Nothing -> Nothing+            Nothing -> []             Just include ->               case include of-                EnableTesting | not enabled -> Just (Enable name)-                DisableTesting | enabled -> Just (Disable name)-                _ -> Nothing-      | otherwise = Nothing+                EnableTesting | not enabled -> [Enable name]+                DisableTesting | enabled -> [Disable name]+                _ -> []+      | otherwise = [] -readRepo :: FilePath -> IO RepoState-readRepo file =-  parseRepo file . lines <$> readFile file+readRepos :: FilePath -> IO [RepoState]+readRepos file =+  parseRepos file . lines <$> readFile file  -- was called parseIni-parseRepo :: FilePath -> [String] -> RepoState-parseRepo file [] = error' $ "empty ini file: " ++ file-parseRepo file (l:ls) =-  case trim l of-    "" -> parseRepo file ls-    ('#':_) -> parseRepo file ls-    sec ->-      let section = secName sec-          enabled =-            case dropWhile (not . ("enabled=" `isPrefixOf`)) ls of+parseRepos :: FilePath -> [String] -> [RepoState]+parseRepos file ls =+  case nextSection ls of+    Nothing -> []+    Just (section,rest) ->+      let (enabled,more) =+            case dropWhile (not . ("enabled=" `isPrefixOf`)) rest of               [] -> error' $ "no enabled field for " ++ section-              (e:_) ->+              (e:more') ->                 case trim e of-                  "enabled=1" -> True-                  "enabled=0" -> False+                  "enabled=1" -> (True,more')+                  "enabled=0" -> (False,more')                   _ -> error' $ "unknown enabled state " ++ e ++ " for " ++ section-      in (section,enabled,file)+      in (section,enabled,file) : parseRepos file more   where-    secName :: String -> String-    secName sec =-      if ' ' `elem` sec-      then error' $ "section contains space: " ++ sec-      else case sec of+    nextSection :: [String] -> Maybe (String,[String])+    nextSection [] = Nothing+    nextSection (l:ls') =+      case l of         ('[' : rest) ->           if last rest == ']'-          then init rest-          else error' $ "bad section " ++ sec ++ " in " ++ file-        _ -> error' $ "section not found in " ++ file+          then Just (init rest, ls')+          else error' $ "bad section " ++ l ++ " in " ++ file+        _ -> nextSection ls'