From 4bc7a166a5af7eaaab2d142bbc5ad1cac2231230 Mon Sep 17 00:00:00 2001 From: Arjun Raja Yogidas Date: Sun, 8 Sep 2024 23:48:04 +0000 Subject: [PATCH] feat: add release workflow Signed-off-by: Arjun Raja Yogidas --- .github/workflows/release-automation.yaml | 88 +++++++++++++++++ Makefile | 6 ++ scripts/create-releases.sh | 77 +++++++++++++++ scripts/verify-release-artifacts.sh | 110 ++++++++++++++++++++++ 4 files changed, 281 insertions(+) create mode 100644 .github/workflows/release-automation.yaml create mode 100644 scripts/create-releases.sh create mode 100644 scripts/verify-release-artifacts.sh diff --git a/.github/workflows/release-automation.yaml b/.github/workflows/release-automation.yaml new file mode 100644 index 0000000..3881b97 --- /dev/null +++ b/.github/workflows/release-automation.yaml @@ -0,0 +1,88 @@ +name: Release + +on: + push: + tags: + - 'v[0-9]+.[0-9]+.[0-9]+' + pull_request: + branches: ['main', 'release/**'] + paths: + - '.github/workflows/releases.yml' + - 'scripts/**' + - 'Makefile' + +env: + GO_VERSION: '1.23.0' + +permissions: + contents: write + deployments: write + +jobs: + generate-artifacts: + runs-on: ubuntu-latest + env: + # Set during setup. + RELEASE_TAG: '' + DYNAMIC_BINARY_NAME: '' + STATIC_BINARY_NAME: '' + steps: + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - name: Export cleaned release tag + run: | + export release_tag=${GITHUB_REF#refs/*/} # Strip down to raw tag name + echo $release_tag + echo "RELEASE_TAG=${release_tag}" >> $GITHUB_ENV + - name: Create dummy environment for release + if: github.event_name == 'pull_request' + run: | + touch THIRD_PARTY_LICENSES + echo "RELEASE_TAG=v0.0.0" >> $GITHUB_ENV + - name: Setup variables and release directories + run: | + export release_tag=${{ env.RELEASE_TAG }} + export release_version=${release_tag/v/} # Remove v from tag name + echo "DYNAMIC_BINARY_NAME=finch-daemon-${release_version}-linux-amd64.tar.gz" >> $GITHUB_ENV + + mkdir release + - name: Create release binaries + run: make RELEASE_TAG=${{ env.RELEASE_TAG }} release + - uses: actions/upload-artifact@v4 + with: + name: artifacts + path: release/ + if-no-files-found: error + + outputs: + release_tag: ${{ env.RELEASE_TAG }} + dynamic_binary_name: ${{ env.DYNAMIC_BINARY_NAME }} + static_binary_name: ${{ env.STATIC_BINARY_NAME }} + + validate-artifacts: + needs: generate-artifacts + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/download-artifact@v4 + with: + name: artifacts + path: release/ + - run: bash scripts/verify-release-artifacts.sh ${{ needs.generate-artifacts.outputs.release_tag }} + + create-release: + if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') + needs: [generate-artifacts, validate-artifacts] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/download-artifact@v4 + with: + name: artifacts + - uses: softprops/action-gh-release@v2 + with: + draft: true + prerelease: false + generate_release_notes: false + files: | + ${{ needs.generate-artifacts.outputs.dynamic_binary_name }} + ${{ needs.generate-artifacts.outputs.dynamic_binary_name }}.sha256sum diff --git a/Makefile b/Makefile index 8cee17b..ac967f1 100644 --- a/Makefile +++ b/Makefile @@ -3,6 +3,7 @@ GINKGO = go run github.com/onsi/ginkgo/v2/ginkgo GFLAGS ?= --race --randomize-all --randomize-suites BIN = $(PWD)/bin FINCH_ROOT ?= /Applications/Finch +FINCH_DAEMON_PROJECT_ROOT ?= $(shell pwd) # Linux or macOS targets .PHONY: build @@ -91,3 +92,8 @@ run-e2e-tests: linux DOCKER_API_VERSION="v1.41" \ RUN_E2E_TESTS=1 \ $(GINKGO) $(GFLAGS) ./e2e/... + +.PHONY: release +release: linux + @echo "$@" + @$(FINCH_DAEMON_PROJECT_ROOT)/scripts/create-releases.sh $(RELEASE_TAG) diff --git a/scripts/create-releases.sh b/scripts/create-releases.sh new file mode 100644 index 0000000..58262bf --- /dev/null +++ b/scripts/create-releases.sh @@ -0,0 +1,77 @@ +#!/bin/bash + +# Copyright The Finch Daemon Authors. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# A script to generate release artifacts. +# This will create a folder in your project root called release. +# This will contain the dynamic + static binaries +# as well as their respective sha256 checksums. +# NOTE: this will mutate your $FINCH_DAEMON_PROJECT_ROOT/out folder. + +set -eux -o pipefail + +CUR_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +FINCH_DAEMON_PROJECT_ROOT="$(cd -- "$CUR_DIR"/.. && pwd)" +OUT_DIR="${FINCH_DAEMON_PROJECT_ROOT}/bin" +RELEASE_DIR="${FINCH_DAEMON_PROJECT_ROOT}/release" +LICENSE_FILE=${FINCH_DAEMON_PROJECT_ROOT}/THIRD_PARTY_LICENSES +NOTICE_FILE=${FINCH_DAEMON_PROJECT_ROOT}/NOTICE.md +TAG_REGEX="v[0-9]+.[0-9]+.[0-9]+" + +ARCH="" +case $(uname -m) in + x86_64) ARCH="amd64" ;; + *) echo "Error: Unsupported arch"; exit 1 ;; +esac + +if [ "$#" -ne 1 ]; then + echo "Expected 1 parameter, got $#." + echo "Usage: $0 [release_tag]" + exit 1 +fi + +if ! [[ "$1" =~ $TAG_REGEX ]]; then + echo "Improper tag format. Format should match regex $TAG_REGEX" + exit 1 +fi + +if [ -d "$RELEASE_DIR" ]; then + rm -rf "${RELEASE_DIR:?}"/* +else + mkdir "$RELEASE_DIR" +fi + +release_version=${1/v/} # Remove v from tag name +dynamic_binary_name=finch-daemon-${release_version}-linux-${ARCH}.tar.gz +# static_binary_name=finch-daemon-${release_version}-linux-${ARCH}-static.tar.gz + +make build +cp "$NOTICE_FILE" "$LICENSE_FILE" "${OUT_DIR}" +pushd "$OUT_DIR" +tar -czvf "$RELEASE_DIR"/"$dynamic_binary_name" -- * +popd +rm -rf "{$OUT_DIR:?}"/* + +# STATIC=1 make build +# cp "$NOTICE_FILE" "$LICENSE_FILE" "$OUT_DIR" +# pushd "$OUT_DIR" +# tar -czvf "$RELEASE_DIR"/"$static_binary_name" -- * +# popd +# rm -rf "{$OUT_DIR:?}"/* + +pushd "$RELEASE_DIR" +sha256sum "$dynamic_binary_name" > "$RELEASE_DIR"/"$dynamic_binary_name".sha256sum +# sha256sum "$static_binary_name" > "$RELEASE_DIR"/"$static_binary_name".sha256sum +popd diff --git a/scripts/verify-release-artifacts.sh b/scripts/verify-release-artifacts.sh new file mode 100644 index 0000000..7f245c0 --- /dev/null +++ b/scripts/verify-release-artifacts.sh @@ -0,0 +1,110 @@ +# #!/usr/bin/env bash + +# # Copyright The Soci Snapshotter Authors. + +# # Licensed under the Apache License, Version 2.0 (the "License"); +# # you may not use this file except in compliance with the License. +# # You may obtain a copy of the License at + +# # http://www.apache.org/licenses/LICENSE-2.0 + +# # Unless required by applicable law or agreed to in writing, software +# # distributed under the License is distributed on an "AS IS" BASIS, +# # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# # See the License for the specific language governing permissions and +# # limitations under the License. + +# # A script to verify artifacts from release automation. + +set -o pipefail + +cur_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +dinch_daemon_project_root="$(cd -- "$cur_dir"/.. && pwd)" +release_dir="${dinch_daemon_project_root}/release" + +arch="amd64" + +# if [ "${arch}" != "amd64" ] ; then +# echo "Error: unsupported arch" +# exit 1 +# fi + +function usage { + echo "Usage: $0 " +} + +if [ $# -eq 0 ]; then + echo "$0: Missing required argument" + usage + exit 1 +fi + +if [ ! -d "$release_dir" ]; then + echo "$0: Release directory not found in $release_dir" + exit 1 +fi + +release_tag=$1 +# Strip 'v' from release tag. +release_version=${release_tag/v/} + +pushd "$release_dir" || exit 1 +tarballs=("finch-daemon-${release_version}-linux-${arch}.tar.gz") +expected_contents=("finch-daemon" "THIRD_PARTY_LICENSES" "NOTICE.md") +release_is_valid=true + +for t in "${tarballs[@]}"; do + # Verify each expected tarball was generated. + if [[ ! -e $t ]]; then + echo "$t: MISSING" + release_is_valid=false + continue + fi + + # Verify the tarball's checksum is present and valid. + if [[ ! -e "$t.sha256sum" ]] ; then + echo "$t.sha256sum: MISSING" + release_is_valid=false + continue + elif ( ! sha256sum -c "$t.sha256sum" &>/dev/null); then + echo "$t.sha256sum: INVALID" + release_is_valid=false + continue + fi + + # Read file names from tarball and strip './' if found. + mapfile -t found_contents < <(tar -tf "$t" | sed -r 's/^.\///') + + content_matches=true + + # Verify the tarball only contains the expected contents. + for file in "${found_contents[@]}"; do + if [[ ! ${expected_contents[*]} =~ $file ]]; then + echo "$file: UNEXPECTED" + release_is_valid=false + content_matches=false + fi + done + + # Verify the tarball is not missing any content. + for file in "${expected_contents[@]}"; do + if [[ ! ${found_contents[*]} =~ $file ]]; then + echo "$file: MISSING" + release_is_valid=false + content_matches=false + fi + done + + if ${content_matches}; then + echo "$t: OK" + else + echo "$t: INVALID" + fi +done + +if ( ! ${release_is_valid} ); then + exit 1 +fi + +popd || exit 1 +exit 0