You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

255 lines
7.5 KiB

  1. #!/bin/sh
  2. # exit codes
  3. success=0
  4. release_failed=1
  5. rollback_failed=2
  6. release_preconditions_failed=4
  7. #The actual exit code
  8. exit_code=$success
  9. # color definitions
  10. KRST="\033[0m"
  11. KBLD="\033[1m"
  12. KFNT="\033[2m"
  13. KNRM="\033[22m"
  14. KRED="\033[31m"
  15. KGRN="\033[32m"
  16. KYEL="\033[33m"
  17. KBLU="\033[34m"
  18. KMAG="\033[35m"
  19. KCYN="\033[36m"
  20. KWHT="\033[37m"
  21. C_ERR="$KRED"
  22. C_INF="$KCYN"
  23. C_QRY="$KYEL"
  24. # file to store release info
  25. # used after interruption and for rollback
  26. release_info="release_info"
  27. # suffix for development snapshots
  28. version_snapshot_suffix="-SNAPSHOT"
  29. # calculate proposal for next version number
  30. calculate_next_version() {
  31. case "$1" in
  32. *$version_snapshot_suffix)
  33. # Cut off -SNAPSHOT suffix
  34. result=$(echo "$1" | sed -e "s/$version_snapshot_suffix$//")
  35. ;;
  36. *)
  37. # Search for the last version part and increase by 1
  38. # taken from https://stackoverflow.com/q/8653126/572645
  39. result=$(echo "$1" | gawk -F"." '{$NF+=1}{print $0RT}' OFS="." ORS="")
  40. ;;
  41. esac
  42. # if a second parameter is given, use this as the next snapshot suffix
  43. if [ ! -z "$2" ]; then
  44. echo $result"$2"
  45. else
  46. echo $result
  47. fi
  48. }
  49. # Upload release to github
  50. # (hub is GitHub’s official tool that wraps git in order to extend it
  51. # with extra functionality that make working with GitHub easier.)
  52. gh_upload_release() {
  53. for asset in dist/*; do
  54. assets="$assets"" -a ""$asset"
  55. done
  56. hub release create $assets $new_version_tag
  57. }
  58. # Delete release from github
  59. gh_delete_release() {
  60. hub release delete $new_version_tag
  61. }
  62. # Perform a rollback
  63. rollback() {
  64. if [ -z $prev_version_commit_hash ]; then
  65. printf "$C_ERR""Missing release info. Rollback not possible.""$KRST""\n"
  66. exit_code=$(echo $exit_code + $rollback_failed | bc)
  67. exit $exit_code
  68. fi
  69. #TODO check that no additional commit was made since release?
  70. printf "$C_INF""Rollback to""$KRST""\n $(git log --oneline --no-decorate $prev_version_commit_hash^..$prev_version_commit_hash)\n""$C_INF""?\n""$KRST"
  71. printf "$C_INF""The following commits will be dropped:\n""$KRST"
  72. git log --oneline --no-decorate $prev_version_commit_hash..HEAD | sed "s/^/\ \ /"
  73. read -p $(printf "$C_QRY")"Perform rollback? ("$(printf "$KBLD")"y/n"$(printf "$KNRM")"): "$(printf "$KRST") yn
  74. case $yn in
  75. [yY] )
  76. gh_delete_release $new_version_tag
  77. git tag -d $new_version_tag
  78. git reset --hard $prev_version_commit_hash
  79. rm $release_info
  80. printf "$C_INF""Changes were rolled back locally. Please double check them and push them to remote via\n git push --force-with-lease""$KRST"
  81. break
  82. ;;
  83. * )
  84. printf "$C_INF""Rollback aborted""$KRST"
  85. ;;
  86. esac
  87. exit $exit_code
  88. }
  89. # Collection all necessary information for a release
  90. prepare_release() {
  91. touch $release_info
  92. # remember current state in release_info
  93. if [ -z $prev_version_commit_hash ]; then
  94. prev_version_commit_hash=$(git rev-parse HEAD)
  95. sed -i '/prev_version_commit_hash=/d' $release_info
  96. echo "prev_version_commit_hash=$prev_version_commit_hash" >> $release_info
  97. fi
  98. if [ -z $prev_version ]; then
  99. prev_version=$(sed -n "/^\S*\s*(\([^(-)]*\)[-)].*/{ s//\1/p; q0 }; q1" debian/changelog || echo -n "undefined previous version" )
  100. sed -i '/prev_version=/d' $release_info
  101. echo "prev_version=$prev_version" >> $release_info
  102. fi
  103. # now request info from user
  104. if [ -z $new_version ]; then
  105. proposed_version=$(calculate_next_version $prev_version)
  106. read -p $(printf "$C_QRY")"Current version is $prev_version. Please enter the new release version (default: "$(printf "$KBLD")"$proposed_version"$(printf "$KNRM")"): "$(printf "$KRST") user_input
  107. if [ ! -z $user_input ]; then
  108. new_version=$user_input
  109. else
  110. new_version=$proposed_version
  111. fi
  112. sed -i '/new_version=/d' $release_info
  113. echo "new_version=$new_version" >> $release_info
  114. fi
  115. if [ -z $new_version_tag ]; then
  116. proposed_tag=v$new_version
  117. read -p $(printf "$C_QRY")"Please enter the VCS tag for the new release (default: "$(printf "$KBLD")"$proposed_tag"$(printf "$KNRM")"): "$(printf "$KRST") user_input
  118. if [ ! -z $user_input ]; then
  119. new_version_tag=$user_input
  120. else
  121. new_version_tag=$proposed_tag
  122. fi
  123. sed -i '/new_version_tag=/d' $release_info
  124. echo "new_version_tag=$new_version_tag" >> $release_info
  125. fi
  126. if [ -z $next_version ]; then
  127. proposed_version=$(calculate_next_version $new_version $version_snapshot_suffix)
  128. read -p $(printf "$C_QRY")"Please enter the next development version (default: "$(printf "$KBLD")"$proposed_version"$(printf "$KNRM")"): "$(printf "$KRST") user_input
  129. if [ ! -z $user_input ]; then
  130. next_version=$user_input
  131. else
  132. next_version=$proposed_version
  133. fi
  134. sed -i '/next_version=/d' $release_info
  135. echo "next_version=$next_version" >> $release_info
  136. fi
  137. }
  138. # Perform the release
  139. perform_release() {
  140. echo $prev_version_commit_hash
  141. {
  142. dch -v "$new_version""-1" --preserve && \
  143. dch --release "" --preserve && \
  144. git commit -m "[release] Create new release" debian/changelog && \
  145. git tag $new_version_tag && \
  146. make clean dist && \
  147. git commit -m "[release] Prepare next development version" --allow-empty && \
  148. git push && \
  149. git push origin $new_version_tag && \
  150. gh_upload_release $new_version_tag dist/*$new_version* && \
  151. rm release_info
  152. #TODO: Provide changelog to gh_upload_release
  153. } || {
  154. printf "$C_ERR""Error performing release.\n""$KRST"
  155. exit_code=$(echo $exit_code + $release_failed | bc)
  156. rollback
  157. }
  158. }
  159. # Check that the repository is in a clean state
  160. check_clean_state() {
  161. # check that no uncommited changes exist
  162. if [ -n "$(git status --porcelain)" ]; then
  163. # Uncommitted changes
  164. printf "$C_ERR""You have uncommited changes. Commit them or stash them away before attempting to perform a release.""$KRST"
  165. exit_code=$(echo $exit_code + $release_preconditions_failed | bc)
  166. exit $exit_code
  167. fi
  168. # check that no remote changes exist that need to be pulled
  169. # Taken from https://stackoverflow.com/a/3278427/572645
  170. git remote update
  171. UPSTREAM=${1:-'@{u}'}
  172. LOCAL=$(git rev-parse @)
  173. REMOTE=$(git rev-parse "$UPSTREAM")
  174. BASE=$(git merge-base @ "$UPSTREAM")
  175. if [ $LOCAL = $REMOTE ]; then
  176. # "Up-to-date"
  177. :
  178. elif [ $LOCAL = $BASE ]; then
  179. echo "$C_ERR""There are remote changes. Pull and merge them before attempting to perform a release.""$KRST"
  180. exit_code=$(echo $exit_code + $release_preconditions_failed | bc)
  181. exit $exit_code
  182. elif [ $REMOTE = $BASE ]; then
  183. # "Need to push"
  184. :
  185. else
  186. printf "$C_ERR""Your local and your remote branch have diverged. Please rectify the situation before attempting to perform a release.""$KRST"
  187. exit_code=$(echo $exit_code + $release_preconditions_failed | bc)
  188. exit $exit_code
  189. fi
  190. }
  191. # Cleanup release info
  192. clean() {
  193. rm -f release_info
  194. }
  195. # Display usage help
  196. show_help() {
  197. printf "$C_INF"
  198. printf "Usage: $0 [perform|rollback|clean]\n"
  199. printf "Options:\n"
  200. printf " perform: Perform a release\n"
  201. printf " rollback: Rollback a release\n"
  202. printf " prepare: Like perform, but stop before non-local changes starts.\n"
  203. printf " clean: Cleanup release info (No rollback will be possible afterwards)\n"
  204. printf "$KRST"
  205. }
  206. # read existing release info
  207. if [ -f "$release_info" ]; then
  208. . ./$release_info
  209. fi
  210. # evaluate commandline arguments
  211. case "$1" in
  212. perform)
  213. check_clean_state && prepare_release && perform_release
  214. ;;
  215. rollback)
  216. rollback
  217. ;;
  218. prepare) # Do not includes non-local operations
  219. check_clean_state && prepare_release
  220. ;;
  221. clean)
  222. clean
  223. ;;
  224. *)
  225. show_help
  226. ;;
  227. esac