stack-clean-old 0.2 → 0.2.1
raw patch · 9 files changed
+394/−280 lines, 9 filesdep +filemanip
Dependencies added: filemanip
Files
- ChangeLog.md +5/−0
- Main.hs +0/−278
- src/Directories.hs +36/−0
- src/GHC.hs +82/−0
- src/Main.hs +65/−0
- src/Remove.hs +13/−0
- src/Snapshots.hs +166/−0
- src/Versions.hs +19/−0
- stack-clean-old.cabal +8/−2
ChangeLog.md view
@@ -1,5 +1,10 @@ # Revision history for stack-clean-old-work +## 0.2.1 (2020-11-11)+- remove-earlier-minor can now take a major version+- major internal refactors with VersionSnapshots type+- split code into modules+ ## 0.2 (2020-10-26) - add --dryrun to all remove commands - remove-earlier-minor subcommands to purge for previous ghc minor versions
− Main.hs
@@ -1,278 +0,0 @@-{-# LANGUAGE CPP #-}--import Control.Monad.Extra-import Data.List.Extra-import Data.Maybe-import Data.Version.Extra-import SimpleCmd-#if MIN_VERSION_simple_cmd(0,2,1)- hiding (ifM, whenM)-#endif-import SimpleCmdArgs-import System.Directory-import System.FilePath-import System.IO (BufferMode(NoBuffering), hSetBuffering, stdout)-import Text.Printf--import Paths_stack_clean_old (version)--main :: IO ()-main = do- hSetBuffering stdout NoBuffering- simpleCmdArgs (Just version) "Stack clean up tool"- "Cleans away old stack-work builds (and pending: stack snapshots) to recover diskspace." $- subcommands- [ Subcommand "project" "Commands for project .stack-work builds" $- subcommands- [ Subcommand "size" "Total size of project's .stack-work/install" $- sizeStackWork <$> dirOption <*> notHumanOpt- , Subcommand "list" "List builds in .stack-work/install per ghc version" $- listGhcSnapshots . setStackWorkDir <$> dirOption <*> optional ghcVerArg- , Subcommand "remove-version" "Remove builds in .stack-work/install for a ghc version" $- cleanGhcSnapshots . setStackWorkDir <$> dirOption <*> dryrun <*> ghcVerArg- , Subcommand "remove-earlier-minor" "Remove builds in .stack-work/install for previous ghc minor versions" $- cleanMinorSnapshots . setStackWorkDir <$> dirOption <*> dryrun <*> optional ghcVerArg- , Subcommand "remove-older" "Purge older builds in .stack-work/install" $- cleanOldStackWork <$> dryrun <*> keepOption <*> optional (strArg "PROJECTDIR")- ]- , Subcommand "snapshots" "Commands for ~/.stack/snapshots" $- subcommands- [ Subcommand "size" "Total size of all stack build snapshots" $- sizeSnapshots <$> notHumanOpt- , Subcommand "list" "List build snapshots per ghc version" $- listGhcSnapshots setStackSnapshotsDir <$> optional ghcVerArg- , Subcommand "remove-version" "Remove build snapshots for a ghc version" $- cleanGhcSnapshots setStackSnapshotsDir <$> dryrun <*> ghcVerArg- , Subcommand "remove-earlier-minor" "Remove build snapshots for previous ghc minor versions" $- cleanMinorSnapshots setStackSnapshotsDir <$> dryrun <*> optional ghcVerArg- ]- , Subcommand "ghc" "Commands on stack's ghc compiler installations" $- subcommands- [ Subcommand "size" "Total size of installed stack ghc compilers" $- sizeGhcInstalls <$> notHumanOpt- , Subcommand "list" "List installed stack ghc compiler versions" $- listGhcInstallation <$> optional ghcVerArg- , Subcommand "remove-version" "Remove installation of a stack ghc compiler version" $- removeGhcVersionInstallation <$> dryrun <*> ghcVerArg- , Subcommand "remove-earlier-minor" "Remove installations of stack ghc previous minor versions" $- removeGhcMinorInstallation <$> dryrun <*> optional ghcVerArg- ]- ]- where- dryrun = switchWith 'n' "dryrun" "Show what would be done, without removing"-- notHumanOpt = switchWith 'H' "not-human-size" "Do not use du --human-readable"-- dirOption = optional (strOptionWith 'd' "dir" "PROJECTDIR" "Path to project")- ghcVerArg = readVersion <$> strArg "GHCVER"-- keepOption = positive <$> optionalWith auto 'k' "keep" "INT" "number of project builds per ghc version [default 5]" 5-- positive :: Int -> Int- positive n = if n > 0 then n else error' "Must be positive integer"--stackWorkInstall :: FilePath-stackWorkInstall = ".stack-work/install"--sizeStackWork :: Maybe FilePath -> Bool -> IO ()-sizeStackWork mdir nothuman = do- let path = fromMaybe "" mdir </> stackWorkInstall- whenM (doesDirectoryExist path) $- cmd_ "du" $ ["-h" | not nothuman] ++ ["-s", path]--printTotalGhcSize :: [FilePath] -> IO ()-printTotalGhcSize ds = do- total <- head . words . last <$> cmdLines "du" ("-shc":ds)- printf "%4s %-6s (%d dirs)\n" total ((takeFileName . head) ds) (length ds)--setStackWorkDir :: Maybe FilePath -> IO ()-setStackWorkDir mdir = do- whenJust mdir $ \ dir -> setCurrentDirectory dir- switchToSystemDirUnder stackWorkInstall--setStackSnapshotsDir :: IO ()-setStackSnapshotsDir = do- home <- getHomeDirectory- switchToSystemDirUnder $ home </> ".stack/snapshots"--getSnapshotDirs :: IO [FilePath]-getSnapshotDirs = do- lines <$> shell ( unwords $ "ls" : ["-d", "*/*"])--takeGhcSnapshots :: Version -> [FilePath] -> [FilePath]-takeGhcSnapshots ghcver =- map takeDirectory . filter ((== ghcver) . (if isMajorVersion ghcver then majorVersion else id) . readVersion . takeFileName)--sizeSnapshots :: Bool -> IO ()-sizeSnapshots nothuman = do- home <- getHomeDirectory- cmd_ "du" $ ["-h" | not nothuman] ++ ["-s", home </> ".stack/snapshots"]--listGhcSnapshots :: IO () -> Maybe Version -> IO ()-listGhcSnapshots setdir mghcver = do- setdir- dirs <- sortOn (readVersion . takeFileName) <$> getSnapshotDirs- case mghcver of- Nothing -> do- let ghcs = groupOn takeFileName dirs- mapM_ printTotalGhcSize ghcs- Just ghcver | isMajorVersion ghcver -> do- let ghcs = groupOn takeFileName $ filter ((== ghcver) . majorVersion . readVersion . takeFileName) dirs- mapM_ printTotalGhcSize ghcs- Just ghcver -> do- let ds = takeGhcSnapshots ghcver dirs- unless (null ds) $- cmd_ "du" ("-shc":ds)--doRemoveDirectory :: Bool -> FilePath -> IO ()-doRemoveDirectory dryrun dir =- unless dryrun $- removeDirectoryRecursive dir--cleanGhcSnapshots :: IO () -> Bool -> Version -> IO ()-cleanGhcSnapshots setDir dryrun ghcver = do- setDir- dirs <- takeGhcSnapshots ghcver <$> getSnapshotDirs- unless (dryrun || null dirs) $- when (isMajorVersion ghcver) $ do- putStr $ "Press Enter to delete all " ++ showVersion ghcver ++ " builds: "- void getLine- unless (null dirs) $ do- mapM_ (doRemoveDirectory dryrun) dirs- putStrLn $ show (length dirs) ++ " snapshots removed for " ++ showVersion ghcver--cleanMinorSnapshots :: IO () -> Bool -> Maybe Version -> IO ()-cleanMinorSnapshots setDir dryrun mghcver = do- setDir- dirs <- sortOn (readVersion . takeFileName) <$> getSnapshotDirs- case mghcver of- Nothing -> do- let ghcs = map (groupOn takeFileName) $ groupOn (majorVersion . readVersion) dirs- forM_ ghcs $ \ gmajor ->- when (length gmajor > 1) $- forM_ (init gmajor) $ \ gminor -> do- mapM_ (doRemoveDirectory dryrun) gminor- putStrLn $ show (length gminor) ++ " snapshots removed for " ++ takeFileName (head gminor)- Just ghcver -> do- let major =- if length (versionBranch ghcver) == 2- then majorVersion ghcver- else error' "Please specify minor version X.Y.Z"- gmajor = groupOn takeFileName $ filter (olderMinor major ghcver) dirs- when (length gmajor > 1) $- forM_ (init gmajor) $ \ gminor -> do- mapM_ (doRemoveDirectory dryrun) gminor- putStrLn $ show (length gminor) ++ " snapshots removed for " ++ takeFileName (head gminor)- where- olderMinor :: Version -> Version -> FilePath -> Bool- olderMinor major ghcver d =- ((== major) . majorVersion . readVersion) d &&- ((< ghcver) . readVersion . takeFileName) d--majorVersion :: Version -> Version-majorVersion ver =- let vernums = versionBranch ver in- case length vernums of- 2 -> ver- 3 -> (makeVersion . init) vernums- _ -> error' $ "Bad ghc version " ++ showVersion ver--switchToSystemDirUnder :: FilePath -> IO ()-switchToSystemDirUnder dir = do- ifM (doesDirectoryExist dir)- (setCurrentDirectory dir)- (error' $ dir ++ "not found")- systems <- listDirectory "."- let system = case systems of- [] -> error' $ "No OS system in " ++ dir- [sys] -> sys- _ -> error' "More than one OS systems found " ++ dir ++ " (unsupported)"- setCurrentDirectory system--cleanOldStackWork :: Bool -> Int -> Maybe FilePath -> IO ()-cleanOldStackWork dryrun keep mdir = do- setStackWorkDir mdir- dirs <- sortOn takeFileName . lines <$> shell ( unwords $ "ls" : ["-d", "*/*"])- let ghcs = groupOn takeFileName dirs- mapM_ removeOlder ghcs- where- removeOlder :: [FilePath] -> IO ()- removeOlder dirs = do- let ghcver = (takeFileName . head) dirs- oldfiles <- drop keep . reverse <$> sortedByAge- mapM_ (doRemoveDirectory dryrun . takeDirectory) oldfiles- unless (null oldfiles) $- putStrLn $ show (length oldfiles) ++ " dirs removed for " ++ ghcver- where- sortedByAge = do- fileTimes <- mapM newestTimeStamp dirs- return $ map fst $ sortOn snd fileTimes-- newestTimeStamp dir = do- withCurrentDirectory dir $ do- files <- listDirectory "."- timestamp <- maximum <$> mapM getModificationTime files- return (dir, timestamp)--stackProgramsDir :: FilePath-stackProgramsDir = ".stack/programs"--sizeGhcInstalls :: Bool -> IO ()-sizeGhcInstalls nothuman = do- home <- getHomeDirectory- cmd_ "du" $ ["-h" | not nothuman] ++ ["-s", home </> stackProgramsDir]--setStackProgramsDir :: IO ()-setStackProgramsDir = do- home <- getHomeDirectory- switchToSystemDirUnder $ home </> stackProgramsDir--getGhcInstallDirs :: IO [FilePath]-getGhcInstallDirs = do- setStackProgramsDir- listDirectory "." >>= fmap sort . filterM doesDirectoryExist--ghcInstallVersion :: FilePath -> Version-ghcInstallVersion =- readVersion . takeWhileEnd (/= '-')--isMajorVersion :: Version -> Bool-isMajorVersion ver =- majorVersion ver == ver--listGhcInstallation :: Maybe Version -> IO ()-listGhcInstallation mghcver = do- dirs <- sortOn ghcInstallVersion <$> getGhcInstallDirs- mapM_ putStrLn $ case mghcver of- Nothing -> dirs- Just ghcver -> filter ((== ghcver) . (if isMajorVersion ghcver then majorVersion else id) . ghcInstallVersion) dirs--removeGhcVersionInstallation :: Bool -> Version -> IO ()-removeGhcVersionInstallation dryrun ghcver = do- installs <- filter ((== ghcver) . (if isMajorVersion ghcver then majorVersion else id) . ghcInstallVersion) <$> getGhcInstallDirs- case installs of- [] -> error' $ "stack ghc compiler version " ++ showVersion ghcver ++ " not found"- [g] | not (isMajorVersion ghcver) -> doRemoveGhcVersion dryrun g- gs -> if isMajorVersion ghcver then do- putStr $ "Press Enter to delete all stack " ++ showVersion ghcver ++ " installations: "- void getLine- mapM_ (doRemoveGhcVersion dryrun) gs- else error' "more than one match found!!"--removeGhcMinorInstallation :: Bool -> Maybe Version -> IO ()-removeGhcMinorInstallation dryrun mghcver = do- dirs <- sortOn ghcInstallVersion <$> getGhcInstallDirs- case mghcver of- Nothing -> do- let majors = groupOn (majorVersion . ghcInstallVersion) dirs- forM_ majors $ \ minors ->- forM_ (init minors) $ doRemoveGhcVersion dryrun- Just ghcver -> do- let minors = filter ((== majorVersion ghcver) . majorVersion . readVersion) dirs- forM_ (init minors) $ doRemoveGhcVersion dryrun--doRemoveGhcVersion :: Bool -> FilePath -> IO ()-doRemoveGhcVersion dryrun ghcinst = do- doRemoveDirectory dryrun ghcinst- unless dryrun (removeFile (ghcinst <.> "installed"))- putStrLn $ ghcinst ++ " removed"
+ src/Directories.hs view
@@ -0,0 +1,36 @@+module Directories (+ getStackSubdir,+ globDirs,+ switchToSystemDirUnder+ )+where++import Control.Monad.Extra+import Data.List.Extra+import SimpleCmd (error')+import System.Directory+import System.FilePath+import System.FilePath.Glob++globDirs :: String -> IO [FilePath]+globDirs pat = do+ map (dropPrefix "./") <$> namesMatching (pat ++ "/")++getStackSubdir :: FilePath -> IO FilePath+getStackSubdir subdir = do+ home <- getHomeDirectory+ return $ home </> ".stack" </> subdir++switchToSystemDirUnder :: FilePath -> IO ()+switchToSystemDirUnder dir = do+ ifM (doesDirectoryExist dir)+ (setCurrentDirectory dir)+ (error' $ dir ++ "not found")+ systems <- listDirectory "."+ -- FIXME be more precise/check "system" dirs+ -- eg 64bit intel Linux: x86_64-linux-tinfo6+ let system = case systems of+ [] -> error' $ "No OS system in " ++ dir+ [sys] -> sys+ _ -> error' "More than one OS systems found " ++ dir ++ " (unsupported)"+ setCurrentDirectory system
+ src/GHC.hs view
@@ -0,0 +1,82 @@+module GHC (+ listGhcInstallation,+ removeGhcMinorInstallation,+ removeGhcVersionInstallation,+ sizeGhcInstalls+ )+where++import Control.Monad.Extra+import Data.List.Extra+import Data.Version.Extra+import SimpleCmd+import System.FilePath++import Directories (getStackSubdir, globDirs, switchToSystemDirUnder)+import qualified Remove+import Versions++getStackProgramsDir :: IO FilePath+getStackProgramsDir =+ getStackSubdir "programs"++sizeGhcInstalls :: Bool -> IO ()+sizeGhcInstalls nothuman = do+ programs <- getStackProgramsDir+ cmd_ "du" $ ["-h" | not nothuman] ++ ["-s", programs]++setStackProgramsDir :: IO ()+setStackProgramsDir =+ getStackProgramsDir >>= switchToSystemDirUnder++getGhcInstallDirs :: Maybe Version -> IO [FilePath]+getGhcInstallDirs mghcver = do+ setStackProgramsDir+ sortOn ghcInstallVersion <$> globDirs matchVersion+ where+ matchVersion =+ case mghcver of+ Nothing -> "*"+ Just ver ->+ "*-" ++ showVersion ver ++ if isMajorVersion ver then "*" else ""++ghcInstallVersion :: FilePath -> Version+ghcInstallVersion =+ readVersion . takeWhileEnd (/= '-')++listGhcInstallation :: Maybe Version -> IO ()+listGhcInstallation mghcver = do+ dirs <- getGhcInstallDirs mghcver+ mapM_ (putStrLn . showVersion . ghcInstallVersion) $ case mghcver of+ Nothing -> dirs+ Just ghcver -> filter ((== ghcver) . (if isMajorVersion ghcver then majorVersion else id) . ghcInstallVersion) dirs++removeGhcVersionInstallation :: Bool -> Version -> IO ()+removeGhcVersionInstallation dryrun ghcver = do+ installs <- getGhcInstallDirs (Just ghcver)+ case installs of+ [] -> error' $ "stack ghc compiler version " ++ showVersion ghcver ++ " not found"+ [g] | not (isMajorVersion ghcver) -> doRemoveGhcVersion dryrun g+ gs -> if isMajorVersion ghcver then do+ putStr $ "Press Enter to delete all stack " ++ showVersion ghcver ++ " installations: "+ void getLine+ mapM_ (doRemoveGhcVersion dryrun) gs+ else error' "more than one match found!!"++removeGhcMinorInstallation :: Bool -> Maybe Version -> IO ()+removeGhcMinorInstallation dryrun mghcver = do+ dirs <- getGhcInstallDirs (majorVersion <$> mghcver)+ case mghcver of+ Nothing -> do+ let majors = groupOn (majorVersion . ghcInstallVersion) dirs+ forM_ majors $ \ minors ->+ forM_ (init minors) $ doRemoveGhcVersion dryrun+ Just ghcver -> do+ let minors = filter ((< ghcver) . ghcInstallVersion) dirs+ forM_ minors $ doRemoveGhcVersion dryrun++doRemoveGhcVersion :: Bool -> FilePath -> IO ()+doRemoveGhcVersion dryrun ghcinst = do+ Remove.doRemoveDirectory dryrun ghcinst+ unless dryrun (Remove.removeFile (ghcinst <.> "installed"))+ putStrLn $ ghcinst ++ " removed"
+ src/Main.hs view
@@ -0,0 +1,65 @@+module Main (main) where++import Data.Version.Extra+import SimpleCmd+import SimpleCmdArgs+import System.IO (BufferMode(NoBuffering), hSetBuffering, stdout)++import GHC+import Paths_stack_clean_old (version)+import Snapshots++main :: IO ()+main = do+ hSetBuffering stdout NoBuffering+ simpleCmdArgs (Just version) "Stack clean up tool"+ "Cleans away old stack-work builds (and pending: stack snapshots) to recover diskspace." $+ subcommands+ [ Subcommand "project" "Commands for project .stack-work builds" $+ subcommands+ [ Subcommand "size" "Total size of project's .stack-work/install" $+ sizeStackWork <$> dirOption <*> notHumanOpt+ , Subcommand "list" "List builds in .stack-work/install per ghc version" $+ listGhcSnapshots . setStackWorkDir <$> dirOption <*> optional ghcVerArg+ , Subcommand "remove-version" "Remove builds in .stack-work/install for a ghc version" $+ cleanGhcSnapshots . setStackWorkDir <$> dirOption <*> dryrun <*> ghcVerArg+ , Subcommand "remove-earlier-minor" "Remove builds in .stack-work/install for previous ghc minor versions" $+ cleanMinorSnapshots . setStackWorkDir <$> dirOption <*> dryrun <*> optional ghcVerArg+ , Subcommand "remove-older" "Purge older builds in .stack-work/install" $+ cleanOldStackWork <$> dryrun <*> keepOption <*> optional (strArg "PROJECTDIR")+ ]+ , Subcommand "snapshots" "Commands for ~/.stack/snapshots" $+ subcommands+ [ Subcommand "size" "Total size of all stack build snapshots" $+ sizeSnapshots <$> notHumanOpt+ , Subcommand "list" "List build snapshots per ghc version" $+ listGhcSnapshots setStackSnapshotsDir <$> optional ghcVerArg+ , Subcommand "remove-version" "Remove build snapshots for a ghc version" $+ cleanGhcSnapshots setStackSnapshotsDir <$> dryrun <*> ghcVerArg+ , Subcommand "remove-earlier-minor" "Remove build snapshots for previous ghc minor versions" $+ cleanMinorSnapshots setStackSnapshotsDir <$> dryrun <*> optional ghcVerArg+ ]+ , Subcommand "ghc" "Commands on stack's ghc compiler installations" $+ subcommands+ [ Subcommand "size" "Total size of installed stack ghc compilers" $+ sizeGhcInstalls <$> notHumanOpt+ , Subcommand "list" "List installed stack ghc compiler versions" $+ listGhcInstallation <$> optional ghcVerArg+ , Subcommand "remove-version" "Remove installation of a stack ghc compiler version" $+ removeGhcVersionInstallation <$> dryrun <*> ghcVerArg+ , Subcommand "remove-earlier-minor" "Remove installations of stack ghc previous minor versions" $+ removeGhcMinorInstallation <$> dryrun <*> optional ghcVerArg+ ]+ ]+ where+ dryrun = switchWith 'n' "dryrun" "Show what would be done, without removing"++ notHumanOpt = switchWith 'H' "not-human-size" "Do not use du --human-readable"++ dirOption = optional (strOptionWith 'd' "dir" "PROJECTDIR" "Path to project")+ ghcVerArg = readVersion <$> strArg "GHCVER"++ keepOption = positive <$> optionalWith auto 'k' "keep" "INT" "number of project builds per ghc version [default 5]" 5++ positive :: Int -> Int+ positive n = if n > 0 then n else error' "Must be positive integer"
+ src/Remove.hs view
@@ -0,0 +1,13 @@+module Remove (+ doRemoveDirectory,+ removeFile+ )+where++import Control.Monad+import System.Directory++doRemoveDirectory :: Bool -> FilePath -> IO ()+doRemoveDirectory dryrun dir =+ unless dryrun $+ removeDirectoryRecursive dir
+ src/Snapshots.hs view
@@ -0,0 +1,166 @@+{-# LANGUAGE CPP #-}++module Snapshots (+ --getSnapshotDirs+ --, VersionSnapshots(..)+ cleanGhcSnapshots,+ cleanMinorSnapshots,+ cleanOldStackWork,+ listGhcSnapshots,+ setStackSnapshotsDir,+ setStackWorkDir,+ sizeSnapshots,+ sizeStackWork+ )+where++import Control.Monad.Extra+import Data.List.Extra+import Data.Maybe+import Data.Version.Extra+import SimpleCmd+#if MIN_VERSION_simple_cmd(0,2,1)+ hiding (whenM)+#endif+import System.Directory hiding (removeDirectoryRecursive, removeFile)+import System.FilePath+import Text.Printf++import Directories (globDirs, getStackSubdir, switchToSystemDirUnder)+import qualified Remove+import Versions++data SnapshotInstall =+ SnapInst { snapHash :: String,+ snapGHC :: Version}+ deriving Eq++instance Ord SnapshotInstall where+ compare s1 s2 = compare (snapGHC s1) (snapGHC s2)++data VersionSnapshots =+ VersionSnaps {snapsVersion :: Version,+ snapsHashes :: [String]}+ deriving Eq++instance Ord VersionSnapshots where+ compare s1 s2 = compare (snapsVersion s1) (snapsVersion s2)++instance Show VersionSnapshots where+ show (VersionSnaps ver snaps) = showVersion ver ++ ":" ++ show snaps++readVersionedSnaps :: [FilePath] -> [VersionSnapshots]+readVersionedSnaps snaps =+ let ghcs = (groupOn snapGHC . sort) (map readSnapshot snaps)+ in map installVerSnaps ghcs+ where+ installVerSnaps :: [SnapshotInstall] -> VersionSnapshots+ installVerSnaps versnaps =+ let ver = snapGHC (head versnaps) in+ VersionSnaps ver (map snapHash versnaps)++ readSnapshot :: FilePath -> SnapshotInstall+ readSnapshot pth =+ let (hash,ver) = splitFileName pth+ -- remove trailing '/'+ in SnapInst (init hash) (readVersion ver)++getSnapshotDirs :: Maybe Version -> IO [VersionSnapshots]+getSnapshotDirs mghcver = do+ let pat = maybe "*" versionMatch mghcver+ readVersionedSnaps <$> globDirs ("*" </> pat)+ where+ versionMatch :: Version -> String+ versionMatch ghcver =+ showVersion ghcver ++ if isMajorVersion ghcver then ".*" else ""++sizeSnapshots :: Bool -> IO ()+sizeSnapshots nothuman = do+ snapshots <- getStackSubdir "snapshots"+ cmd_ "du" $ ["-h" | not nothuman] ++ ["-s", snapshots]++listGhcSnapshots :: IO () -> Maybe Version -> IO ()+listGhcSnapshots setdir mghcver = do+ setdir+ ghcs <- getSnapshotDirs mghcver+ mapM_ printTotalGhcSize ghcs++removeVersionSnaps :: Bool -> VersionSnapshots -> IO ()+removeVersionSnaps dryrun versnap = do+ let dirs = snapsHashes versnap+ mapM_ (Remove.doRemoveDirectory dryrun) dirs+ putStrLn $ show (length dirs) ++ " snapshots removed for " ++ showVersion (snapsVersion versnap)++cleanGhcSnapshots :: IO () -> Bool -> Version -> IO ()+cleanGhcSnapshots setDir dryrun ghcver = do+ setDir+ ghcs <- getSnapshotDirs (Just ghcver)+ when (isMajorVersion ghcver) $ do+ putStr $ "Press Enter to delete all " ++ showVersion ghcver ++ " builds: "+ void getLine+ mapM_ (removeVersionSnaps dryrun) ghcs++cleanMinorSnapshots :: IO () -> Bool -> Maybe Version -> IO ()+cleanMinorSnapshots setDir dryrun mghcver = do+ setDir+ ghcs <- getSnapshotDirs (majorVersion <$> mghcver)+ case mghcver of+ Nothing -> do+ let majors = groupOn (majorVersion . snapsVersion) ghcs+ forM_ majors $ \ gmajor ->+ when (length gmajor > 1) $+ mapM_ (removeVersionSnaps dryrun) (init gmajor)+ Just ghcver -> do+ let newestMinor = if isMajorVersion ghcver+ then (snapsVersion . last) ghcs+ else ghcver+ gminors = filter ((< newestMinor) . snapsVersion) ghcs+ mapM_ (removeVersionSnaps dryrun) gminors++cleanOldStackWork :: Bool -> Int -> Maybe FilePath -> IO ()+cleanOldStackWork dryrun keep mdir = do+ setStackWorkDir mdir+ dirs <- sortOn takeFileName . lines <$> shell ( unwords $ "ls" : ["-d", "*/*"])+ let ghcs = groupOn takeFileName dirs+ mapM_ removeOlder ghcs+ where+ removeOlder :: [FilePath] -> IO ()+ removeOlder dirs = do+ let ghcver = (takeFileName . head) dirs+ oldfiles <- drop keep . reverse <$> sortedByAge+ mapM_ (Remove.doRemoveDirectory dryrun . takeDirectory) oldfiles+ unless (null oldfiles) $+ putStrLn $ show (length oldfiles) ++ " dirs removed for " ++ ghcver+ where+ sortedByAge = do+ fileTimes <- mapM newestTimeStamp dirs+ return $ map fst $ sortOn snd fileTimes++ newestTimeStamp dir = do+ withCurrentDirectory dir $ do+ files <- listDirectory "."+ timestamp <- maximum <$> mapM getModificationTime files+ return (dir, timestamp)++stackWorkInstall :: FilePath+stackWorkInstall = ".stack-work/install"++sizeStackWork :: Maybe FilePath -> Bool -> IO ()+sizeStackWork mdir nothuman = do+ let path = fromMaybe "" mdir </> stackWorkInstall+ whenM (doesDirectoryExist path) $+ cmd_ "du" $ ["-h" | not nothuman] ++ ["-s", path]++printTotalGhcSize :: VersionSnapshots -> IO ()+printTotalGhcSize versnaps = do+ total <- head . words . last <$> cmdLines "du" ("-shc":snapsHashes versnaps)+ printf "%4s %-6s (%d dirs)\n" total ((showVersion . snapsVersion) versnaps) (length (snapsHashes versnaps))++setStackWorkDir :: Maybe FilePath -> IO ()+setStackWorkDir mdir = do+ whenJust mdir $ \ dir -> setCurrentDirectory dir+ switchToSystemDirUnder stackWorkInstall++setStackSnapshotsDir :: IO ()+setStackSnapshotsDir = do+ getStackSubdir "snapshots" >>= switchToSystemDirUnder
+ src/Versions.hs view
@@ -0,0 +1,19 @@+module Versions (+ isMajorVersion,+ majorVersion+ )+where++import Data.Version++majorVersion :: Version -> Version+majorVersion ver =+ let vernums = versionBranch ver in+ case length vernums of+ 2 -> ver+ 3 -> (makeVersion . init) vernums+ _ -> error $ "Bad ghc version " ++ showVersion ver++isMajorVersion :: Version -> Bool+isMajorVersion ver =+ majorVersion ver == ver
stack-clean-old.cabal view
@@ -1,5 +1,5 @@ name: stack-clean-old-version: 0.2+version: 0.2.1 synopsis: Clean away old stack build artefacts description: A tool for cleaning away old .stack snapshots and .stack-work builds@@ -24,11 +24,17 @@ executable stack-clean-old main-is: Main.hs other-modules: Paths_stack_clean_old--- hs-source-dirs: app+ Directories+ GHC+ Remove+ Snapshots+ Versions+ hs-source-dirs: src build-depends: base < 5 , directory >= 1.2.5 , extra >= 1.4.3 , filepath+ , filemanip , simple-cmd >= 0.1.4 , simple-cmd-args >= 0.1.2 default-language: Haskell2010