packages feed

hercules-ci-agent-0.8.0: hercules-ci-agent/Hercules/Agent/EnvironmentInfo.hs

module Hercules.Agent.EnvironmentInfo where

import Control.Lens
  ( to,
    (^..),
    (^?),
  )
import qualified Data.Aeson as Aeson
import Data.Aeson.Lens
  ( key,
    _Array,
    _Number,
    _String,
  )
import qualified Data.ByteString.Lazy as LBS
import qualified Data.Text as T
import qualified Hercules.API.Agent.LifeCycle.AgentInfo as AgentInfo
import Hercules.Agent.CabalInfo as CabalInfo
import Hercules.Agent.Cachix.Info as Cachix.Info
import qualified Hercules.Agent.Config as Config
import Hercules.Agent.Env as Env
import Hercules.Agent.Log
import Network.HostName (getHostName)
import Protolude hiding (to)
import qualified System.Process as Process

extractAgentInfo :: App AgentInfo.AgentInfo
extractAgentInfo = do
  hostname <- liftIO getHostName
  nix <- liftIO getNixInfo
  cachixPushCaches <- Cachix.Info.activePushCaches
  pushCaches <- Env.activePushCaches
  concurrentTasks <- asks (Config.concurrentTasks . Env.config)
  let s =
        AgentInfo.AgentInfo
          { hostname = toS hostname,
            agentVersion = CabalInfo.herculesAgentVersion, -- TODO: Add git revision
            nixVersion = nixExeVersion nix,
            platforms = nixPlatforms nix,
            cachixPushCaches = cachixPushCaches,
            pushCaches = pushCaches,
            systemFeatures = nixSystemFeatures nix,
            substituters = nixSubstituters nix, -- TODO: Add cachix substituters
            concurrentTasks = fromIntegral concurrentTasks
          }
  logLocM DebugS $ "Determined environment info: " <> logStr (show s :: Text)
  pure s

data NixInfo = NixInfo
  { nixExeVersion :: Text,
    nixPlatforms :: [Text],
    nixSystemFeatures :: [Text],
    nixSubstituters :: [Text],
    nixTrustedPublicKeys :: [Text],
    nixNarinfoCacheNegativeTTL :: Maybe Integer,
    nixNetrcFile :: Maybe Text
  }

getNixInfo :: IO NixInfo
getNixInfo = do
  let stdinEmpty = ""
  version <- Process.readProcess "nix" ["--version"] stdinEmpty
  rawJson <- Process.readProcess "nix" ["show-config", "--json"] stdinEmpty
  cfg <-
    case Aeson.eitherDecode (LBS.fromStrict $ encodeUtf8 $ toS rawJson) of
      Left e -> panic $ "Could not parse nix show-config --json: " <> show e
      Right r -> pure r
  pure
    NixInfo
      { nixExeVersion = T.dropAround isSpace (toS version),
        nixPlatforms =
          ((cfg :: Aeson.Value) ^.. key "system" . key "value" . _String)
            <> ( cfg
                   ^.. key "extra-platforms"
                     . key "value"
                     . _Array
                     . traverse
                     . _String
               ),
        nixSystemFeatures =
          cfg
            ^.. key "system-features"
              . key "value"
              . _Array
              . traverse
              . _String,
        nixSubstituters =
          cfg
            ^.. key "substituters"
              . key "value"
              . _Array
              . traverse
              . _String
              . to cleanUrl,
        nixTrustedPublicKeys =
          cfg
            ^.. key "trusted-public-keys"
              . key "value"
              . _Array
              . traverse
              . _String
              . to cleanUrl,
        nixNarinfoCacheNegativeTTL =
          cfg
            ^? key "narinfo-cache-negative-ttl"
              . key "value"
              . _Number
              . to floor,
        nixNetrcFile =
          cfg
            ^? key "netrc-file"
              . key "value"
              . _String
      }

cleanUrl :: Text -> Text
cleanUrl t | "@" `T.isInfixOf` t = "<URI censored; might contain secret>"
cleanUrl t = t