Skip to content

Commit

Permalink
Merge pull request #46 from adambkaplan/fix-release
Browse files Browse the repository at this point in the history
Verifiable Release Process
  • Loading branch information
openshift-merge-robot authored Dec 17, 2021
2 parents 621ca24 + 8a47952 commit fb59ae9
Show file tree
Hide file tree
Showing 4 changed files with 239 additions and 24 deletions.
151 changes: 151 additions & 0 deletions .github/draft_release_notes.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
#! /bin/bash
# Copyright The Shipwright Contributors
#
# SPDX-License-Identifier: Apache-2.0

# This script assumes the GITHUB_TOKEN and REPOSITORY environment variables have been set;
# The PREVIOUS_TAG environment variable is optional - if omitted it defaults to the first commit of
# the repository.
# The script produces a 'Changes.md' file as its final output;
# the file 'last-300-prs-with-release-note.txt' that is produces is intermediate data; it is not
# pruned for now to assist development of the release notes process (we are still curating all this)

if [ -z ${GITHUB_TOKEN+x} ]; then
echo "Error: GITHUB_TOKEN is not set"
exit 1
fi

if [ -z "${REPOSITORY}" ]; then
echo "Error: GitHub repository was not set"
exit 1
fi

# sudo apt-get -y update
# sudo apt-get -y install wget curl git
curl -L https://github.com/github/hub/releases/download/v2.14.2/hub-linux-amd64-2.14.2.tgz | tar xzf -
PWD="$(pwd)"
export PATH=$PWD/hub-linux-amd64-2.14.2/bin:$PATH
git fetch --all --tags --prune --force

if [ -z ${PREVIOUS_TAG+x} ]; then
PREVIOUS_TAG=$(git rev-list --max-parents=0 HEAD)
fi

echo "# Draft Release changes since ${PREVIOUS_TAG}" > Changes.md
echo > Features.md
echo "## Features" >> Features.md
echo > Fixes.md
echo "## Fixes" >> Fixes.md
echo > API.md
echo "## API Changes" >> API.md
echo > Docs.md
echo "## Docs" >> Docs.md
echo > Misc.md
echo "## Misc" >> Misc.md

# this effectively gets the commit associated with github.event.inputs.tags
COMMON_ANCESTOR=$(git merge-base $PREVIOUS_TAG HEAD)
echo "COMMON_ANCESTOR is ${COMMON_ANCESTOR}"
# in theory the new tag has not been created yet; do we want another input that specifies the existing
# commit desired for drafting the release? for now, we are using HEAD in the above git merge-base call
# and PR cross referencing below

# use of 'hub', which is an extension of the 'git' CLI, allows for pulling of PRs, though we can't search based on commits
# associated with those PRs, so we grab a super big number, 300, which should guarantee grabbing all the PRs back to
# github.events.inputs.tags; we use grep -v to filter out release-note-none and release-note-action-required.
# NOTE: investigated using the new 'gh' cli command, but its 'gh pr list' does not currently support the -f option so
# staying with 'hub' for now.
hub pr list --state merged -L 300 -f "%sm;%au;%i;%t;%L%n" | grep -E ", release-note|release-note," | grep -v release-note-none | grep -v release-note-action-required > last-300-prs-with-release-note.txt
# this is for debug while we sort out env differences between Gabe's fedora and GitHub Actions' ubuntu
echo "start dump last-300-prs-with-release-note.txt for potential debug"
cat last-300-prs-with-release-note.txt
echo "end dump last-300-prs-with-release-note.txt for potential debug"
# now we cylce through last-300-prs-with-release-note.txt, filtering out stuff that is too old or other anomalies,
# and update Changes.md with the release note.
while IFS= read -r pr; do
SHA=$(echo $pr | cut -d';' -f1)

# skip the common ancestor, which in essences is the commit associated with the tag github.event.inputs.tags
if [ "$SHA" == "$COMMON_ANCESTOR" ]; then
continue
fi

# stylistic clarification, purposefully avoiding slicker / cleverer / more compact scripting conventions

