Skip to content
Merged
Show file tree
Hide file tree
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
11 changes: 11 additions & 0 deletions .github/actions/setup-goreleaser/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
name: Setup verified GoReleaser
description: Install GoReleaser only after verifying a repository-reviewed archive digest
runs:
using: composite
steps:
- name: Install verified GoReleaser
shell: bash
run: |
installation_directory="$RUNNER_TEMP/openai-cli-goreleaser"
./scripts/install-goreleaser "$installation_directory"
printf '%s\n' "$installation_directory" >> "$GITHUB_PATH"
15 changes: 7 additions & 8 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,14 @@ jobs:
- name: Bootstrap
run: ./scripts/bootstrap

- name: Read GoReleaser version
id: goreleaser-version
run: echo "version=$(cat .goreleaser-version)" >> "$GITHUB_OUTPUT"
- name: Test verified GoReleaser installer
run: ./scripts/test-goreleaser-installer

- name: Run goreleaser
uses: goreleaser/goreleaser-action@9ed2f89a662bf1735a48bc8557fd212fa902bebf # v6.1.0
with:
version: ${{ steps.goreleaser-version.outputs.version }}
args: release --snapshot --clean --skip=publish
- name: Set up verified GoReleaser
uses: ./.github/actions/setup-goreleaser

- name: Run GoReleaser
run: goreleaser release --snapshot --clean --skip=publish
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Expand Down
10 changes: 3 additions & 7 deletions .github/workflows/publish-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ jobs:
with:
fetch-depth: 0
persist-credentials: false
- name: Set up verified GoReleaser
uses: ./.github/actions/setup-goreleaser
- name: Ensure release tag is on main
env:
GITHUB_TOKEN: ${{ github.token }}
Expand Down Expand Up @@ -74,14 +76,8 @@ jobs:
repositories: homebrew-tools
permission-contents: write
permission-pull-requests: write
- name: Read GoReleaser version
id: goreleaser-version
run: echo "version=$(cat .goreleaser-version)" >> "$GITHUB_OUTPUT"
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@9ed2f89a662bf1735a48bc8557fd212fa902bebf # v6.1.0
with:
version: ${{ steps.goreleaser-version.outputs.version }}
args: release --clean
run: goreleaser release --clean
env:
GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}
HOMEBREW_TAP_GITHUB_TOKEN: ${{ steps.tap-token.outputs.token }}
Expand Down
4 changes: 4 additions & 0 deletions .goreleaser-checksums
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
d9cd0978d64686eb54e53e9f08b400edc8b1f9034f98fca43ec7a3f641327081 v2.15.4/goreleaser_Darwin_arm64.tar.gz
f9096d896139581cb7aeb4abcee1e7280357a3cd525a22440127be5ea52cf901 v2.15.4/goreleaser_Darwin_x86_64.tar.gz
de01ca1497571e9b348413cd2e7f74be49b8d57696ae386f7eedd06176544a88 v2.15.4/goreleaser_Linux_arm64.tar.gz
aae00c71a4a6d55e08cce9273a1516bdce33c1e07cffb7e502fa6fec4377dede v2.15.4/goreleaser_Linux_x86_64.tar.gz
56 changes: 56 additions & 0 deletions docs/releasing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Release tooling maintenance

CI and release publishing install GoReleaser with `scripts/install-goreleaser`.
The installer downloads only the version in `.goreleaser-version`, requires a
matching, repository-reviewed SHA-256 digest in `.goreleaser-checksums`, and
verifies the archive before extracting or executing anything from it. Release
write tokens and signing/notarization secrets are not requested until this
verification succeeds.

The reviewed checksums cover Linux and macOS hosts on x86_64 and arm64. The
host architecture does not limit the Linux, macOS, and Windows release targets
configured in `.goreleaser.yml`.

The publish workflow installs GoReleaser from its trusted workflow checkout
before switching to the requested release tag. This allows existing release tags
that predate the installer to be republished; the workflow checkout, not the
historical tag, determines the reviewed GoReleaser version.

