post-commit 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. #!/bin/bash
  2. # git-packaging-hooks - git hooks to semi-automate releases and distro packaging
  3. #
  4. # Copyright 2017 bill-auger <https://github.com/bill-auger/git-packaging-hooks/issues>
  5. #
  6. # git-packaging-hooks is free software: you can redistribute it and/or modify
  7. # it under the terms of the GNU General Public License version 3 as published by
  8. # the Free Software Foundation, either version 3 of the License, or
  9. # (at your option) any later version.
  10. #
  11. # git-packaging-hooks is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. # GNU General Public License for more details.
  15. #
  16. # You should have received a copy of the GNU General Public License version 3
  17. # along with git-packaging-hooks. If not, see <http://www.gnu.org/licenses/>.
  18. readonly HOOKS_PATH=$(git config --local core.hooksPath)
  19. readonly COMMON_DEFS_FILE=$HOOKS_PATH/common.sh.inc
  20. source $COMMON_DEFS_FILE
  21. [ ! -f $COMMON_DEFS_FILE ] && TraceError "missing $COMMON_DEFS_FILE - aborting commit" && exit 1
  22. if (($IS_STAGING_BRANCH))
  23. then TraceStage "running staging-specific post-commit hooks"
  24. # set "release" tag
  25. for revision_tag in $REVISION_TAGS
  26. do TraceStep "removing un-merged revision tag '$revision_tag'"
  27. git tag --delete $revision_tag > /dev/null
  28. done
  29. if [ ! -f $TAGS_FILE ]
  30. then TraceStep "adding revision tag '$VERSION_STRING'"
  31. git tag $VERSION_STRING
  32. else declare -a orphaned_tags=( $(cat $TAGS_FILE | sort | uniq) )
  33. orphaned_minor_tag=${orphaned_tags[0]}
  34. orphaned_rev_tag=${orphaned_tags[1]}
  35. orphaned_other_tags=${orphaned_tags[@]:2}
  36. [ "$orphaned_minor_tag" ] && TraceStep "restoring minor version tag '$orphaned_minor_tag'"
  37. [ "$orphaned_rev_tag" ] && TraceStep "adding revision tag '$orphaned_rev_tag'"
  38. [ "$orphaned_other_tags" ] && TraceStep "restoring other tags '$orphaned_other_tags'"
  39. for orphaned_tag in "${orphaned_tags[@]}" ; do git tag $orphaned_tag ; done
  40. rm $TAGS_FILE
  41. fi
  42. # re-configure autotools, rebuild, install, and verify
  43. TraceStep "re-configuring autotools"
  44. sudo make uninstall 2&> /dev/null
  45. make distclean 2&> /dev/null
  46. autoreconf --force --install
  47. TraceStep "verifying re-build/install"
  48. ./configure 2&> /dev/null
  49. make -j$N_MAKE_JOBS 2&> /dev/null
  50. sudo make install 2&> /dev/null
  51. make installcheck 2&> /dev/null
  52. sudo make uninstall 2&> /dev/null
  53. make distclean 2&> /dev/null
  54. # commit automated changes, and ensure signed
  55. git add --all
  56. git diff --exit-code --cached > /dev/null && is_index_clean=1 || is_index_clean=0
  57. git verify-commit --raw HEAD 2> /dev/null && has_trusted_signature=1 || has_trusted_signature=0
  58. (($is_index_clean )) || TraceStep "amending commit with automated changes"
  59. (($has_trusted_signature)) || TraceStep "amending commit to ensure signature exists"
  60. (($is_index_clean)) && (($has_trusted_signature)) || \
  61. git commit --gpg-sign --amend --reuse-message=HEAD > /dev/null
  62. TraceStage "completed staging-specific post-commit hooks"
  63. elif (($IS_PACKAGING_BRANCH))
  64. then (($IS_AMEND_COMMIT)) && exit 0
  65. TraceStage "running packaging-specific post-commit hooks"
  66. # implode tarball
  67. TraceStep "imploding tarball"
  68. rm $TARBALL_FILE $TARBALL_FILE.sig $PKGBUILD_FILE.sig 2> /dev/null
  69. git archive --prefix=$TARBALL_INNER_DIR/ --output=$TARBALL_FILE $VERSION_STRING
  70. # generate tarball metadata
  71. TraceStep "generating checksums"
  72. TARBALL_MD5SUM="$( md5sum $TARBALL_FILE | cut --delimiter=' ' --fields=1)"
  73. TARBALL_SHA1SUM="$( sha1sum $TARBALL_FILE | cut --delimiter=' ' --fields=1) "
  74. TARBALL_SHA256SUM="$(sha256sum $TARBALL_FILE | cut --delimiter=' ' --fields=1)"
  75. FILE_SIZE=$( wc --bytes $TARBALL_FILE | cut --delimiter=' ' --fields=1)
  76. # PKGBUILD_MD5SUMS="'$TARBALL_MD5SUM' SKIP SKIP"
  77. PKGBUILD_MD5SUMS="'$TARBALL_MD5SUM'"
  78. # inject tarball metadata into packaging files
  79. TraceStep "preparing packaging files"
  80. for dsc_file in ${DSC_FILES[@]}
  81. do echo "Checksums-Sha1:" >> $dsc_file
  82. echo " $TARBALL_SHA1SUM $FILE_SIZE $DEB_TARBALL_FILENAME" >> $dsc_file
  83. echo "Checksums-Sha256:" >> $dsc_file
  84. echo " $TARBALL_SHA256SUM $FILE_SIZE $DEB_TARBALL_FILENAME" >> $dsc_file
  85. echo "Files:" >> $dsc_file
  86. echo " $FAUX_DSC_MD5SUM $FAUX_DSC_SIZE $DEB_TARBALL_FILENAME" >> $dsc_file
  87. echo " $FAUX_DSC_MD5SUM $FAUX_DSC_SIZE $DEB_DIFFBALL_FILENAME" >> $dsc_file
  88. done
  89. sed --in-place "s/^md5sums=.*$/md5sums=( $PKGBUILD_MD5SUMS )/" $PKGBUILD_FILE
  90. # set uniform commit message and ensure signed
  91. git commit --gpg-sign --amend --allow-empty --message="$GIT_COMMIT_MSG" > /dev/null
  92. # sign tarball and PKGBUILD
  93. gpg --detach-sign --yes $TARBALL_FILE
  94. gpg --detach-sign --yes $PKGBUILD_FILE
  95. ### github steps ###
  96. # create upstream tarball
  97. TraceStep "pushing staging branch, packaging branch, and revision tag to github"
  98. git push --force $REMOTE_NAME $STAGING_BRANCH $PACKAGING_BRANCH $VERSION_STRING 2> /dev/null
  99. # destroy any existing github "tag release" with same tag
  100. TraceStep "querying existing github \"tag release\" '$VERSION_STRING'"
  101. resp_json=$(curl $CURL_RESP_ARGS $GITHUB_API_URL/tags/$VERSION_STRING)
  102. release_id=$(echo $resp_json | grep '"assets_url":' | sed "$RELEASEID_REGEX")
  103. error_resp=$(echo $resp_json | grep '"message": "Not Found"')
  104. if [ -n "$(echo $release_id | sed 's/[^0-9]//g')" -a -z "$error_resp" ]
  105. then TraceStep "deleting existing github \"tag release\" - id: '$release_id'"
  106. resp_status=$(curl --request DELETE \
  107. --header "Authorization: token $GITHUB_AUTH_TOKEN" \
  108. --header "Content-Type: application/json" \
  109. --header "Accept: application/json" \
  110. $CURL_STATUS_ARGS \
  111. $GITHUB_API_URL/$release_id )
  112. if [ "$resp_status" == "204" ]
  113. then TraceStep "deleted github \"tag release\" - id: '$release_id'"
  114. else TraceStep "failed to delete github \"tag release\" - id: '$release_id'" ; exit 1 ;
  115. fi
  116. fi
  117. # create github "tag release"
  118. TraceStep "creating github \"tag release\" '$VERSION_STRING'"
  119. resp_status=$(curl --request POST --data "$GITHUB_RELEASE_JSON" \
  120. --header "Authorization: token $GITHUB_AUTH_TOKEN" \
  121. --header "Content-Type: application/json" \
  122. --header "Accept: application/json" \
  123. $CURL_STATUS_ARGS \
  124. $GITHUB_API_URL )
  125. if [ "$resp_status" == "201" ]
  126. then TraceStep "created github \"tag release\" '$VERSION_STRING'"
  127. else TraceStep "failed to create github \"tag release\" '$VERSION_STRING'" ; exit 1 ;
  128. fi
  129. # ensure the new github "tag release" is accessible via the API
  130. TraceStep "confirming existence of new github \"tag release\" '$VERSION_STRING'"
  131. resp_json=$(curl $CURL_RESP_ARGS $GITHUB_API_URL/tags/$VERSION_STRING)
  132. release_id=$( echo $resp_json | grep '"assets_url":' | sed "$RELEASEID_REGEX")
  133. upload_url=$( echo $resp_json | grep '"upload_url":' | sed "$UPLOADURL_REGEX")
  134. error_resp=$( echo $resp_json | grep '"message": "Not Found"' )
  135. if [ -n "$(echo $release_id | sed 's/[^0-9]//g')" -a -z "$error_resp" ]
  136. then TraceStep "confirmed existence of new github \"tag release\" - id: '$release_id'"
  137. else TraceStep "failed to fetch new github \"tag release\" '$VERSION_STRING'" ; exit 1 ;
  138. fi
  139. # upload signatures to github "tag release"
  140. for upload_file in ${UPLOAD_FILES[@]}
  141. do filename=$(basename $upload_file)
  142. TraceStep "uploading '$filename' to github \"tag release\" '$VERSION_STRING'"
  143. resp_status=$(curl --request POST --data-binary "@$upload_file" \
  144. --header "Authorization: token $GITHUB_AUTH_TOKEN" \
  145. --header "Content-Type: text/plain" \
  146. --header "Accept: application/json" \
  147. $CURL_STATUS_ARGS \
  148. ${upload_url}?name=$filename )
  149. if [ "$resp_status" == "201" ]
  150. then TraceStep "uploaded '$upload_file'"
  151. else TraceStep "failed to upload '$upload_file'" ; exit 1 ;
  152. fi
  153. done
  154. # download and verify github "tag release" tarball and PKGBUILD
  155. TraceStep "downloading remote tarball, PKGBUILD, and signatures"
  156. curl $CURL_FETCH_ARGS $TARBALL_URL ; curl $CURL_FETCH_ARGS $PKGBUILD_URL ;
  157. curl $CURL_FETCH_ARGS $TARBALL_SIG_URL ; curl $CURL_FETCH_ARGS $PKGBUILD_SIG_URL ;
  158. for remote_file in ${REMOTE_FILENAMES[@]}
  159. do if [ -f $remote_file -a "$(stat --printf='%s' $remote_file)" != "9" ] # 'Not Found'
  160. then TraceStep "downloaded $remote_file"
  161. else TraceStep "failed to download $remote_file"
  162. # source $(git config --local core.hooksPath)/debug-remote-files.sh.inc
  163. exit 1
  164. fi
  165. done
  166. if gpg --verify --verify-options no-show-photos ./$TARBALL_FILENAME.sig 2> /dev/null && \
  167. diff $TARBALL_FILE ./$TARBALL_FILENAME > /dev/null && \
  168. diff $TARBALL_FILE.sig ./$TARBALL_FILENAME.sig > /dev/null && \
  169. gpg --verify --verify-options no-show-photos ./PKGBUILD.sig 2> /dev/null && \
  170. diff $PKGBUILD_FILE ./PKGBUILD > /dev/null && \
  171. diff $PKGBUILD_FILE.sig ./PKGBUILD.sig > /dev/null
  172. then TraceStep "verified remote tarball, PKGBUILD, and signatures"
  173. else TraceStep "failed to verify remote tarball, PKGBUILD, or signatures"
  174. # source $(git config --local core.hooksPath)/debug-remote-files.sh.inc
  175. exit 1
  176. fi
  177. ### OBS steps ###
  178. # copy OBS files to local OSC build directory and trigger remote builds
  179. TraceStep "populating OSC build directory"
  180. cd $OSC_DIR/ && rm ./*
  181. for obs_file in ${OBS_FILES[@]}
  182. do cp $obs_file $OSC_DIR/
  183. osc add ./$(basename $obs_file) > /dev/null
  184. done
  185. TraceStep "triggering OSC remote builds"
  186. osc checkin --message=$OSC_COMMIT_MSG > /dev/null
  187. # cleanup transient files
  188. TraceStep "cleaning up transient files"
  189. cd $PROJECT_DIR
  190. for transient_file in ${CLEANUP_FILES[@]} ; do rm $transient_file ; done ;
  191. git clean $GIT_CLEAN_OPTIONS &> /dev/null
  192. ### debian steps ###
  193. # rename tarball and prepare working tree for debain
  194. rm -rf ../${DEBIAN_NAME}*${VERSION}*
  195. mv $TARBALL_FILE ../$DEB_TARBALL_FILENAME
  196. [ "$DEB_BUILD_TOOL" == 'gbp' -a ! -f debian/gbp.conf ] && cp $HOOKS_PATH/gbp.conf debian/
  197. # build/validate debian package
  198. TraceStep "building debian package with '$"$DEB_BUILD_TOOL"'"
  199. case "$DEB_BUILD_TOOL" in
  200. 'debuild') debuild --unsigned-changes --unsigned-source
  201. deb_file=$(ls ../${DEBIAN_NAME}_${VERSION}-*_${DEB_BUILD_ARCH}.deb 2> /dev/null)
  202. sudo piuparts --save=$CHROOTBALL --basetgz=$CHROOTBALL \
  203. --distribution=$DEB_BUILD_DIST --arch=$DEB_BUILD_ARCH \
  204. --mirror="$DEBOOTSTRAP_REPO" $deb_file
  205. (($?)) && echo "piuparts failed for '$deb_file'" && exit 1
  206. ;;
  207. 'sbuild') sudo sbuild-createchroot --make-sbuild-tarball=$CHROOTBALL $DEB_BUILD_DIST \
  208. `mktemp --directory` "$DEBOOTSTRAP_REPO"
  209. sbuild --dist=$DEB_BUILD_DIST --arch=$DEB_BUILD_ARCH \
  210. --run-piuparts --piuparts-opts="--basetgz=$CHROOTBALL"
  211. ;;
  212. 'gbp') DIST=$DEB_BUILD_DIST ARCH=$DEB_BUILD_ARCH git-pbuilder create --extrapackages 'eatmydata' &> $LOG_FILE
  213. DIST=$DEB_BUILD_DIST ARCH=$DEB_BUILD_ARCH git-pbuilder update --extrapackages 'eatmydata' &> $LOG_FILE || true
  214. [ ! -d $PIUPARTS_CHROOT ] && echo "could not create chroot '$PIUPARTS_CHROOT'" && exit 1
  215. gbp buildpackage --git-dist=$DEB_BUILD_DIST --git-arch=$DEB_BUILD_ARCH \
  216. --git-pbuilder --git-tag --git-retag --git-notify=off &> $LOG_FILE
  217. deb_file=$(ls ../${DEBIAN_NAME}_${VERSION}-*_${DEB_BUILD_ARCH}.deb 2> /dev/null)
  218. [ ! -f "$deb_file" ] && echo "package creation failed" && exit 1
  219. TraceStep "testing debian package install/uninstall"
  220. sudo piuparts --distribution=$DEB_BUILD_DIST --arch=$DEB_BUILD_ARCH \
  221. --existing-chroot=$PIUPARTS_CHROOT $deb_file &> $LOG_FILE
  222. (($?)) && echo "piuparts failed for '$deb_file'" && exit 1
  223. debian_version=debian/$(dpkg-parsechangelog --show-field version)
  224. debian_tag=$(git tag --points-at HEAD)
  225. [ "$debian_version" != "$debian_tag" ] && TraceError "improper debian tag" && exit 1
  226. TraceStep "pushing debian tag to github"
  227. git push --force $REMOTE_NAME $debian_tag 2> /dev/null
  228. ;;
  229. esac
  230. TraceStage "completed packaging-specific post-commit hooks"
  231. fi