Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add release workflow #280

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
205 changes: 205 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
# Copyright 2024 The Sigstore 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.

name: "Release sigstore-go"

on:
push:
tags:
- "v*.*.*"

env:
REPO: 'https://github.com/${{ github.event.repository.full_name }}'
REPO_OWNER: '${{ github.event.repository.owner.name }}'
REPO_NAME: '${{ github.event.repository.name }}'
RELEASE_TAG: '${{ github.ref_name }}'

permissions:
id-token: write
contents: write
attestations: write

# This workflow cuts a release. The workflow then downloads
# the source archives from different GitHub URLs against the
# source code sigstore-go released.
# A source archive is a compressed archive that GitHub creates
# by way of "git archive" and makes available for anyone to
# download.
# The workflow first checks out the release tag and then
# verifies these source archives against the checked out
# source. As such, the checked out source is our source
# of truth, and to pass verification, the downloaded source
# archives must match our source of truth.
#
# When this workflow completes, sigstore-go will issue a release,
# and consumers can get donwload the source code by and any
# of the following methods:
# 1. curl -L \
# -H "Accept: application/vnd.github+json" \
# -H "Authorization: Bearer <YOUR-TOKEN>" \
# -H "X-GitHub-Api-Version: 2022-11-28" \
# https://api.github.com/repos/sigstore/sigstore-go/zipball/REF (or "tarball")
# 2. Manually downloading the source archive from a release page.
# 3. wget https://github.com/sigstore/sigstore-go/archive/tags/REF.zip (or .tar.gz)
#
# Consumers can then verify the source archive by way of:
# gh attestation verify SOURCE_ARCHIVE.zip/tar.gz --repo=sigstore/sigstore-go
jobs:
build:
strategy:
fail-fast: false
runs-on: [ubuntu-latest]
steps:
# Check out the repo _before_ issuing the release
# so we have the ground truth.
- name: Checkout repository
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
with:
fetch-depth: 0
fetch-tags: true
ref: ${{ github.ref_name }}
- name: Issue release
uses: softprops/action-gh-release@v2

# Download the source archives from the
# "zipball_url" and "tarball_url" values
# from https://docs.github.com/en/rest/releases/releases?apiVersion=2022-11-28#list-releases
- name: Download source archives from API
uses: robinraju/release-downloader@v1
with:
latest: true
zipBall: true
tarBall: true
out-file-path: '${{ github.workspace }}/../source-archives' # download-path
tag: ${{ github.ref_name }}

