blob: edb4c1f82d2f7488fa22d870f0a3b5fdd18f5d28 [file] [log] [blame]
#!/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.
#
# Action will publish the specified documentation to the specified documentation branch
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 "$variableName=\"\$decidedValue\""
}
set_path_value_or_error() {
local variableName="$3"
set_value_or_error "$@"
if [[ "${decidedValue}" == /* || "${decidedValue}" == ./* ]]; then
echo "ERROR: ${variableName} must not start with '/' or './'. Got: '${decidedValue}'"
exit 1
fi
}
publish_artifacts() {
if [[ -z "${SOURCE_FOLDER}" || -z "${PUBLISH_PATH}" || -z "${BASE_PUBLISH_PATH}" ]]; then
echo "ERROR: SOURCE_FOLDER(${SOURCE_FOLDER}), PUBLISH_PATH(${PUBLISH_PATH}), BASE_PUBLISH_PATH(${BASE_PUBLISH_PATH}) must be set."
return 1
fi
if [[ "${PURGE_EXISTING}" == "true" ]]; then
local purgePath
if [[ "${PURGE_BY_BASE_PATH}" == "false" ]]; then
purgePath="${PUBLISH_PATH}"
else
purgePath="${BASE_PUBLISH_PATH}"
fi
if [[ -d "${purgePath}" ]]; then
if [[ "$(ls -A "${purgePath}" 2>/dev/null)" ]]; then
echo "Removing existing directory at ${purgePath}"
git rm -rf "${purgePath}"
fi
elif [[ -e "${purgePath}" ]]; then
echo "Removing existing file at ${purgePath}"
git rm -rf "${purgePath}"
fi
fi
echo "Publishing ${SOURCE_FOLDER} to ${DOCUMENTATION_BRANCH}:${PUBLISH_PATH}"
mkdir -p "${PUBLISH_PATH}"
cp -r "../${SOURCE_FOLDER}/." "${PUBLISH_PATH}"
git add --verbose "${PUBLISH_PATH}"/*
}
is_highest_version() {
local new_folder="$1" # e.g. "7.0.x"
# Strip the trailing ".x" → "7.0", then parse into major/minor
local new_major new_minor
local folder_no_x="${new_folder%.x}" # "7.0"
IFS="." read -r new_major new_minor <<< "$folder_no_x"
# Loop over all folders that look like 7.0.x
for folder in [0-9]*.x; do
[[ -d "$folder" ]] || continue # skip if not a real folder
# Use a regex match to parse existing folders into major/minor
if [[ "$folder" =~ ^([0-9]+)\.([0-9]+)\.x$ ]]; then
local existing_major="${BASH_REMATCH[1]}"
local existing_minor="${BASH_REMATCH[2]}"
# Compare numeric major/minor
if (( existing_major > new_major )); then
# Found a folder with a higher major
return 1
elif (( existing_major == new_major && existing_minor > new_minor )); then
# Found a folder with same major but higher minor
return 1
fi
fi
done
return 0
}
set -e
set_value_or_error "${DOCUMENTATION_BRANCH}" 'gh-pages' 'DOCUMENTATION_BRANCH'
# GH_TOKEN - the token to access the github repository, can be GITHUB_TOKEN if the same repo and permissions are set correctly
set_value_or_error "${GH_TOKEN}" "${GITHUB_TOKEN}" "GH_TOKEN"
# GITHUB_USER_NAME - the username to commit to documentation branch, defaults to GITHUB_ACTOR (assumes permissions are set correctly)
set_value_or_error "${GITHUB_USER_NAME}" "${GITHUB_ACTOR}" "GITHUB_USER_NAME"
# LAST_RELEASE_FOLDER - when a release is performed, instead of just copying it to a version number, copy it to this static folder name
set_value_or_error "${LAST_RELEASE_FOLDER}" "latest" "LAST_RELEASE_FOLDER"
# SKIP_RELEASE_FOLDER - if copying to the release folder should be skipped
set_value_or_error "${SKIP_RELEASE_FOLDER}" "false" "SKIP_RELEASE_FOLDER" "true false"
# LAST_SNAPSHOT_FOLDER - when a snapshot is performed, instead of just copying it a version number, copy it to this static folder name
set_path_value_or_error "${LAST_SNAPSHOT_FOLDER}" "snapshot" "LAST_SNAPSHOT_FOLDER"
# SKIP_SNAPSHOT_FOLDER - if copying to the snapshot folder should be skipped
set_path_value_or_error "${SKIP_SNAPSHOT_FOLDER}" "false" "SKIP_SNAPSHOT_FOLDER" "true false"
# GRADLE_PUBLISH_RELEASE - Whether the documents being published is a release or not, expects 'true', 'false' values
set_value_or_error "${GRADLE_PUBLISH_RELEASE}" "" "GRADLE_PUBLISH_RELEASE" "true false"
# TARGET_REPOSITORY - the document repository to commit to, including owner
set_value_or_error "${TARGET_REPOSITORY}" "${GITHUB_REPOSITORY}" "TARGET_REPOSITORY"
if [[ ! "$TARGET_REPOSITORY" =~ ^[^/]+/[^/]+$ ]]; then
echo "ERROR: TARGET_REPOSITORY must be in the format 'owner/repo'" >&2
exit 1
fi
# SOURCE_FOLDER - the relative path of the source documentation folder from the root of the repo
set_path_value_or_error "${SOURCE_FOLDER}" "" "SOURCE_FOLDER"
# TARGET_FOLDER - the base folder to publish documentation to
set_path_value_or_error "${TARGET_FOLDER}" "." "TARGET_FOLDER"
mkdir -p "${TARGET_FOLDER}"
# TARGET_SUBFOLDER - an optional sub folder to publish to
set_path_value_or_error "${TARGET_SUBFOLDER}" "." "TARGET_SUBFOLDER"
if [ "${TARGET_SUBFOLDER}" == "." ]; then
unset TARGET_SUBFOLDER;
fi
# PURGE_EXISTING - whether to remove the files before upload
set_value_or_error "${PURGE_EXISTING}" "true" "PURGE_EXISTING" "true false"
# PURGE_BY_BASE_PATH - sometimes it's useful to purge the base version folder, instead of the targeted nested sub folder
set_value_or_error "${PURGE_BY_BASE_PATH}" "false" "PURGE_BY_BASE_PATH" "true false"
# GITHUB_WORKSPACE - the safe directory to checkout to
set_value_or_error "${GITHUB_WORKSPACE}" "" "GITHUB_WORKSPACE"
if [[ "$SKIP_SNAPSHOT_FOLDER" == "true" && "$GRADLE_PUBLISH_RELEASE" == "false" ]]; then
echo "Snapshot detected and snapshot publishing is disabled. Skipping documentation deployment."
exit 0
fi
set_value_or_error "${GITHUB_URL_BASE}" "github.com" "GITHUB_URL_BASE"
set_value_or_error "${GIT_TRANSFER_PROTOCOL}" "https" "GIT_TRANSFER_PROTOCOL"
GIT_REPO_URL="${GIT_TRANSFER_PROTOCOL}://${GITHUB_USER_NAME}:${GH_TOKEN}@${GITHUB_URL_BASE}/${TARGET_REPOSITORY}.git"
# Initialize a Git Repository under a separate location from the existing checkout that will be the documentation branch
cd "${GITHUB_WORKSPACE}"
git init
git config --global user.email "${GITHUB_USER_NAME}@users.noreply.github.com"
git config --global user.name "${GITHUB_USER_NAME}"
git config --global http.version HTTP/1.1
git config --global http.postBuffer 157286400
# Create or checkout the documentation branch
if git ls-remote --heads "${GIT_REPO_URL}" "${DOCUMENTATION_BRANCH}" | grep -q "refs/heads/${DOCUMENTATION_BRANCH}"; then
echo "::group::Checkout documentation branch"
echo "documentation branch found, cloning"
git clone "${GIT_REPO_URL}" "${DOCUMENTATION_BRANCH}" --branch "${DOCUMENTATION_BRANCH}" --single-branch --depth 1
cd ${DOCUMENTATION_BRANCH}
echo "::endgroup::"
else
echo "::group::Creating documentation branch"
echo "Creating documentation branch ${DOCUMENTATION_BRANCH} as it does not exist"
mkdir "${DOCUMENTATION_BRANCH}"
cd "${DOCUMENTATION_BRANCH}"
git init
git checkout -b "${DOCUMENTATION_BRANCH}"
git remote add origin "${GIT_REPO_URL}"
echo "::endgroup::"
fi
# grails repos have a convention that they create a ghpages.html to replace the root index.html
if [[ -f "../${SOURCE_FOLDER}/ghpages.html" ]]; then
echo "::group::Staging root index.html"
echo "${SOURCE_FOLDER}/ghpages.html detected, replacing root index.html"
cp "../${SOURCE_FOLDER}/ghpages.html" index.html
git add index.html
echo "::endgroup::"
fi
# stage the documents
if [[ "$GRADLE_PUBLISH_RELEASE" == "false" ]]; then
echo "::group::Publishing Snapshot"
echo "Snapshot detected"
# Subfolder support
BASE_PUBLISH_PATH="${TARGET_FOLDER}/${LAST_SNAPSHOT_FOLDER}"
if [ -n "${TARGET_SUBFOLDER}" ]; then
PUBLISH_PATH="${TARGET_FOLDER}/${LAST_SNAPSHOT_FOLDER}/${TARGET_SUBFOLDER}"
else
PUBLISH_PATH="${TARGET_FOLDER}/${LAST_SNAPSHOT_FOLDER}"
fi
publish_artifacts
echo "::endgroup::"
else
echo "Release detected"
# RELEASE_TAG_PREFIX - the tag prefix to use for a release version, defaults to 'v'
set_value_or_error "${RELEASE_TAG_PREFIX}" "v" "RELEASE_TAG_PREFIX"
# VERSION - the version number of this snapshot or release, v7.0.2 will be `7.0.2`, 7.0.x will be 7.0.x
set_value_or_error "${VERSION}" "${GITHUB_REF_NAME}" "VERSION"
if [[ ! "${VERSION}" =~ ^(${RELEASE_TAG_PREFIX})?[^.]+\.[^.]+\.[^.]+$ ]]; then
echo "ERROR: VERSION must be in the format 'X.X.X' or '${RELEASE_TAG_PREFIX}X.X.X'. Got: '${VERSION}'"
exit 1
fi
if [[ $VERSION == "${RELEASE_TAG_PREFIX}"* ]]; then
VERSION=${VERSION:${#RELEASE_TAG_PREFIX}}
fi
# Publish to the specific version folder
echo "::group::Publishing Specific Release Version: ${VERSION}"
BASE_PUBLISH_PATH="${TARGET_FOLDER}/${VERSION}"
if [ -n "${TARGET_SUBFOLDER}" ]; then
PUBLISH_PATH="${TARGET_FOLDER}/${VERSION}/${TARGET_SUBFOLDER}"
else
PUBLISH_PATH="${TARGET_FOLDER}/${VERSION}"
fi
publish_artifacts
echo "Published release documentation to ${PUBLISH_PATH}"
echo "::endgroup::"
# Publish to the generic version folder
genericVersionFolder="${VERSION%.*}"
genericVersionFolder="${genericVersionFolder}.x"
echo "::group::Publishing Generic Release Version: ${genericVersionFolder}"
BASE_PUBLISH_PATH="${TARGET_FOLDER}/${genericVersionFolder}"
if [ -n "${TARGET_SUBFOLDER}" ]; then
PUBLISH_PATH="${TARGET_FOLDER}/${genericVersionFolder}/${TARGET_SUBFOLDER}"
else
PUBLISH_PATH="${TARGET_FOLDER}/${genericVersionFolder}"
fi
publish_artifacts
echo "Published release documentation to ${genericVersionFolder}"
echo "::endgroup::"
# Publish to the latest release folder if needed
if [[ "$SKIP_RELEASE_FOLDER" == "false" ]]; then
if is_highest_version "${genericVersionFolder}"; then
echo "::group::Overwriting ${LAST_RELEASE_FOLDER} with the latest release documentation"
BASE_PUBLISH_PATH="${TARGET_FOLDER}/${LAST_RELEASE_FOLDER}"
if [ -n "${TARGET_SUBFOLDER}" ]; then
PUBLISH_PATH="${TARGET_FOLDER}/${LAST_RELEASE_FOLDER}/${TARGET_SUBFOLDER}"
else
PUBLISH_PATH="${TARGET_FOLDER}/${LAST_RELEASE_FOLDER}"
fi
publish_artifacts
echo "Published a copy of documentation to ${PUBLISH_PATH}"
echo "::endgroup::"
else
echo "::group::Skipped Latest Release Documentation - not highest"
echo "Skipping documentation copy to '${LAST_RELEASE_FOLDER}' because ${genericVersionFolder} is NOT the highest."
echo "::endgroup::"
fi
else
echo "::group::Skipped Latest Release Documentation"
echo "Skipping documentation copy to ${LAST_RELEASE_FOLDER}"
echo "::endgroup::"
fi
fi
echo "::group::Committing Changes"
echo "Detected the following delta for commit:"
git status
echo "Committing changes."
git commit -m "Deploying to documentation branch - $(date +"%T")" --quiet --allow-empty
git push "${GIT_REPO_URL}" "${DOCUMENTATION_BRANCH}"
echo "Deployment successful!"
echo "::endgroup::"