Safe Haskell | None |
---|---|
Language | Haskell2010 |
Development.GitRev.Typed
Description
Typed version of Development.GitRev.
Since: 0.1
Synopsis
- gitBranch :: Code Q String
- gitCommitCount :: Code Q String
- gitCommitDate :: Code Q String
- gitDescribe :: Code Q String
- gitDiff :: Code Q String
- gitDirty :: Code Q Bool
- gitDirtyTracked :: Code Q Bool
- gitHash :: Code Q String
- gitShortHash :: Code Q String
- gitTree :: Code Q String
- gitBranchQ :: Q (Either GitError String)
- gitCommitCountQ :: Q (Either GitError String)
- gitCommitDateQ :: Q (Either GitError String)
- gitDescribeQ :: Q (Either GitError String)
- gitDiffQ :: Q (Either GitError String)
- gitDirtyQ :: Q (Either GitError Bool)
- gitDirtyTrackedQ :: Q (Either GitError Bool)
- gitHashQ :: Q (Either GitError String)
- gitShortHashQ :: Q (Either GitError String)
- gitTreeQ :: Q (Either GitError String)
- envValQ :: String -> Q (Either LookupEnvError String)
- runGitInEnvDirQ :: String -> Q (Either GitError a) -> Q (Either GitOrLookupEnvError a)
- qToCode :: Lift a => Q a -> Code Q a
- newtype QFirst e a = MkQFirst {
- unQFirst :: Q (Either (Exceptions e) a)
- mkQFirst :: Q (Either e a) -> QFirst e a
- firstSuccessQ :: Q (Either e a) -> [Q (Either e a)] -> Q (Either (Exceptions e) a)
- newtype Exceptions e = MkExceptions (NonEmpty e)
- mkExceptions :: e -> Exceptions e
- projectStringUnknown :: Functor f => f (Either e String) -> f String
- projectString :: Functor f => String -> f (Either e String) -> f String
- projectFalse :: Functor f => f (Either e Bool) -> f Bool
- projectError :: forall f e a. (Exception e, Functor f) => f (Either e a) -> f a
- projectErrorMap :: Functor f => (e -> String) -> f (Either e a) -> f a
- data GitOrLookupEnvError
- data GitError
- newtype LookupEnvError = MkLookupEnvError String
- embedGitError :: forall f p a. (Bifunctor p, Functor f) => f (p GitError a) -> f (p GitOrLookupEnvError a)
- embedLookupEnvError :: forall f p a. (Bifunctor p, Functor f) => f (p LookupEnvError a) -> f (p GitOrLookupEnvError a)
- joinLookupEnvGitErrors :: (Bifunctor p, forall e. Monad (p e)) => p LookupEnvError (p GitError a) -> p GitOrLookupEnvError a
- joinGitLookupEnvErrors :: (Bifunctor p, forall e. Monad (p e)) => p GitError (p LookupEnvError a) -> p GitOrLookupEnvError a
Basic functions
These functions are simple, merely a typed version of Development.GitRev's API.
NOTE: These functions do not error if git fails to run, opting
instead to return some default value (e.g. string UNKNOWN
, boolean False
).
gitBranch :: Code Q String Source #
Return the branch (or tag) name of the current git commit, or UNKNOWN
if not in a git repository. For detached heads, this will just be
HEAD.
Examples
λ. $$(gitBranch) "main"
>>>
$$(gitBranch)
...
Since: 0.1
gitCommitCount :: Code Q String Source #
Return the number of commits in the current head.
Examples
λ. $$(gitCommitCount) "47"
>>>
$$(gitCommitCount)
...
Since: 0.1
gitCommitDate :: Code Q String Source #
Return the commit date of the current head.
Examples
λ. $$(gitCommitDate) "Mon Apr 14 22:14:44 2025 +1200"
>>>
$$(gitCommitDate)
...
Since: 0.1
gitDescribe :: Code Q String Source #
Return the long git description for the current git commit, or
UNKNOWN
if not in a git repository.
Examples
λ. $$(gitDescribe) "e67e943"
>>>
$$(gitDescribe)
...
Since: 0.1
gitDiff :: Code Q String Source #
Return the diff of the working copy with HEAD.
Examples
λ. $$(gitDiff) "Mon Apr 14 22:14:44 2025 +1200"
>>>
$$(gitDiff)
...
gitDirty :: Code Q Bool Source #
Return True
if there are non-committed files present in the
repository.
Examples
λ. $$(gitDirty) False
>>>
$$(gitDirty)
...
Since: 0.1
gitDirtyTracked :: Code Q Bool Source #
Return True
if there are non-commited changes to tracked files
present in the repository.
Examples
λ. $$(gitDirtyTracked) False
>>>
$$(gitDirtyTracked)
...
Since: 0.1
gitHash :: Code Q String Source #
Return the hash of the current git commit, or UNKNOWN
if not in
a git repository.
Examples
λ. $$(gitHash) "e67e943dd03744d3f93c21f84e127744e6a04543"
>>>
$$(gitHash)
...
Since: 0.1
gitShortHash :: Code Q String Source #
Return the short hash of the current git commit, or UNKNOWN
if not in
a git repository.
Examples
λ. $$(gitShortHash) "e67e943"
>>>
$$(gitShortHash)
...
Since: 0.1
gitTree :: Code Q String Source #
Return the hash of the current tree.
Examples
λ. $$(gitTree) "Mon Apr 14 22:14:44 2025 +1200"
>>>
$$(gitTree)
...
Custom behavior
These functions allow defining custom behavior. For instance, using
the primitive getHashQ
and combinator projectError
, we can
define a variant of gitHash
that instead fails to compile if there are
any problems with git:
>>>
:{
let gitHashOrDie :: Code Q String gitHashOrDie = qToCode $ projectError gitHashQ :}
We can also define a function that falls back to an environment variable,
in case the git command fails. firstSuccessQ
takes the first action
that returns Right
.
>>>
:{
let gitHashEnv :: String -> Code Q (Either (Exceptions GitOrLookupEnvError) String) gitHashEnv var = qToCode $ firstSuccessQ (embedGitError gitHashQ) [embedLookupEnvError $ envValQ var] :}
Naturally, these can be combined:
>>>
:{
let gitHashEnvOrDie :: String -> Code Q String gitHashEnvOrDie var = qToCode . projectError $ firstSuccessQ (embedGitError gitHashQ) [embedLookupEnvError $ envValQ var] :}
"Out-of-tree" builds
An example where custom definitions are useful is "out-of-tree" builds, where the build takes place outside of the normal git tree.
These builds present a problem, as we normally rely on building in the
project directory where the .git
directory is easy to locate. For
example, while gitHash
will work for 'cabal build'
, it will not work
for 'nix build'
or 'cabal install'
. Fortunately, there are
workarounds, both relying on passing the right data via environment
variables.
Passing the git directory.
For situations where we can pass the current directory during installation e.g.
$ export EXAMPLE_HOME=$(pwd); cabal install example
We can define
>>>
:{
let gitHashSrcDir :: Code Q String gitHashSrcDir = qToCode . projectStringUnknown $ firstSuccessQ -- 1. We first try normal gitHashQ. (embedGitError gitHashQ) -- 2. If that fails, we try again in the directory pointed -- to by "EXAMPLE_HOME". [runGitInEnvDirQ "EXAMPLE_HOME" gitHashQ] :}If the initial call to
gitHashQ
fails, then we will try again, running the command from the directory pointed to byEXAMPLE_HOME
.firstSuccessQ
ensures we do not run the second action unless the first fails.Passing the value itself.
This approach can work well with nix, as nix flakes provides a variety of revisions via its
self
interface. For example:# Injecting the git hash via EXAMPLE_HASH where drv is the normal # derivation. drv.overrideAttrs (oldAttrs: { # Also: self.shortRev, self.dirtyShortRev EXAMPLE_HASH = "${self.rev or self.dirtyRev}"; });
Then we can define
>>>
:{
let gitHashVal :: Code Q String gitHashVal = qToCode . projectStringUnknown $ firstSuccessQ -- 1. We first try normal gitHashQ. (embedGitError gitHashQ) -- 2. If that fails, get the value directly from -- "EXAMPLE_HASH". [embedLookupEnvError $ envValQ "EXAMPLE_HASH"] :}Once again, if the first attempt fails, we will run the second action, looking for the value of
EXAMPLE_HASH
.
Finally, we can compose these together to make a function that works for all three cases:
>>>
:{
let gitHashValSrc :: Code Q String gitHashValSrc = qToCode . projectStringUnknown $ firstSuccessQ (embedGitError gitHashQ) [ embedLookupEnvError $ envValQ "EXAMPLE_HASH", runGitInEnvDirQ "EXAMPLE_HOME" gitHashQ ] :}
A final note on laziness: As alluded to above, Q's default semigroup instance is not lazy enough:
>>>
:{
$$( qToCode $ (runIO (putStrLn "in q1") $> (Right "q1") :: Q (Either () String)) <> (runIO (putStrLn "in q2") $> Left ()) ) :} in q1 in q2 Right "q1"
For this reason, we introduce the QFirst
newtype:
>>>
:{
$$( qToCode $ unQFirst $ (mkQFirst $ runIO (putStrLn "in q1") $> (Right "q1") :: QFirst () String) <> (mkQFirst $ runIO (putStrLn "in q2") $> Left ()) ) :} in q1 Right "q1"
The convenience function
firstSuccessQ
:: Q (Either e a) -> [Q (Either e a)] -> Q (Either e a)
utilizes QFirst
for sequencing a series of Q actions, stopping after the
first success.
Git Primitives
gitBranchQ :: Q (Either GitError String) Source #
Returns the current git branch.
Examples
>>>
$$(qToCode gitBranchQ)
Right ...
Since: 0.1
gitCommitCountQ :: Q (Either GitError String) Source #
Returns the git commit count.
Examples
>>>
$$(qToCode gitCommitCountQ)
Right ...
Since: 0.1
gitCommitDateQ :: Q (Either GitError String) Source #
Returns the latest git commit date.
Examples
>>>
$$(qToCode gitCommitDateQ)
Right ...
Since: 0.1
gitDescribeQ :: Q (Either GitError String) Source #
Returns the git description.
Examples
>>>
$$(qToCode gitDescribeQ)
Right ...
Since: 0.1
gitDiffQ :: Q (Either GitError String) Source #
Return the diff of the working copy with HEAD.
Examples
>>>
$$(qToCode gitDiffQ)
Right ...
Since: 0.1
gitDirtyQ :: Q (Either GitError Bool) Source #
Returns the git dirty status.
Examples
>>>
$$(qToCode gitDirtyQ)
Right ...
Since: 0.1
gitDirtyTrackedQ :: Q (Either GitError Bool) Source #
Returns the git dirty status, ignoring untracked files.
Examples
>>>
$$(qToCode gitDirtyTrackedQ)
Right ...
Since: 0.1
gitHashQ :: Q (Either GitError String) Source #
Returns the latest git hash.
Examples
>>>
$$(qToCode gitHashQ)
Right ...
Since: 0.1
gitShortHashQ :: Q (Either GitError String) Source #
Returns the latest git short hash.
Examples
>>>
$$(qToCode gitShortHashQ)
Right ...
Since: 0.1
gitTreeQ :: Q (Either GitError String) Source #
Returns the hash of the current tree.
Examples
>>>
$$(qToCode gitTreeQ)
Right ...
Since: 0.1
Environment lookup
Arguments
:: String | The environment variable |
-> Q (Either LookupEnvError String) | The result |
Performs an environment variable lookup in Q
.
Examples
>>>
setEnv "SOME_VAR" "val"
>>>
$$(qToCode $ envValQ "SOME_VAR")
Right "val"
Since: 0.1
Arguments
:: String | Environment variable pointing to a directory path, in which we run the git process. |
-> Q (Either GitError a) | Git process to run. |
-> Q (Either GitOrLookupEnvError a) | The result. |
runGitInEnvDirQ var q
runs q
in the directory given by the
environment variable.
Examples
>>>
setEnv "SOME_DIR" "./"
>>>
$$(qToCode $ runGitInEnvDirQ "SOME_DIR" gitHashQ)
Right ...
Since: 0.1
Q to Code
Q Combinators
First success
Wrapper for Q
over Either
with a lazier Semigroup
. With this, we
can run:
MkQFirst q1 <> MkQFirst q2
This will only execute q2
if q1
returns Left
, unlike Q
's normal
Semigroup
instance.
QFirst
also collects all errors in Exceptions
.
Since: 0.1
Constructors
MkQFirst | |
Fields
|
firstSuccessQ :: Q (Either e a) -> [Q (Either e a)] -> Q (Either (Exceptions e) a) Source #
newtype Exceptions e Source #
Collects multiple exceptions.
Since: 0.1
Constructors
MkExceptions (NonEmpty e) |
Instances
mkExceptions :: e -> Exceptions e Source #
Since: 0.1
Eliminating Either
projectStringUnknown :: Functor f => f (Either e String) -> f String Source #
Projects Left
to the string UNKNOWN
.
Examples
>>>
:{
let gitHashUnknownQ :: Q String gitHashUnknownQ = projectStringUnknown gitHashQ -- inling gitHashUnknownQ here due to stage restriction in $$(qToCode $ projectStringUnknown gitHashQ) :} ...
>>>
$$(qToCode $ projectStringUnknown (pure $ Left ()))
"UNKNOWN"
Since: 0.1
projectString :: Functor f => String -> f (Either e String) -> f String Source #
Projects Left
to the given string.
Examples
>>>
:{
let gitHashDefStringQ :: Q String gitHashDefStringQ = projectString "FAILURE" gitHashQ in $$(qToCode $ projectString "FAILURE" gitHashQ) :} ...
>>>
$$(qToCode $ projectString "FAILURE" (pure $ Left ()))
"FAILURE"
Since: 0.1
projectError :: forall f e a. (Exception e, Functor f) => f (Either e a) -> f a Source #
Projects Left
via error
, rendering via displayException
. Hence
an error will cause a compilation failure.
Examples
>>>
:{
let gitHashOrDieQ :: Q String gitHashOrDieQ = projectError gitHashQ in $$(qToCode $ projectError gitHashQ) :} ...
Since: 0.1
Errors
data GitOrLookupEnvError Source #
Git or env lookup error.
Since: 0.1
Constructors
GitOrLookupEnvGit GitError | Since: 0.1 |
GitOrLookupEnvLookupEnv LookupEnvError | Since: 0.1 |
Instances
Exception GitOrLookupEnvError Source # | Since: 0.1 |
Defined in Development.GitRev.Utils | |
Show GitOrLookupEnvError Source # | Since: 0.1 |
Defined in Development.GitRev.Utils Methods showsPrec :: Int -> GitOrLookupEnvError -> ShowS # show :: GitOrLookupEnvError -> String # showList :: [GitOrLookupEnvError] -> ShowS # | |
Eq GitOrLookupEnvError Source # | Since: 0.1 |
Defined in Development.GitRev.Utils Methods (==) :: GitOrLookupEnvError -> GitOrLookupEnvError -> Bool # (/=) :: GitOrLookupEnvError -> GitOrLookupEnvError -> Bool # | |
Lift GitOrLookupEnvError Source # | Since: 0.1 |
Defined in Development.GitRev.Utils Methods lift :: Quote m => GitOrLookupEnvError -> m Exp # liftTyped :: forall (m :: Type -> Type). Quote m => GitOrLookupEnvError -> Code m GitOrLookupEnvError # |
Errors that can be encountered with git.
Since: 0.1
Constructors
GitNotFound | Since: 0.1 |
GitRunError String | Since: 0.1 |
Instances
Exception GitError Source # | Since: 0.1 |
Defined in Development.GitRev.Utils.Git Methods toException :: GitError -> SomeException # fromException :: SomeException -> Maybe GitError # displayException :: GitError -> String # backtraceDesired :: GitError -> Bool # | |
Show GitError Source # | Since: 0.1 |
Eq GitError Source # | Since: 0.1 |
Lift GitError Source # | Since: 0.1 |
newtype LookupEnvError Source #
Environment variable lookup failure. The value is the variable we attempted to look up.
Since: 0.1
Constructors
MkLookupEnvError String |
Instances
Exception LookupEnvError Source # | Since: 0.1 |
Defined in Development.GitRev.Utils.Environment Methods toException :: LookupEnvError -> SomeException # fromException :: SomeException -> Maybe LookupEnvError # displayException :: LookupEnvError -> String # backtraceDesired :: LookupEnvError -> Bool # | |
Show LookupEnvError Source # | Since: 0.1 |
Defined in Development.GitRev.Utils.Environment Methods showsPrec :: Int -> LookupEnvError -> ShowS # show :: LookupEnvError -> String # showList :: [LookupEnvError] -> ShowS # | |
Eq LookupEnvError Source # | Since: 0.1 |
Defined in Development.GitRev.Utils.Environment Methods (==) :: LookupEnvError -> LookupEnvError -> Bool # (/=) :: LookupEnvError -> LookupEnvError -> Bool # | |
Lift LookupEnvError Source # | Since: 0.1 |
Defined in Development.GitRev.Utils.Environment Methods lift :: Quote m => LookupEnvError -> m Exp # liftTyped :: forall (m :: Type -> Type). Quote m => LookupEnvError -> Code m LookupEnvError # |
Utilities
Arguments
:: forall f p a. (Bifunctor p, Functor f) | |
=> f (p GitError a) | . |
-> f (p GitOrLookupEnvError a) |
Embeds a GitError
in the larger GitOrLookupEnvError
.
Examples
>>>
:{
let q :: Q (Either GitError ()) q = pure (Left GitNotFound) in runQ $ embedGitError q :} Left (GitOrLookupEnvGit GitNotFound)
Since: 0.1
Arguments
:: forall f p a. (Bifunctor p, Functor f) | |
=> f (p LookupEnvError a) | . |
-> f (p GitOrLookupEnvError a) |
Embeds a LookupEnvError
in the larger GitOrLookupEnvError
.
Examples
>>>
:{
let q :: Q (Either LookupEnvError ()) q = pure (Left $ MkLookupEnvError "VAR") in runQ $ embedLookupEnvError q :} Left (GitOrLookupEnvLookupEnv (MkLookupEnvError "VAR"))
Since: 0.1
joinLookupEnvGitErrors Source #
Arguments
:: (Bifunctor p, forall e. Monad (p e)) | |
=> p LookupEnvError (p GitError a) | . |
-> p GitOrLookupEnvError a |
Utility function for joining lookup and git errors.
Examples
>>>
:{
let e :: Either LookupEnvError (Either GitError ()) e = Right (Left GitNotFound) in joinLookupEnvGitErrors e :} Left (GitOrLookupEnvGit GitNotFound)
Since: 0.1
joinGitLookupEnvErrors Source #
Arguments
:: (Bifunctor p, forall e. Monad (p e)) | |
=> p GitError (p LookupEnvError a) | . |
-> p GitOrLookupEnvError a |
Utility function for joining git and lookup errors.
Examples
>>>
:{
let e :: Either GitError (Either LookupEnvError ()) e = Right (Left $ MkLookupEnvError "VAR") in joinGitLookupEnvErrors e :} Left (GitOrLookupEnvLookupEnv (MkLookupEnvError "VAR"))
Since: 0.1