tricorder-0.1.0.0: src/Tricorder/Version.hs
{-# OPTIONS_GHC -fforce-recomp #-}
-- | Build-time version information.
--
-- The git hash is spliced in at compile time via Template Haskell, so it
-- carries zero runtime overhead and requires no file I/O at startup.
--
-- The @-fforce-recomp@ pragma ensures GHC re-evaluates the TH splice on every
-- build invocation. Without it, GHC may skip recompilation when no source
-- files have changed (e.g. after a @git commit@), leaving the old hash baked
-- in until something else triggers a rebuild.
module Tricorder.Version (gitHash, VersionMismatch (..), checkVersion) where
import Language.Haskell.TH (litE, runIO, stringL)
import System.Environment (lookupEnv)
import System.IO.Error (tryIOError)
import System.Process (readProcess)
-- | Short git hash of the commit this binary was built from.
--
-- Resolution order at compile time:
--
-- 1. @TRICORDER_VERSION@ environment variable — useful for non-git VCS
-- (e.g. Jujutsu workspaces without a colocated @.git@) and for CI
-- pipelines that inject the version externally.
-- 2. @git rev-parse --short HEAD@ — the common case for git checkouts.
-- 3. @"unknown"@ — fallback when @git@ is unavailable.
gitHash :: Text
gitHash =
toText
( $( do
hash <- runIO $ do
override <- lookupEnv "TRICORDER_VERSION"
case override of
Just v -> pure v
Nothing ->
either (const "unknown") (filter (/= '\n'))
<$> tryIOError (readProcess "git" ["rev-parse", "--short", "HEAD"] "")
litE (stringL hash)
)
:: String
)
data VersionMismatch = VersionMismatch
{ expected :: Text
, received :: Text
}
deriving stock (Show)
checkVersion :: Text -> Either VersionMismatch ()
checkVersion clientVersion
| clientVersion == gitHash = Right ()
| otherwise = Left VersionMismatch {expected = gitHash, received = clientVersion}