diff --git a/Makefile b/Makefile index d996197a..93a20549 100644 --- a/Makefile +++ b/Makefile @@ -6,6 +6,7 @@ KO = $(BIN_DIR)/ko SETUP_ENVTEST = $(BIN_DIR)/setup-envtest GINKGO = $(BIN_DIR)/ginkgo GUM = $(BIN_DIR)/gum +GH = $(BIN_DIR)/gh HELM_BASE_OPTS ?= --set aws.region=${AWS_REGION},serviceAccount.name=${SERVICE_ACCOUNT_NAME},serviceAccount.annotations.eks\\.amazonaws\\.com/role-arn=${SERVICE_ACCOUNT_ROLE_ARN} GINKGO_BASE_OPTS ?= --coverpkg $(shell head -n 1 $(PROJECT_DIR)/go.mod | cut -s -d ' ' -f 2)/pkg/... KODATA = \ @@ -67,6 +68,9 @@ $(KO): $(GUM): @$(PROJECT_DIR)/scripts/download-gum.sh "$(BIN_DIR)" +$(GH): + @$(PROJECT_DIR)/scripts/download-gh.sh "$(BIN_DIR)" + $(SETUP_ENVTEST): GOBIN="$(BIN_DIR)" go install sigs.k8s.io/controller-runtime/tools/setup-envtest@v0.0.0-20220217150738-f62a0f579d73 PATH="$(BIN_DIR):$(PATH)" $(SCRIPTS_DIR)/download-kubebuilder-assets.sh @@ -151,5 +155,9 @@ latest-release-tag: ## Get tag of most recent release. repo-full-name: ## Get the full name of the GitHub repository for Node Termination Handler. @echo "$(GITHUB_REPO_FULL_NAME)" +.PHONY: ekscharts-sync-release +ekscharts-sync-release: $(GH) + @PATH="$(BIN_DIR):$(PATH)" $(PROJECT_DIR)/scripts/sync-to-aws-eks-charts.sh -n + .PHONY: version version: latest-release-tag ## Get the most recent release version. diff --git a/scripts/download-gh.sh b/scripts/download-gh.sh new file mode 100644 index 00000000..c1745b35 --- /dev/null +++ b/scripts/download-gh.sh @@ -0,0 +1,54 @@ +#!/usr/bin/env bash + +set -euo pipefail + +usage=$(cat << EOM +usage: $(basename "$0") -h | DIR_PATH + + Download the gh binary to DIR_PATH. + + Arguments: + -h Print usage message then exit. + +EOM +) + +while getopts "h" opt; do + case $opt in + h ) echo "${usage}" + exit 0 + ;; + \? ) echo "${usage}" 1>&2 + exit 1 + ;; + esac +done + +dir_path="$1" + +if [[ -z "${dir_path}" ]]; then + echo "error: missing directory path" 1>&2 + echo 1>&2 + echo "${usage}" 1>&2 + exit 1 +fi + +if ! command -v wget >/dev/null; then + echo "error: wget not installed" 1>&2 + exit 1 +fi + +version="2.16.1" +arch="$(go env GOHOSTARCH)" +os="$(go env GOHOSTOS)" + +if [[ "${os}" == "darwin" ]]; then + os="macOS" +fi + +echo "Downloading github.com/cli/cli@v${version} ..." + +mkdir -p "${dir_path}" +cd "${dir_path}" +wget https://github.com/cli/cli/releases/download/v${version}/gh_${version}_${os}_${arch}.tar.gz -O - | \ + tar xzf - gh diff --git a/scripts/sync-to-aws-eks-charts.sh b/scripts/sync-to-aws-eks-charts.sh new file mode 100755 index 00000000..57fb3f64 --- /dev/null +++ b/scripts/sync-to-aws-eks-charts.sh @@ -0,0 +1,198 @@ +#!/usr/bin/env bash + +set -euo pipefail + +repo_root_path="$(cd "$(dirname "$0")"; cd ..; pwd -P)" +makefile_path="${repo_root_path}/Makefile" + +if ! command -v gh >/dev/null; then + echo "error: required executable 'gh' not found" >&2 + exit 1 +fi + +github_nth_full_repo_name="$(make -s -f "${makefile_path}" repo-full-name)" +nth_version="$(make -s -f "${makefile_path}" version)" + +helm_chart_name="aws-node-termination-handler-2" +helm_chart_path="${repo_root_path}/charts/${helm_chart_name}" + +github_eks_charts_full_repo_name="aws/eks-charts" +github_username="${GITHUB_USERNAME:-}" +github_token="${GITHUB_TOKEN:-}" + +include_notes=0 + +usage=$(cat << EOM +usage: $(basename $0) -h | [-n] [-r REPOSITORY] [-e REPOSITORY] + + Open Pull Request to eks-charts repository with the latest ${helm_chart_name} Helm chart. + + Options: + -h Display this help message then exit. + -n Include application release node in the created Pull Request. + -r REPOSITORY Node Termination Handler GitHub repository. Defaults to "${github_nth_full_repo_name}". + + Options for testing: + -e REPOSITORY EKS Charts GitHub repository. Defaults to "${github_eks_charts_full_repo_name}". + -u USERNAME GitHub username. Defaults to the value of the environment variable, GITHUB_USERNAME. + -t TOKEN GitHub token. Defaults to the value of the environment variable, GITHUB_TOKEN . + -v VERSION NTH version. Defaults to "${nth_version}". + +EOM +) + +while getopts "nr:e:u:t:v:h" opt; do + case "${opt}" in + n ) include_notes=1 + ;; + r ) github_nth_full_repo_name="${OPTARG}" + ;; + e ) github_eks_charts_full_repo_name="${OPTARG}" + ;; + u ) github_username="${OPTARG}" + ;; + t ) github_token="${OPTARG}" + ;; + v ) nth_version="${OPTARG}" + ;; + h ) echo "${usage}" + exit 0 + ;; + \?) echo "${usage}" >&2 + exit 1 + ;; + esac +done + +assert_not_empty() { + if [[ -z "${!1}" ]]; then + echo "error: missing argument ${1}" >&2 + echo "${usage}" >&2 + exit 1 + fi +} + +assert_not_empty github_nth_full_repo_name +assert_not_empty github_eks_charts_full_repo_name +assert_not_empty github_username +assert_not_empty github_token +assert_not_empty nth_version + +github_eks_charts_repo_name="$(echo "${github_eks_charts_full_repo_name}" | cut -d '/' -f2)" + +################################################# + +echo -e "🥑 Configure gh cli" + +gh_client_config_dir="${HOME}/.config/gh" +gh_client_config_path="${gh_client_config_dir}/config.yml" +gh_client_config_backup_path="${gh_client_config_dir}/config.yml.backup" + +restore_gh_config() { + if [[ -f "${gh_client_config_backup_path}" ]]; then + echo -e "🥑 Restore gh cli configuration" + mv -f "${gh_client_config_backup_path}" "${gh_client_config_path}" || : + fi +} + +trap restore_gh_config EXIT + +echo "Backing up existing configuration" +mkdir -p "${gh_client_config_dir}" +mv -f "${gh_client_config_path}" "${gh_client_config_backup_path}" + +echo "Writing custom configuration" +cat << EOF > "${gh_client_config_path}" +hosts: + github.com: + oauth_token: "${github_token}" + user: "${github_username}" +EOF + +################################################# + +echo -e "🥑 Clone ${github_eks_charts_full_repo_name}" + +eks_sync_path="${repo_root_path}/build/eks-sync" +rm -rf "${eks_sync_path}" +mkdir -p "${eks_sync_path}" + +cd "${eks_sync_path}" +gh repo fork "${github_eks_charts_full_repo_name}" --clone --remote +eks_charts_repo_path="${eks_sync_path}/${github_eks_charts_repo_name}" + +cd "${eks_charts_repo_path}" +git_default_branch="$(git rev-parse --abbrev-ref HEAD | tr -d '\n')" +git merge --ff-only "upstream/${git_default_branch}" +git remote set-url origin "https://${github_username}:${github_token}@github.com/${github_username}/${github_eks_charts_repo_name}.git" +git push origin "${git_default_branch}" + +################################################# + +echo -e "🥑 Check whether chart is in sync" + +eks_charts_nth_path="${eks_charts_repo_path}/stable/${helm_chart_name}" +if diff -x ".*" -r "${helm_chart_path}/" "${eks_charts_nth_path}/" &>/dev/null ; then + echo " ✅ Charts already in sync; no updates needed" + exit +fi + +echo -e "🚨 Charts are NOT in sync" + +################################################# + +echo -e "🥑 Copy updates to chart" + +rm -rf "${eks_charts_nth_path}" +cp -R "${helm_chart_path}/" "${eks_charts_nth_path}/" + +################################################# + +echo -e "🥑 Commit updates" + +helm_chart_version="$(cat "${helm_chart_path}/Chart.yaml" | grep "version:" | cut -d ' ' -f2 | tr -d '"')" +pr_id="$(uuidgen | cut -d '-' -f1)" +git_release_branch="${helm_chart_name}-${helm_chart_version}-${pr_id}" +git checkout -b "${git_release_branch}" + +git add --all +git commit --author="ec2-bot 🤖 " -m "${helm_chart_name}: ${helm_chart_version}" +git push -u origin "${git_release_branch}" + +################################################# + +echo -e "🥑 Create pull request" + +format_release_notes() { + echo "## ${helm_chart_name} ${helm_chart_version} Automated Chart Sync! 🤖🤖" + + if [[ ${include_notes} -ne 1 ]]; then + return 0 + fi + + local authorization_header="Authorization: token ${GITHUB_TOKEN}" + local release_id="$(curl -s -H "${authorization_header}" \ + https://api.github.com/repos/${github_nth_full_repo_name}/releases | \ + jq --arg ver "${nth_version}" '.[] | select(.tag_name==$ver) | .id')" + + echo + echo "### 📝 Release Notes 📝" + echo + echo "$(curl -s -H "${authorization_header}" \ + https://api.github.com/repos/${github_nth_full_repo_name}/releases/${release_id} | \ + jq -r '.body')" + echo +} + +if ! gh pr create \ + --title "🥳 ${helm_chart_name} ${helm_chart_version} Automated Release! 🥑" \ + --body "$(format_release_notes)" \ + --repo "${github_eks_charts_full_repo_name}" \ + --base "master"; then + echo -e "❌ Failed to create pull request" + exit 1 +fi + +echo -e "✅ Pull request created" + +echo -e "✅ EKS charts sync complete"