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
40 changes: 40 additions & 0 deletions .github/workflows/release-publish-qualification.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: Release publish qualification

on:
pull_request:
workflow_dispatch:

permissions:
contents: read
packages: read

env:
QUALIFICATION_VERSION: '0.2.3'

jobs:
qualify:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install Skopeo
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends skopeo
- name: Install Cosign
uses: sigstore/cosign-installer@ba7bc0a3fef59531c69a25acd34668d6d3fe6f22 # v4.1.0
- name: Validate release scripts and workflows
shell: bash
run: |
set -euo pipefail
bash -n scripts/qualify_signature.sh
grep -q 'skopeo inspect --no-tags' .github/workflows/sign-release.yml
! grep -q 'docker buildx imagetools inspect' .github/workflows/sign-release.yml
grep -q 'skopeo inspect --no-tags' scripts/qualify_signature.sh
! grep -q 'docker buildx imagetools inspect' scripts/qualify_signature.sh
- name: Qualify existing signed release through current verification path
env:
VERSION: ${{ env.QUALIFICATION_VERSION }}
WAIT_ATTEMPTS: '12'
WAIT_SECONDS: '5'
run: bash scripts/qualify_signature.sh
8 changes: 5 additions & 3 deletions .github/workflows/sign-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@ jobs:
sign-release:
runs-on: ubuntu-latest
steps:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Install Skopeo
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends skopeo
- name: Install Cosign
uses: sigstore/cosign-installer@ba7bc0a3fef59531c69a25acd34668d6d3fe6f22 # v4.1.0
- name: Log in to GHCR
Expand All @@ -38,7 +40,7 @@ jobs:
ref="$IMAGE:$version"
digest=""
for attempt in $(seq 1 "$WAIT_ATTEMPTS"); do
digest="$(docker buildx imagetools inspect "$ref" 2>/dev/null | awk '/^Digest:/ {print $2; exit}' || true)"
digest="$(skopeo inspect --no-tags --format '{{.Digest}}' "docker://$ref" 2>/dev/null || true)"
if [[ "$digest" =~ ^sha256:[0-9a-f]{64}$ ]]; then
echo "Found $ref at $digest (attempt $attempt/$WAIT_ATTEMPTS)"
break
Expand Down
6 changes: 4 additions & 2 deletions .github/workflows/signature-verification.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,10 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Install Skopeo
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends skopeo
- name: Install Cosign
uses: sigstore/cosign-installer@ba7bc0a3fef59531c69a25acd34668d6d3fe6f22 # v4.1.0
- name: Log in to GHCR
Expand Down
34 changes: 34 additions & 0 deletions docs/M0_15_RELEASE_PUBLISH_QUALIFICATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# M0.15 release/publish qualification

M0.15 qualifies the repository's release path before publishing the next immutable tag.

## Scope

The next release candidate is `v0.2.4`, created from the post-M0.14 `main` state. Existing tags are immutable and must not be moved.

The container workflow remains responsible for publishing the multi-platform OCI image and its provenance/SBOM attestations. The release-signing workflow waits for the versioned image, resolves its OCI digest, and signs that immutable digest with GitHub Actions OIDC and Cosign.

## M0.15 hardening

Release digest resolution now uses Skopeo instead of piping `docker buildx imagetools inspect` into an early-exiting `awk`. This removes the previously identified SIGPIPE/pipefail race from the release-signing and signature-qualification paths and keeps digest resolution consistent with the runtime-neutral verifier.

The `Release publish qualification` workflow runs on pull requests and requires:

- release/signature scripts to pass shell syntax validation;
- `sign-release.yml` to use Skopeo digest resolution and not the old Buildx/awk path;
- `scripts/qualify_signature.sh` to use the same Skopeo path;
- the current verification implementation to successfully verify the known-good signed `v0.2.3` release.

## v0.2.4 acceptance

After this change is merged, create `v0.2.4` at the exact qualified `main` commit. The release is accepted only when the tag-triggered workflows prove all of the following:

1. the `0.2.4` OCI index is published;
2. `linux/amd64` and `linux/arm64` manifests are present;
3. provenance and SBOM attestations are present;
4. the immutable index digest is signed by the exact `sign-release.yml@refs/tags/v0.2.4` GitHub Actions identity;
5. signature verification passes for that exact digest;
6. release qualification confirms semantic aliases and `latest` resolve to the expected release digest;
7. runtime qualification passes on the published image.

Only after these gates are green should a GitHub Release object for `v0.2.4` be published.
9 changes: 8 additions & 1 deletion scripts/qualify_signature.sh
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,19 @@ if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+([.-][0-9A-Za-z.-]+)?$ ]]; then
exit 1
fi

for command in skopeo cosign; do
if ! command -v "$command" >/dev/null 2>&1; then
echo "required command not found: $command" >&2
exit 1
fi
done

primary="$IMAGE:$VERSION"
identity="https://github.com/Ploos-AS/glowing-bear/.github/workflows/sign-release.yml@refs/tags/$TAG"

image_digest=""
for attempt in $(seq 1 "$WAIT_ATTEMPTS"); do
image_digest="$(docker buildx imagetools inspect "$primary" 2>/dev/null | awk '/^Digest:/ {print $2; exit}' || true)"
image_digest="$(skopeo inspect --no-tags --format '{{.Digest}}' "docker://$primary" 2>/dev/null || true)"
if [[ "$image_digest" =~ ^sha256:[0-9a-f]{64}$ ]]; then
echo "Found $primary at $image_digest (attempt $attempt/$WAIT_ATTEMPTS)"
break
Expand Down
Loading