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 +9/−0
- README.md +35/−20
- dl-fedora.cabal +6/−4
- src/Main.hs +95/−73
- test/tests.hs +4/−4
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 ()