Working Saturnin-based meta-command

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. #!/bin/bash
  2. . "$MKIT_DIR/include/ini.sh" || die "cannot import ini.sh"
  3. git_bool() {
  4. #
  5. # Get git bool (ie. exit status counts) $1
  6. #
  7. local bool_name="$1"
  8. git_present || warn "can't give bool outside git repo: $bool_name"
  9. case "$bool_name" in
  10. dirty_files)
  11. git diff-files | grep -qm 1 .
  12. ;;
  13. dirty_index)
  14. git diff-index HEAD | grep -qm 1 .
  15. ;;
  16. dirty)
  17. git_bool dirty_files || git_bool dirty_index
  18. ;;
  19. *)
  20. warn "unknown git bool asked: $bool_name"
  21. return 2
  22. ;;
  23. esac
  24. }
  25. git_fact() {
  26. #
  27. # Get git fact $1
  28. #
  29. local fact_name="$1"
  30. git_present || warn "can't give fact outside git repo: $fact_name"
  31. case "$fact_name" in
  32. latest_tag)
  33. git log --format="%d" \
  34. | sed 's/,/\n/g' \
  35. | sed 's/^[[:blank:]]*//' \
  36. | grep -E '^\(?tag' \
  37. | tr -cd '[[:digit:]].v\n' \
  38. | grep . -m 1
  39. ;;
  40. latest_version)
  41. git_fact latest_tag | git_tag2ver
  42. ;;
  43. current_branch)
  44. git rev-parse --abbrev-ref HEAD
  45. ;;
  46. reldiff)
  47. git log --oneline "$(git_fact latest_tag)..HEAD" --name-only
  48. ;;
  49. latest_sha)
  50. git log -1 --pretty=format:%h HEAD
  51. ;;
  52. *)
  53. warn "unknown git fact asked: $fact_name"
  54. ;;
  55. esac
  56. }
  57. git_present() {
  58. #
  59. # True if we're in a git repo
  60. #
  61. git rev-parse HEAD >&/dev/null
  62. }
  63. git_tag2ver() {
  64. #
  65. # Convert tag to version
  66. #
  67. sed s/^v//
  68. }
  69. git_ver2tag() {
  70. #
  71. # Convert version to tag
  72. #
  73. sed s/^/v/
  74. }
  75. git_lasthash() {
  76. #
  77. # Show last commit hash (with .dirty suffix if needed)
  78. #
  79. # If outside git repo, get it from .mkit/git_lasthash, which
  80. # should have been put there by dist target. (I.e., this won't
  81. # work if you got outside the git repo in other way than dist
  82. # target, but that's actually expected.)
  83. #
  84. if git_present;
  85. then # we are in git repo
  86. local last_hash=$(git rev-parse HEAD)
  87. echo -n "$last_hash"
  88. git_bool dirty && echo -n ".dirty"
  89. else # we are outside (eg. distributor's build dir')
  90. grep . .mkit/git_lasthash || {
  91. echo UNKNOWN
  92. warn "malformed source, could not determine git hash"
  93. }
  94. fi
  95. }
  96. semver() {
  97. #
  98. # Build semver version string with build metadata
  99. #
  100. # Build version string from available info using following
  101. # logic:
  102. #
  103. # 1. use project.version (from mkit.ini)
  104. # 2. if we are in git, override the version with last tag
  105. # 3. if set, add project:prerl (from mkit.ini) as pre-release ID
  106. # (afer dash)
  107. # 4. if we are at a later commit than the last tag, add branch
  108. # name and commit sha1 to build metadata (after plus sign)
  109. # 5. if the tree is "dirty", i.e. has uncommited changes,
  110. # add "dirty" to build metadata
  111. #
  112. # The version is compatible with SemVer 2.0.0.
  113. #
  114. # Examples:
  115. #
  116. # myprog v1.0.7 # all clear
  117. # myprog v1.0.7-alpha # mkit.ini: project:prerl="alpha"
  118. # myprog v1.0.7-alpha+g1aef811.master # ^^ + some commits after
  119. # myprog v1.0.7-alpha+gf14fc4f.api2 # ^^ + on a feature branch
  120. # myprog v1.0.7-alpha+gf14fc4f.api2.dirty # ^^ + tree edited
  121. # myprog v1.0.7-alpha+dirty # tag OK but tree edited
  122. # myprog v1.0.7+dirty # ^^ but no pre-release id
  123. #
  124. # Note that versions with "dirty" should be perceived as kind of
  125. # dangerous outside developer's own machine. Versions with sha1 are
  126. # safer but must not be released.
  127. #
  128. # I have considered decorating the git commit refs to make them
  129. # sort of sortable (e.g. "r1.g1aef811"), but on second thought,
  130. # I don't think it's good idea to give *any* semantics to meta-data
  131. # at all. First, there is no rule that r1<r2<r3; a commit can be
  132. # removing what other just added and one change can be split to
  133. # multiple commits. Also, the whole thing breaks anyway once you
  134. # rebase your branch (no, it's not a sin). The sole purpose of
  135. # meta-data is to *identify* the code, and provide safe path back
  136. # to tree; commit refs are already perfect for that.
  137. #
  138. # FIXME: Using project:prerl for release IDs may not be compatible with
  139. # release strategy implemented in release.sh
  140. #
  141. local version=$(ini 1value project:version)
  142. local prerl=$(ini 1value project:prerl)
  143. grep ":" <<<"$prerl" && warn "colon in project:prerl may corrupt version data: $prerl"
  144. if git_present;
  145. then # we are in git repo... so we can get smart
  146. local latest_tag=$(git_fact latest_tag)
  147. if ! git describe --tags --exact-match HEAD >&/dev/null;
  148. then # we are at a later commit than the last tag
  149. local commit="$(git_fact current_branch).g$(git_fact latest_sha)"
  150. fi
  151. local dirty=""
  152. local suffix=""
  153. git_bool dirty; dirty=$?
  154. test -n "$latest_tag" && version=${latest_tag:1}
  155. case "$dirty:$commit" in
  156. 1:) suffix="" ;;
  157. 0:) suffix="+dirty" ;;
  158. 1:*) suffix="+$commit" ;;
  159. 0:*) suffix="+$commit.dirty" ;;
  160. *) suffix=MKIT_BUG;
  161. warn "MKIT_BUG: bad dirt/commit detection" ;;
  162. esac
  163. test -n "$prerl" && suffix="-$prerl$suffix"
  164. version="$version$suffix"
  165. fi
  166. echo "$version"
  167. }