4 # Licensed to the Apache Software Foundation (ASF) under one or more
5 # contributor license agreements. See the NOTICE file distributed with
6 # this work for additional information regarding copyright ownership.
7 # The ASF licenses this file to You under the Apache License, Version 2.0
8 # (the "License"); you may not use this file except in compliance with
9 # the License. You may obtain a copy of the License at
11 # http://www.apache.org/licenses/LICENSE-2.0
13 # Unless required by applicable law or agreed to in writing, software
14 # distributed under the License is distributed on an "AS IS" BASIS,
15 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 # See the License for the specific language governing permissions and
17 # limitations under the License.
20 # Source this file if you want to use any of its utiilty (also useful
21 # testing the below functions). Do "$ . ./release-util.sh" and then
22 # you can do stuff like call the CHANGES updating function
23 # update_releasenotes:
25 # $ update_releasenotes ~/checkouts/hbase.apache.git 2.3.4
27 # Just make sure any environment variables needed are predefined
30 DRY_RUN
=${DRY_RUN:-1} #default to dry run
33 GPG_ARGS
=(--no-autostart --batch)
34 if [ -n "${GPG_KEY}" ]; then
35 GPG_ARGS
=("${GPG_ARGS[@]}" --local-user "${GPG_KEY}")
37 # Maven Profiles for publishing snapshots and release to Maven Central and Dist
38 PUBLISH_PROFILES
=("-P" "apache-release,release")
45 function read_config
{
50 read -r -p "$PROMPT [$DEFAULT]: " REPLY
51 local RETVAL
="${REPLY:-$DEFAULT}"
52 if [ -z "$RETVAL" ]; then
53 error
"$PROMPT must be provided."
58 function parse_version
{
59 grep -e '<version>.*</version>' | \
60 head -n 2 |
tail -n 1 | cut
-d'>' -f2 | cut
-d '<' -f1
65 echo "========================"
71 echo "$(date -u +"%Y-
%m-
%dT
%H
:%M
:%SZ
") ${1}"
74 # current number of seconds since epoch
88 log
"Log file: $LOG_FILE"
89 start_time
="$(get_ctime)"
91 if ! "$@" 1>"$LOG_FILE" 2>&1; then
92 log
"Command FAILED. Check full logs for details."
96 stop_time
="$(get_ctime)"
97 log
"SUCCESS ($((stop_time - start_time)) seconds)"
100 function fcreate_secure
{
107 # API compare version.
108 function get_api_diff_version
{
112 rev=$
(echo "$version" | cut
-d .
-f 3)
113 if [ "$rev" != 0 ]; then
115 short_version
="$(echo "$version" | cut -d . -f 1-2)"
116 api_diff_tag
="rel/${short_version}.$((rev - 1))"
119 major
="$(echo "$version" | cut -d . -f 1)"
120 minor
="$(echo "$version" | cut -d . -f 2)"
121 if [ "$minor" != 0 ]; then
122 api_diff_tag
="rel/${major}.$((minor - 1)).0"
124 api_diff_tag
="rel/$((major - 1)).0.0"
127 api_diff_tag
="$(read_config "api_diff_tag
" "$api_diff_tag")"
131 # Get all branches that begin with 'branch-', the hbase convention for
132 # release branches, sort them and then pop off the most recent.
133 function get_release_info
{
134 PROJECT
="$(read_config "PROJECT
" "$PROJECT")"
137 if [[ -z "${ASF_REPO}" ]]; then
138 ASF_REPO
="https://gitbox.apache.org/repos/asf/${PROJECT}.git"
140 if [[ -z "${ASF_REPO_WEBUI}" ]]; then
141 ASF_REPO_WEBUI
="https://gitbox.apache.org/repos/asf?p=${PROJECT}.git"
143 if [[ -z "${ASF_GITHUB_REPO}" ]]; then
144 ASF_GITHUB_REPO
="https://github.com/apache/${PROJECT}"
146 if [ -z "$GIT_BRANCH" ]; then
147 # If no branch is specified, find out the latest branch from the repo.
148 GIT_BRANCH
="$(git ls-remote --heads "$ASF_REPO" |
149 grep refs/heads/branch- |
156 GIT_BRANCH
="$(read_config "GIT_BRANCH
" "$GIT_BRANCH")"
159 # Find the current version for the branch.
161 version
="$(curl -s "$ASF_REPO_WEBUI;a
=blob_plain
;f
=pom.xml
;hb
=refs
/heads
/$GIT_BRANCH" |
163 log
"Current branch VERSION is $version."
165 NEXT_VERSION
="$version"
167 SHORT_VERSION
="$(echo "$version" | cut -d . -f 1-2)"
168 if [[ ! "$version" =~ .
*-SNAPSHOT ]]; then
169 RELEASE_VERSION
="$version"
171 RELEASE_VERSION
="${version/-SNAPSHOT/}"
175 REV
="$(echo "${RELEASE_VERSION}" | cut -d . -f 3)"
177 # Find out what RC is being prepared.
178 # - If the current version is "x.y.0", then this is RC0 of the "x.y.0" release.
179 # - If not, need to check whether the previous version has been already released or not.
180 # - If it has, then we're building RC0 of the current version.
181 # - If it has not, we're building the next RC of the previous version.
182 if [[ -z "${RC_COUNT}" ]]; then
184 if [ "$REV" != 0 ]; then
185 local PREV_REL_REV
=$
((REV
- 1))
186 PREV_REL_TAG
="rel/${SHORT_VERSION}.${PREV_REL_REV}"
187 if git ls-remote
--tags "$ASF_REPO" "$PREV_REL_TAG" |
grep -q "refs/tags/${PREV_REL_TAG}$" ; then
190 NEXT_VERSION
="${SHORT_VERSION}.${REV}-SNAPSHOT"
192 RELEASE_VERSION
="${SHORT_VERSION}.${PREV_REL_REV}"
193 RC_COUNT
="$(git ls-remote --tags "$ASF_REPO" "${RELEASE_VERSION}RC
*" | wc -l)"
194 # This makes a 'number' of it.
195 RC_COUNT
=$
((RC_COUNT
))
199 NEXT_VERSION
="${SHORT_VERSION}.${REV}-SNAPSHOT"
204 RELEASE_VERSION
="$(read_config "RELEASE_VERSION
" "$RELEASE_VERSION")"
205 NEXT_VERSION
="$(read_config "NEXT_VERSION
" "$NEXT_VERSION")"
206 export RELEASE_VERSION NEXT_VERSION
208 RC_COUNT
="$(read_config "RC_COUNT
" "$RC_COUNT")"
209 if [[ -z "${RELEASE_TAG}" ]]; then
210 RELEASE_TAG
="${RELEASE_VERSION}RC${RC_COUNT}"
211 RELEASE_TAG
="$(read_config "RELEASE_TAG
" "$RELEASE_TAG")"
214 # Check if the RC already exists, and if re-creating the RC, skip tag creation.
216 if git ls-remote
--tags "$ASF_REPO" "$RELEASE_TAG" |
grep -q "refs/tags/${RELEASE_TAG}$" ; then
217 read -r -p "$RELEASE_TAG already exists. Continue anyway [y/n]? " ANSWER
218 if [ "$ANSWER" != "y" ]; then
225 export RELEASE_TAG SKIP_TAG
227 GIT_REF
="$RELEASE_TAG"
229 log
"This is a dry run. If tag does not actually exist, please confirm the ref that will be built for testing."
230 GIT_REF
="$(read_config "GIT_REF
" "$GIT_REF")"
234 API_DIFF_TAG
="$(get_api_diff_version "$RELEASE_VERSION")"
236 # Gather some user information.
237 ASF_USERNAME
="$(read_config "ASF_USERNAME
" "$LOGNAME")"
239 GIT_NAME
="$(git config user.name || echo "")"
240 GIT_NAME
="$(read_config "GIT_NAME
" "$GIT_NAME")"
242 GIT_EMAIL
="$ASF_USERNAME@apache.org"
243 if [[ -z "${GPG_KEY}" ]]; then
244 GPG_KEY
="$(read_config "GPG_KEY
" "$GIT_EMAIL")"
246 if ! GPG_KEY_ID
=$
("${GPG}" "${GPG_ARGS[@]}" --keyid-format 0xshort --list-public-key "${GPG_KEY}" |
grep "\[S\]" |
grep -o "0x[0-9A-F]*") ||
247 [ -z "${GPG_KEY_ID}" ] ; then
248 GPG_KEY_ID
=$
("${GPG}" "${GPG_ARGS[@]}" --keyid-format 0xshort --list-public-key "${GPG_KEY}" |
head -n 1 |
grep -o "0x[0-9A-F]*" || true
)
250 read -r -p "Does the GPG key '${GPG_KEY}' corresponds to the GPG key id '${GPG_KEY_ID}'. Is this correct [y/n]? " ANSWER
251 if [ "$ANSWER" = "y" ]; then
252 GPG_KEY
="${GPG_KEY_ID}"
254 export API_DIFF_TAG ASF_USERNAME GIT_NAME GIT_EMAIL GPG_KEY
259 GIT_BRANCH: $GIT_BRANCH
260 RELEASE_VERSION: $RELEASE_VERSION
261 NEXT_VERSION: $NEXT_VERSION
262 RELEASE_TAG: $RELEASE_TAG $([[ "$GIT_REF" != "$RELEASE_TAG" ]] && printf "\n%s\n" "GIT_REF: $GIT_REF")
263 API_DIFF_TAG: $API_DIFF_TAG
264 ASF_USERNAME: $ASF_USERNAME
267 GIT_EMAIL: $GIT_EMAIL
268 DRY_RUN: $(is_dry_run && echo "yes" || echo "NO, THIS BUILD WILL BE PUBLISHED!")
272 read -r -p "Is this info correct [y/n]? " ANSWER
273 if [ "$ANSWER" != "y" ]; then
277 GPG_ARGS
=("${GPG_ARGS[@]}" --local-user "${GPG_KEY}")
279 if ! is_dry_run
; then
280 if [ -z "$ASF_PASSWORD" ]; then
281 stty
-echo && printf "ASF_PASSWORD: " && read -r ASF_PASSWORD
&& printf '\n' && stty
echo
284 ASF_PASSWORD
="***INVALID***"
290 function is_dry_run
{
298 function check_get_passwords
{
300 if [ -z "${!env}" ]; then
301 log
"The environment variable $env is not set. Please enter the password or passphrase."
303 # shellcheck disable=SC2229
304 stty
-echo && printf "%s : " "$env" && read -r "$env" && printf '\n' && stty
echo
306 # shellcheck disable=SC2163
311 function check_needed_vars
{
314 if [ -z "${!env}" ]; then
315 log
"$env must be set to run this script"
318 # shellcheck disable=SC2163
322 (( missing
> 0 )) && exit_with_usage
326 function init_locale
{
330 Darwin
*) locale_value
="en_US.UTF-8";;
331 Linux
*) locale_value
="C.UTF-8";;
332 *) error
"unknown OS";;
334 export LC_ALL
="$locale_value"
335 export LANG
="$locale_value"
338 # Initializes JAVA_VERSION to the version of the JVM in use.
340 if [ -z "$JAVA_HOME" ]; then
341 error
"JAVA_HOME is not set."
343 JAVA_VERSION
=$
("${JAVA_HOME}"/bin
/javac
-version 2>&1 | cut
-d " " -f 2)
344 log
"java version: $JAVA_VERSION"
348 function init_python
{
349 if ! [ -x "$(command -v python2)" ]; then
350 error
'python2 needed by yetus. Install or add link? E.g: sudo ln -sf /usr/bin/python2.7 /usr/local/bin/python2'
352 log
"python version: $(python2 --version)"
357 if [ -n "$MAVEN_HOME" ]; then
358 MVN
=("${MAVEN_HOME}/bin/mvn")
359 elif [ "$(type -P mvn)" ]; then
362 error
"MAVEN_HOME is not set nor is mvn on the current path."
367 echo -n "mvn version: "
368 "${MVN[@]}" --version
372 function init_yetus
{
373 declare YETUS_VERSION
374 if [ -z "${YETUS_HOME}" ]; then
375 error
"Missing Apache Yetus."
377 # Work around yetus bug by asking test-patch for the version instead of rdm.
378 YETUS_VERSION
=$
("${YETUS_HOME}/bin/test-patch" --version)
379 log
"Apache Yetus version ${YETUS_VERSION}"
382 function configure_maven
{
383 # Add timestamps to mvn logs.
384 MAVEN_OPTS
="-Dorg.slf4j.simpleLogger.showDateTime=true -Dorg.slf4j.simpleLogger.dateTimeFormat=HH:mm:ss ${MAVEN_OPTS}"
385 # Suppress gobs of "Download from central:" messages
386 MAVEN_OPTS
="-Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn ${MAVEN_OPTS}"
387 MAVEN_LOCAL_REPO
="${REPO:-$(pwd)/$(mktemp -d hbase-repo-XXXXX)}"
388 [[ -d "$MAVEN_LOCAL_REPO" ]] || mkdir
-p "$MAVEN_LOCAL_REPO"
389 MAVEN_SETTINGS_FILE
="${MAVEN_LOCAL_REPO}/tmp-settings.xml"
390 MVN
=("${MVN[@]}" --settings "${MAVEN_SETTINGS_FILE}")
391 export MVN MAVEN_OPTS MAVEN_SETTINGS_FILE MAVEN_LOCAL_REPO
392 export ASF_USERNAME ASF_PASSWORD
393 # reference passwords from env rather than storing in the settings.xml file.
394 cat <<'EOF' > "$MAVEN_SETTINGS_FILE"
395 <?xml version="1.0" encoding="UTF-8"?>
396 <settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
397 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
398 xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
399 <localRepository>/${env.MAVEN_LOCAL_REPO}</localRepository>
401 <server><id>apache.snapshots.https</id><username>${env.ASF_USERNAME}</username>
402 <password>${env.ASF_PASSWORD}</password></server>
403 <server><id>apache.releases.https</id><username>${env.ASF_USERNAME}</username>
404 <password>${env.ASF_PASSWORD}</password></server>
409 <activeByDefault>true</activeByDefault>
412 <gpg.keyname>${env.GPG_KEY}</gpg.keyname>
420 # clone of the repo, deleting anything that exists in the working directory named after the project.
421 # optionally with auth details for pushing.
422 function git_clone_overwrite
{
424 if [ -z "${PROJECT}" ] || [ "${PROJECT}" != "${PROJECT#/}" ]; then
425 error
"Project name must be defined and not start with a '/'. PROJECT='${PROJECT}'"
429 if [[ -z "${GIT_REPO}" ]]; then
430 asf_repo
="gitbox.apache.org/repos/asf/${PROJECT}.git"
431 log
"Clone will be of the gitbox repo for ${PROJECT}."
432 if [ -n "${ASF_USERNAME}" ] && [ -n "${ASF_PASSWORD}" ]; then
434 encoded_username
=$
(python
-c "import urllib; print urllib.quote('''$ASF_USERNAME''', '')")
435 encoded_password
=$
(python
-c "import urllib; print urllib.quote('''$ASF_PASSWORD''', '')")
436 GIT_REPO
="https://$encoded_username:$encoded_password@${asf_repo}"
438 GIT_REPO
="https://${asf_repo}"
441 log
"Clone will be of provided git repo."
443 # N.B. we use the shared flag because the clone is short lived and if a local repo repo was
444 # given this will let us refer to objects there directly instead of hardlinks or copying.
445 # The option is silently ignored for non-local repositories. see the note on git help clone
446 # for the --shared option for details.
447 git clone
--shared -b "${GIT_BRANCH}" -- "${GIT_REPO}" "${PROJECT}"
448 # If this was a host local git repo then add in an alternates and remote that will
449 # work back on the host if the RM needs to do any post-processing steps, i.e. pushing the git tag
450 # for more info see 'git help remote' and 'git help repository-layout'.
451 if [ -n "$HOST_GIT_REPO" ]; then
452 echo "${HOST_GIT_REPO}/objects" >> "${PROJECT}/.git/objects/info/alternates"
453 (cd "${PROJECT}"; git remote add
host "${HOST_GIT_REPO}")
457 function start_step
{
459 if [ -z "${name}" ]; then
460 name
="${FUNCNAME[1]}"
462 log
"${name} start" >&2
470 if [ -z "${name}" ]; then
471 name
="${FUNCNAME[1]}"
473 stop_time
="$(get_ctime)"
474 log
"${name} stop ($((stop_time - start_time)) seconds)"
477 # Writes report into cwd!
478 # TODO should have option for maintenance release that include LimitedPrivate in report
479 function generate_api_report
{
481 local previous_tag
="$2"
482 local release_tag
="$3"
483 local previous_version
485 timing_token
="$(start_step)"
486 # Generate api report.
487 # Filter out some jar types. Filters are tricky. Python regex on
488 # file basename. Exclude the saved-aside original jars... they are
489 # not included in resulting artifact. Also, do not include the
490 # hbase-shaded-testing-util.* jars. This jar is unzip'able on mac
491 # os x as is because has it a META_INF/LICENSE file and then a
492 # META_INF/license directory for the included jar's licenses;
493 # it fails to unjar on mac os x which this tool does making its checks
494 # (Its exclusion should be fine; it is just an aggregate of other jars).
495 "${project}"/dev-support
/checkcompatibility.py
--annotation \
496 org.apache.yetus.audience.InterfaceAudience.Public \
497 -e "original-hbase.*.jar" \
498 -e "hbase-shaded-testing-util.*.jar" \
499 "$previous_tag" "$release_tag"
500 previous_version
="$(echo "${previous_tag}" | sed -e 's/rel\///')"
501 cp "${project}/target/compat-check/report.html" "./api_compare_${previous_version}_to_${release_tag}.html"
502 stop_step
"${timing_token}"
505 # Look up the Jira name associated with project.
506 # Returns result on stdout.
507 # Currently all the 'hbase-*' projects share the same HBASE jira name. This works because,
508 # by convention, the HBASE jira "Fix Version" field values have the sub-project name pre-pended,
509 # as in "hbase-operator-tools-1.0.0".
510 # TODO: For non-hbase-related projects, enhance this to use Jira API query instead of text lookup.
511 function get_jira_name
{
515 hbase
*) jira_name
="HBASE";;
518 if [[ -z "$jira_name" ]]; then
519 error
"Sorry, can't determine the Jira name for project $project"
524 # Update the CHANGES.md
525 # DOES NOT DO COMMITS! Caller should do that.
526 # requires yetus to have a defined home already.
527 # yetus requires python2 to be on the path.
528 function update_releasenotes
{
529 local project_dir
="$1"
530 local jira_fix_version
="$2"
533 timing_token
="$(start_step)"
534 changelog
="CHANGELOG.${jira_fix_version}.md"
535 releasenotes
="RELEASENOTES.${jira_fix_version}.md"
536 if [ -f ${changelog} ]; then
539 if [ -f ${releasenotes} ]; then
542 jira_project
="$(get_jira_name "$
(basename "$project_dir")")"
543 "${YETUS_HOME}/bin/releasedocmaker" -p "${jira_project}" --fileversions -v "${jira_fix_version}" \
544 -l --sortorder=newer
--skip-credits || true
545 # First clear out the changes written by previous RCs.
546 if [ -f "${project_dir}/CHANGES.md" ]; then
548 "/^## Release ${jira_fix_version}/,/^## Release/ {//!d; /^## Release ${jira_fix_version}/d;}" \
549 "${project_dir}/CHANGES.md" || true
551 if [ -f "${project_dir}/RELEASENOTES.md" ]; then
553 "/^# ${jira_project} ${jira_fix_version} Release Notes/,/^# ${jira_project}/{//!d; /^# ${jira_project} ${jira_fix_version} Release Notes/d;}" \
554 "${project_dir}/RELEASENOTES.md" || true
557 # Yetus will not generate CHANGES if no JIRAs fixed against the release version
558 # (Could happen if a release were bungled such that we had to make a new one
560 if [ ! -f "${changelog}" ]; then
561 echo -e "## Release ${jira_fix_version} - Unreleased (as of `date`)\nNo changes\n" > "${changelog}"
563 if [ ! -f "${releasenotes}" ]; then
564 echo -e "# hbase ${jira_fix_version} Release Notes\nNo changes\n" > "${releasenotes}"
567 # The releasedocmaker call above generates RELEASENOTES.X.X.X.md and CHANGELOG.X.X.X.md.
568 if [ -f "${project_dir}/CHANGES.md" ]; then
569 # To insert into project's CHANGES.md...need to cut the top off the
570 # CHANGELOG.X.X.X.md file removing license and first line and then
571 # insert it after the license comment closing where we have a
572 # DO NOT REMOVE marker text!
573 sed -i -e '/## Release/,$!d' "${changelog}"
574 sed -i -e '2,${/^# HBASE Changelog/d;}' "${project_dir}/CHANGES.md"
575 sed -i -e "/DO NOT REMOVE/r ${changelog}" "${project_dir}/CHANGES.md"
577 mv "${changelog}" "${project_dir}/CHANGES.md"
579 if [ -f "${project_dir}/RELEASENOTES.md" ]; then
580 # Similar for RELEASENOTES but slightly different.
581 sed -i -e '/Release Notes/,$!d' "${releasenotes}"
582 sed -i -e '2,${/^# RELEASENOTES/d;}' "${project_dir}/RELEASENOTES.md"
583 sed -i -e "/DO NOT REMOVE/r ${releasenotes}" "${project_dir}/RELEASENOTES.md"
585 mv "${releasenotes}" "${project_dir}/RELEASENOTES.md"
587 stop_step
"${timing_token}"
591 # Takes as arguments first the project name -- e.g. hbase or hbase-operator-tools
592 # -- and then the version string. Expects to find checkout adjacent to this script
593 # named for 'project', the first arg passed.
594 # Expects the following three defines in the environment:
595 # - GPG needs to be defined, with the path to GPG: defaults 'gpg'.
596 # - GIT_REF which is the tag to create the tgz from: defaults to 'master'.
598 # $ GIT_REF="master" make_src_release hbase-operator-tools 1.0.0
600 # Tar up the src and sign and hash it.
603 local base_name
="${project}-${version}"
605 timing_token
="$(start_step)"
606 rm -rf "${base_name}"-src*
607 tgz
="${base_name}-src.tar.gz"
608 cd "${project}" ||
exit
610 git archive
--format=tar.gz
--output="../${tgz}" --prefix="${base_name}/" "${GIT_REF:-master}"
612 $GPG "${GPG_ARGS[@]}" --armor --output "${tgz}.asc" --detach-sig "${tgz}"
613 $GPG "${GPG_ARGS[@]}" --print-md SHA512 "${tgz}" > "${tgz}.sha512"
614 stop_step
"${timing_token}"
617 # Make binary release.
618 # Takes as arguments first the project name -- e.g. hbase or hbase-operator-tools
619 # -- and then the version string. Expects to find checkout adjacent to this script
620 # named for 'project', the first arg passed.
621 # Expects the following three defines in the environment:
622 # - GPG needs to be defined, with the path to GPG: defaults 'gpg'.
623 # - GIT_REF which is the tag to create the tgz from: defaults to 'master'.
624 # - MVN Default is "mvn -B --settings $MAVEN_SETTINGS_FILE".
626 # $ GIT_REF="master" make_src_release hbase-operator-tools 1.0.0
627 make_binary_release
() {
630 local base_name
="${project}-${version}"
632 timing_token
="$(start_step)"
633 rm -rf "${base_name}"-bin*
634 cd "$project" ||
exit
637 # Three invocations of maven. This seems to work. One to
638 # populate the repo, another to build the site, and then
639 # a third to assemble the binary artifact. Trying to do
640 # all in the one invocation fails; a problem in our
641 # assembly spec to in maven. TODO. Meantime, three invocations.
642 "${MVN[@]}" clean
install -DskipTests
643 "${MVN[@]}" site
-DskipTests
645 "${MVN[@]}" install assembly
:single
-DskipTests -Dcheckstyle.skip
=true
"${PUBLISH_PROFILES[@]}"
647 # Check there is a bin gz output. The build may not produce one: e.g. hbase-thirdparty.
648 local f_bin_prefix
="./${PROJECT}-assembly/target/${base_name}"
649 if ls "${f_bin_prefix}"*-bin.
tar.gz
&>/dev
/null
; then
650 cp "${f_bin_prefix}"*-bin.
tar.gz ..
652 for i
in "${base_name}"*-bin.
tar.gz
; do
653 "${GPG}" "${GPG_ARGS[@]}" --armour --output "${i}.asc" --detach-sig "${i}"
654 "${GPG}" "${GPG_ARGS[@]}" --print-md SHA512 "${i}" > "${i}.sha512"
658 log
"No ${f_bin_prefix}*-bin.tar.gz product; expected?"
661 stop_step
"${timing_token}"
664 # "Wake up" the gpg agent so it responds properly to maven-gpg-plugin, and doesn't cause timeout.
665 # Specifically this is done between invocation of 'mvn site' and 'mvn assembly:single', because
666 # the 'site' build takes long enough that the gpg-agent does become unresponsive and the following
667 # 'assembly' build (where gpg signing occurs) experiences timeout, without this "kick".
668 function kick_gpg_agent
{
669 # All that's needed is to run gpg on a random file
670 # TODO could we just call gpg-connect-agent /bye
673 echo "This is a test file" > "$i"
674 "${GPG}" "${GPG_ARGS[@]}" --armour --output "${i}.asc" --detach-sig "${i}"
678 # Do maven command to set version into local pom
679 function maven_set_version
{ #input: <version_to_set>
680 local this_version
="$1"
681 log
"${MVN[@]}" versions
:set -DnewVersion="$this_version"
682 "${MVN[@]}" versions
:set -DnewVersion="$this_version" |
grep -v "no value" # silence logs
685 # Do maven command to read version from local pom
686 function maven_get_version
{
687 # shellcheck disable=SC2016
688 "${MVN[@]}" -q -N -Dexec.executable
="echo" -Dexec.args
='${project.version}' exec:exec
691 # Do maven deploy to snapshot or release artifact repository, with checks.
692 function maven_deploy
{ #inputs: <snapshot|release> <log_file_path>
694 # Invoke with cwd=$PROJECT
695 local deploy_type
="$1"
696 local mvn_log_file
="$2" #secondary log file used later to extract staged_repo_id
697 if [[ "$deploy_type" != "snapshot" && "$deploy_type" != "release" ]]; then
698 error
"unrecognized deploy type, must be 'snapshot'|'release'"
700 if [[ -z "$mvn_log_file" ]] ||
! touch "$mvn_log_file"; then
701 error
"must provide writable maven log output filepath"
703 timing_token
=$
(start_step
)
704 # shellcheck disable=SC2153
705 if [[ "$deploy_type" == "snapshot" ]] && ! [[ "$RELEASE_VERSION" =~
-SNAPSHOT$
]]; then
706 error
"Snapshots must have a version with suffix '-SNAPSHOT'; you gave version '$RELEASE_VERSION'"
707 elif [[ "$deploy_type" == "release" ]] && [[ "$RELEASE_VERSION" =~ SNAPSHOT
]]; then
708 error
"Non-snapshot release version must not include the word 'SNAPSHOT'; you gave version '$RELEASE_VERSION'"
710 # Publish ${PROJECT} to Maven repo
711 # shellcheck disable=SC2154
712 log
"Publishing ${PROJECT} checkout at '$GIT_REF' ($git_hash)"
713 log
"Publish version is $RELEASE_VERSION"
714 # Coerce the requested version
715 maven_set_version
"$RELEASE_VERSION"
716 # Prepare for signing
718 declare -a mvn_goals
=(clean
)
719 if ! is_dry_run
; then
720 mvn_goals
=("${mvn_goals[@]}" deploy
)
722 log
"${MVN[@]}" -DskipTests -Dcheckstyle.skip=true "${PUBLISH_PROFILES[@]}" "${mvn_goals[@]}"
723 log
"Logging to ${mvn_log_file}. This will take a while..."
724 rm -f "$mvn_log_file"
725 # The tortuous redirect in the next command allows mvn's stdout and stderr to go to mvn_log_file,
726 # while also sending stderr back to the caller.
727 # shellcheck disable=SC2094
728 if ! "${MVN[@]}" -DskipTests -Dcheckstyle.skip
=true
"${PUBLISH_PROFILES[@]}" \
729 "${mvn_goals[@]}" 1>> "$mvn_log_file" 2> >( tee -a "$mvn_log_file" >&2 ); then
730 error
"Deploy build failed, for details see log at '$mvn_log_file'."
733 stop_step
"${timing_token}"
740 function get_host_os
() {
741 uname
-s |
tr '[:lower:]' '[:upper:]'