diff --git a/ci/promote-overlay/README.md b/ci/promote-overlay/README.md new file mode 100644 index 00000000..1dd04580 --- /dev/null +++ b/ci/promote-overlay/README.md @@ -0,0 +1,88 @@ + +# Promotion of Release Service overlays in the infra-deployments repo + +## Introduction + +The Release Service in RHTAP is deployed to environments using ArgoCD. The specific content to be deployed to each environment is controlled by overlays. + +There are 3 overlays: +- development +- staging +- production + +Currently, when a PR is merged in the release-service repo, a PR in the infra-deployments repo is created by a RHTAP release pipeline which updates the **development** overlay. + +Promotion from a lower environment to higher environment is done manually by updating the target overlay. + +This script helps to automate this promotion. It analyzes the commits between the source and the target overlays and creates an infra-deployments PR updating the overlay file and includes a changelog. + +![Example PR](infra-pr.png) + +## Setup + +* The script requires an environment variable called _**GITHUB_TOKEN**_ to exist. This token should have the following scopes: + +![Required Github token scopes](github-token-scopes.png) + +* The script also has 3 required arguments + * **source-overlay**: Name of the source overlay to promote to target + * **target-overlay**: Name of the overlay to target for promotion + * **fork-owner**: Name of the owner of your infra-deployments fork in Github + +## Running the script + +``` +% ./ci/promote-overlay/promote-overlay.sh --source-overlay development --target-overlay staging --fork-owner scoheb +``` + +## Example output +``` +--- +Promoting release-service development to staging in redhat-appstudio/infra-deployments +--- + +Sync fork with upstream: +This branch is not behind the upstream redhat-appstudio:main. +Cloning into 'infra-deployments'... +remote: Enumerating objects: 21113, done. +remote: Counting objects: 100% (3766/3766), done. +remote: Compressing objects: 100% (430/430), done. +remote: Total 21113 (delta 3391), reused 3417 (delta 3324), pack-reused 17347 +Receiving objects: 100% (21113/21113), 3.52 MiB | 3.78 MiB/s, done. +Resolving deltas: 100% (13649/13649), done. +Cloning into 'release-service'... +remote: Enumerating objects: 2995, done. +remote: Counting objects: 100% (801/801), done. +remote: Compressing objects: 100% (287/287), done. +remote: Total 2995 (delta 566), reused 673 (delta 505), pack-reused 2194 +Receiving objects: 100% (2995/2995), 30.64 MiB | 4.33 MiB/s, done. +Resolving deltas: 100% (1749/1749), done. +HEAD is now at 2421fc66 release-service update (#2595) +branch 'release-service-staging-update-2023_10_22__12_33_39' set up to track 'origin/main'. +Switched to a new branch 'release-service-staging-update-2023_10_22__12_33_39' + +release-service source overlay commit -> 68f2f2c678f726ffa20166adcab8e7e0204a8c69 +release-service target overlay commit -> 9a0f08573e4ca3f0d5deda2cbda2575b7e7093bd + +Run standard RH pre-commit checks........................................Passed +[release-service-staging-update-2023_10_22__12_33_39 e5f53283] Promote release-service from development to staging +1 file changed, 2 insertions(+), 2 deletions(-) +Enumerating objects: 11, done. +Counting objects: 100% (11/11), done. +Delta compression using up to 16 threads +Compressing objects: 100% (1/1), done. +Writing objects: 100% (6/6), 728 bytes | 728.00 KiB/s, done. +Total 6 (delta 4), reused 5 (delta 4), pack-reused 0 +remote: Resolving deltas: 100% (4/4), completed with 4 local objects. +remote: +remote: Create a pull request for 'release-service-staging-update-2023_10_22__12_33_39' on GitHub by visiting: +remote: https://github.com/scoheb/infra-deployments/pull/new/release-service-staging-update-2023_10_22__12_33_39 +remote: +To github.com:scoheb/infra-deployments.git +* [new branch] release-service-staging-update-2023_10_22__12_33_39 -> release-service-staging-update-2023_10_22__12_33_39 + +================================== +Pull request created successfully: +- https://github.com/redhat-appstudio/infra-deployments/pull/2606 +================================== +``` \ No newline at end of file diff --git a/ci/promote-overlay/github-token-scopes.png b/ci/promote-overlay/github-token-scopes.png new file mode 100644 index 00000000..7bd6efcd Binary files /dev/null and b/ci/promote-overlay/github-token-scopes.png differ diff --git a/ci/promote-overlay/infra-pr.png b/ci/promote-overlay/infra-pr.png new file mode 100644 index 00000000..8a9de9a9 Binary files /dev/null and b/ci/promote-overlay/infra-pr.png differ diff --git a/ci/promote-overlay/promote-overlay.sh b/ci/promote-overlay/promote-overlay.sh new file mode 100755 index 00000000..ddf1a0e8 --- /dev/null +++ b/ci/promote-overlay/promote-overlay.sh @@ -0,0 +1,176 @@ +#!/usr/bin/env bash +set -e + +# Function to make a string JSON-safe +make_json_safe() { + local json_safe_string + json_safe_string=$(jq -s -R -r @json <<< "$1") + echo "$json_safe_string" +} + +OPTIONS=$(getopt -l "skip-cleanup,source-overlay:,target-overlay:,fork-owner:,help" -o "sc,src:,tgt:,fo:,h" -a -- "$@") +eval set -- "$OPTIONS" +while true; do + case "$1" in + -sc|--skip-cleanup) + CLEANUP="true" + ;; + -src|--source-overlay) + SOURCE_OVERLAY="$2" + shift 2 + ;; + -tgt|--target-overlay) + TARGET_OVERLAY="$2" + shift 2 + ;; + -fo|--fork-owner) + FORK_OWNER="$2" + shift 2 + ;; + -h|--help) + print_help + exit + ;; + --) + shift + break + ;; + *) echo "Error: Unexpected option: $1" % >2 + esac +done + +print_help(){ + echo -e "$0 --source-overlay SOURCE_OVERLAY --target-overlay TARGET_OVERLAY --fork-owner FORK_OWNER \ + [ --skip-cleanup ]\n" + echo -e "\t--source-overlay SOURCE_OVERLAY\tName of the source overlay to promote to target" + echo -e "\t--target-overlay TARGET_OVERLAY\tName of the overlay to target for promotion" + echo -e "\t--fork-owner FORK_OWNER\tName of the owner of your infra-deployments fork in Github" + echo -e "\t--skip-cleanup\tDisable cleanup after test. Useful for debugging" +} + +if [ -z "${SOURCE_OVERLAY}" ]; then + echo -e "Error: missing 'source-overlay' argument\n\n" + print_help + exit 1 +fi +if [ -z "${TARGET_OVERLAY}" ]; then + echo -e "Error: missing 'target-overlay' argument\n\n" + print_help + exit 1 +fi +if [ -z "${FORK_OWNER}" ]; then + echo -e "Error: missing 'fork-owner' argument\n\n" + print_help + exit 1 +fi +if [ -z "${GITHUB_TOKEN}" ]; then + echo -e "Error: missing 'GITHUB_TOKEN' environment variable\n\n" + print_help + exit 1 +fi + +UPDATE_BRANCH_NAME="release-service-${TARGET_OVERLAY}-update-"$(date '+%Y_%m_%d__%H_%M_%S') + +# GitHub repository details +owner="redhat-appstudio" +repo="infra-deployments" + +# Personal access token with appropriate permissions +token="${GITHUB_TOKEN}" + +# New branch and commit details +new_branch=${UPDATE_BRANCH_NAME} +commit_message="Promote release-service from ${SOURCE_OVERLAY} to ${TARGET_OVERLAY}" + +# Fork repository and branch parameters +fork_repo="infra-deployments" # Change this to your fork's repository +base_branch="main" # Change this to the base branch you want to create the PR against + +# PR description +description="Included PRs:\r\n" + +# Clone the repository +tmpDir=$(mktemp -d) +infraDeploymentDir=${tmpDir}/infra-deployments +releaseServiceDir=${tmpDir}/release-service +mkdir -p ${infraDeploymentDir} +mkdir -p ${releaseServiceDir} + +if [ "${CLEANUP}" != "true" ]; then + trap "rm -rf ${tmpDir}" EXIT +else + echo "Temporary git clone directory: ${tmpDir}" +fi + +echo -e "---\nPromoting release-service ${SOURCE_OVERLAY} to ${TARGET_OVERLAY} in ${owner}/${repo}\n---\n" +cd ${tmpDir} + +echo -e "Sync fork with upstream:" +sync_fork_json=$(curl -s -L \ + -X POST \ + -H "Accept: application/vnd.github+json" \ + -H "Authorization: Bearer ${GITHUB_TOKEN}" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + https://api.github.com/repos/${FORK_OWNER}/infra-deployments/merge-upstream \ + -d '{"branch":"'${base_branch}'"}') + +echo $sync_fork_json + +git clone "git@github.com:$FORK_OWNER/$repo.git" +git clone "git@github.com:$owner/release-service.git" +cd ${infraDeploymentDir} + +git fetch --all --tags --prune + +# Create a new branch +git reset --hard HEAD +git checkout -b "$new_branch" origin/"$base_branch" + +RS_SOURCE_OVERLAY_COMMIT=$(yq '.images[0].newTag' < components/release/${SOURCE_OVERLAY}/kustomization.yaml) +RS_TARGET_OVERLAY_COMMIT=$(yq '.images[0].newTag' < components/release/${TARGET_OVERLAY}/kustomization.yaml) + +echo "" +echo 'release-service source overlay commit -> '"$RS_SOURCE_OVERLAY_COMMIT" +echo 'release-service target overlay commit -> '"$RS_TARGET_OVERLAY_COMMIT" +echo "" + +cd ${releaseServiceDir} +git fetch --all --tags --prune +RS_COMMITS=($(git rev-list --first-parent --ancestry-path "$RS_TARGET_OVERLAY_COMMIT"'...'"$RS_SOURCE_OVERLAY_COMMIT")) +## now loop through the above array +for RS_COMMIT in "${RS_COMMITS[@]}" +do + PR_URL=$(curl -s -H 'Authorization: token '"$token" 'https://api.github.com/search/issues?q=sha:'"$RS_COMMIT" | jq -r '.items[0].pull_request.html_url') + # or do whatever with individual element of the array + description="$description"' - '"$PR_URL"'\r\n' +done + +cd ${infraDeploymentDir} +sed -i "s/$RS_TARGET_OVERLAY_COMMIT/$RS_SOURCE_OVERLAY_COMMIT/g" components/release/${TARGET_OVERLAY}/kustomization.yaml + +git add components/release/${TARGET_OVERLAY}/kustomization.yaml +git commit -m "$commit_message" + +git push origin "$new_branch" + +# Create a pull request using GitHub API +pr_creation_json=$(curl -s -X POST "https://api.github.com/repos/$owner/$repo/pulls" \ + -H "Authorization: token $token" \ + -d '{ + "title": "'"$commit_message"'", + "head": "'"$FORK_OWNER:$new_branch"'", + "base": "'"$base_branch"'", + "body": "'"$description"'" + }') + +pr_url=$(echo $pr_creation_json | jq -r .html_url) + +if [ "${pr_url}" == "null" ]; then + echo -e "\nError: failed to create PR. See output: \n${pr_creation_json}" + exit 1 +fi + + +echo -e "\n==================================" +echo -e "Pull request created successfully:\n- ${pr_url}" +echo "=================================="