From e9e8644c0be5fe013786600bf072617df161d2ff Mon Sep 17 00:00:00 2001 From: Carina Kothe <69976260+grischperl@users.noreply.github.com> Date: Fri, 5 Jan 2024 13:33:19 +0100 Subject: [PATCH] Release workflow (#32) * Release workflow * Rename to `sec-scan` * Fix all variables to `eventing-publisher-proxy` * Fix output of next release version * Fix run name to branch name Use correct script in `Get the next release version` step Add lines between steps * Use correct image in `sec-scanners-config` * Use env variable for using job output Move `outputs` at beginning of step for readability * Rename scripts to be more descriptive Rename steps to be more clear * Use script with more comments --- .github/workflows/create-release.yaml | 91 ++++++++++++++++++++ scripts/check_tags_in_sec_scanners_config.sh | 32 +++++++ scripts/create_changelog.sh | 56 ++++++++++++ scripts/create_draft_release.sh | 40 +++++++++ scripts/get_next_release_version.sh | 31 +++++++ scripts/publish_release.sh | 23 +++++ scripts/verify_prow_post_job_status.sh | 75 ++++++++++++++++ sec-scanners-config.yaml | 2 +- 8 files changed, 349 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/create-release.yaml create mode 100755 scripts/check_tags_in_sec_scanners_config.sh create mode 100755 scripts/create_changelog.sh create mode 100755 scripts/create_draft_release.sh create mode 100755 scripts/get_next_release_version.sh create mode 100755 scripts/publish_release.sh create mode 100755 scripts/verify_prow_post_job_status.sh diff --git a/.github/workflows/create-release.yaml b/.github/workflows/create-release.yaml new file mode 100644 index 0000000..36858f6 --- /dev/null +++ b/.github/workflows/create-release.yaml @@ -0,0 +1,91 @@ +name: Create Release +run-name: Create Release ${{ github.ref_name }} + +env: + IMAGE_REPO: europe-docker.pkg.dev/kyma-project/prod/eventing-publisher-proxy + +on: workflow_dispatch + +jobs: + verify-release: + name: Verify image version + runs-on: ubuntu-latest + outputs: + release_version: ${{ steps.release-version.outputs.release_version }} + steps: + - name: Checkout EPP repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Verify that the current branch has a name that starts with 'release-' + run: | + CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD) + if [[ "$CURRENT_BRANCH" == release-* ]]; then + echo "current_branch=$CURRENT_BRANCH" >> $GITHUB_ENV + echo "Branch name starts with 'release-'." + else + echo "Branch name does not start with 'release-'." + exit 1 + fi + + - name: Get the next release version + id: release-version + run: | + RELEASE_VERSION=$(./scripts/get_next_release_version.sh "$current_branch") + echo "release_version=$RELEASE_VERSION" >> $GITHUB_OUTPUT + + - name: Check if tags in sec_scanners_config.yaml match the release version + env: + RELEASE_VERSION: ${{ steps.release-version.outputs.release_version }} + run: ./scripts/check_tags_in_sec_scanners_config.sh $RELEASE_VERSION + + create-draft: + name: Create the draft release + needs: verify-release + runs-on: ubuntu-latest + env: + RELEASE_VERSION: ${{ needs.release-version.outputs.release_version }} + outputs: + release_id: ${{ steps.create-draft.outputs.release_id }} + steps: + - name: Checkout EPP repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Create changelog + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: ./scripts/create_changelog.sh $RELEASE_VERSION + + - name: Create the draft release + id: create-draft + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + RELEASE_ID=$(./scripts/create_draft_release.sh $RELEASE_VERSION) + echo "release_id=$RELEASE_ID" >> $GITHUB_OUTPUT + + - name: Add lightweight tag to trigger release EPP build job + run: | + git tag $RELEASE_VERSION + git push origin $RELEASE_VERSION + + - name: Verify build job status + run: ./scripts/verify_prow_post_job_status.sh ${{ github.ref_name }} 600 10 30 + + publish-release: + name: Publish release + needs: [verify-release, create-draft] + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Publish release + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: ./scripts/publish_release.sh ${{ needs.create-draft.outputs.release_id }} diff --git a/scripts/check_tags_in_sec_scanners_config.sh b/scripts/check_tags_in_sec_scanners_config.sh new file mode 100755 index 0000000..b238be9 --- /dev/null +++ b/scripts/check_tags_in_sec_scanners_config.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env bash + +############################## +# Check tags in sec-scanners-config.yaml +# Image Tag, rc-tag +############################## + + +# Get release version +DESIRED_TAG="${1:-"main"}" + +# Get eventing-publisher-proxy tag from sec-scanners-config.yaml +SEC_SCAN_TO_CHECK="${2:-europe-docker.pkg.dev/kyma-project/prod/eventing-publisher-proxy}" +IMAGE_TAG=$(cat sec-scanners-config.yaml | grep "${SEC_SCAN_TO_CHECK}" | cut -d : -f 2) + +# Get rc-tag +RC_TAG_TO_CHECK="${3:-rc-tag}" +RC_TAG=$(cat sec-scanners-config.yaml | grep "${RC_TAG_TO_CHECK}" | cut -d : -f 2 | xargs) + +# Check IMAGE_TAG and required image tag +if [[ "$IMAGE_TAG" != "$DESIRED_TAG" ]] || [[ "$RC_TAG" != "$DESIRED_TAG" ]]; then + # ERROR: Tag issue + echo "Tags are not correct: + - wanted: $DESIRED_TAG + - sec-scanner image tag: $IMAGE_TAG + - rc-tag: $RC_TAG" + exit 1 +fi + +# OK: Everything is fine +echo "Tags are correct" +exit 0 diff --git a/scripts/create_changelog.sh b/scripts/create_changelog.sh new file mode 100755 index 0000000..0ca0868 --- /dev/null +++ b/scripts/create_changelog.sh @@ -0,0 +1,56 @@ +#!/usr/bin/env bash + +PREVIOUS_RELEASE=$2 # for testability + +# standard bash error handling +set -o nounset # treat unset variables as an error and exit immediately. +set -o errexit # exit immediately when a command fails. +set -E # needs to be set if we want the ERR trap +set -o pipefail # prevents errors in a pipeline from being masked + +RELEASE_TAG=$1 + +REPOSITORY=${REPOSITORY:-kyma-project/eventing-publisher-proxy} +GITHUB_URL=https://api.github.com/repos/${REPOSITORY} +GITHUB_AUTH_HEADER="Authorization: token ${GITHUB_TOKEN}" +CHANGELOG_FILE="CHANGELOG.md" + +if [ "${PREVIOUS_RELEASE}" == "" ] +then + PREVIOUS_RELEASE=$(git describe --tags --abbrev=0) +fi + +echo "## What has changed" >> ${CHANGELOG_FILE} + +git log ${PREVIOUS_RELEASE}..HEAD --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') + if [ "${COMMIT_AUTHOR}" != "kyma-bot" ]; then + git show -s ${commit} --format="* %s by @${COMMIT_AUTHOR}" >> ${CHANGELOG_FILE} + fi +done + +NEW_CONTRIB=$$.new + +join -v2 \ +<(curl -H "${GITHUB_AUTH_HEADER}" -sS "${GITHUB_URL}/compare/$(git rev-list --max-parents=0 HEAD)...${PREVIOUS_RELEASE}" | jq -r '.commits[].author.login' | sort -u) \ +<(curl -H "${GITHUB_AUTH_HEADER}" -sS "${GITHUB_URL}/compare/${PREVIOUS_RELEASE}...HEAD" | jq -r '.commits[].author.login' | sort -u) >${NEW_CONTRIB} + +if [ -s ${NEW_CONTRIB} ] +then + echo -e "\n## New contributors" >> ${CHANGELOG_FILE} + while read -r user + do + REF_PR=$(grep "@${user}" ${CHANGELOG_FILE} | head -1 | grep -o " (#[0-9]\+)" || true) + if [ -n "${REF_PR}" ] #reference found + then + REF_PR=" in ${REF_PR}" + fi + echo "* @${user} made first contribution${REF_PR}" >> ${CHANGELOG_FILE} + done <${NEW_CONTRIB} +fi + +echo -e "\n**Full changelog**: https://github.com/$REPOSITORY/compare/${PREVIOUS_RELEASE}...${RELEASE_TAG}" >> ${CHANGELOG_FILE} + +# cleanup +rm ${NEW_CONTRIB} || echo "cleaned up" diff --git a/scripts/create_draft_release.sh b/scripts/create_draft_release.sh new file mode 100755 index 0000000..2cb5793 --- /dev/null +++ b/scripts/create_draft_release.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env bash + +# This script creates a draft release and returns its id . + +# Error handling: +set -o nounset # treat unset variables as an error and exit immediately. +set -o errexit # exit immediately when a command fails. +set -E # needs to be set if we want the ERR trap +set -o pipefail # prevents errors in a pipeline from being masked + +RELEASE_TAG=$1 + +REPOSITORY=${REPOSITORY:-kyma-project/eventing-publisher-proxy} +GITHUB_URL=https://api.github.com/repos/${REPOSITORY} +GITHUB_AUTH_HEADER="Authorization: Bearer ${GITHUB_TOKEN}" +CHANGELOG_FILE=$(cat CHANGELOG.md) + +# Create the json payload to create a draft release. +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 + }') + +# Send the payload to github to create the draft release. The response contains the id of the release. +CURL_RESPONSE=$(curl -L \ + -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 draft release id. +echo "$(echo $CURL_RESPONSE | jq -r ".id")" diff --git a/scripts/get_next_release_version.sh b/scripts/get_next_release_version.sh new file mode 100755 index 0000000..6d0e090 --- /dev/null +++ b/scripts/get_next_release_version.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash + +# This script returns the next release version depending on the given release-major.minor version + +# standard bash error handling +set -o nounset # treat unset variables as an error and exit immediately. +set -o errexit # exit immediately when a command fails. +set -E # needs to be set if we want the ERR trap +set -o pipefail # prevents errors in a pipeline from being masked + +BRANCH_NAME="$1" + +MAJOR_MINOR_VERSION=${BRANCH_NAME#"release-"} + +RESPONSE=$(curl -s "https://api.github.com/repos/kyma-project/eventing-publisher-proxy/releases") + +LATEST_PATCH_VERSION=$(echo "$RESPONSE" | jq -r --arg version "$MAJOR_MINOR_VERSION" ' + map(select(.tag_name | startswith("v" + $version + ".") or startswith($version + "."))) + | map(.tag_name | ltrimstr("v") | ltrimstr($version + ".")) + | map(select(test("^[0-9]+$"))) + | map(. | tonumber) + | max + | if . then "\($version).\(.)" else null end +') + +# If no version found, set the patch version to 0 +NEXT_PATCH_VERSION=$(echo "$LATEST_PATCH_VERSION" | awk -F'.' '{print ($3 == "" || $3 == "unset") ? 0 : $3 + 1}') + +# Print the next release version +NEXT_RELEASE_VERSION="$MAJOR_MINOR_VERSION.$NEXT_PATCH_VERSION" +echo $NEXT_RELEASE_VERSION diff --git a/scripts/publish_release.sh b/scripts/publish_release.sh new file mode 100755 index 0000000..0c6194a --- /dev/null +++ b/scripts/publish_release.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash + +# This script publishes a draft release + +# standard bash error handling +set -o nounset # treat unset variables as an error and exit immediately. +set -o errexit # exit immediately when a command fails. +set -E # needs to be set if we want the ERR trap +set -o pipefail # prevents errors in a pipeline from being masked + +RELEASE_ID=$1 + +REPOSITORY=${REPOSITORY:-kyma-project/eventing-publisher-proxy} +GITHUB_URL=https://api.github.com/repos/${REPOSITORY} +GITHUB_AUTH_HEADER="Authorization: Bearer ${GITHUB_TOKEN}" + +CURL_RESPONSE=$(curl -L \ + -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}') diff --git a/scripts/verify_prow_post_job_status.sh b/scripts/verify_prow_post_job_status.sh new file mode 100755 index 0000000..961925c --- /dev/null +++ b/scripts/verify_prow_post_job_status.sh @@ -0,0 +1,75 @@ +#!/usr/bin/env bash + +echo "Checking status of POST Jobs for Eventing-Publisher-Proxy" + +REF_NAME="${1:-"main"}" +TIMEOUT_TIME="${2:-600}" +INTERVAL_TIME="${3:-3}" +INITIAL_WAIT_TIME="${4:-30}" + +# Generate job Status URL +STATUS_URL="https://api.github.com/repos/kyma-project/eventing-publisher-proxy/commits/${REF_NAME}/status" + +# Dates +START_TIME=$(date +%s) +TODAY_DATE=$(date '+%Y-%m-%d') + +# Retry function +function retry { + + # Get status result + local statusresult=$(curl -L -H "Accept: application/vnd.github+json" -H "X-GitHub-Api-Version: 2022-11-28" ${STATUS_URL}) + + # Get overall state + fullstatus=$(echo $statusresult | jq '.state' | tr -d '"') + + # Collect latest run related data + local latestrun=$(echo $statusresult | jq '.statuses[-1]') + local latestrun_state=$(echo $latestrun | jq '.state' | tr -d '"') + local latestrun_createdat=$(echo $latestrun | jq '.created_at' | tr -d '"') + local latestrun_targeturl=$(echo $latestrun | jq '.target_url' | tr -d '"') + + # Check Today's run data + if [[ $latestrun_createdat == *"$TODAY_DATE"* ]]; then + echo $latestrun_createdat + echo $latestrun_state + echo $latestrun_targeturl + fi + + # Show all execution for Today + echo $statusresult | jq --arg t $TODAY_DATE '.statuses[]|select(.created_at | contains($t))' + + # Date time for time-out + local CURRENT_TIME=$(date +%s) + local elapsed_time=$((CURRENT_TIME - START_TIME)) + + # Check time-out + if [ $elapsed_time -ge $TIMEOUT_TIME ]; then + echo "Timeout reached. Exiting." + exit 1 + fi + + if [ "$fullstatus" == "success" ]; then + echo "Success!" + elif [ "$fullstatus" == "failed" ]; then + # Show overall state to user + echo "$statusresult" + echo "Failure! Exiting with an error." + exit 1 + elif [ "$fullstatus" == "pending" ]; then + echo "Status is '$fullstatus'. Retrying in $INTERVAL_TIME seconds..." + sleep $INTERVAL_TIME + else + echo "Invalid result: $result" + exit 1 + fi + +} + +# Initial wait +sleep $INITIAL_WAIT_TIME +# Call retry function +retry +while [ "$fullstatus" == "pending" ]; do + retry +done diff --git a/sec-scanners-config.yaml b/sec-scanners-config.yaml index c834a66..7e8fba4 100644 --- a/sec-scanners-config.yaml +++ b/sec-scanners-config.yaml @@ -1,7 +1,7 @@ module-name: eventing-publisher-proxy rc-tag: 0.0.0 protecode: - - europe-docker.pkg.dev/kyma-project/prod/event-publisher-proxy:v20231025-3f5d1600 + - europe-docker.pkg.dev/kyma-project/prod/eventing-publisher-proxy:0.0.0 whitesource: language: golang-mod subprojects: false