# this makes sure that this PR has merged
git merge-base --is-ancestor $SHA HEAD
rc=$?
if [ ${rc} -eq 1 ]; then
continue
fi
# otherwise, if the current commit from the last 300 PRs is not an ancestor of github.event.inputs.tags, we have gone too far, so skip
git merge-base --is-ancestor $COMMON_ANCESTOR $SHA
rc=$?
if [ ${rc} -eq 1 ]; then
continue
fi
# if we are at this point, we have a PR with a release note to add
AUTHOR=$(echo $pr | cut -d';' -f2)
PR_NUM=$(echo $pr | cut -d';' -f3)
echo "Examining from ${AUTHOR} PR ${PR_NUM}"
PR_BODY=$(wget -q -O- "https://api.github.com/repos/${REPOSITORY}/issues/${PR_NUM:1}")
echo "$PR_BODY" | grep -oPz '(?s)(?<=```release-note..)(.+?)(?=```)' > /dev/null 2>&1
rc=$?
if [ ${rc} -eq 1 ]; then
echo "First validation: the release-note field for PR ${PR_NUM} was not properly formatted. Until it is fixed, it will be skipped for release note inclusion."
echo "See the PR template at https://raw.githubusercontent.com/${REPOSITORY}/master/.github/pull_request_template.md for verification steps"
continue
fi
PR_BODY_FILTER_ONE=$(echo $PR_BODY | grep -oPz '(?s)(?<=```release-note..)(.+?)(?=```)')
echo $PR_BODY_FILTER_ONE | grep -avP '\W*(Your release note here|action required: your release note here|NONE)\W*' > /dev/null 2>&1
rc=$?
if [ ${rc} -eq 1 ]; then
echo "Second validation: the release-note field for PR ${PR_NUM} was not properly formatted. Until it is fixed, it will be skipped for release note inclusion."
echo "See the PR template at https://raw.githubusercontent.com/${REPOSITORY}/master/.github/pull_request_template.md for verification steps"
continue
fi
PR_RELEASE_NOTE=$(echo $PR_BODY_FILTER_ONE | grep -avP '\W*(Your release note here|action required: your release note here|NONE)\W*')
PR_RELEASE_NOTE_NO_NEWLINES=$(echo $PR_RELEASE_NOTE | sed 's/\\n//g' | sed 's/\\r//g')
MISC=yes
echo $pr | grep 'kind/bug'
rc=$?
if [ ${rc} -eq 0 ]; then
echo >> Fixes.md
echo "$PR_NUM by @${AUTHOR}: $PR_RELEASE_NOTE_NO_NEWLINES" >> Fixes.md
MISC=no
fi
echo $pr | grep 'kind/api-change'
rc=$?
if [ ${rc} -eq 0 ]; then
echo >> API.md
echo "$PR_NUM by @${AUTHOR}: $PR_RELEASE_NOTE_NO_NEWLINES" >> API.md
MISC=no
fi
echo $pr | grep 'kind/feature'
rc=$?
if [ ${rc} -eq 0 ]; then
echo >> Features.md
echo "$PR_NUM by @${AUTHOR}: $PR_RELEASE_NOTE_NO_NEWLINES" >> Features.md
MISC=no
fi
echo $pr | grep 'kind/documentation'
rc=$?
if [ ${rc} -eq 0 ]; then
echo >> Docs.md
echo "$PR_NUM by @${AUTHOR}: $PR_RELEASE_NOTE_NO_NEWLINES" >> Docs.md
MISC=no
fi
if [ "$MISC" == "yes" ]; then
echo >> Misc.md
echo "$PR_NUM by @${AUTHOR}: $PR_RELEASE_NOTE_NO_NEWLINES" >> Misc.md
fi
# update the PR template if our greps etc. for pulling the release note changes
#PR_RELEASE_NOTE=$(wget -q -O- https://api.github.com/repos/${REPOSITORY}/issues/${PR_NUM:1} | grep -oPz '(?s)(?<=```release-note..)(.+?)(?=```)' | grep -avP '\W*(Your release note here|action required: your release note here|NONE)\W*')
echo "Added from ${AUTHOR} PR ${PR_NUM:1} to the release note draft"
done < last-300-prs-with-release-note.txt

cat Features.md >> Changes.md
cat Fixes.md >> Changes.md
cat API.md >> Changes.md
cat Docs.md >> Changes.md
cat Misc.md >> Changes.md
73 changes: 50 additions & 23 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,36 +6,63 @@ on:
release:
description: 'Desired tag'
required: true
tags:
previous-tag:
description: 'Previous tag'
required: true

required: false
jobs:
release:
if: ${{ github.repository == 'shipwright-io/operator' }}
# if: ${{ github.repository == 'shipwright-io/build' }}
runs-on: ubuntu-latest
permissions:
id-token: write # To be able to get OIDC ID token to sign images.
contents: write # To be able to update releases.
packages: write # To be able to push images and signatures.
env:
IMAGE_HOST: ghcr.io
IMAGE_NAMESPACE: ${{ github.repository }}
VERSION: ${{ github.event.inputs.release }}
steps:
- name: Checkout code
uses: actions/checkout@v2

