#!/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 [] [extensions]" echo " options:" echo " -s Specify destination GitHub repository (org/name) - defaults to the git origin" echo " -b Specify destination GitHub branch" echo " -t 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 Specify destination container registry for built images" echo " -o Specify destination container registry organization for built images" echo " -i 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