{-# LANGUAGE BlockArguments #-} {-# LANGUAGE ScopedTypeVariables #-} module Hercules.Agent.Worker.Effect where import Control.Monad.Catch (MonadThrow) import Hercules.Agent.Worker.Build.Prefetched (buildDerivation) import Hercules.Agent.Worker.Build.Prefetched qualified as Build import Hercules.Agent.WorkerProtocol.Command.Effect qualified as Command.Effect import Hercules.Agent.WorkerProtocol.ViaJSON (fromViaJSON) import Hercules.CNix (Store) import Hercules.CNix qualified as CNix import Hercules.CNix.Store (Derivation) import Hercules.Effect (RunEffectParams (..)) import Hercules.Effect qualified as Effect import Katip (KatipContext) import Protolude import UnliftIO.Directory (getCurrentDirectory) runEffect :: (KatipContext m, MonadThrow m) => [(Text, Text)] -> Store -> Command.Effect.Effect -> m ExitCode runEffect extraNixOptions store command = do derivation <- prepareDerivation store command dir <- getCurrentDirectory Effect.runEffect RunEffectParams { runEffectDerivation = derivation, runEffectToken = Just $ Command.Effect.token command, runEffectSecretsConfigPath = Just $ Command.Effect.secretsPath command, runEffectServerSecrets = Command.Effect.serverSecrets command <&> fromViaJSON, runEffectConfiguredMountables = Command.Effect.configuredMountables command <&> fromViaJSON, runEffectApiBaseURL = Command.Effect.apiBaseURL command, runEffectDir = dir, runEffectProjectId = Just $ Command.Effect.projectId command, runEffectProjectPath = Just $ Command.Effect.projectPath command, runEffectSecretContext = Just $ Command.Effect.secretContext command, runEffectUseNixDaemonProxy = True, runEffectExtraNixOptions = extraNixOptions, runEffectFriendly = False } prepareDerivation :: (MonadIO m) => Store -> Command.Effect.Effect -> m Derivation prepareDerivation store command = do let extraPaths = Command.Effect.inputDerivationOutputPaths command drvPath = encodeUtf8 $ Command.Effect.drvPath command ensureDeps = for_ extraPaths $ \input -> liftIO $ do p <- CNix.parseStorePath store input CNix.addTemporaryRoot store p CNix.ensurePath store p drvStorePath <- liftIO $ CNix.parseStorePath store drvPath liftIO $ do ensureDeps `catch` \e -> do CNix.logInfo $ "while retrieving dependencies: " <> toS (displayException (e :: SomeException)) CNix.logInfo "unable to retrieve dependency; attempting fallback to local build" -- TODO read directly from cache? CNix.ensurePath store drvStorePath derivation <- CNix.getDerivation store drvStorePath depDrvPaths <- CNix.getDerivationInputs store derivation for_ depDrvPaths \(depDrv, _outputs) -> do depDerivation <- CNix.getDerivation store depDrv _nixBuildResult <- liftIO $ buildDerivation store depDrv depDerivation mempty pass derivation <- liftIO (Build.getDerivation store drvStorePath) >>= \case Just drv -> pure drv Nothing -> panic $ "Could not retrieve derivation " <> show drvPath <> " from local store or binary caches." sources <- liftIO $ CNix.getDerivationSources store derivation for_ sources \src -> liftIO do CNix.addTemporaryRoot store src CNix.ensurePath store src pure derivation