lib: simpler more robust version strings, with date/without patchlevel

We no longer rely on git tags and git describe output, since it's
hard to reliably select the correct tag eg for minor releases.
We might use them again in future for dev builds, but it requires
adding git describe --match support to githash. For now,

* Program name, OS and architecture are always shown.
* The package version is always shown.
* If there is git info at build time, the latest commit hash and commit date are shown.

Example outputs:
A homebrew binary, not built in git repo: hledger-ui 1.24, mac-aarch64
A CI release build, built in git repo:    hledger 1.24.1-g455b35293-20211210, mac-x86_64

API changes:

* new type synonyms ProgramName, PackageVersion, VersionString
* versionStringForProgname -> versionString with extra argument
* versionStringFor -> versionStringWith with extra argument
This commit is contained in:
Simon Michael 2021-12-10 08:19:38 -10:00
parent 0c6575ee8a
commit 1ef8f329c6
5 changed files with 96 additions and 77 deletions

View File

@ -193,12 +193,6 @@ DOCSOURCEFILES:= \
# We use hledger CLI's current version (XXX for all packages, which isn't quite right).
VERSION=$(shell cat hledger/.version)
# the number of commits since the last tag
PATCHLEVEL=$(shell git describe --tags --match 'hledger-[0-9]*' --long | awk -F- '{print $$3}')
#PATCHLEVEL:=$(shell git describe --tags --match 'hledger-web-[0-9]*' --long | awk -F- '{print $$4}')
# the number of commits since the last_release tag
#PATCHLEVEL:=$(shell git rev-list last_release..HEAD | wc -l)
# flags for ghc builds
WARNINGS:=\
@ -223,7 +217,6 @@ BUILDFLAGS=\
$(WARNINGS) \
$(INCLUDEPATHS) \
$(GHCLOWMEMFLAGS) $(CABALMACROSFLAGS) \
-DPATCHLEVEL=$(PATCHLEVEL) \
-DDEVELOPMENT \
-DVERSION="\"$(VERSION)\"" \
# -fhide-source-paths \

View File