## Update GoReleaser

1. Choose and review the upstream release, then set its version without the `v`
prefix in `.goreleaser-version`.
2. Download its checksum manifest and Sigstore bundle:

```sh
version="$(cat .goreleaser-version)"
gh release download "v$version" \
--repo goreleaser/goreleaser \
--pattern checksums.txt \
--pattern checksums.txt.sigstore.json
```

3. Verify that the manifest was signed by GoReleaser's upstream release workflow
for the exact tag, using an independently trusted `cosign` installation:

```sh
cosign verify-blob \
--certificate-identity "https://github.com/goreleaser/goreleaser/.github/workflows/release.yml@refs/tags/v$version" \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
--bundle checksums.txt.sigstore.json \
checksums.txt
```

4. Replace the reviewed manifest with the four supported host-archive digests:

```sh
awk -v version="$version" \
'$2 ~ /^goreleaser_(Darwin|Linux)_(arm64|x86_64)\.tar\.gz$/ {
print $1 " v" version "/" $2
}' checksums.txt > .goreleaser-checksums
test "$(wc -l < .goreleaser-checksums | tr -d ' ')" -eq 4
```

5. Run `./scripts/test-goreleaser-installer` and review the version and all four
digest changes together. The installer intentionally fails before downloading
if a version or host platform does not have exactly one reviewed digest.
61 changes: 61 additions & 0 deletions scripts/install-goreleaser
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#!/usr/bin/env bash

set -euo pipefail

if [[ $# -ne 1 ]]; then
echo "Usage: $0 INSTALLATION_DIRECTORY" >&2
exit 1
fi

repository_root="$(cd "$(dirname "$0")/.." && pwd)"
installation_directory="$1"
# Update the version and reviewed digests together; see docs/releasing.md.
version="$(cat "$repository_root/.goreleaser-version")"

if [[ ! "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "Invalid GoReleaser version: $version" >&2
exit 1
fi

case "$(uname -s)" in
Darwin) platform=Darwin ;;
Linux) platform=Linux ;;
*)
echo "Unsupported GoReleaser host operating system" >&2
exit 1
;;
esac

case "$(uname -m)" in
amd64 | x86_64) architecture=x86_64 ;;
aarch64 | arm64) architecture=arm64 ;;
*)
echo "Unsupported GoReleaser host architecture" >&2
exit 1
;;
esac

archive_name="goreleaser_${platform}_${architecture}.tar.gz"
release_asset="v${version}/${archive_name}"
expected_checksum="$(awk -v asset="$release_asset" '$2 == asset { print $1 }' "$repository_root/.goreleaser-checksums")"

if [[ ! "$expected_checksum" =~ ^[[:xdigit:]]{64}$ ]]; then
echo "Expected exactly one reviewed SHA-256 digest for $release_asset" >&2
exit 1
fi

temporary_directory="$(mktemp -d)"
trap 'rm -rf "$temporary_directory"' EXIT
archive_path="$temporary_directory/$archive_name"

curl --fail --silent --show-error --location --proto '=https' --tlsv1.2 \
--output "$archive_path" \
"https://github.com/goreleaser/goreleaser/releases/download/$release_asset"

printf '%s %s\n' "$expected_checksum" "$archive_path" | shasum -a 256 --check -

tar -xzf "$archive_path" -C "$temporary_directory" goreleaser
mkdir -p "$installation_directory"
install -m 0755 "$temporary_directory/goreleaser" "$installation_directory/goreleaser"

echo "Installed verified GoReleaser $version for $platform/$architecture"
102 changes: 102 additions & 0 deletions scripts/test-goreleaser-installer
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
#!/usr/bin/env bash

set -euo pipefail

repository_root="$(cd "$(dirname "$0")/.." && pwd)"
temporary_directory="$(mktemp -d)"
trap 'rm -rf "$temporary_directory"' EXIT

