cabal-cache-0.2.0.2: src/App/Commands/SyncToArchive.hs
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
module App.Commands.SyncToArchive
( cmdSyncToArchive
) where
import Antiope.Core (toText)
import Antiope.Env (LogLevel, mkEnv)
import App.Commands.Options.Parser (optsSyncToArchive)
import App.Static (homeDirectory)
import Control.Lens hiding ((<.>))
import Control.Monad (unless, when)
import Control.Monad.Except
import Control.Monad.Trans.Resource (runResourceT)
import Data.Generics.Product.Any (the)
import Data.List (isSuffixOf)
import Data.Semigroup ((<>))
import HaskellWorks.Ci.Assist.Core (PackageInfo (..), Presence (..), Tagged (..), getPackages, loadPlan, relativePaths, relativePaths2)
import HaskellWorks.Ci.Assist.Location ((<.>), (</>))
import HaskellWorks.Ci.Assist.PackageConfig (templateConfig)
import HaskellWorks.Ci.Assist.Show
import HaskellWorks.Ci.Assist.Tar (updateEntryWith)
import Options.Applicative hiding (columns)
import System.Directory (createDirectoryIfMissing, doesDirectoryExist)
import qualified App.Commands.Options.Types as Z
import qualified Codec.Archive.Tar as F
import qualified Codec.Compression.GZip as F
import qualified Data.ByteString.Lazy as LBS
import qualified Data.Text as T
import qualified HaskellWorks.Ci.Assist.GhcPkg as GhcPkg
import qualified HaskellWorks.Ci.Assist.IO.Console as CIO
import qualified HaskellWorks.Ci.Assist.IO.Error as IO
import qualified HaskellWorks.Ci.Assist.IO.File as IO
import qualified HaskellWorks.Ci.Assist.IO.Lazy as IO
import qualified HaskellWorks.Ci.Assist.IO.Tar as IO
import qualified HaskellWorks.Ci.Assist.Types as Z
import qualified System.Directory as IO
import qualified System.IO as IO
import qualified System.IO.Temp as IO
import qualified UnliftIO.Async as IO
{-# ANN module ("HLint: ignore Reduce duplication" :: String) #-}
{-# ANN module ("HLint: ignore Redundant do" :: String) #-}
runSyncToArchive :: Z.SyncToArchiveOptions -> IO ()
runSyncToArchive opts = do
let storePath = opts ^. the @"storePath"
let archiveUri = opts ^. the @"archiveUri"
let threads = opts ^. the @"threads"
CIO.putStrLn $ "Store path: " <> toText storePath
CIO.putStrLn $ "Archive URI: " <> toText archiveUri
CIO.putStrLn $ "Threads: " <> tshow threads
mbPlan <- loadPlan
case mbPlan of
Right planJson -> do
let compilerId = planJson ^. the @"compilerId"
envAws <- mkEnv (opts ^. the @"region") (\_ _ -> pure ())
let archivePath = archiveUri </> compilerId
IO.createLocalDirectoryIfMissing archivePath
let baseDir = opts ^. the @"storePath"
CIO.putStrLn "Extracting package list"
packages <- getPackages baseDir planJson
let storeCompilerPath = baseDir </> T.unpack compilerId
let storeCompilerPackageDbPath = storeCompilerPath </> "package.db"
storeCompilerPackageDbPathExists <- doesDirectoryExist storeCompilerPackageDbPath
unless storeCompilerPackageDbPathExists $
GhcPkg.init storeCompilerPackageDbPath
CIO.putStrLn $ "Syncing " <> tshow (length packages) <> " packages"
IO.withSystemTempDirectory "cabal-cache" $ \tempPath -> do
CIO.putStrLn $ "Temp path: " <> tshow tempPath
CIO.putStrLn "Copying package.db directory for transformation"
let workingStoreCompilerPath = tempPath </> T.unpack compilerId
let workingStoreCompilerPackageDbPath = tempPath </> T.unpack compilerId </> "package.db"
runExceptT $ IO.exceptFatal "Fatal error" $ do
liftIO $ IO.createDirectoryIfMissing True workingStoreCompilerPackageDbPath
packageDbFiles <- IO.listDirectory storeCompilerPackageDbPath
let confFiles = filter (isSuffixOf ".conf") packageDbFiles
forM_ confFiles $ \confFile -> do
stream <- LBS.readFile (storeCompilerPackageDbPath </> confFile)
LBS.writeFile (workingStoreCompilerPackageDbPath </> confFile) (templateConfig baseDir stream)
IO.pooledForConcurrentlyN_ (opts ^. the @"threads") packages $ \pInfo -> do
let archiveFileBasename = packageDir pInfo <.> ".tar.gz"
let archiveFile = archiveUri </> T.pack archiveFileBasename
let packageStorePath = baseDir </> packageDir pInfo
archiveFileExists <- runResourceT $ IO.resourceExists envAws archiveFile
unless archiveFileExists $ do
packageStorePathExists <- doesDirectoryExist packageStorePath
when packageStorePathExists $ void $ runExceptT $ IO.exceptWarn "Warning" $ do
let rp2 = relativePaths2 storePath tempPath pInfo
CIO.putStrLn $ "Creating " <> toText archiveFile
let tempArchiveFile = tempPath </> archiveFileBasename
IO.createTar tempArchiveFile rp2
liftIO (LBS.readFile tempArchiveFile >>= IO.writeResource envAws archiveFile)
return ()
Left errorMessage -> do
CIO.hPutStrLn IO.stderr $ "ERROR: Unable to parse plan.json file: " <> T.pack errorMessage
return ()
cmdSyncToArchive :: Mod CommandFields (IO ())
cmdSyncToArchive = command "sync-to-archive" $ flip info idm $ runSyncToArchive <$> optsSyncToArchive