Skip to content
Closed
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
91 changes: 91 additions & 0 deletions .github/workflows/release-acceptance.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
name: Release acceptance

on:
push:
tags: ['v*']

permissions:
contents: read

jobs:
build:
name: Build tagged release evidence
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
ref: ${{ github.sha }}

- name: Verify exact checked-out revision
env:
EXPECTED_SHA: ${{ github.sha }}
run: test "$(git rev-parse HEAD)" = "$EXPECTED_SHA"

- name: Use preinstalled Temurin JDK 21
run: |
echo "JAVA_HOME=$JAVA_HOME_21_X64" >> "$GITHUB_ENV"
echo "$JAVA_HOME_21_X64/bin" >> "$GITHUB_PATH"

- name: Bind tag to Maven version with pinned help:evaluate -Dexpression=project.version
shell: bash
run: |
set -euo pipefail
PROJECT_VERSION="$(mvn -q --no-transfer-progress org.apache.maven.plugins:maven-help-plugin:3.5.1:evaluate -Dexpression=project.version -DforceStdout)"
test -n "$PROJECT_VERSION"
test "$GITHUB_REF_NAME" = "v$PROJECT_VERSION"
printf '%s\n' "$PROJECT_VERSION" > "$RUNNER_TEMP/clearfolio-project-version"

- name: Run authoritative Maven acceptance
run: mvn -B --no-transfer-progress verify

- name: Generate CycloneDX SBOM
run: mvn -B --no-transfer-progress org.cyclonedx:cyclonedx-maven-plugin:2.9.1:makeAggregateBom

- name: Enforce buyer-release license policy
run: |
python3 scripts/check_sbom_license_policy.py \
--sbom target/bom.json \
--policy docs/security/2026-07-02-license-policy.json \
--summary target/license-policy.json \
--require-no-review

- name: Build immutable release evidence bundle
shell: bash
env:
SOURCE_REVISION: ${{ github.sha }}
run: |
set -euo pipefail
PROJECT_VERSION="$(cat "$RUNNER_TEMP/clearfolio-project-version")"
JAR="target/clearfolio-viewer-${PROJECT_VERSION}.jar"
test -f "$JAR"
test -f target/bom.json
test -f target/license-policy.json

mkdir -p target/release-evidence
cp "$JAR" target/release-evidence/
cp target/bom.json target/release-evidence/sbom-cyclonedx.json
cp target/license-policy.json target/release-evidence/license-policy.json
cp pom.xml target/release-evidence/pom.xml
printf '%s\n' "$SOURCE_REVISION" > target/release-evidence/source-revision.txt
printf '%s\n' "$GITHUB_REF_NAME" > target/release-evidence/source-tag.txt
(
cd target/release-evidence
sha256sum \
"clearfolio-viewer-${PROJECT_VERSION}.jar" \
sbom-cyclonedx.json \
license-policy.json \
pom.xml \
source-revision.txt \
source-tag.txt \
> release-manifest.sha256
)

- name: Upload tagged release acceptance evidence
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a
with:
name: clearfolio-release-acceptance-${{ github.ref_name }}
path: target/release-evidence/
if-no-files-found: error
retention-days: 90
compression-level: 0
34 changes: 34 additions & 0 deletions scripts/test_release_acceptance_workflow.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
from pathlib import Path


WORKFLOW = Path(".github/workflows/release-acceptance.yml")


def test_release_acceptance_is_tag_scoped_and_exact_revision_bound() -> None:
text = WORKFLOW.read_text(encoding="utf-8")
assert "tags: [ 'v*' ]" in text or "tags: ['v*']" in text
assert "permissions:\n contents: read" in text
assert "actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1" in text
assert "persist-credentials: false" in text
assert "ref: ${{ github.sha }}" in text
assert 'test "$(git rev-parse HEAD)" = "$EXPECTED_SHA"' in text


def test_release_acceptance_verifies_build_sbom_license_and_tag_version() -> None:
text = WORKFLOW.read_text(encoding="utf-8")
assert "mvn -B --no-transfer-progress verify" in text
assert "org.cyclonedx:cyclonedx-maven-plugin:2.9.1:makeAggregateBom" in text
assert "scripts/check_sbom_license_policy.py" in text
assert "--require-no-review" in text
assert "help:evaluate -Dexpression=project.version" in text
assert "GITHUB_REF_NAME" in text


def test_release_acceptance_persists_byte_digests_without_publish_authority() -> None:
text = WORKFLOW.read_text(encoding="utf-8")
assert "sha256sum" in text
assert "release-manifest.sha256" in text
assert "actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a" in text
assert "if-no-files-found: error" in text
assert "contents: write" not in text
assert "gh release" not in text
Loading