packages feed

hakyllbars-1.0.0.1: src/Hakyllbars/Field/Git.hs

module Hakyllbars.Field.Git
  ( gitFields,
    gitSha1Compiler,
    gitMessageCompiler,
    gitLogField,
    gitFileField,
    gitFileCompiler,
    gitBranchCompiler,
    gitBranch,
  )
where

import Data.Binary
import GHC.Generics (Generic)
import Hakyllbars.Common
import Hakyllbars.Context
import System.Exit
import System.Process

-- | The Git fields configuration.
gitFields ::
  -- | The configured hakyll provider directory.
  String ->
  -- | The base url to the online git repository for browsing.
  String ->
  Context a
gitFields providerDirectory gitWebUrl =
  mconcat
    [ constField "gitWebUrl" gitWebUrl,
      field "gitSha1" (gitSha1Compiler providerDirectory),
      field "gitMessage" (gitMessageCompiler providerDirectory),
      field "gitBranch" gitBranchCompiler,
      gitFileField providerDirectory "gitFilePath" gitFilePath,
      gitFileField providerDirectory "gitFileName" (takeFileName . gitFilePath),
      gitFileField providerDirectory "isFromSource" gitFileIsFromSource,
      gitFileField providerDirectory "isChanged" gitFileIsChanged
    ]

-- | Gets the git-sha1 hash of the current item.
gitSha1Compiler :: String -> Item a -> TemplateRunner a String
gitSha1Compiler = gitLogField "%h"

-- | Gets the git commit message of the current item.
gitMessageCompiler :: String -> Item a -> TemplateRunner a String
gitMessageCompiler = gitLogField "%s"

type LogFormat = String

-- | Extracts a latest git log field from the current item.
gitLogField :: LogFormat -> String -> Item a -> TemplateRunner a String
gitLogField format providerDirectory item =
  lift $ unsafeCompiler do
    maybeResult <- gitLog format (Just $ providerDirectory </> toFilePath (itemIdentifier item))
    case maybeResult of
      Just result -> return result
      Nothing -> fromJust <$> gitLog format Nothing

data GitFile = GitFile
  { gitFilePath :: String,
    gitFileIsFromSource :: Bool,
    gitFileIsChanged :: Bool
  }
  deriving (Generic)

instance Binary GitFile where
  get = GitFile <$> get <*> get <*> get
  put (GitFile x y z) = put x >> put y >> put z

-- | gets a given field from the git file.
gitFileField ::
  (IntoValue v a) =>
  -- | The hakyll provider directory.
  String ->
  -- | The field name.
  String ->
  -- | The getter for the git file field.
  (GitFile -> v) ->
  Context a
gitFileField providerDirectory key f = field key $ fmap f . gitFileCompiler providerDirectory

-- | Compiles the git file for the given item.
gitFileCompiler ::
  -- | The hakyll provider directory.
  String ->
  -- | The item to compile.
  Item a ->
  TemplateRunner a GitFile
gitFileCompiler providerDirectory item =
  lift $
    GitFile gitFilePath
      <$> unsafeCompiler (doesFileExist gitFilePath)
      <*> unsafeCompiler (isChanged gitFilePath)
  where
    gitFilePath = providerDirectory </> toFilePath (itemIdentifier item)
    isChanged filePath = do
      let args = ["diff", "HEAD", filePath]
      (exitCode, stdout, _stderr) <- readProcessWithExitCode "git" args ""
      return $ not (exitCode == ExitSuccess && null stdout)

gitLog :: LogFormat -> Maybe FilePath -> IO (Maybe String)
gitLog format filePath = do
  let fpArgs = bool [] [fromJust filePath] (isJust filePath)
  let args = ["log", "-1", "HEAD", "--pretty=format:" ++ format] ++ fpArgs
  (_exitCode, stdout, _stderr) <- readProcessWithExitCode "git" args ""
  return if null stdout then Nothing else Just stdout

gitBranchCompiler :: Item a -> TemplateRunner a String
gitBranchCompiler _ = lift $ unsafeCompiler gitBranch

gitBranch :: IO String
gitBranch = do
  let args = ["branch", "--show-current"]
  (exitCode, stdout, stderr) <- readProcessWithExitCode "git" args ""
  if exitCode == ExitSuccess
    then return stdout
    else fail $ "Unable to get current branch: " ++ stderr