From 464006f92d65add31d0960b1abf47e4de2b224ee Mon Sep 17 00:00:00 2001 From: Pranshu Srivastava Date: Mon, 19 Feb 2024 13:33:09 +0530 Subject: [PATCH] chore: automate release workflow Signed-off-by: Pranshu Srivastava --- .github/workflows/release.yml | 86 ++++++++++++++++++++++++++ Makefile | 2 +- README.md | 2 +- scripts/generate-release-notes.sh | 20 ++++++ scripts/update-compatibility-matrix.sh | 53 ++++++++++++++++ tools/tools.go | 2 +- 6 files changed, 162 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/release.yml create mode 100755 scripts/generate-release-notes.sh create mode 100755 scripts/update-compatibility-matrix.sh diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000000..7e967a70c4 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,86 @@ +name: Release + +on: + push: + tags: + - v* + +permissions: + contents: write + pull-requests: write + +env: + GO_VERSION: "^1.21" + GOLANGCI_LINT_VERSION: "v1.54.2" + E2E_SETUP_KIND: yes + E2E_SETUP_KUBECTL: yes + +jobs: + release: + runs-on: ubuntu-latest + steps: + - name: Extract pushed release tag + id: extract_tag + run: echo "::set-output name=tag::${GITHUB_REF##*/}" + - name: Checkout into the corresponding release branch + uses: actions/checkout@v4 + - name: Create VCS sandbox + run: | + git checkout -b release-${{ steps.extract_tag.outputs.tag }} + - name: Set up the Go@${{ env.GO_VERSION }} environment + uses: actions/setup-go@v5 + with: + go-version: ${{ env.GO_VERSION }} + - name: Get the pushed tag + id: get_tag + run: echo ::set-output name=tag::${GITHUB_REF#refs/*/} + - name: Store the previous tag + id: get_previous_tag + run: echo ::set-output name=previous_tag::$(cat VERSION | cut -d. -f1,2) + - name: Update the VERSION manifest to the pushed tag + run: echo "${{ steps.get_tag.outputs.tag }}" | sed 's/^v//' > VERSION + - name: Update the compatibility matrix (README.md) + run: ./scripts/update-compatibility-matrix.sh + - name: Generate the release notes (CHANGELOG.md) + run: ./scripts/generate-release-notes.sh + - name: Install tools + run: make install-tools + - name: Lint + run: | + curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | \ + sh -s -- -b $(go env GOPATH)/bin ${{ env.GOLANGCI_LINT_VERSION }} + make lint-fix + - name: Generate manifests + run: make examples + - name: Run rule tests + run: PROMTOOL_CLI=./promtool make install-promtool test-rules + - name: Run unit tests + run: make test-unit + - name: Run end-to-end tests + run: make e2e + - name: Update the remote and commit the changes + run: | + git config --local user.email "ksm-release-bot@mock-k8s.io" + git config --local user.name "KSM Release Bot" + git add . + git commit -m "chore: Cut ${{ steps.get_tag.outputs.tag }}" + git push origin HEAD:release-${{ steps.get_tag.outputs.tag }} --force-with-lease + - name: Benchmark tests + run: LATEST_RELEASE_BRANCH=release-${{ steps.get_previous_tag.outputs.previous_tag }} make test-benchmark-compare + - name: Validate docs + run: make doccheck + - name: Validate manifests + run: make validate-manifests + - name: Validate go modules + run: make validate-modules + - name: Create a pull request + run: | + gh pr create \ + --title "chore: Cut ${{ steps.get_tag.outputs.tag }}" \ + --body "This PR was automatically created by the release workflow, and should be targeted to the corresponding release branch." \ + --base main \ + --head release-${{ steps.get_tag.outputs.tag }} + --reviewer @sig-instrumentation-approvers \ + --assignee @sig-instrumentation-leads + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/Makefile b/Makefile index 885154c430..76f86c6ea5 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ REGISTRY ?= gcr.io/k8s-staging-kube-state-metrics TAG_PREFIX = v VERSION = $(shell cat VERSION) TAG ?= $(TAG_PREFIX)$(VERSION) -LATEST_RELEASE_BRANCH := release-$(shell grep -ohE "[0-9]+.[0-9]+" VERSION) +LATEST_RELEASE_BRANCH ?= release-$(shell grep -ohE "[0-9]+.[0-9]+" VERSION) BRANCH = $(strip $(shell git rev-parse --abbrev-ref HEAD)) DOCKER_CLI ?= docker PROMTOOL_CLI ?= promtool diff --git a/README.md b/README.md index 702f7cc072..05854bc447 100644 --- a/README.md +++ b/README.md @@ -270,7 +270,7 @@ To have Prometheus discover kube-state-metrics instances it is advised to create kubectl create clusterrolebinding cluster-admin-binding --clusterrole=cluster-admin --user=$(gcloud info --format='value(config.account)') ``` -Note that your GCP identity is case sensitive but `gcloud info` as of Google Cloud SDK 221.0.0 is not. This means that if your IAM member contains capital letters, the above one-liner may not work for you. If you have 403 forbidden responses after running the above command and `kubectl apply -f examples/standard`, check the IAM member associated with your account at https://console.cloud.google.com/iam-admin/iam?project=PROJECT_ID. If it contains capital letters, you may need to set the --user flag in the command above to the case-sensitive role listed at https://console.cloud.google.com/iam-admin/iam?project=PROJECT_ID. +Note that your GCP identity is case sensitive but `gcloud info` as of Google Cloud SDK 221.0.0 is not. This means that if your IAM member contains capital letters, the above one-liner may not work for you. If you have 403 forbidden responses after running the above command and `kubectl apply -f examples/standard`, check the IAM member associated with your account at . If it contains capital letters, you may need to set the --user flag in the command above to the case-sensitive role listed at . After running the above, if you see `Clusterrolebinding "cluster-admin-binding" created`, then you are able to continue with the setup of this service. diff --git a/scripts/generate-release-notes.sh b/scripts/generate-release-notes.sh new file mode 100755 index 0000000000..9d1cafa487 --- /dev/null +++ b/scripts/generate-release-notes.sh @@ -0,0 +1,20 @@ +#!/bin/bash +set -exuo pipefail + +CHANGELOG="CHANGELOG.md" + +# Fetch the latest two tags. +# NOTE: This assumes a patch release for an older release is not made after a later minor release (which is the case right now). +# Backports will be handled manually, on a case-by-case basis. +last_two_tags=$(git tag --sort=-creatordate | head -n 2) + +# read will return a non-zero exit code when it hits EOF, so we need to disable the exit-on-error option. +set +e +IFS=$'\n' read -d '' -r -a tags <<< "$last_two_tags" +set -e + +# Get the commits between the two tags. +commits=$(git log --pretty=format:"%h: %an <%ae>: %s" "${tags[1]}".."${tags[0]}" | grep -i -v -e 'fixup' -e 'merge' -e 'dependabot') + +# Update the changelog with the latest release notes. +echo -e "## ${tags[0]} / $(date "+%Y-%m-%d")\n\n$commits\n\n$(cat $CHANGELOG)" > $CHANGELOG diff --git a/scripts/update-compatibility-matrix.sh b/scripts/update-compatibility-matrix.sh new file mode 100755 index 0000000000..b5bdfe3f94 --- /dev/null +++ b/scripts/update-compatibility-matrix.sh @@ -0,0 +1,53 @@ +#!/bin/bash +set -exuo pipefail + +# Determine the OS to use the correct version of sed and awk. +# shellcheck disable=SC2209 +SED=sed +# shellcheck disable=SC2209 +AWK=awk +if [[ $(uname) == "Darwin" ]]; then + # Check if gnu-sed and gawk are installed. + if ! command -v gsed &> /dev/null; then + echo "gnu-sed is not installed. Please install it using 'brew install gnu-sed'." >&2 + exit 1 + fi + if ! command -v gawk &> /dev/null; then + echo "gawk is not installed. Please install it using 'brew install gawk'." >&2 + exit 1 + fi + AWK=gawk + SED=gsed +fi + +# Fetch the latest tag. +git fetch --tags +latest_tag=$(git describe --tags "$(git rev-list --tags --max-count=1)") + +# Determine if it's a patch release or not (minor and major releases are handled the same way in the compatibility matrix). +if [[ $latest_tag =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + IFS='.' read -ra TAG <<< "$latest_tag" + if [[ ${#TAG[@]} -eq 2 || ${#TAG[@]} -eq 3 && ${TAG[2]} -eq 0 ]]; then + # Prep for a non-patch release. + # shellcheck disable=SC2016 + main_client_go_version=$($AWK '/\| \*\*main\*\*/ {print $4}' README.md) + $SED -i "/|\s*\*\*main\*\*\s*|\s*$main_client_go_version\s*|/i| \*\*$latest_tag\*\* | $main_client_go_version |" README.md + # shellcheck disable=SC2016 + oldest_supported_client_go_version=$($AWK '/\| kube-state-metrics \| Kubernetes client-go Version \|/ {getline; getline; print $4; exit}' README.md) + # Remove the first row (i.e., the oldest supported client-go version). + $SED -i "/|\s*\*\*.*\*\*\s*|\s*$oldest_supported_client_go_version\s*|/d" README.md + else + # Prep for a patch release. + minor_release="${TAG[0]}.${TAG[1]}" + # Get the client-go version of the corresponding minor release row (that needs to be updated with the patch release version). + # shellcheck disable=SC2016 + last_client_go_version=$($AWK '/\| kube-state-metrics \| Kubernetes client-go Version \|/ {getline; getline; getline; getline; getline; getline; print $4; exit}' README.md) + # Update the row with the latest tag and client-go version. + $SED -i "s/|\s*\*\*$minor_release.*\*\*\s*|\s*$last_client_go_version\s*|/| \*\*$latest_tag\*\* | $last_client_go_version |/" README.md + fi +else + echo -e "Bad tag format: $latest_tag, expected one of the following formats:\n + * vMAJOR.MINOR (non-patch release)\n + * vMAJOR.MINOR.PATCH (patch release)" + exit 1 +fi diff --git a/tools/tools.go b/tools/tools.go index 0f2db0a141..bd493c9915 100644 --- a/tools/tools.go +++ b/tools/tools.go @@ -20,7 +20,7 @@ limitations under the License. package tools import ( - _ "github.com/Kunde21/markdownfmt/v3" + _ "github.com/Kunde21/markdownfmt/v3/markdown" _ "github.com/brancz/gojsontoyaml" _ "github.com/campoy/embedmd" _ "github.com/google/go-jsonnet/cmd/jsonnet"