-- SPDX-License-Identifier: BSD-3-Clause
module Main (main) where
import Control.Monad.Extra (unless, void, when, whenJust)
import Data.List (isPrefixOf, isInfixOf)
import SimpleCmd
import SimplePrompt
import System.Directory (createDirectoryIfMissing, doesDirectoryExist,
doesFileExist, removeFile, renameFile)
import System.Environment.XDG.BaseDir (getUserCacheDir)
import System.FilePath ((</>))
import System.IO (BufferMode(..), hSetBuffering, stdout)
rpmostree :: String
rpmostree = "/usr/bin/rpm-ostree"
data Mode = Update | Changelog
instance Show Mode where
show Update = "update"
show Changelog = "changelog"
modeArgs :: Mode -> [String]
modeArgs Update = ["update", "--preview"]
modeArgs Changelog = ["db", "diff", "-c"]
main :: IO ()
main = do
hSetBuffering stdout NoBuffering
let sysrootDir = "/sysroot"
sysroot <- doesDirectoryExist sysrootDir
unless sysroot $
error' $ "no" +-+ sysrootDir +-+ "found: not a rpm-ostree system?"
exists <- doesFileExist rpmostree
unless exists $
error' $ rpmostree +-+ ": not found"
cachedir <- getUserCacheDir "rpmostree-updates"
createDirectoryIfMissing True cachedir
-- whether latest is a staged deployment
staged <- ("true" `isInfixOf`) <$> cmd rpmostree ["status", "-J", "$.deployments[0].staged"]
-- FIXME
unless staged $
putStrLn "current latest deployment is live"
putStrLn "Preview:"
changed <- cachedRpmOstree staged cachedir Update
when changed $ do
prompt_ "Press Enter to update"
cmd_ rpmostree ["update"]
showChangelog <- yesno (Just changed) "Show changelog"
when showChangelog $
void $ cachedRpmOstree staged cachedir Changelog
cachedRpmOstree :: Bool -> FilePath -> Mode -> IO Bool
cachedRpmOstree staged cachedir mode = do
let latest = cachedir </> "latest-" ++ show mode
mprevious <- cacheFile latest
ok <- pipeBool (rpmostree, modeArgs mode) ("tee", [latest])
if not ok
then do
whenJust mprevious $ \previous ->
renameFile previous latest
return False
else
case mprevious of
Just previous -> do
(diff,_err) <- cmdStdErr "diff" ["-u0", previous, latest]
let diffs = lines diff
when (length (filter (not . noise) diffs) > 3) $
putStrLn $ "\nDiff with last rpm-ostree update:\n" ++ unlines diffs
if length diffs <= 9
-- FIXME print old timestamp
then do
putStrLn "no new changes"
return False
else return True
Nothing -> not <$> grep_ "No updates available." latest
where
cacheFile :: FilePath -> IO (Maybe FilePath)
cacheFile latestCache = do
let previousCache = cachedir </> "previous-" ++ show mode
haveLatest <- doesFileExist latestCache
if haveLatest
then do
useCache <-
case mode of
Update -> return staged
Changelog -> do
-- ostree diff commit from: booted deployment (81ad560ed850559259e46d5739e8d9ac8714fbcbbd0d24257a8934db7730edab)
cachedBootedDeployment <- init . tail . last . words <$> cmd "head" ["-1", latestCache]
bootedChecksum <- cmd rpmostree ["status", "-b", "-J", "$.deployments[0].checksum"]
return $ cachedBootedDeployment `isInfixOf` bootedChecksum
if useCache
then do
renameFile latestCache previousCache
return $ Just previousCache
else do
removeFile latestCache
return Nothing
else return Nothing
noise :: String -> Bool
noise cs = "@@ " `isPrefixOf` cs ||
" 0 content objects fetched; " `isInfixOf` cs