dashboard/shell/scripts/extension/publish

477 lines
16 KiB
Bash
Executable File

#!/usr/bin/env bash
# set -eo pipefail comment out for now because of a Harvester issue
SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
BASE_DIR="$(pwd)"
CYAN="\033[96m"
YELLOW="\033[93m"
RESET="\033[0m"
BOLD="\033[1m"
NORMAL="\033[22m"
CHECK="\xE2\x9C\x94"
PUSH=""
REGISTRY=""
REGISTRY_ORG=""
IMAGE_PREFIX="ui-extension-"
FORCE="false"
GITHUB_BUILD="true"
GITHUB_RELEASE_TAG=""
PODMAN_CONTAINER="false"
UPDATE_OLD_PACKAGES="true"
UPDATE_LATEST_PACAKGES="true"
GITHUB_SOURCE=$(git config --get remote.origin.url | sed -e 's/^git@.*:\([[:graph:]]*\).git/\1/')
GITHUB_BRANCH="main"
usage() {
echo "Usage: $0 [<options>] [extensions]"
echo " options:"
echo " -s <repo> Specify destination GitHub repository (org/name) - defaults to the git origin"
echo " -b <branch> Specify destination GitHub branch"
echo " -t <tag> Specify the Github release tag to build when a release has been tagged and published"
echo " -f Force building the chart even if it already exists"
echo " -c Build as a container image rather than publishing to Github"
echo " -p Push container images on build"
echo " -r <name> Specify destination container registry for built images"
echo " -o <name> Specify destination container registry organization for built images"
echo " -i <prefix> Specify prefix for the built container image (default: 'ui-extension-')"
echo " -l Specify Podman container build"
echo " -n Do not check for updates to existing Charts (Chart.yaml)"
echo " -e Only check for updates to existing Charts (Chart.yaml)"
exit 1
}
while getopts "hvr:o:pi:fcb:t:s:lne" opt; do
case $opt in
h)
usage
;;
p)
PUSH="--push"
;;
r)
REGISTRY="${OPTARG%/}/"
;;
o)
REGISTRY_ORG="${OPTARG}"
;;
i)
IMAGE_PREFIX="${OPTARG}"
;;
f)
FORCE="true"
;;
c)
GITHUB_BUILD="false"
;;
s)
GITHUB_BUILD="true"
GITHUB_SOURCE="${OPTARG}"
;;
b)
GITHUB_BUILD="true"
GITHUB_BRANCH="${OPTARG}"
;;
t)
GITHUB_RELEASE_TAG="${OPTARG}"
;;
l)
PODMAN_CONTAINER="true"
;;
n)
UPDATE_OLD_PACKAGES="false"
;;
e)
UPDATE_OLD_PACKAGES="true"
UPDATE_LATEST_PACAKGES="false"
;;
*)
usage
;;
esac
done
shift $((OPTIND-1))
PLUGINS=( "$@" )
BUILT="false"
echo -e "${CYAN}${BOLD}Publishing UI Extensions${RESET}"
pushd ${BASE_DIR} > /dev/null
if [ "${GITHUB_BUILD}" == "true" ]; then
# Determine if gh-pages build is possible
if [[ "${GITHUB_BRANCH}" == "gh-pages" ]] && ! git show-ref -q gh-pages; then
echo -e "${YELLOW}'gh-pages' branch not found, this branch must exist before running this script${RESET}"
exit 1
fi
echo -e "${CYAN}GitHub Repository: ${GITHUB_SOURCE}${RESET}"
echo -e "${CYAN}GitHub Branch : ${GITHUB_BRANCH}${RESET}"
if [[ -n "${GITHUB_RELEASE_TAG}" ]]; then
echo -e "${CYAN}GitHub Release : ${GITHUB_RELEASE_TAG}${RESET}"
fi
else
echo -e ${CYAN}"Image prefix: ${IMAGE_PREFIX}${RESET}"
fi
# --------------------------------------------------------------------------------
# Check that we have the required commands avaialble for this script
# --------------------------------------------------------------------------------
if ! [[ -f ${BASE_DIR}/package.json ]]; then
echo -e "${YELLOW}You must run from the top-level folder${RESET}"
exit 1
fi
# Check this is a Rancher extension
IS_SHELL=$(grep "\"@rancher/shell" package.json -c)
if [ "${IS_SHELL}" -ne 1 ]; then
echo -e "${YELLOW}Current folder does not appear to contain Rancher Extensions${RESET}"
exit 1
fi
if ! [[ -d ${BASE_DIR}/node_modules ]]; then
echo -e "${YELLOW}You must run ${BOLD}yarn install${NORMAL} before running this script${RESET}"
exit 1
fi
COMMANDS=("node" "jq" "yq" "git" "helm" "yarn")
if [ "${GITHUB_BUILD}" == "false" ]; then
if [ "${PODMAN_CONTAINER}" == "true" ]; then
COMMANDS+=("podman")
else
COMMANDS+=("docker")
fi
fi
HAVE_COMMANDS="true"
for CMD in "${COMMANDS[@]}"
do
if ! command -v ${CMD} >/dev/null; then
echo -e "${YELLOW}This script requires ${BOLD}${CMD}${NORMAL} to be installed and on your PATH${RESET}"
HAVE_COMMANDS="false"
fi
done
if [ "${HAVE_COMMANDS}" == "false" ]; then
exit 1
fi
# --------------------------------------------------------------------------------
# Only do container args checks if not GitHub publish
# --------------------------------------------------------------------------------
if [ "${GITHUB_BUILD}" == "false" ]; then
BASE_EXT=$(jq -r .name ${BASE_DIR}/package.json)
EXT_VERSION=$(jq -r .version ${BASE_DIR}/package.json)
if [ -z ${EXT_VERSION} ]; then
EXT_VERSION="0.0.0"
fi
if [[ -z ${REGISTRY_ORG} ]]; then
# Infer that the user has the same Docker registry org as their GitHub org
GITHUB_REPO=$(git config --get remote.origin.url | sed -e 's/^git@.*:\([[:graph:]]*\).git/\1/')
REGISTRY_ORG=$(dirname ${GITHUB_REPO})
echo -e "Inferring built images will reside in registry organization ${CYAN}${BOLD}${REGISTRY}${REGISTRY_ORG}${RESET} based on configured origin remote pointing to ${CYAN}${BOLD}${GITHUB_REPO}${RESET}"
fi
if [[ -z ${REGISTRY_ORG} ]]; then
# Inferring from the git config still failed
echo "Cannot build images without valid organization for Docker images. Unable to infer REGISTRY_ORG="
exit 1
fi
if [ -n "${GITHUB_RELEASE_TAG}" ] && [[ ${GITHUB_RELEASE_TAG} != "${BASE_EXT}-${EXT_VERSION}" ]]; then
echo -e "${YELLOW}Github tagged release name does not match ${RESET}${BOLD}${BASE_EXT}-${EXT_VERSION}${RESET}"
echo -e "${YELLOW}Stopping build${RESET}"
exit 1
fi
if [ "${PODMAN_CONTAINER}" == "false" ]; then
docker images > /dev/null
if [ $? -ne 0 ]; then
echo "docker is not running - this is required to build container images for the UI Extensions"
exit 1
fi
fi
fi
ASSETS=${BASE_DIR}/assets
CHARTS=${BASE_DIR}/charts
mkdir -p ${ASSETS}
mkdir -p ${CHARTS}
TMP=${BASE_DIR}/tmp
CHART_TMP=${BASE_DIR}/tmp/_charts
rm -rf ${TMP}
mkdir -p ${TMP}
# --------------------------------------------------------------------------------
# Copy the plugin server template into the temporary folder
# --------------------------------------------------------------------------------
pushd ${TMP} > /dev/null
cp -r ${SCRIPT_DIR}/helm .
popd > /dev/null
CHART_TEMPLATE=${BASE_DIR}/tmp/helm
ROOT_INDEX=${BASE_DIR}/index.yaml
# --------------------------------------------------------------------------------
# Iterate through all existing charts and look for updates/removals
# --------------------------------------------------------------------------------
if [ "${UPDATE_OLD_PACKAGES}" = "true" ]; then
pushd ${ASSETS} > /dev/null
echo -e "${CYAN}${BOLD}Checking existing charts for updates ...${RESET}"
for FOLDER_NAME in */ ; do
EXT_NAME=${FOLDER_NAME::${#FOLDER_NAME}-1}
echo -e "${CYAN}${EXT_NAME}${RESET}"
for CHART_ARCHIVE in ${EXT_NAME}/*.tgz ; do
rm -f ${TMP}/Chart.yaml
# path in chart may be different to tar file name, so extratct the Chart.yaml file to the top-level folder
tar --strip-components=1 -C ${TMP} -xf ${CHART_ARCHIVE} */Chart.yaml
EXISTING_MD5=$(md5 -q ${TMP}/Chart.yaml)
VERSION=$(yq eval .version ${TMP}/Chart.yaml)
CHART_FOLDER=${CHARTS}/${EXT_NAME}/${VERSION}
LATEST=${CHART_FOLDER}/Chart.yaml
if [ ! -f "${LATEST}" ]; then
echo " - Chart for ${EXT}-${VERSION} appears to have been deleted - removing chart archive: ${CHART_ARCHIVE}"
rm -f ${ASSETS}/${CHART_ARCHIVE}
BUILT="true"
else
# Re-package the helm chart so that the Chart.yaml is processed in the same way so we can compare it
mkdir -p ${TMP}/tmp_chart
helm package ${CHART_FOLDER} -d ${TMP}/tmp_chart > /dev/null
rm ${TMP}/Chart.yaml
# path in chart may be different to tar file name, so list the files to find what the chart archive is named
FILENAME=$(cd ${TMP}/tmp_chart; ls)
# path in chart may be different to tar file name, so extratct the Chart.yaml file to the top-level folder
tar --strip-components=1 -C ${TMP} -xf ${TMP}/tmp_chart/${FILENAME} */Chart.yaml
LATEST_MD5=$(md5 -q ${TMP}/Chart.yaml)
rm -f $TMP/tmp_chart/${FILENAME}
if [ "${EXISTING_MD5}" != "${LATEST_MD5}" ]; then
BUILT="true"
echo " + Chart.yaml has changed for ${CHART_ARCHIVE}"
# Chart was already re-generated - update ir
rm ${CHART_ARCHIVE}
helm package ${CHART_FOLDER} -d ${ASSETS}/${EXT_NAME}
fi
fi
rm -d ${TMP}/tmp_chart
rm ${TMP}/Chart.yaml
done
done
popd > /dev/null
fi
# --------------------------------------------------------------------------------
# Iterate through all packages - built them all or build only those specified on the command line
# --------------------------------------------------------------------------------
if [ "${UPDATE_LATEST_PACAKGES}" = "true" ]; then
for d in pkg/*/ ; do
pkg=$(basename $d)
if [ -z "$1" ] || [[ " ${PLUGINS[*]} " =~ " ${pkg} " ]]; then
# Check we don't already have a published version by looking in the assets folder
PKG_VERSION=$(jq -r .version ./pkg/${pkg}/package.json)
PKG_NAME="${pkg}-${PKG_VERSION}"
PKG_ASSET=${ASSETS}/${pkg}/${PKG_NAME}.tgz
# Skip the build for a package that does not match the tagged release name
if [[ -n "${GITHUB_RELEASE_TAG}" ]] && [ ${GITHUB_BUILD} == "true" ] && [[ ${GITHUB_RELEASE_TAG} != ${PKG_NAME} ]]; then
echo -e "${YELLOW}Github release tag ${RESET}${BOLD}${GITHUB_RELEASE_TAG}${RESET}${YELLOW} does not match asset name${RESET}"
echo -e "${YELLOW}Skipping ${RESET}${BOLD}${PKG_NAME}${RESET}"
continue
fi
echo -e "${CYAN}${BOLD}Building extension: ${pkg} (${PKG_VERSION}) ${RESET}"
echo "Package version: ${PKG_VERSION}"
echo "Package folder: ${PKG_NAME}"
# --------------------------------------------------------------------------------
# Build the plugin from source
# --------------------------------------------------------------------------------
echo -e "${CYAN}Building extension from source code${RESET}"
FORCE_COLOR=1 yarn build-pkg $pkg | cat
echo -e "${CYAN}Adding extension code ...${RESET}"
EXT_FOLDER=${BASE_DIR}/extensions/${pkg}/${PKG_VERSION}
PKG_DIST="${BASE_DIR}/dist-pkg/${PKG_NAME}"
rm -rf ${EXT_FOLDER}
mkdir -p ${EXT_FOLDER}/plugin
# Copy the code into the folder
cp -R ${PKG_DIST}/* ${EXT_FOLDER}/plugin
pushd ${BASE_DIR}/extensions/${pkg}/${PKG_VERSION} > /dev/null
rm -f plugin/report.html
find plugin -type f | sort > files.txt
popd > /dev/null
# --------------------------------------------------------------------------------
# Package extensions into tgz for the compressedEndpoint
# --------------------------------------------------------------------------------
echo -e "${CYAN}Packaging extension into tgz${RESET}"
tar -czf ${BASE_DIR}/extensions/${pkg}/${PKG_VERSION}.tgz -C ${BASE_DIR}/extensions/${pkg} ${PKG_VERSION}
# --------------------------------------------------------------------------------
# Create the Helm chart
# --------------------------------------------------------------------------------
if [ -f ${PKG_ASSET} ] && [ "${FORCE}" == "false" ]; then
echo -e "${YELLOW}Helm chart has already been created - skipping (run with -f to force build)${RESET}"
continue;
fi
CHART_FOLDER=${CHARTS}/${pkg}/${PKG_VERSION}
mkdir -p ${ASSETS}/${pkg}
rm -rf ${CHART_FOLDER}
mkdir -p ${CHART_FOLDER}
cp -R ${CHART_TEMPLATE}/charts/ui-plugin-server/* ${CHART_FOLDER}
# Update Chart.yaml and values.yaml from the package file metadata
# Use the script from the template repository
echo -e "${CYAN}Patching Helm chart template${RESET}"
CHART=${CHART_FOLDER} REGISTRY="${REGISTRY}" ORG="${REGISTRY_ORG}" PACKAGE_JSON=${BASE_DIR}/pkg/${pkg}/package.json ${CHART_TEMPLATE}/scripts/patch
# Copy README file from the plugin to the Helm chart, if there is one
if [ -f "./pkg/${pkg}/README.md" ]; then
cp ./pkg/${pkg}/README.md ${CHART_FOLDER}/README.md
fi
export GITHUB_BUILD
export IMAGE_PREFIX
export BASE_EXT
export GITHUB_SOURCE
export GITHUB_BRANCH
# Additional patches
${SCRIPT_DIR}/helmpatch ${CHART_FOLDER} "${BASE_DIR}/pkg/${pkg}/package.json"
# Package into a .tgz helm chart
helm package ${CHART_FOLDER} -d ${ASSETS}/${pkg}
BUILT="true"
fi
done
fi
# Update the Helm Index for all charts, if changes were made
if [ "${BUILT}" == "true" ]; then
# --------------------------------------------------------------------------------
# Update the helm index for all charts
# --------------------------------------------------------------------------------
HELM_INDEX=${BASE_DIR}/index.yaml
echo "Updating Helm Index file"
if [ -f "${HELM_INDEX}" ]; then
UPDATE="--merge ${HELM_INDEX}"
elif [ "${GITHUB_BUILD}" == "true" ] && [[ -n ${GITHUB_SOURCE} ]]; then
# Check if git branch contains index.yaml
GITHUB_SOURCE=$(echo -e "${GITHUB_SOURCE}" | sed 's/https:\/\///g')
wget -P ${BASE_DIR} https://raw.githubusercontent.com/${GITHUB_SOURCE}/${GITHUB_BRANCH}/index.yaml 2>/dev/null
HELM_INDEX="${BASE_DIR}/index.yaml"
if [ -f "${HELM_INDEX}" ]; then
UPDATE="--merge ${HELM_INDEX}"
fi
fi
# Base URL referencing assets directly from GitHub
BASE_URL="assets/"
# Relative URL references assets within the container deployment
RELATIVE_URL="plugin/"
if [ "${GITHUB_BUILD}" == "true" ]; then
helm repo index ${ASSETS} --url ${BASE_URL} ${UPDATE}
elif ! [ -e "${HELM_INDEX}" ]; then
helm repo index ${ASSETS} --url ${RELATIVE_URL}
fi
fi
if [ "${GITHUB_BUILD}" == "true" ] && [[ -n "${GITHUB_RELEASE_TAG}" ]] && [ "${BUILT}" == "false" ]; then
echo -e "${YELLOW}Github release tag ${RESET}${BOLD}${GITHUB_RELEASE_TAG}${RESET}${YELLOW} did not match any package name.${RESET}"
echo -e "${YELLOW}Stopping build${RESET}"
rm -rf ${CHART_TMP}
rm -rf ${TMP}
exit 1
fi
if [ -f ${ROOT_INDEX} ] && [ "${GITHUB_BUILD}" == "false" ]; then
UPDATE="--merge ${ROOT_INDEX}"
helm repo index ${ASSETS} ${UPDATE}
fi
if [ -f ${ASSETS}/index.yaml ] && ! [ -e "${ROOT_INDEX}" ]; then
cp ${ASSETS}/index.yaml ${BASE_DIR}
fi
if [ "${GITHUB_BUILD}" == "false" ]; then
echo -e "${CYAN}Base extension: ${BASE_EXT}${RESET}"
echo -e "${CYAN}Extension version: ${EXT_VERSION}${RESET}"
# Build the container image
${SCRIPT_DIR}/bundle "${BASE_EXT}" "${EXT_VERSION}" "${REGISTRY}" "${REGISTRY_ORG}" "${IMAGE_PREFIX}" "${PUSH}" "${PODMAN_CONTAINER}"
else
rm -rf ${CHART_TEMPLATE}
fi
if [ "${GITHUB_BUILD}" == "true" ] && [ -f ${ROOT_INDEX} ]; then
if [[ -n "${GITHUB_RELEASE_TAG}" ]] && [ -f ${ASSETS}/index.yaml ]; then
cp ${ASSETS}/index.yaml tmp
else
cp ${ROOT_INDEX} tmp
fi
fi
if [ "${GITHUB_BUILD}" == "true" ] && [ "${BUILT}" == "true" ]; then
echo -e "${BOLD}${CYAN}Packaging for Github Build${RESET}"
# Make temp directory for workflow build artifact
mkdir -p tmp/{assets,charts,extensions}
cp -r ${ASSETS}/* tmp/assets
cp -r ${CHARTS}/* tmp/charts
cp -r ${BASE_DIR}/extensions/* tmp/extensions
fi
if [ "${BUILT}" == "true" ]; then
echo -e "${CYAN}${BOLD}${CHECK} One or more packages built${RESET}"
fi
# Clean up
rm -rf ${CHART_TMP}
if [ "${GITHUB_BUILD}" == "false" ]; then
rm -rf ${TMP} ${ASSETS} ${CHARTS} ${BASE_DIR}/extensions ${BASE_DIR}/dist-pkg ${ROOT_INDEX}
fi