packages feed

dl-fedora 0.9.6 → 1.0

raw patch · 5 files changed

+149/−101 lines, 5 filesdep +simple-promptdep ~simple-cmd

Dependencies added: simple-prompt

Dependency ranges changed: simple-cmd

Files

CHANGELOG.md view
@@ -1,5 +1,14 @@ # Changelog +## 1.0 (2023-09-15)+- default to download.fp.o: replace --no-dl with --latest+- new --check (-c) command mode: checks for newer image+- --local mode now only derefs symlink to show latest local image+- help: list ostree editions last+- add Onyx (F39 Budgie ostree image)+- allow "sb" as alias for Silverblue+- offer to fix permissions of a partially downloaded run iso file+ ## 0.9.6 (2023-08-18) - --no-dl option to avoid dl.fp.o even if newer - support c8s and add options for centos-stream channels
README.md view
@@ -20,8 +20,13 @@  `dl-fedora --run 39` : will download Fedora Workstation and boot the Live image with qemu-kvm. -`dl-fedora --local rawhide` : shows the current locally available image (as well as the latest one). With `--dryrun` it doesn't check for newest iso.+`dl-fedora --check respin` : checks if there is a newer respin iso image+available. +`dl-fedora --local rawhide` : shows the current locally available image.+It can be combined with `--run` to quickly run the latest local image,+without a newer download.+ `dl-fedora c9s` : downloads a Centos Stream 9 net installer.  By default dl-fedora downloads to `~/Downloads/`@@ -29,8 +34,11 @@ but if you create an `iso` subdirectory there (`~/Downloads/iso/`) it will use that directory instead. -`dl-fedora` downloads the latest image redirected from `download.fedoraproject.org` by default, after checking for the latest image at `dl.fedoraproject.org`.-However if the mirror is not synced yet it will download from `dl.fedoraproject.org` instead. You can use `--no-dl` to avoid checking and downloading from the main dl.fedoraproject.org mirror (in this case a nightly image might not be the very latest).+`dl-fedora` downloads the latest mirrored image redirected from+`download.fedoraproject.org` by default.+If you want to ensure getting the very latest image you can use `--latest`,+which will then download from `dl.fedoraproject.org` instead+_if_ your mirror is not synced yet. (This behavior changed with 0.10.)  If the image is already found to be downloaded it will not be re-downloaded of course.@@ -44,40 +52,42 @@ ## Usage ```shellsession $ dl-fedora --version-0.9.6+1.0 $ dl-fedora --help Fedora iso downloader -Usage: dl-fedora [--version] [-g|--gpg-keys]-                 [(-C|--no-checksum) | (-c|--checksum)] [-n|--dry-run] [--debug]-                 [-T|--no-http-timeout] [-l|--local] [-r|--run] [-R|--replace]-                 [(-d|--dl) | (-D|--no-dl) | (-k|--koji) | (-m|--mirror URL)]+Usage: dl-fedora [--version] [-g|--gpg-keys] [--no-checksum | --checksum]+                 [--debug] [-T|--no-http-timeout]+                 [(-c|--check) | (-l|--local) | (-R|--replace)] [-n|--dry-run]+                 [-r|--run]+                 [(-L|--latest) | (-d|--dl) | (-k|--koji) | (-m|--mirror URL)]                  [--cs-devel | --cs-test] [-a|--arch ARCH] RELEASE [EDITION]    Tool for downloading Fedora iso file images.   RELEASE = release number, respin, rawhide, test (Beta), stage (RC), eln, c8s, c9s-  EDITION = {cloud,container,everything,server,workstation,silverblue,kinoite,-             sericea,budgie,cinnamon,i3,kde,lxde,lxqt,mate,soas,sway,-             xfce} [default: workstation]+  EDITION = {cloud,container,everything,server,workstation,budgie,cinnamon,i3,+             kde,lxde,lxqt,mate,soas,sway,xfce,silverblue,kinoite,onyx,+             sericea} [default: workstation] -  See <https://fedoraproject.org/wiki/Infrastructure/MirrorManager>-  and also <https://fedoramagazine.org/verify-fedora-iso-file>.+  See <https://github.com/juhp/dl-fedora/#readme>  Available options:   -h,--help                Show this help text   --version                Show version   -g,--gpg-keys            Import Fedora GPG keys for verifying checksum file-  -C,--no-checksum         Do not check checksum-  -c,--checksum            Do checksum even if already downloaded-  -n,--dry-run             Don't actually download anything+  --no-checksum            Do not check checksum+  --checksum               Do checksum even if already downloaded   --debug                  Debug output   -T,--no-http-timeout     Do not timeout for http response-  -l,--local               Show current local image via symlink-  -r,--run                 Boot image in Qemu+  -c,--check               Check if newer image available+  -l,--local               Show current local image   -R,--replace             Delete previous snapshot image after downloading                            latest one+  -n,--dry-run             Don't actually download anything+  -r,--run                 Boot image in QEMU+  -L,--latest              Get latest image either from mirror or dl.fp.o if+                           newer   -d,--dl                  Use dl.fedoraproject.org (dl.fp.o)-  -D,--no-dl               Do not use dl.fp.o (even if newer)   -k,--koji                Use koji.fedoraproject.org   -m,--mirror URL          Mirror url for /pub [default                            https://download.fedoraproject.org/pub]@@ -87,7 +97,12 @@   -a,--arch ARCH           Specify arch [default: x86_64] ``` +## References+See <https://fedoraproject.org/wiki/Infrastructure/MirrorManager>,+<https://admin.fedoraproject.org/mirrormanager/>,+and also <https://fedoramagazine.org/verify-fedora-iso-file>.+ ## Contribution-dl-fedora is distributed under the GPL 3 license or later.+dl-fedora is distributed under the GPL license version 3 or later.  Please report issues or pull requests at <https://github.com/juhp/dl-fedora>.
dl-fedora.cabal view
@@ -1,6 +1,6 @@ cabal-version:       1.18 name:                dl-fedora-version:             0.9.6+version:             1.0 synopsis:            Fedora image download tool description:             Tool to download Fedora and Centos Stream iso and image files.@@ -16,8 +16,9 @@ build-type:          Simple extra-doc-files:     README.md                    , CHANGELOG.md-tested-with:         GHC == 9.4.5, GHC == 9.2.8, GHC == 9.0.2, GHC == 8.10.7,-                     GHC == 8.8.4, GHC == 8.6.5, GHC == 8.4.4, GHC == 8.2.2+tested-with:         GHC == 9.6.2, GHC == 9.4.7, GHC == 9.2.8, GHC == 9.0.2,+                     GHC == 8.10.7, GHC == 8.8.4, GHC == 8.6.5, GHC == 8.4.4,+                     GHC == 8.2.2  source-repository head   type:     git@@ -41,8 +42,9 @@                        http-types,                        optparse-applicative,                        regex-posix,-                       simple-cmd >= 0.2.0,+                       simple-cmd >= 0.2.7,                        simple-cmd-args >= 0.1.8,+                       simple-prompt >= 0.2,                        text,                        time,                        unix,
src/Main.hs view
@@ -29,8 +29,9 @@ import Paths_dl_fedora (version)  import SimpleCmd (cmd, cmd_, cmdN, error', grep_, pipe_, pipeBool, pipeFile_,-                  {-removePrefix,-} warning, (+-+))+                  sudoLog, warning, (+-+)) import SimpleCmdArgs+import SimplePrompt (yesNo)  import System.Directory (createDirectory, doesDirectoryExist, doesFileExist,                          findExecutable, getPermissions,@@ -40,6 +41,7 @@                         (</>), (<.>)) import System.Posix.Files (createSymbolicLink, fileSize, getFileStatus,                            readSymbolicLink)+import System.Posix.User (getLoginName)  import Text.Read import qualified Text.ParserCombinators.ReadP as R@@ -54,10 +56,7 @@                    | Everything                    | Server                    | Workstation-                   | Silverblue-                   | Kinoite-                   | Sericea-                   | Budgie  -- first spin used below+                   | Budgie  -- first spin: used below                    | Cinnamon                    | I3                    | KDE@@ -66,7 +65,11 @@                    | MATE                    | SoaS                    | Sway-                   | Xfce+                   | Xfce  -- last spin: used below+                   | Silverblue+                   | Kinoite+                   | Onyx+                   | Sericea  deriving (Show, Enum, Bounded, Eq)  showEdition :: FedoraEdition -> String@@ -82,6 +85,7 @@     s <- look     let e = lower s         editionMap =+          ("sb", Silverblue) :           map (\ ed -> (lowerEdition ed, ed)) [minBound..maxBound]         res = lookup e editionMap     case res of@@ -91,7 +95,7 @@ type URL = String  fedoraSpins :: [FedoraEdition]-fedoraSpins = [Budgie ..]+fedoraSpins = [Budgie .. Xfce]  data CheckSum = AutoCheckSum | NoCheckSum | CheckSum   deriving Eq@@ -115,6 +119,8 @@ showChannel CSTest = "test" showChannel CSDevelopment = "development" +data Mode = Check | Local | Download Bool -- replace+ main :: IO () main = do   let pdoc = Just $ P.vcat@@ -122,20 +128,20 @@                P.text ("RELEASE = " <> intercalate ", " ["release number", "respin", "rawhide", "test (Beta)", "stage (RC)", "eln", "c8s", "c9s"]),                P.text "EDITION = " <> P.lbrace <> P.align (P.fillCat (P.punctuate P.comma (map (P.text . lowerEdition) [(minBound :: FedoraEdition)..maxBound])) <> P.rbrace) <> P.text " [default: workstation]" ,                P.text "",-               P.text "See <https://fedoraproject.org/wiki/Infrastructure/MirrorManager>",-               P.text "and also <https://fedoramagazine.org/verify-fedora-iso-file>."+               P.text "See <https://github.com/juhp/dl-fedora/#readme>"              ]   sysarch <- readArch <$> cmd "rpm" ["--eval", "%{_arch}"]   simpleCmdArgsWithMods (Just version) (fullDesc <> header "Fedora iso downloader" <> progDescDoc pdoc) $     program     <$> switchWith 'g' "gpg-keys" "Import Fedora GPG keys for verifying checksum file"     <*> checkSumOpts-    <*> switchWith 'n' "dry-run" "Don't actually download anything"     <*> switchLongWith "debug" "Debug output"     <*> switchWith 'T' "no-http-timeout" "Do not timeout for http response"-    <*> switchWith 'l' "local" "Show current local image via symlink"-    <*> switchWith 'r' "run" "Boot image in Qemu"-    <*> switchWith 'R' "replace" "Delete previous snapshot image after downloading latest one"+    <*> (flagWith' Check 'c' "check" "Check if newer image available" <|>+         flagWith' Local 'l' "local" "Show current local image" <|>+         Download <$> switchWith 'R' "replace" "Delete previous snapshot image after downloading latest one")+    <*> switchWith 'n' "dry-run" "Don't actually download anything"+    <*> switchWith 'r' "run" "Boot image in QEMU"     <*> mirrorOpt     <*> (flagLongWith' CSDevelopment "cs-devel" "Use centos-stream development compose" <|>          flagLongWith CSProduction CSTest "cs-test" "Use centos-stream test compose (default is production)")@@ -145,25 +151,25 @@   where     mirrorOpt :: Parser Mirror     mirrorOpt =+      flagWith' DefaultLatest 'L' "latest" "Get latest image either from mirror or dl.fp.o if newer" <|>       flagWith' DlFpo 'd' "dl" "Use dl.fedoraproject.org (dl.fp.o)" <|>-      flagWith' UseMirror 'D' "no-dl" "Do not use dl.fp.o (even if newer)" <|>       flagWith' KojiFpo 'k' "koji" "Use koji.fedoraproject.org" <|>       Mirror <$> strOptionWith 'm' "mirror" "URL" ("Mirror url for /pub [default " ++ downloadFpo ++ "]") <|>-      pure DefaultLatest+      pure UseMirror      checkSumOpts :: Parser CheckSum     checkSumOpts =-      flagWith' NoCheckSum 'C' "no-checksum" "Do not check checksum" <|>-      flagWith AutoCheckSum CheckSum 'c' "checksum" "Do checksum even if already downloaded"+      flagLongWith' NoCheckSum "no-checksum" "Do not check checksum" <|>+      flagLongWith AutoCheckSum CheckSum "checksum" "Do checksum even if already downloaded"  data Primary = Primary {primaryUrl :: String,                         primarySize :: Maybe Integer,                         primaryTime :: Maybe UTCTime} -program :: Bool -> CheckSum -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool+program :: Bool -> CheckSum -> Bool -> Bool -> Mode -> Bool -> Bool         -> Mirror -> CentosChannel -> Arch -> String -> FedoraEdition         -> IO ()-program gpg checksum dryrun debug notimeout local run removeold mirror channel arch tgtrel edition = do+program gpg checksum debug notimeout mode dryrun run mirror channel arch tgtrel edition = do   let mirrorUrl =         case mirror of           Mirror m -> m@@ -178,38 +184,50 @@   mgr <- if notimeout          then newManager (tlsManagerSettings {managerResponseTimeout = responseTimeoutNone})          else httpManager-  if local-    then do-    symlink <--      if dryrun-      then do-      filePrefix <- getFilePrefix showdestdir-      -- FIXME support non-iso-      return $ filePrefix <> (if tgtrel == "eln" then "-" <> showArch arch else "") <> "-latest" <.> "iso"-      else do-      (fileurl, filenamePrefix, prime, _mchecksum, _done) <--        findURL mgr mirrorUrl showdestdir-      tz <- getCurrentTimeZone-      putStrLn $ unwords ["Newest:", takeFileName fileurl, renderTime tz (primaryTime prime)]-      putStrLn fileurl-      return $ filenamePrefix <> (if tgtrel == "eln" then "-" <> showArch arch else "") <> "-latest" <.> takeExtension fileurl-    if run-      then bootImage symlink showdestdir-      else showSymlink symlink showdestdir-    else do-    (fileurl, filenamePrefix, prime, mchecksum, done) <- findURL mgr mirrorUrl showdestdir-    let symlink = filenamePrefix <> (if tgtrel == "eln" then "-" <> showArch arch else "") <> "-latest" <.> takeExtension fileurl-    downloadFile dryrun debug done mgr fileurl prime showdestdir >>=-      fileChecksum mgr mchecksum showdestdir-    unless dryrun $ do-      let localfile = takeFileName fileurl-      updateSymlink localfile symlink showdestdir-      when run $ bootImage localfile showdestdir+  case mode of+    Check -> do+      (fileurl, filenamePrefix, _prime, _mchecksum, done) <-+        findURL mgr mirrorUrl showdestdir True+      if done+        then do+        putStrLn $ "Local:" +-+ takeFileName fileurl +-+ "is latest"+        when debug $+          putStrLn fileurl+        else do+        let symlink = filenamePrefix <> (if tgtrel == "eln" then "-" <> showArch arch else "") <> "-latest" <.> takeExtension fileurl+        mtarget <- derefSymlink symlink+        case mtarget of+          Just target -> do+            if target == takeFileName fileurl+              then putStrLn $ "Latest:" +-+ target +-+ "(locally incomplete)"+              else do+              putStrLn $ "Local:" +-+ target+              putStrLn $ "Newer:" +-+ takeFileName fileurl+          Nothing -> putStrLn $ "Available:" +-+ takeFileName fileurl+    Local -> do+      prefix <- getFilePrefix showdestdir+        -- FIXME support non-iso+      let symlink = prefix <> (if tgtrel == "eln" then "-" <> showArch arch else "") <> "-latest" <.> "iso"+      mtarget <- derefSymlink symlink+      whenJust mtarget $ \target -> do+        putStrLn $ "Local:" +-+ target+        when run $+          bootImage dryrun target showdestdir+    Download removeold -> do+      (fileurl, filenamePrefix, prime, mchecksum, done) <-+        findURL mgr mirrorUrl showdestdir False+      let symlink = filenamePrefix <> (if tgtrel == "eln" then "-" <> showArch arch else "") <> "-latest" <.> takeExtension fileurl+      downloadFile dryrun debug done mgr fileurl prime showdestdir >>=+        fileChecksum mgr mchecksum showdestdir+      unless dryrun $ do+        let localfile = takeFileName fileurl+        updateSymlink localfile symlink showdestdir removeold+        when run $ bootImage dryrun localfile showdestdir   where-    findURL :: Manager -> String -> String+    findURL :: Manager -> String -> String -> Bool             -- urlpath, fileprefix, primary, checksum, downloaded             -> IO (URL, String, Primary, Maybe String, Bool)-    findURL mgr mirrorUrl showdestdir = do+    findURL mgr mirrorUrl showdestdir quiet = do       (path,mrelease) <- urlPathMRel mgr       -- use http-directory trailingSlash (0.1.7)       let primaryDir =@@ -218,7 +236,7 @@               "eln" -> odcsFpo               "c8s" -> odcsStream               "c9s" -> odcsStream-              _ -> if mirror == DefaultLatest+              _ -> if mirror `elem` [DefaultLatest, DlFpo]                    then dlFpo                    else downloadFpo             +/+ path <> "/"@@ -241,12 +259,19 @@             exists <- doesFileExist localfile             if exists               then do-              done <- checkLocalFileSize localfile primeSize primeTime showdestdir+              done <- checkLocalFileSize localfile primeSize primeTime showdestdir quiet               if done                 then return (primeUrl,True)                 else do-                unlessM (writable <$> getPermissions localfile) $-                  error' $ localfile <> " does have write permission, aborting!"+                unlessM (writable <$> getPermissions localfile) $ do+                  putStrLn $ localfile <> " does not have write permission!"+                  yes <- yesNo "Fix ownership"+                  if yes+                    then do+                    user <- getLoginName+                    sudoLog "chown" [user,localfile]+                    else+                    error' $ localfile <> " does have write permission, aborting!"                 findMirror primeUrl path file               else findMirror primeUrl path file           let finalDir = dropFileName finalurl@@ -301,14 +326,14 @@         _ -> error' $ tgtrel ++ " is unsupported with --dryrun"      checkLocalFileSize :: FilePath -> Maybe Integer -> Maybe UTCTime-                       -> String -> IO Bool-    checkLocalFileSize localfile mprimeSize mprimeTime showdestdir = do+                       -> String -> Bool -> IO Bool+    checkLocalFileSize localfile mprimeSize mprimeTime showdestdir quiet = do       localsize <- toInteger . fileSize <$> getFileStatus localfile       if Just localsize == mprimeSize         then do-        when (not run && takeExtension localfile == ".iso") $ do+        unless quiet $ do           tz <- getCurrentTimeZone-          -- FIXME abbreviate size+          -- FIXME abbreviate size?           putStrLn $ unwords [showdestdir </> localfile, renderTime tz mprimeTime, "size " ++ show localsize ++ " okay"]         return True         else do@@ -442,7 +467,7 @@       if not exists then return False         else do         (primeSize,primeTime) <- httpFileSizeTime mgr url-        ok <- checkLocalFileSize checksumfile primeSize primeTime showdestdir+        ok <- checkLocalFileSize checksumfile primeSize primeTime showdestdir True         unless ok $ error' $ "Checksum file filesize mismatch for " ++ checksumfile         return ok @@ -450,8 +475,8 @@     checkForFedoraKeys =       pipeBool ("gpg",["--list-keys"]) ("grep", ["-q", " Fedora .*(" <> tgtrel <> ").*@fedoraproject.org>"]) -    updateSymlink :: FilePath -> FilePath -> FilePath -> IO ()-    updateSymlink target symlink showdestdir = do+    updateSymlink :: FilePath -> FilePath -> FilePath -> Bool -> IO ()+    updateSymlink target symlink showdestdir removeold = do       mmsymlinkTarget <- do         havefile <- doesFileExist symlink         if havefile@@ -476,17 +501,12 @@           createSymbolicLink target symlink           putStrLn $ unwords [showdestdir </> symlink, "->", target] -    showSymlink :: FilePath -> FilePath -> IO ()-    showSymlink symlink showdestdir = do-      msymlinkTarget <- do-        havefile <- doesFileExist symlink-        if havefile-          then Just <$> readSymbolicLink symlink-          else return Nothing-      case msymlinkTarget of-        Just symlinktarget ->-          putStrLn $ "Local: " ++ showdestdir </> symlinktarget-        _ -> return ()+    derefSymlink :: FilePath -> IO (Maybe FilePath)+    derefSymlink symlink = do+      havefile <- doesFileExist symlink+      if havefile+        then Just <$> readSymbolicLink symlink+        else return Nothing  renderTime :: TimeZone -> Maybe UTCTime -> String renderTime tz mprimeTime =@@ -522,6 +542,7 @@ editionType :: FedoraEdition -> String editionType Server = "dvd" editionType Kinoite = "ostree"+editionType Onyx = "ostree" editionType Sericea = "ostree" editionType Silverblue = "ostree" editionType Everything = "netinst"@@ -537,8 +558,8 @@ liveRespin :: FedoraEdition -> String liveRespin = take 4 . upper . showEdition -bootImage :: FilePath -> String -> IO ()-bootImage img showdir = do+bootImage :: Bool -> FilePath -> String -> IO ()+bootImage dryrun img showdir = do   let fileopts =         case takeExtension img of           ".iso" -> ["-boot", "d", "-cdrom"]@@ -548,7 +569,8 @@     Just qemu -> do       let args = ["-m", "2048", "-rtc", "base=localtime", "-cpu", "host"] ++ fileopts       cmdN qemu (args ++ [showdir </> img])-      cmd_ qemu (args ++ [img])+      unless dryrun $+        cmd_ qemu (args ++ [img])     Nothing -> error' "Need qemu to run image"  #if !MIN_VERSION_http_directory(0,1,6)
test/tests.hs view
@@ -16,21 +16,21 @@  tests :: Bool -> [[String]] tests ghAction =-  [["-n", previous, "-c"]+  [["-n", previous, "--checksum"]   ,["-n", "rawhide", "silverblue"]-  ,["-n", "respin"]+  ,["-c", "respin"]   ,["-l", current]   ,["-l", "rawhide", "-n"]   ] ++   if ghAction then []   else-    [["-n", current, "silverblue"]+    [["-c", current, "silverblue"]     ,["-n", previous, "kde"]     ,["-T", "-n", current, "everything"]     ,["-n", previous, "server", "--arch", "aarch64"]     ,["-n", "c8s"]     ,["-n", "c9s"]-    ,["-n", "eln"]+    ,["-c", "eln"]     ]  main :: IO ()