123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. #!/bin/bash
  2. # MKit - simple install helper
  3. # See LICENSE file for copyright and license details.
  4. mkit_import ini
  5. git_bool() {
  6. #
  7. # Get git bool (ie. exit status counts) $1
  8. #
  9. local bool_name=$1 # name of boolean to get
  10. git_present || warn "can't give bool outside git repo: $bool_name"
  11. case "$bool_name" in
  12. dirty_files)
  13. git diff-files | grep -qm 1 .
  14. ;;
  15. dirty_index)
  16. git diff-index HEAD | grep -qm 1 .
  17. ;;
  18. dirty)
  19. git_bool dirty_files || git_bool dirty_index
  20. ;;
  21. *)
  22. warn "unknown git bool asked: $bool_name"
  23. return 2
  24. ;;
  25. esac
  26. }
  27. git_fact() {
  28. #
  29. # Get git fact $1
  30. #
  31. local fact_name=$1 # name of fact to get
  32. git_present || warn "can't give fact outside git repo: $fact_name"
  33. case "$fact_name" in
  34. latest_tag)
  35. git log --format="%d" \
  36. | sed 's/,/\n/g' \
  37. | sed 's/^[[:blank:]]*//' \
  38. | grep -E '^\(?tag' \
  39. | tr -cd '[[:digit:]].v\n' \
  40. | grep . -m 1
  41. ;;
  42. latest_version)
  43. git_fact latest_tag | git_tag2ver
  44. ;;
  45. current_branch)
  46. git rev-parse --abbrev-ref HEAD
  47. ;;
  48. reldiff)
  49. git log --oneline "$(git_fact latest_tag)..HEAD" --name-only
  50. ;;
  51. latest_sha)
  52. git log -1 --pretty=format:%h HEAD
  53. ;;
  54. *)
  55. warn "unknown git fact asked: $fact_name"
  56. ;;
  57. esac
  58. }
  59. git_present() {
  60. #
  61. # True if we're in a git repo
  62. #
  63. git rev-parse HEAD >&/dev/null
  64. }
  65. git_tag2ver() {
  66. #
  67. # Convert tag to version
  68. #
  69. sed s/^v//
  70. }
  71. git_ver2tag() {
  72. #
  73. # Convert version to tag
  74. #
  75. sed s/^/v/
  76. }
  77. git_lasthash() {
  78. #
  79. # Show last commit hash (with .dirty suffix if needed)
  80. #
  81. # We can't do it outside git repo (or without git) but we should
  82. # not be asked to; targets that don't require git should make use
  83. # of cache built by dist target.
  84. #
  85. local last_hash # last commit hash
  86. git_present || {
  87. echo UNKNOWN_HASH
  88. warn "no git present; could not determine last hash"
  89. return 3
  90. }
  91. last_hash=$(git rev-parse HEAD)
  92. echo -n "$last_hash"
  93. git_bool dirty && echo -n ".dirty"
  94. }
  95. semver() {
  96. #
  97. # Build proper SemVer version string
  98. #
  99. # Build version string from available info using following
  100. # logic:
  101. #
  102. # 1. Use version from last git tag (or mkit.ini if there is no
  103. # tag, which is possible on new project)
  104. # 2. if set, add project:prerl (from mkit.ini) as pre-release ID
  105. # (afer dash)
  106. # 3. if we are at a later commit than the last tag, add branch
  107. # name and commit sha1 to build metadata (after plus sign)
  108. # 4. if the tree is "dirty", i.e. has uncommited changes,
  109. # add "dirty" to build metadata
  110. #
  111. # The version is compatible with SemVer 2.0.0.
  112. #
  113. # Examples:
  114. #
  115. # foo v1.0.7 # all clear; proper release
  116. # foo v1.0.7-beta # mkit.ini: project:prerl="beta"
  117. # foo v1.0.7-beta+g1aef811.master # ^^ + some commits after
  118. # foo v1.0.7-beta+gf14fc4f.api2 # ^^ + on a feature branch
  119. # foo v1.0.7-beta+gf14fc4f.api2.dirty # ^^ + tree edited
  120. # foo v1.0.7-beta+dirty # tag OK but tree edited
  121. # foo v1.0.7+dirty # ^^ but no pre-release id
  122. #
  123. # Note that versions with "dirty" should be perceived as kind of
  124. # dangerous outside developer's own machine. Versions with sha1 are
  125. # safer but must not be released.
  126. #
  127. # FIXME: Using project:prerl for release IDs may not be compatible with
  128. # release strategy implemented in release.sh
  129. #
  130. local xyz # base version string
  131. local prerl # pre-release keyword (from mkit.ini, eg. 'beta')
  132. local latest_tag # latest git tag
  133. local commit # commit indicator (CURRENT_BRANCH.gHASH)
  134. local dirty=F # F if dirty, T if clean
  135. local btime # hex timestamp or nothing (see $MKIT_TTAG)
  136. local suffix # version suffix
  137. prerl=$(ini 1value project:prerl)
  138. case $MKIT_TTAG in
  139. none) btime= ;;
  140. btime) btime=$(printf '%08x' "$(date +%s)") ;;
  141. esac
  142. grep ":" <<<"$prerl" \
  143. && warn "colon in project:prerl may corrupt version data: $prerl"
  144. git_present || {
  145. echo UNKNOWN_VERSION
  146. warn "no git present; could not determine SemVer"
  147. return 3
  148. }
  149. latest_tag=$(git_fact latest_tag)
  150. case $latest_tag in
  151. v*) xyz=${latest_tag:1} ;;
  152. "") warn "no tags, using base version from mkit.ini (ok for new project)"
  153. xyz=$(ini 1value project:version) ;;
  154. *) warn "bad form of last tag, using base version from mkit.ini: tag is '$latest_tag'"
  155. xyz=$(ini 1value project:version) ;;
  156. esac
  157. if ! git describe --tags --exact-match HEAD >&/dev/null;
  158. then # we are at a later commit than the last tag
  159. commit="$(git_fact current_branch).g$(git_fact latest_sha)"
  160. fi
  161. git_bool dirty && dirty=T
  162. case "$dirty:$btime:$commit" in
  163. F:*:) suffix="" ;;
  164. T::) suffix="+dirty" ;;
  165. T:*:) suffix="+t$btime.dirty" ;;
  166. F::*) suffix="+$commit" ;;
  167. F:*:*) suffix="+t$btime.$commit" ;;
  168. T::*) suffix="+$commit.dirty" ;;
  169. T:*:*) suffix="+t$btime.$commit.dirty" ;;
  170. *) suffix=MKIT_BUG
  171. warn "MKIT_BUG: bad dirt/commit detection" ;;
  172. esac
  173. test -n "$prerl" && suffix="-$prerl$suffix"
  174. echo "$xyz$suffix"
  175. }