fixture_repository="$temporary_directory/repository"
fixture_archive_directory="$temporary_directory/archive"
fixture_tools="$temporary_directory/tools"
curl_log="$temporary_directory/curl.log"
execution_log="$temporary_directory/executed.log"
reviewed_version="$(cat "$repository_root/.goreleaser-version")"

mkdir -p "$fixture_repository/scripts" "$fixture_archive_directory" "$fixture_tools"
cp "$repository_root/scripts/install-goreleaser" "$fixture_repository/scripts/install-goreleaser"
printf '%s\n' "$reviewed_version" > "$fixture_repository/.goreleaser-version"

cat > "$fixture_archive_directory/goreleaser" <<'EOF'
#!/usr/bin/env bash
printf 'executed\n' >> "$TEST_EXECUTION_LOG"
EOF
chmod +x "$fixture_archive_directory/goreleaser"
tar -czf "$temporary_directory/verified.tar.gz" -C "$fixture_archive_directory" goreleaser

cat > "$fixture_tools/curl" <<'EOF'
#!/usr/bin/env bash

set -euo pipefail

while [[ $# -gt 0 ]]; do
case "$1" in
--output)
output="$2"
shift 2
;;
*) shift ;;
esac
done

printf 'downloaded\n' >> "$TEST_CURL_LOG"
cp "$TEST_ARCHIVE" "$output"
EOF
chmod +x "$fixture_tools/curl"

case "$(uname -s)" in
Darwin) platform=Darwin ;;
Linux) platform=Linux ;;
*) echo "Unsupported test host operating system" >&2; exit 1 ;;
esac

case "$(uname -m)" in
amd64 | x86_64) architecture=x86_64 ;;
aarch64 | arm64) architecture=arm64 ;;
*) echo "Unsupported test host architecture" >&2; exit 1 ;;
esac

archive_name="goreleaser_${platform}_${architecture}.tar.gz"
checksum="$(shasum -a 256 "$temporary_directory/verified.tar.gz" | awk '{print $1}')"
printf '%s v%s/%s\n' "$checksum" "$reviewed_version" "$archive_name" > "$fixture_repository/.goreleaser-checksums"

export PATH="$fixture_tools:$PATH"
export TEST_ARCHIVE="$temporary_directory/verified.tar.gz"
export TEST_CURL_LOG="$curl_log"
export TEST_EXECUTION_LOG="$execution_log"

"$fixture_repository/scripts/install-goreleaser" "$temporary_directory/installed"
[[ -x "$temporary_directory/installed/goreleaser" ]]
[[ ! -e "$execution_log" ]]

printf 'tampered archive\n' > "$temporary_directory/tampered.tar.gz"
TEST_ARCHIVE="$temporary_directory/tampered.tar.gz"
export TEST_ARCHIVE

if "$fixture_repository/scripts/install-goreleaser" "$temporary_directory/tampered-installation"; then
echo "Installer accepted an archive with the wrong digest" >&2
exit 1
fi
[[ ! -e "$temporary_directory/tampered-installation" ]]

printf '%s v%s/%s\n' "$checksum" "$reviewed_version" "$archive_name" >> "$fixture_repository/.goreleaser-checksums"
: > "$curl_log"

if "$fixture_repository/scripts/install-goreleaser" "$temporary_directory/ambiguous-installation"; then
echo "Installer accepted multiple reviewed digests for one archive" >&2
exit 1
fi
[[ ! -s "$curl_log" ]]
[[ ! -e "$temporary_directory/ambiguous-installation" ]]

printf '0.0.0\n' > "$fixture_repository/.goreleaser-version"
: > "$curl_log"

if "$fixture_repository/scripts/install-goreleaser" "$temporary_directory/unreviewed-installation"; then
echo "Installer accepted a version without a reviewed digest" >&2
exit 1
fi
[[ ! -s "$curl_log" ]]
[[ ! -e "$temporary_directory/unreviewed-installation" ]]

echo "GoReleaser installer verification tests passed"
Loading