- name: Install Go
uses: actions/setup-go@v2

- name: Login to docker
- uses: actions/checkout@v2
with:
fetch-depth: 0 # Fetch all history, needed for release note generation.
# Install tools
- uses: actions/setup-go@v2
with:
go-version: 1.17.x
- uses: sigstore/cosign-installer@v1.2.0
- name: Build Release Images
env:
REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }}
REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME }}
run: |
echo "$REGISTRY_PASSWORD" | docker login --username "$REGISTRY_USERNAME" --password-stdin
- name: Build and upload Operator Image
REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }}
REGISTRY_USERNAME: ${{ github.repository_owner }}
run:
make release
- name: Sign released images
env:
VERSION: ${{ github.events.input.release }}
# This enables keyless mode
# (https://github.com/sigstore/cosign/blob/main/KEYLESS.md) which signs
# images using an ephemeral key tied to the GitHub Actions identity via
# OIDC.
COSIGN_EXPERIMENTAL: "true"
run: |
make ko-publish
- name: Build and upload Operator Bundle Image
grep -o "ghcr.io[^\"]*" "${GITHUB_WORKSPACE}/bundle/manifests/shipwright-operator.clusterserviceversion.yaml" | xargs cosign sign \
-a sha=${{ github.sha }} \
-a run_id=${{ github.run_id }} \
-a run_attempt=${{ github.run_attempt }}
- name: Build Release Changelog
env:
VERSION: ${{ github.events.input.release }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PREVIOUS_TAG: ${{ github.event.inputs.previous-tag }}
REPOSITORY: ${{ github.repository }}
run: |
make bundle-push
"${GITHUB_WORKSPACE}/.github/draft_release_notes.sh"
- name: Draft release
id: draft_release
uses: actions/create-release@v1
with:
release_name: ${{ github.event.inputs.release }}
tag_name: ${{ github.event.inputs.release }}
draft: true
prerelease: true
body_path: Changes.md
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
8 changes: 7 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@ GOBIN=$(shell go env GOBIN)
endif

CONTAINER_ENGINE ?= docker
IMAGE_REPO ?= quay.io/shipwright
IMAGE_HOST ?= quay.io
IMAGE_NAMESPACE ?= shipwright
IMAGE_REPO ?= $(IMAGE_HOST)/$(IMAGE_NAMESPACE)
TAG ?= $(VERSION)
IMAGE_PUSH ?= true

Expand Down Expand Up @@ -186,6 +188,10 @@ bundle-build: bundle
bundle-push: bundle-build
$(CONTAINER_ENGINE) push $(BUNDLE_IMG)

.PHONY: release
release: ko
CONTAINER_ENGINE="$(CONTAINER_ENGINE)" KO_BIN="$(KO)" IMAGE_HOST=${IMAGE_HOST} IMAGE_NAMESPACE=${IMAGE_NAMESPACE} TAG=${TAG} hack/release.sh

# Install OLM on the current cluster
.PHONY: install-olm
install-olm: operator-sdk
Expand Down
31 changes: 31 additions & 0 deletions hack/release.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#!/usr/bin/env bash

set -euo pipefail

KO_BIN=${KO_BIN:-ko}
CONTAINER_ENGINE=${CONTAINER_ENGINE:-docker}
USERNAME=${REGISTRY_USERNAME:-""}
PASSWORD=${REGISTRY_PASSWORD:-""}

function login() {
echo "Logging into container registry $IMAGE_HOST"
echo "$PASSWORD" | ${KO_BIN} login -u "$USERNAME" --password-stdin "$IMAGE_HOST"
echo "$PASSWORD" | ${CONTAINER_ENGINE} login -u "$USERNAME" --password-stdin "$IMAGE_HOST"
}

if [ -z "${IMAGE_HOST}" ]; then
echo "Error: image host is not set"
exit 1
fi

if [ -n "${USERNAME}" ] && [ -n "${PASSWORD}" ]; then
login
else
echo "Skipping registry login - build will rely on existing credentials"
fi

echo "Building and pushing operator image"
make ko-publish

echo "Regenerating bundle and pushing bundle image"
make bundle-push CONTAINER_ENGINE="${CONTAINER_ENGINE}"

0 comments on commit fb59ae9

Please sign in to comment.