@ -18,18 +18,19 @@ import Hledger.UI.Theme (themes, themeNames)
-- cf Hledger.Cli.Version
packageversion :: String
packageversion :: PackageVersion
packageversion =
#ifdef VERSION
packageversion = VERSION
VERSION
#else
packageversion = ""
""
#endif
progname :: String
progname :: ProgramName
progname = "hledger-ui"
prognameandversion :: String
prognameandversion = versionStringForProgname progname
prognameandversion :: VersionString
prognameandversion = versionString progname packageversion
uiflags = [
-- flagNone ["debug-ui"] (setboolopt "rules-file") "run with no terminal output, showing console"

View File

@ -20,18 +20,19 @@ import Hledger.Web.Settings (defhost, defport, defbaseurl)
-- cf Hledger.Cli.Version
packageversion :: String
packageversion :: PackageVersion
packageversion =
#ifdef VERSION
packageversion = VERSION
VERSION
#else
packageversion = ""
""
#endif
progname :: String
progname :: ProgramName
progname = "hledger-web"
prognameandversion :: String
prognameandversion = versionStringForProgname progname
prognameandversion :: VersionString
prognameandversion = versionString progname packageversion
webflags :: [Flag RawOpts]

View File

@ -17,7 +17,7 @@ module Hledger.Cli (
module Hledger,
module System.Console.CmdArgs.Explicit,
prognameandversion,
versionStringForProgname
versionString
)
where
@ -31,9 +31,21 @@ import Hledger.Cli.DocFiles
import Hledger.Cli.Utils
import Hledger.Cli.Version
-- | The program name and the best version information we can obtain
-- from git describe or build variables.
prognameandversion = versionStringForProgname progname
versionStringForProgname = versionStringFor $$tGitInfoCwdTry
-- | The program name and version string for this build of the hledger tool,
-- including any git info available at build time.
prognameandversion :: String
prognameandversion = versionString progname packageversion
-- | A helper to generate the best version string we can from the given
-- program name and package version strings, current os and architecture,
-- and any git info available at build time (commit hash, commit date, branch
-- name, patchlevel since latest release tag for that program's package).
-- Typically called for programs "hledger", "hledger-ui", or "hledger-web".
--
-- The git info changes whenever any file in the repository changes.
-- Keeping this template haskell call here and not down in Hledger.Cli.Version
-- helps reduce the number of modules recompiled.
versionString :: ProgramName -> PackageVersion -> String
versionString = versionStringWith $$tGitInfoCwdTry
-- unit tests (tests_Hledger_Cli) are defined in Hledger.Cli.Commands

View File

@ -4,79 +4,91 @@ Version number-related utilities. See also the Makefile.
-}
module Hledger.Cli.Version (
ProgramName,
PackageVersion,
VersionString,
packageversion,
progname,
versionStringFor,
versionStringWith,
)
where
import GitHash (GitInfo, giDescribe)
import GitHash (GitInfo, giHash, giCommitDate)
import System.Info (os, arch)
import Hledger.Utils
import Data.List (intercalate)
import Data.Maybe (fromMaybe)
-- | This package's version, passed in as VERSION build variable, or a generic description.
packageversion :: String
type ProgramName = String
type PackageVersion = String
type VersionString = String
-- | The VERSION string defined with -D in this package's package.yaml/.cabal file
-- (by Shake setversion), if any. Normally a dotted number string with 1-3 components.
packageversion :: PackageVersion
packageversion =
#ifdef VERSION
packageversion = VERSION
VERSION
#else
packageversion = "dev build"
""
#endif
-- | A period and the patch level (number of patches added since the package version),
-- passed in as PATCHLEVEL build variable, or the empty string.
-- If PATCHLEVEL is defined it must be a number, or this will fail.
patchlevel :: String
#ifdef PATCHLEVEL
patchlevel = "." ++ show (PATCHLEVEL :: Int)
#else
patchlevel = ""
#endif
-- | The version and patchlevel passed in as build variables, combined and prettified.
-- This will raise an error if VERSION is has <1 or >3 components,
-- or if PATCHLEVEL is defined but not a number.
-- Used as a fallback if git describe is unavailable.
buildversion :: String
buildversion = prettify . splitAtElement '.' $ packageversion ++ patchlevel
where
prettify [major,minor,bugfix,patches] =
major ++ "." ++ minor ++ bugfix' ++ patches'
where
bugfix' = if bugfix == "0" then "" else '.' : bugfix
patches' = if patches == "0" then "" else '+' : patches
prettify [major,minor,bugfix] = prettify [major,minor,bugfix,"0"]
prettify [major,minor] = prettify [major,minor,"0","0"]
prettify [major] = prettify [major,"0","0","0"]
prettify [] = error' "VERSION is empty, please fix" -- PARTIAL:
prettify _ = error' "VERSION has too many components, please fix"
-- | The name of this package's main executable.
progname :: String
progname :: ProgramName
progname = "hledger"
-- | Given a program name, make a version string consisting of:
-- | Given possible git state info from the build directory (or an error message, which is ignored),
-- the name of a program (executable) in the currently building package,
-- and the package's version, make a complete version string. Here is the logic:
--
-- * Program name, OS and architecture are always shown.
-- * The package version is always shown.
-- * If there is git info at build time, the latest commit hash and commit date are shown.
-- * (TODO, requires adding --match support to githash:
-- If there are tags matching THISPKG-[0-9]*, the latest one is used to calculate patch level
-- (number of commits since tag), and if non-zero, it and the branch name are shown.)
--
-- * the program name
-- * the output of "git describe" in the current repo at build time
-- (last tag, commit count since then, HEAD's git hash);
-- or if that fails, buildversion
-- * the platform (OS) name
-- * the processor architecture name.
-- Some example outputs:
--
versionStringFor :: Either String GitInfo -> String -> String
versionStringFor gitinfo progname = concat [
progname
, " "
, either (const buildversion) giDescribe gitinfo
, ", "
, os'
, "-"
, arch
]
-- * A homebrew binary, not built in git repo: hledger-ui 1.24, mac-aarch64
-- * A CI release build, built in git repo at release tag: hledger-ui 1.24.1-g455b35293-20211210, mac-x86_64
-- * (TODO) A dev build, built in git repo: hledger-ui 1.24.1+1-g4abd8ef10-20211210 (1.24-branch), mac-x86_64
--
-- This function requires git log to show the default (rfc2822-style) date format,
-- so that must not be overridden by a log.date git config variable.
--
versionStringWith :: Either String GitInfo -> ProgramName -> PackageVersion -> VersionString
versionStringWith egitinfo progname packageversion =
concat [ progname , " " , version , ", " , os' , "-" , arch ]
where
os' | os == "darwin" = "mac"
| os == "mingw32" = "windows"
| otherwise = os
version = case egitinfo of
Left _err -> packageversion
Right gitinfo -> intercalate "-" [packageversion , hash, date]
where
hash = 'g' : take 9 (giHash gitinfo) -- like git describe
date = concat [year,mm,dd]
where
-- XXX PARTIAL: depends on git log's date format, which by default
-- is --date=default ("similar to --date=rfc2822"), but could be
-- overridden by a log.date config variable in repo or user git config.
_weekday:mon:day:_localtime:year:_offset:_ = words $ giCommitDate gitinfo
mm = fromMaybe mon $ lookup mon $ [
("Jan","01")
,("Feb","02")
,("Mar","03")
,("Apr","04")
,("May","05")
,("Jun","06")
,("Jul","07")
,("Aug","08")
,("Sep","09")
,("Oct","10")
,("Nov","11")
,("Dec","12")
]
dd = (if length day < 2 then ('0':) else id) day
-- -- | Given a program name, return a precise platform-specific executable
-- -- name suitable for naming downloadable binaries. Can raise an error if