From 0fbf0b8ec83ac46e815422dd53fde7dcd61cb2b0 Mon Sep 17 00:00:00 2001 From: Tomasz Smelcerz Date: Tue, 21 Nov 2023 11:59:49 +0100 Subject: [PATCH] feat: Add release workflow (#166) Add release workflow --- .github/workflows/create-release.yaml | 99 +++++++++++++++++++ README.md | 1 + docs/how_to_release.md | 15 +++ .../scripts/release/create_changelog.sh | 25 +++++ .../scripts/release/create_draft_release.sh | 40 ++++++++ .../scripts/release/get_last_release.sh | 23 +++++ .../scripts/release/get_release_by_tag.sh | 26 +++++ .../scripts/release/publish_release.sh | 23 +++++ .../scripts/release/wait_for_image.sh | 25 +++++ 9 files changed, 277 insertions(+) create mode 100644 .github/workflows/create-release.yaml create mode 100644 docs/how_to_release.md create mode 100755 runtime-watcher/scripts/release/create_changelog.sh create mode 100755 runtime-watcher/scripts/release/create_draft_release.sh create mode 100755 runtime-watcher/scripts/release/get_last_release.sh create mode 100755 runtime-watcher/scripts/release/get_release_by_tag.sh create mode 100755 runtime-watcher/scripts/release/publish_release.sh create mode 100755 runtime-watcher/scripts/release/wait_for_image.sh diff --git a/.github/workflows/create-release.yaml b/.github/workflows/create-release.yaml new file mode 100644 index 00000000..168d7e40 --- /dev/null +++ b/.github/workflows/create-release.yaml @@ -0,0 +1,99 @@ +name: "Create release" + +env: + IMAGE_REPO: europe-docker.pkg.dev/kyma-project/prod/runtime-watcher-skr + CODE_REPOSITORY: kyma-project/runtime-watcher +on: + push: + tags: + - '[0-9]+.[0-9]+.[0-9]-?*' +jobs: + validate-release: + name: Validate release + runs-on: ubuntu-latest + outputs: + current_release_tag: ${{ steps.get_current_release_tag.outputs.current_release_tag }} + last_release_tag: ${{ steps.get_last_release_tag.outputs.last_release_tag }} + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Find the release tag + id: get_current_release_tag + run: | + echo "Current release tag: ${GITHUB_REF#refs/*/}" + echo "current_release_tag=${GITHUB_REF#refs/*/}" >> $GITHUB_OUTPUT + - name: Check if release doesn't exist yet + env: + CURRENT_RELEASE_TAG: ${{ steps.get_current_release_tag.outputs.current_release_tag }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: ./runtime-watcher/scripts/release/get_release_by_tag.sh $CURRENT_RELEASE_TAG + - name: Get last release version + id: get_last_release_tag + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + set +e + SCRIPT_OUTPUT=$(./runtime-watcher/scripts/release/get_last_release.sh) + SCRIPT_EXIT_CODE=$? + if [[ $SCRIPT_EXIT_CODE != 0 ]]; then + echo "$SCRIPT_OUTPUT" + exit $SCRIPT_EXIT_CODE + fi + set -e + echo "Last Release version: $SCRIPT_OUTPUT" + echo "last_release_tag=$SCRIPT_OUTPUT" >> $GITHUB_OUTPUT + - name: Wait for the Docker image + timeout-minutes: 15 + env: + ITERATIONS: 30 + SLEEP_SECONDS: 30 + run: ./runtime-watcher/scripts/release/wait_for_image.sh ${{ env.IMAGE_REPO }}:${{ steps.get_current_release_tag.outputs.current_release_tag }} $ITERATIONS $SLEEP_SECONDS + draft-release: + name: Create draft release + runs-on: ubuntu-latest + needs: validate-release + outputs: + release_id: ${{ steps.draft_release.outputs.release_id }} + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Generate changelog + id: generate_changelog + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + CURRENT_RELEASE_TAG: ${{ needs.validate-release.outputs.current_release_tag }} + LAST_RELEASE_TAG: ${{ needs.validate-release.outputs.last_release_tag }} + run: | + echo "Generating changelog for version: ${{ needs.validate-release.outputs.current_release_tag }}" + CHANGELOG_FILE_NAME=$(./runtime-watcher/scripts/release/create_changelog.sh $CURRENT_RELEASE_TAG $LAST_RELEASE_TAG) + echo "changelog_file_name=$CHANGELOG_FILE_NAME" >> $GITHUB_OUTPUT + - name: Create draft release + id: draft_release + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + CURRENT_RELEASE_TAG: ${{ needs.validate-release.outputs.current_release_tag }} + CHANGELOG_FILE_NAME: ${{ steps.generate_changelog.outputs.changelog_file_name }} + run: | + RELEASE_ID=$(./runtime-watcher/scripts/release/create_draft_release.sh $CURRENT_RELEASE_TAG $CHANGELOG_FILE_NAME) + echo "RELEASE_ID=$RELEASE_ID" + echo "release_id=$RELEASE_ID" >> $GITHUB_OUTPUT + publish_release: + name: Publish release + runs-on: ubuntu-latest + needs: draft-release + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Publish release + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + RELEASE_ID: ${{ needs.draft-release.outputs.release_id }} + run: | + echo "RELEASE_ID=$RELEASE_ID" + ./runtime-watcher/scripts/release/publish_release.sh $RELEASE_ID diff --git a/README.md b/README.md index 1bcfac07..7a318c6c 100644 --- a/README.md +++ b/README.md @@ -26,4 +26,5 @@ The [Listener package](https://github.com/kyma-project/runtime-watcher/tree/main ## Read more +The release process is described in the [How To Release](./docs/how_to_release.md) document. For further details on Runtime Watcher's architecture, see the [Architecture](./docs/architecture.md) document. diff --git a/docs/how_to_release.md b/docs/how_to_release.md new file mode 100644 index 00000000..5d2e1cdc --- /dev/null +++ b/docs/how_to_release.md @@ -0,0 +1,15 @@ +# Runtime Watcher Release Procedure + +Use the steps described in this document to release a new version of Runtime Watcher. + +## Steps + +1. Checkout the main branch. +2. Create a new tag for the release in the main branch and push it to the repository. The tag should follow the [Semantic Versioning 2.0.0 format](https://semver.org/), for example, `1.2.3`. + The new tag will trigger a ProwJob that creates and publishes a Docker image with a Runtime Watcher executable. + The new tag will also trigger the release workflow. + +> **NOTE:** The release workflow waits for a Docker image to be published. If the image is not available within the configured time (15 minutes), the workflow fails. In that case, you need to manually re-run it once the Docker image is ready. The expected image URL is: `europe-docker.pkg.dev/kyma-project/prod/runtime-watcher-skr:` + +> **NOTE:** If you want to create a tag without triggering a release, just ensure the tag does not match the Semantic Versioning format. + diff --git a/runtime-watcher/scripts/release/create_changelog.sh b/runtime-watcher/scripts/release/create_changelog.sh new file mode 100755 index 00000000..f61500bd --- /dev/null +++ b/runtime-watcher/scripts/release/create_changelog.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash + +set -o nounset +set -o errexit +set -E +set -o pipefail + +CURRENT_RELEASE_TAG=$1 +LAST_RELEASE_TAG=$2 + +GITHUB_URL=https://api.github.com/repos/${CODE_REPOSITORY} +GITHUB_AUTH_HEADER="Authorization: token ${GITHUB_TOKEN}" +CHANGELOG_FILE="CHANGELOG.md" + +echo "## What has changed" >> ${CHANGELOG_FILE} + +git log ${LAST_RELEASE_TAG}..${CURRENT_RELEASE_TAG} --pretty=tformat:"%h" --reverse | while read -r commit +do + COMMIT_AUTHOR=$(curl -H "${GITHUB_AUTH_HEADER}" -sS "${GITHUB_URL}/commits/${commit}" | jq -r '.author.login') + git show -s ${commit} --format="* %s by @${COMMIT_AUTHOR}" >> ${CHANGELOG_FILE} +done + +echo -e "\n**Full changelog**: $GITHUB_URL/compare/${LAST_RELEASE_TAG}...${CURRENT_RELEASE_TAG}" >> ${CHANGELOG_FILE} + +echo ${CHANGELOG_FILE} diff --git a/runtime-watcher/scripts/release/create_draft_release.sh b/runtime-watcher/scripts/release/create_draft_release.sh new file mode 100755 index 00000000..62ec6b9e --- /dev/null +++ b/runtime-watcher/scripts/release/create_draft_release.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env bash + +set -o nounset +set -o errexit +set -E +set -o pipefail + +RELEASE_TAG=$1 +CHANGELOG_FILE_NAME=$2 +CHANGELOG_FILE=$(cat ${CHANGELOG_FILE_NAME}) + +GITHUB_URL=https://api.github.com/repos/${CODE_REPOSITORY} +GITHUB_AUTH_HEADER="Authorization: Bearer ${GITHUB_TOKEN}" + +#echo "RELEASE_TAG: ${RELEASE_TAG}" +#echo "CHANGELOG_FILE_NAME: ${CHANGELOG_FILE_NAME}" + +JSON_PAYLOAD=$(jq -n \ + --arg tag_name "$RELEASE_TAG" \ + --arg name "$RELEASE_TAG" \ + --arg body "$CHANGELOG_FILE" \ + '{ + "tag_name": $tag_name, + "name": $name, + "body": $body, + "draft": true + }') + +CURL_RESPONSE=$(curl -L \ + -s \ + --fail-with-body \ + -X POST \ + -H "Accept: application/vnd.github+json" \ + -H "${GITHUB_AUTH_HEADER}" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + "${GITHUB_URL}"/releases \ + -d "$JSON_PAYLOAD") + +# return the id of the release draft +echo "$CURL_RESPONSE" | jq -r ".id" diff --git a/runtime-watcher/scripts/release/get_last_release.sh b/runtime-watcher/scripts/release/get_last_release.sh new file mode 100755 index 00000000..94435d9d --- /dev/null +++ b/runtime-watcher/scripts/release/get_last_release.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash + +set -o nounset +set -o pipefail + +GITHUB_URL=https://api.github.com/repos/${CODE_REPOSITORY} + +CURL_RESPONSE=$(curl -L \ + -s \ + --fail-with-body \ + -H "Accept: application/vnd.github+json" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + "${GITHUB_URL}"/releases/latest) + +CURL_EXIT_CODE=$? + +if [[ ${CURL_EXIT_CODE} == 0 ]]; then + echo "${CURL_RESPONSE}" | jq -r .tag_name +else + echo "Can't find any previous release - unable to generate changelog" +fi + +exit ${CURL_EXIT_CODE} diff --git a/runtime-watcher/scripts/release/get_release_by_tag.sh b/runtime-watcher/scripts/release/get_release_by_tag.sh new file mode 100755 index 00000000..39043587 --- /dev/null +++ b/runtime-watcher/scripts/release/get_release_by_tag.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash + +set -o nounset +set -o pipefail + +RELEASE_TAG=$1 + +GITHUB_URL=https://api.github.com/repos/${CODE_REPOSITORY} +GITHUB_AUTH_HEADER="Authorization: Bearer ${GITHUB_TOKEN}" + +CURL_RESPONSE=$(curl -L \ + -s \ + --fail-with-body \ + -H "Accept: application/vnd.github+json" \ + -H "${GITHUB_AUTH_HEADER}" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + "${GITHUB_URL}"/releases/tags/$RELEASE_TAG) + +CURL_EXIT_CODE=$? + +if [[ ${CURL_EXIT_CODE} == 0 ]]; then + echo "Release with tag: $RELEASE_TAG already exists!" + exit 1 +fi + +exit 0 diff --git a/runtime-watcher/scripts/release/publish_release.sh b/runtime-watcher/scripts/release/publish_release.sh new file mode 100755 index 00000000..f1be8cea --- /dev/null +++ b/runtime-watcher/scripts/release/publish_release.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash + +set -o nounset +set -o errexit +set -E +set -o pipefail + +RELEASE_ID=$1 + +GITHUB_URL=https://api.github.com/repos/${CODE_REPOSITORY} +GITHUB_AUTH_HEADER="Authorization: Bearer ${GITHUB_TOKEN}" + +CURL_RESPONSE=$(curl -L \ + -s \ + --fail-with-body \ + -X POST \ + -H "Accept: application/vnd.github+json" \ + -H "${GITHUB_AUTH_HEADER}" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + "${GITHUB_URL}"/releases/"${RELEASE_ID}" \ + -d '{"draft":false}') +echo "$CURL_RESPONSE" + diff --git a/runtime-watcher/scripts/release/wait_for_image.sh b/runtime-watcher/scripts/release/wait_for_image.sh new file mode 100755 index 00000000..bc98fe58 --- /dev/null +++ b/runtime-watcher/scripts/release/wait_for_image.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash + +set -o nounset +set -o errexit +set -E +set -o pipefail + +DOCKER_IMAGE=$1 +ITERATIONS="${2:-30}" +SLEEP_TIME="${3:-30}" + +for (( c=1; c<=$ITERATIONS; c++ )) +do + if $(docker manifest inspect $1 > /dev/null 2>&1); then + exit 0 + fi + echo "Attempt $c: Docker image: $DOCKER_IMAGE doesn't exist" + if [[ $c -lt $ITERATIONS ]]; then + sleep ${SLEEP_TIME} + fi +done + +echo "Fail: Docker image: $DOCKER_IMAGE doesn't exist" +exit 1 +