123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175 |
- #!/bin/bash
-
- . "$MKIT_DIR/include/ini.sh" || die "cannot import ini.sh"
-
- git_bool() {
- #
- # Get git bool (ie. exit status counts) $1
- #
- local bool_name="$1"
- git_present || warn "can't give bool outside git repo: $bool_name"
- case "$bool_name" in
- dirty_files)
- git diff-files | grep -qm 1 .
- ;;
- dirty_index)
- git diff-index HEAD | grep -qm 1 .
- ;;
- dirty)
- git_bool dirty_files || git_bool dirty_index
- ;;
- *)
- warn "unknown git bool asked: $bool_name"
- return 2
- ;;
- esac
- }
-
- git_fact() {
- #
- # Get git fact $1
- #
- local fact_name="$1"
- git_present || warn "can't give fact outside git repo: $fact_name"
- case "$fact_name" in
- latest_tag)
- git log --format="%D" \
- | sed 's/,/\n/g' \
- | sed 's/^[[:blank:]]*//; ' \
- | grep -E '^tag: v[[:digit:]]+\.' \
- | cut -d' ' -f2 \
- | head -1
- ;;
- latest_version)
- git_fact latest_tag | git_tag2ver
- ;;
- current_branch)
- git rev-parse --abbrev-ref HEAD
- ;;
- reldiff)
- git log --oneline "$(git_fact latest_tag)..HEAD" --name-only
- ;;
- latest_sha)
- git log -1 --pretty=format:%h HEAD
- ;;
- *)
- warn "unknown git fact asked: $fact_name"
- ;;
- esac
- }
-
- git_present() {
- #
- # True if we're in a git repo
- #
- git rev-parse HEAD >&/dev/null
- }
-
- git_tag2ver() {
- #
- # Convert tag to version
- #
- sed s/^v//
- }
-
- git_ver2tag() {
- #
- # Convert version to tag
- #
- sed s/^/v/
- }
-
- git_lasthash() {
- #
- # Show last commit hash (with .dirty suffix if needed)
- #
- # If outside git repo, get it from .mkit/git_lasthash, which
- # should have been put there by dist target. (I.e., this won't
- # work if you got outside the git repo in other way than dist
- # target, but that's actually expected.)
- #
- if git_present;
- then # we are in git repo
- local last_hash=$(git rev-parse HEAD)
- echo -n "$last_hash"
- git_bool dirty && echo -n ".dirty"
- else # we are outside (eg. distributor's build dir')
- grep . .mkit/git_lasthash || {
- echo UNKNOWN
- warn "malformed source, could not determine git hash"
- }
- fi
- }
-
- semver() {
- #
- # Build semver version string with build metadata
- #
- # Build version string from available info using following
- # logic:
- #
- # 1. use project.version (from mkit.ini)
- # 2. if we are in git, override the version with last tag
- # 3. if set, add project:prerl (from mkit.ini) as pre-release ID
- # (afer dash)
- # 4. if we are at a later commit than the last tag, add branch
- # name and commit sha1 to build metadata (after plus sign)
- # 5. if the tree is "dirty", i.e. has uncommited changes,
- # add "dirty" to build metadata
- #
- # The version is compatible with SemVer 2.0.0.
- #
- # Examples:
- #
- # myprog v1.0.7 # all clear
- # myprog v1.0.7-alpha # mkit.ini: project:prerl="alpha"
- # myprog v1.0.7-alpha+g1aef811.master # ^^ + some commits after
- # myprog v1.0.7-alpha+gf14fc4f.api2 # ^^ + on a feature branch
- # myprog v1.0.7-alpha+gf14fc4f.api2.dirty # ^^ + tree edited
- # myprog v1.0.7-alpha+dirty # tag OK but tree edited
- # myprog v1.0.7+dirty # ^^ but no pre-release id
- #
- # Note that versions with "dirty" should be perceived as kind of
- # dangerous outside developer's own machine. Versions with sha1 are
- # safer but must not be released.
- #
- # I have considered decorating the git commit refs to make them
- # sort of sortable (e.g. "r1.g1aef811"), but on second thought,
- # I don't think it's good idea to give *any* semantics to meta-data
- # at all. First, there is no rule that r1<r2<r3; a commit can be
- # removing what other just added and one change can be split to
- # multiple commits. Also, the whole thing breaks anyway once you
- # rebase your branch (no, it's not a sin). The sole purpose of
- # meta-data is to *identify* the code, and provide safe path back
- # to tree; commit refs are already perfect for that.
- #
- # FIXME: Using project:prerl for release IDs may not be compatible with
- # release strategy implemented in release.sh
- #
- local version=$(ini 1value project:version)
- local prerl=$(ini 1value project:prerl)
- grep ":" <<<"$prerl" && warn "colon in project:prerl may corrupt version data: $prerl"
- if git_present;
- then # we are in git repo... so we can get smart
- local latest_tag=$(git_fact latest_tag)
- if ! git describe --tags --exact-match HEAD >&/dev/null;
- then # we are at a later commit than the last tag
- local commit="$(git_fact current_branch).g$(git_fact latest_sha)"
- fi
- local dirty=""
- local suffix=""
- git_bool dirty; dirty=$?
- test -n "$latest_tag" && version=${latest_tag:1}
- case "$dirty:$commit" in
- 1:) suffix="" ;;
- 0:) suffix="+dirty" ;;
- 1:*) suffix="+$commit" ;;
- 0:*) suffix="+$commit.dirty" ;;
- *) suffix=MKIT_BUG;
- warn "MKIT_BUG: bad dirt/commit detection" ;;
- esac
- test -n "$prerl" && suffix="-$prerl$suffix"
- version="$version$suffix"
- fi
- echo "$version"
- }
|