cabal-cache-0.2.0.2: src/App/Commands/SyncFromArchive.hs
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
module App.Commands.SyncFromArchive
( cmdSyncFromArchive
) where
import Antiope.Core (runResAws, toText)
import Antiope.Env (LogLevel, mkEnv)
import App.Commands.Options.Parser (optsSyncFromArchive)
import App.Static (homeDirectory)
import Control.Lens hiding ((<.>))
import Control.Monad (unless, void, when)
import Control.Monad.Except
import Control.Monad.IO.Class (liftIO)
import Control.Monad.Trans.Resource (runResourceT)
import Data.Generics.Product.Any (the)
import Data.Semigroup ((<>))
import Data.Text (Text)
import HaskellWorks.Ci.Assist.Core (PackageInfo (..), Presence (..), Tagged (..), getPackages, loadPlan)
import HaskellWorks.Ci.Assist.Location ((<.>), (</>))
import HaskellWorks.Ci.Assist.PackageConfig (unTemplateConfig)
import HaskellWorks.Ci.Assist.Show
import HaskellWorks.Ci.Assist.Tar (mapEntriesWith)
import Network.AWS.Types (Region (Oregon))
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.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) #-}
runSyncFromArchive :: Z.SyncFromArchiveOptions -> IO ()
runSyncFromArchive 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
GhcPkg.testAvailability
mbPlan <- loadPlan
case mbPlan of
Right planJson -> do
env <- mkEnv (opts ^. the @"region") (\_ _ -> pure ())
let compilerId = planJson ^. the @"compilerId"
let archivePath = archiveUri </> compilerId
let baseDir = opts ^. the @"storePath"
let storeCompilerPath = baseDir </> T.unpack compilerId
let storeCompilerPackageDbPath = storeCompilerPath </> "package.db"
let storeCompilerLibPath = storeCompilerPath </> "lib"
CIO.putStrLn "Creating store directories"
createDirectoryIfMissing True baseDir
createDirectoryIfMissing True storeCompilerPath
createDirectoryIfMissing True storeCompilerLibPath
storeCompilerPackageDbPathExists <- doesDirectoryExist storeCompilerPackageDbPath
unless storeCompilerPackageDbPathExists $ do
CIO.putStrLn "Package DB missing. Creating Package DB"
GhcPkg.init storeCompilerPackageDbPath
packages <- getPackages baseDir planJson
IO.withSystemTempDirectory "cabal-cache" $ \tempPath -> do
IO.createDirectoryIfMissing True (tempPath </> T.unpack compilerId </> "package.db")
IO.pooledForConcurrentlyN_ threads packages $ \pInfo -> do
let archiveBaseName = packageDir pInfo <.> ".tar.gz"
let archiveFile = archiveUri </> T.pack archiveBaseName
let packageStorePath = baseDir </> packageDir pInfo
storeDirectoryExists <- doesDirectoryExist packageStorePath
unless storeDirectoryExists $ do
arhiveFileExists <- runResourceT $ IO.resourceExists env archiveFile
when arhiveFileExists $ do
CIO.putStrLn $ "Extracting: " <> T.pack archiveBaseName
runResAws env $ do
maybeArchiveFileContents <- IO.readResource env archiveFile
case maybeArchiveFileContents of
Just archiveFileContents -> do
let tempArchiveFile = tempPath </> archiveBaseName :: FilePath
liftIO $ LBS.writeFile tempArchiveFile archiveFileContents
liftIO $ runExceptT $ IO.extractTar tempArchiveFile storePath
case confPath pInfo of
Tagged conf _ -> do
let theConfPath = storePath </> conf
let tempConfPath = tempPath </> conf
confPathExists <- liftIO $ IO.doesFileExist theConfPath
when confPathExists $ do
confContents <- liftIO $ LBS.readFile theConfPath
liftIO $ LBS.writeFile tempConfPath (unTemplateConfig baseDir confContents)
liftIO $ IO.renamePath tempConfPath theConfPath
Nothing -> do
CIO.putStrLn $ "Archive unavailable: " <> toText archiveFile
CIO.putStrLn "Recaching package database"
GhcPkg.recache storeCompilerPackageDbPath
Left errorMessage -> do
CIO.hPutStrLn IO.stderr $ "ERROR: Unable to parse plan.json file: " <> T.pack errorMessage
return ()
cmdSyncFromArchive :: Mod CommandFields (IO ())
cmdSyncFromArchive = command "sync-from-archive" $ flip info idm $ runSyncFromArchive <$> optsSyncFromArchive