blob: c4d8d351b5bd0a245dce1d15ea784f955fc50105 [file]
#!/bin/bash
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
# The following permissions are required to successfully execute this script:
# permissions:
# issues: write # to close milestone (script will silently fail if not set)
# contents: write # to commit
# pull-requests: write # to create merge PR
#
# This script expects the github action to have checked out the repository with fetch-depth: 0
set_value_or_error() {
local value="$1"
local defaultValue="$2"
local variableName="$3"
local validValues="${4:-}" # optional argument (if empty, skip validation)
if [[ -z "$value" && -z "$defaultValue" ]]; then
echo "ERROR: A value for $variableName is required." >&2
exit 1
fi
if [[ -n "$value" ]]; then
decidedValue="$value"
else
echo "${variableName}: Using default value: ${defaultValue}"
decidedValue="$defaultValue"
fi
if [[ -n "$validValues" ]]; then
local match=false
local v
for v in $validValues; do
if [[ "$decidedValue" == "$v" ]]; then
match=true
break
fi
done
if ! $match; then
echo "ERROR: Invalid value for '$variableName': '$decidedValue'." >&2
echo " Must be one of: $validValues" >&2
return 1
fi
fi
eval "export $variableName=\"\$decidedValue\""
}
set -e
set_value_or_error "${GIT_USER_NAME}" "${GITHUB_ACTOR}" "GIT_USER_NAME"
set_value_or_error "${GITHUB_WORKSPACE}" "." "GIT_SAFE_DIR"
# for testing: git remote get-url origin | sed -E 's#(git@|https://)github.com[:/](.*).git#\2#'
set_value_or_error "${GITHUB_REPOSITORY}" "" "GITHUB_REPOSITORY"
set_value_or_error "${GITHUB_TOKEN}" "" "GITHUB_TOKEN"
set_value_or_error "${GITHUB_EVENT_PATH}" "" "GITHUB_EVENT_PATH"
set_value_or_error "${GITHUB_API_URL}" "" "GITHUB_API_URL"
set_value_or_error "${RELEASE_VERSION}" "${GITHUB_REF#refs/*/}" "RELEASE_VERSION"
set_value_or_error "${RELEASE_TAG_PREFIX}" "v" "RELEASE_TAG_PREFIX"
set_value_or_error "${PROPERTY_FILE_NAME}" "gradle.properties" "PROPERTY_FILE_NAME"
label_failure_message=""
pr_failure_message=""
echo "::group::Determine release version"
if [[ ! "${RELEASE_VERSION}" =~ ^(${RELEASE_TAG_PREFIX})?[^.]+\.[^.]+\.[^.]+$ ]]; then
echo "ERROR: RELEASE_VERSION must be in the format 'X.X.X' or '${RELEASE_TAG_PREFIX}X.X.X'. Got: '${RELEASE_VERSION}'"
exit 1
fi
if [[ $RELEASE_VERSION == "${RELEASE_TAG_PREFIX}"* ]]; then
RELEASE_VERSION=${RELEASE_VERSION:${#RELEASE_TAG_PREFIX}}
else
RELEASE_VERSION="${RELEASE_VERSION}"
fi
echo "Release Version: ${RELEASE_VERSION}"
echo "::endgroup::"
echo "::group::Close Milestone (if it exists)"
set +e
echo -n "Retrieving current milestone number: "
milestone_number=`curl -s ${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/milestones | jq -c ".[] | select (.title == \"${RELEASE_VERSION}\") | .number" | sed -e 's/"//g'`
echo "Found Milestone Id: $milestone_number"
echo "Closing current milestone"
curl -s --request PATCH -H "Authorization: Bearer ${GITHUB_TOKEN}" -H "Content-Type: application/json" "${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/milestones/$milestone_number" --data '{"state":"closed"}'
set -e
echo "::endgroup::"
echo "::group::Determine next version"
echo -n "Next Version: "
export NEXT_VERSION=`/increment_version.sh -p ${RELEASE_VERSION}`
echo "${NEXT_VERSION}"
echo "NEXT_VERSION=${NEXT_VERSION}" >> $GITHUB_OUTPUT
echo "::endgroup::"
echo "::group::Configure Git"
git config --global --add safe.directory "${GIT_SAFE_DIR}"
git config --global user.email "${GIT_USER_NAME}@users.noreply.github.com"
git config --global user.name "${GIT_USER_NAME}"
git config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*"
git fetch
echo "::endgroup::"
echo "::group::Determine target merge branch"
set_value_or_error "${TARGET_BRANCH}" "$(jq -r 'if has("release") and .release.target_commitish != null then .release.target_commitish else "" end' "$GITHUB_EVENT_PATH")" "TARGET_BRANCH"
echo "Target Branch is ${TARGET_BRANCH}"
TARGET_BRANCH="${TARGET_BRANCH#refs/*/}"
echo "Pruned Target Branch is ${TARGET_BRANCH}"
git checkout "${TARGET_BRANCH}"
echo "::endgroup::"
echo "::group::Creating Merge Branch from ${RELEASE_TAG_PREFIX}${RELEASE_VERSION}"
MERGE_BRANCH_NAME="merge-back-${RELEASE_VERSION}"
git checkout -b "${MERGE_BRANCH_NAME}" "${RELEASE_TAG_PREFIX}${RELEASE_VERSION}"
echo "::endgroup::"
echo "::group::Add commit to update to next version"
echo "Setting new snapshot version"
sed -i "s/^projectVersion\=.*$/projectVersion\=${NEXT_VERSION}-SNAPSHOT/" "${PROPERTY_FILE_NAME}"
sed -i "s/^version\=.*$/version\=${NEXT_VERSION}-SNAPSHOT/" "${PROPERTY_FILE_NAME}"
cat "${PROPERTY_FILE_NAME}"
printf "\n"
git add "${PROPERTY_FILE_NAME}"
if [[ -n "${RELEASE_SCRIPT_PATH}" && -x "${GITHUB_WORKSPACE}/${RELEASE_SCRIPT_PATH}" ]]; then
echo "Executing additional release script at ${GITHUB_WORKSPACE}/${RELEASE_SCRIPT_PATH}"
"${GITHUB_WORKSPACE}/${RELEASE_SCRIPT_PATH}"
else
if [[ -n "${RELEASE_SCRIPT_PATH}" ]]; then
echo "ERROR: RELEASE_SCRIPT_PATH is set to '${RELEASE_SCRIPT_PATH}' but is not executable or does not exist." >&2
exit 1
fi
fi
if git diff-index --quiet HEAD --; then
git checkout --
echo "::notice:: No version change – nothing to commit, skipping commit."
else
echo "Committing next version ${NEXT_VERSION}-SNAPSHOT"
git commit -m "[skip ci] Bump version to ${NEXT_VERSION}-SNAPSHOT"
fi
echo "::endgroup::"
echo "::group::Pushing Merge Branch ${MERGE_BRANCH_NAME}"
echo "Pushing changes to side branch: ${MERGE_BRANCH_NAME}"
git push --force origin "${MERGE_BRANCH_NAME}"
echo "::endgroup::"
echo "::group::Open/Reuse pull request"
PR_TITLE="chore: merge ${RELEASE_VERSION}->${TARGET_BRANCH}; bump to ${NEXT_VERSION}-SNAPSHOT"
PR_BODY="Automated snapshot bump after completing release ${RELEASE_VERSION}"
PR_REFERENCE="${MERGE_BRANCH_NAME}"
if ! gh pr create \
--title "${PR_TITLE}" \
--body "${PR_BODY}" \
--base "${TARGET_BRANCH}" \
--head "${MERGE_BRANCH_NAME}" \
--fill >/tmp/pr-url 2>/tmp/pr-err; then
echo "PR creation failed. Checking for an existing PR:" >&2
cat /tmp/pr-err
if PR_REFERENCE="$(gh pr view "${MERGE_BRANCH_NAME}" --web 2>/tmp/pr-view-err)"; then
echo "${PR_REFERENCE}"
else
cat /tmp/pr-view-err >&2
pr_failure_message="ERROR: Merge-back branch ${MERGE_BRANCH_NAME} was pushed, but pull request creation failed and no existing PR could be found. Create the PR manually."
echo "${pr_failure_message}" >&2
PR_REFERENCE=""
fi
else
PR_REFERENCE="$(cat /tmp/pr-url)"
echo "${PR_REFERENCE}"
fi
if [[ -n "${PR_REFERENCE}" && -n "${PR_LABELS}" ]]; then
IFS=',' read -r -a pr_labels <<< "${PR_LABELS}"
label_args=()
for pr_label in "${pr_labels[@]}"; do
trimmed_label="$(printf '%s' "${pr_label}" | xargs)"
if [[ -n "${trimmed_label}" ]]; then
label_args+=(--add-label "${trimmed_label}")
fi
done
if [[ ${#label_args[@]} -gt 0 ]]; then
echo "Applying pull request labels: ${PR_LABELS}"
if ! gh pr edit "${PR_REFERENCE}" "${label_args[@]}"; then
label_failure_message="WARNING: Pull request ${PR_REFERENCE} was created or reused, but one or more labels could not be applied. Verify that all requested labels exist: ${PR_LABELS}"
echo "${label_failure_message}" >&2
fi
fi
fi
echo "::endgroup::"
echo "::group::Update Release Status"
release_update_payload=()
if [[ -n "${RELEASE_PRE_RELEASE}" ]]; then
if [[ "${RELEASE_PRE_RELEASE}" != "true" && "${RELEASE_PRE_RELEASE}" != "false" ]]; then
echo "ERROR: RELEASE_PRE_RELEASE must be 'true' or 'false' if set. Got: '${RELEASE_PRE_RELEASE}'" >&2
exit 1
fi
release_update_payload+=("\"prerelease\": ${RELEASE_PRE_RELEASE}")
fi
if [[ -n "${RELEASE_LATEST}" ]]; then
if [[ "${RELEASE_LATEST}" != "true" && "${RELEASE_LATEST}" != "false" ]]; then
echo "ERROR: RELEASE_LATEST must be 'true' or 'false' if set. Got: '${RELEASE_LATEST}'" >&2
exit 1
fi
release_update_payload+=("\"make_latest\": \"${RELEASE_LATEST}\"")
fi
if [[ ${#release_update_payload[@]} -eq 0 ]]; then
echo "No release flags set (RELEASE_PRE_RELEASE / RELEASE_LATEST). Skipping GitHub Release update."
else
json_payload_content=$(printf "%s, " "${release_update_payload[@]}")
json_payload_content="${json_payload_content%, }" # Remove trailing comma-space
json_payload="{${json_payload_content}}"
echo "PATCH payload: ${json_payload}"
curl -fvs --request PATCH \
-H "Authorization: Bearer ${GITHUB_TOKEN}" -H "Content-Type: application/json" \
"${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/releases/tags/${RELEASE_TAG_PREFIX}${RELEASE_VERSION}" \
--data "${json_payload}"
fi
echo "::endgroup::"
if [[ -n "${pr_failure_message}" ]]; then
echo "${pr_failure_message}" >&2
exit 1
fi
if [[ -n "${label_failure_message}" ]]; then
echo "${label_failure_message}" >&2
exit 1
fi