# Download the source archives from:
# https://api.github.com/repos/OWNER/REPO/zipball/REF
- name: Download source archives from archive/tags and archive/refs/tags
working-directory: ${{ github.workspace }}
env:
DEST_DIR: '${{ github.workspace }}/../source-archives'
run: |
export DEST_DIR="${{ github.workspace }}/../source-archives"
wget "${REPO}"/archive/tags/"${RELEASE_TAG}".zip -O "${DEST_DIR}"/"${REPO_NAME}"-tags-"${RELEASE_TAG}".zip
wget "${REPO}"/archive/tags/"${RELEASE_TAG}".tar.gz -O "${DEST_DIR}"/"${REPO_NAME}"-tags-"${RELEASE_TAG}".tar.gz
wget "${REPO}"/archive/refs/tags/"${RELEASE_TAG}".zip -O "${DEST_DIR}"/"${REPO_NAME}"-refs-tags-"${RELEASE_TAG}".zip
wget "${REPO}"/archive/refs/tags/"${RELEASE_TAG}".tar.gz -O "${DEST_DIR}"/"${REPO_NAME}"-refs-tags-"${RELEASE_TAG}".tar.gz
- name: Verify all downloaded source archives
working-directory: ${{ github.workspace }}
env:
RELEASE_VERSION: '${{ github.ref_name }}'
run: |
export shortened_sha=$(echo "${GITHUB_SHA}" | head -c 7)
export directory="${REPO_NAME}" # The directory to verify source archives against
export TAG_WITHOUT_V=${RELEASE_VERSION#"v"} # eg. v0.0.1 becomes 0.0.1
cd ..
# Check that the source of truth exists
if [ ! -d "$directory" ]; then
exit 1
fi

# Verifies a file from a downloaded source archive
# against the corresponding file in our source of truth.
# Eg, we compare /downloaded-source-archive/file1 against
# /source-of-truth/file1.
verify_file() {
filename=$1
trusted_dir=$2
untrusted_dir=$3
path="${trusted_dir}/${filename#*/}"
cs_file1=$(sha256sum "${path}" | head -c 64)

path2="${untrusted_dir}/${filename#*/}"
cs_file2=$(sha256sum "${path2}" | head -c 64)
echo verifying "${path}" and "${path2}"
if [ "$cs_file1" != "$cs_file2" ]; then exit 1; fi
}
export -f verify_file

# Compares all files in two directories. When we use this
# later, one of the directories is our source of truth,
# and the other is the source archive we have downloaded.
# When we download a source archive, the .git directory
# is not included, but it exists in our source of truth,
# so we do not compare files in that directory.
dirs_are_equal() {
export trusted_dir=$1
export untrusted_dir=$2
find $trusted_dir -type f -not -path "*.git/*" -exec bash -c 'verify_file {} $trusted_dir $untrusted_dir' \;
number_of_files1=$(find "${trusted_dir}" -type f -not -path "*.git/*" | wc -l)
number_of_files2=$(find "${untrusted_dir}" -type f -not -path "*.git/*" | wc -l)
if [ "$number_of_files1" != "$number_of_files2" ]; then echo "WRONG" && exit 1; else echo "SAME AMOUNT OF FILES"; fi
echo $number_of_files1 $number_of_files2
}

# Extracts a compressed source archive file
# and compares its contents against our
# source of truth.
verify_compressed_dir() {
extracted_dir_name=$1
archive_name=$2
compressed_type=$3

export dir_name="${extracted_dir_name}"
if [ -d "${dir_name}" ]; then
echo "${dir_name} already exists but shouldn't."
exit 1
fi
if [ "$compressed_type" = "zip" ]; then
unzip source-archives/"${archive_name}"
else
tar -xvzf source-archives/"${archive_name}"
fi
dirs_are_equal "${directory}" "${dir_name}"
rm -r "${dir_name}"
}

# We have downloaded 6 source archives earlier in the
# workflow. Now we verify these against our source of truth.
ls source-archives
verify_compressed_dir \
"${REPO_OWNER}-${REPO_NAME}-${shortened_sha}" \
"${REPO_NAME}-${RELEASE_TAG}.zip" \
"zip"
verify_compressed_dir \
"${REPO_OWNER}-${REPO_NAME}-${shortened_sha}" \
"${REPO_NAME}-${RELEASE_TAG}.tar.gz" \
"tar.gz"
verify_compressed_dir \
"${REPO_NAME}-tags-${RELEASE_TAG}" \
"${REPO_NAME}-tags-${RELEASE_TAG}.zip" \
"zip"
verify_compressed_dir \
"${REPO_NAME}-tags-${RELEASE_TAG}" \
"${REPO_NAME}-tags-${RELEASE_TAG}.tar.gz" \
"tar.gz"
verify_compressed_dir \
"${REPO_NAME}-${TAG_WITHOUT_V}" \
"${REPO_NAME}-refs-tags-${RELEASE_TAG}.zip" \
"zip"
verify_compressed_dir \
"${REPO_NAME}-${TAG_WITHOUT_V}" \
"${REPO_NAME}-refs-tags-${RELEASE_TAG}.tar.gz" \
"tar.gz"
# We can't use "../" in the "subject-path" for the
# "attest-build-provenance" step, so we move it
# into scope.
- name: Move source archive files
run: |
mv ${{ github.workspace }}/../source-archives ./
- name: Attest all
uses: actions/attest-build-provenance@v1
with:
subject-path: 'source-archives/*'
Loading