Add property-based tests for SBOM/VEX document ordering and Unicode normalization determinism
- Implement `SbomVexOrderingDeterminismProperties` for testing component list and vulnerability metadata hash consistency. - Create `UnicodeNormalizationDeterminismProperties` to validate NFC normalization and Unicode string handling. - Add project file for `StellaOps.Testing.Determinism.Properties` with necessary dependencies. - Introduce CI/CD template validation tests including YAML syntax checks and documentation content verification. - Create validation script for CI/CD templates ensuring all required files and structures are present.
This commit is contained in:
206
.gitea/workflows/cross-platform-determinism.yml
Normal file
206
.gitea/workflows/cross-platform-determinism.yml
Normal file
@@ -0,0 +1,206 @@
|
||||
name: cross-platform-determinism
|
||||
on:
|
||||
workflow_dispatch: {}
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- 'src/__Libraries/StellaOps.Canonical.Json/**'
|
||||
- 'src/__Libraries/StellaOps.Replay.Core/**'
|
||||
- 'src/__Tests/**Determinism**'
|
||||
- '.gitea/workflows/cross-platform-determinism.yml'
|
||||
pull_request:
|
||||
branches: [main]
|
||||
paths:
|
||||
- 'src/__Libraries/StellaOps.Canonical.Json/**'
|
||||
- 'src/__Libraries/StellaOps.Replay.Core/**'
|
||||
- 'src/__Tests/**Determinism**'
|
||||
|
||||
jobs:
|
||||
# DET-GAP-11: Windows determinism test runner
|
||||
determinism-windows:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: "10.0.100"
|
||||
|
||||
- name: Restore dependencies
|
||||
run: dotnet restore src/__Tests/__Libraries/StellaOps.Testing.Determinism.Properties/StellaOps.Testing.Determinism.Properties.csproj
|
||||
|
||||
- name: Run determinism property tests
|
||||
run: |
|
||||
dotnet test src/__Tests/__Libraries/StellaOps.Testing.Determinism.Properties/StellaOps.Testing.Determinism.Properties.csproj `
|
||||
--logger "trx;LogFileName=determinism-windows.trx" `
|
||||
--results-directory ./test-results/windows
|
||||
|
||||
- name: Generate hash report
|
||||
shell: pwsh
|
||||
run: |
|
||||
# Generate determinism baseline hashes
|
||||
$hashReport = @{
|
||||
platform = "windows"
|
||||
timestamp = (Get-Date -Format "o")
|
||||
hashes = @{}
|
||||
}
|
||||
|
||||
# Run hash generation script
|
||||
dotnet run --project tools/determinism-hash-generator -- `
|
||||
--output ./test-results/windows/hashes.json
|
||||
|
||||
# Upload for comparison
|
||||
Copy-Item ./test-results/windows/hashes.json ./test-results/windows-hashes.json
|
||||
|
||||
- name: Upload Windows results
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: determinism-windows
|
||||
path: |
|
||||
./test-results/windows/
|
||||
./test-results/windows-hashes.json
|
||||
|
||||
# DET-GAP-12: macOS determinism test runner
|
||||
determinism-macos:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: "10.0.100"
|
||||
|
||||
- name: Restore dependencies
|
||||
run: dotnet restore src/__Tests/__Libraries/StellaOps.Testing.Determinism.Properties/StellaOps.Testing.Determinism.Properties.csproj
|
||||
|
||||
- name: Run determinism property tests
|
||||
run: |
|
||||
dotnet test src/__Tests/__Libraries/StellaOps.Testing.Determinism.Properties/StellaOps.Testing.Determinism.Properties.csproj \
|
||||
--logger "trx;LogFileName=determinism-macos.trx" \
|
||||
--results-directory ./test-results/macos
|
||||
|
||||
- name: Generate hash report
|
||||
run: |
|
||||
# Generate determinism baseline hashes
|
||||
dotnet run --project tools/determinism-hash-generator -- \
|
||||
--output ./test-results/macos/hashes.json
|
||||
|
||||
cp ./test-results/macos/hashes.json ./test-results/macos-hashes.json
|
||||
|
||||
- name: Upload macOS results
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: determinism-macos
|
||||
path: |
|
||||
./test-results/macos/
|
||||
./test-results/macos-hashes.json
|
||||
|
||||
# Linux runner (baseline)
|
||||
determinism-linux:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: "10.0.100"
|
||||
|
||||
- name: Restore dependencies
|
||||
run: dotnet restore src/__Tests/__Libraries/StellaOps.Testing.Determinism.Properties/StellaOps.Testing.Determinism.Properties.csproj
|
||||
|
||||
- name: Run determinism property tests
|
||||
run: |
|
||||
dotnet test src/__Tests/__Libraries/StellaOps.Testing.Determinism.Properties/StellaOps.Testing.Determinism.Properties.csproj \
|
||||
--logger "trx;LogFileName=determinism-linux.trx" \
|
||||
--results-directory ./test-results/linux
|
||||
|
||||
- name: Generate hash report
|
||||
run: |
|
||||
# Generate determinism baseline hashes
|
||||
dotnet run --project tools/determinism-hash-generator -- \
|
||||
--output ./test-results/linux/hashes.json
|
||||
|
||||
cp ./test-results/linux/hashes.json ./test-results/linux-hashes.json
|
||||
|
||||
- name: Upload Linux results
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: determinism-linux
|
||||
path: |
|
||||
./test-results/linux/
|
||||
./test-results/linux-hashes.json
|
||||
|
||||
# DET-GAP-13: Cross-platform hash comparison report
|
||||
compare-hashes:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [determinism-windows, determinism-macos, determinism-linux]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Download all artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: ./artifacts
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.12'
|
||||
|
||||
- name: Generate comparison report
|
||||
run: |
|
||||
python3 scripts/determinism/compare-platform-hashes.py \
|
||||
--linux ./artifacts/determinism-linux/linux-hashes.json \
|
||||
--windows ./artifacts/determinism-windows/windows-hashes.json \
|
||||
--macos ./artifacts/determinism-macos/macos-hashes.json \
|
||||
--output ./cross-platform-report.json \
|
||||
--markdown ./cross-platform-report.md
|
||||
|
||||
- name: Check for divergences
|
||||
run: |
|
||||
# Fail if any hashes differ across platforms
|
||||
python3 -c "
|
||||
import json
|
||||
import sys
|
||||
|
||||
with open('./cross-platform-report.json') as f:
|
||||
report = json.load(f)
|
||||
|
||||
divergences = report.get('divergences', [])
|
||||
if divergences:
|
||||
print(f'ERROR: {len(divergences)} hash divergence(s) detected!')
|
||||
for d in divergences:
|
||||
print(f' - {d[\"key\"]}: linux={d[\"linux\"]}, windows={d[\"windows\"]}, macos={d[\"macos\"]}')
|
||||
sys.exit(1)
|
||||
else:
|
||||
print('SUCCESS: All hashes match across platforms.')
|
||||
"
|
||||
|
||||
- name: Upload comparison report
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: cross-platform-comparison
|
||||
path: |
|
||||
./cross-platform-report.json
|
||||
./cross-platform-report.md
|
||||
|
||||
- name: Comment on PR (if applicable)
|
||||
if: github.event_name == 'pull_request'
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const fs = require('fs');
|
||||
const report = fs.readFileSync('./cross-platform-report.md', 'utf8');
|
||||
github.rest.issues.createComment({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: '## Cross-Platform Determinism Report\n\n' + report
|
||||
});
|
||||
204
.gitea/workflows/deploy-keyless-verify.yml
Normal file
204
.gitea/workflows/deploy-keyless-verify.yml
Normal file
@@ -0,0 +1,204 @@
|
||||
# .gitea/workflows/deploy-keyless-verify.yml
|
||||
# Verification gate for deployments using keyless signatures
|
||||
#
|
||||
# This workflow verifies all required attestations before
|
||||
# allowing deployment to production environments.
|
||||
#
|
||||
# Dogfooding the StellaOps keyless verification feature.
|
||||
|
||||
name: Deployment Verification Gate
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
image:
|
||||
description: 'Image to deploy (with digest)'
|
||||
required: true
|
||||
type: string
|
||||
environment:
|
||||
description: 'Target environment'
|
||||
required: true
|
||||
type: choice
|
||||
options:
|
||||
- staging
|
||||
- production
|
||||
require_sbom:
|
||||
description: 'Require SBOM attestation'
|
||||
required: false
|
||||
default: true
|
||||
type: boolean
|
||||
require_verdict:
|
||||
description: 'Require policy verdict attestation'
|
||||
required: false
|
||||
default: true
|
||||
type: boolean
|
||||
|
||||
env:
|
||||
STELLAOPS_URL: "https://api.stella-ops.internal"
|
||||
|
||||
jobs:
|
||||
pre-flight:
|
||||
runs-on: ubuntu-22.04
|
||||
outputs:
|
||||
identity-pattern: ${{ steps.config.outputs.identity-pattern }}
|
||||
|
||||
steps:
|
||||
- name: Configure Identity Constraints
|
||||
id: config
|
||||
run: |
|
||||
ENV="${{ github.event.inputs.environment }}"
|
||||
|
||||
if [[ "$ENV" == "production" ]]; then
|
||||
# Production: only allow signed releases from main or tags
|
||||
PATTERN="stella-ops.org/git.stella-ops.org:ref:refs/(heads/main|tags/v.*)"
|
||||
else
|
||||
# Staging: allow any branch
|
||||
PATTERN="stella-ops.org/git.stella-ops.org:ref:refs/heads/.*"
|
||||
fi
|
||||
|
||||
echo "identity-pattern=${PATTERN}" >> $GITHUB_OUTPUT
|
||||
echo "Using identity pattern: ${PATTERN}"
|
||||
|
||||
verify-attestations:
|
||||
needs: pre-flight
|
||||
runs-on: ubuntu-22.04
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
outputs:
|
||||
verified: ${{ steps.verify.outputs.verified }}
|
||||
attestation-count: ${{ steps.verify.outputs.count }}
|
||||
|
||||
steps:
|
||||
- name: Install StellaOps CLI
|
||||
run: |
|
||||
curl -sL https://get.stella-ops.org/cli | sh
|
||||
echo "$HOME/.stellaops/bin" >> $GITHUB_PATH
|
||||
|
||||
- name: Verify All Attestations
|
||||
id: verify
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
IMAGE="${{ github.event.inputs.image }}"
|
||||
IDENTITY="${{ needs.pre-flight.outputs.identity-pattern }}"
|
||||
ISSUER="https://git.stella-ops.org"
|
||||
|
||||
VERIFY_ARGS=(
|
||||
--artifact "${IMAGE}"
|
||||
--certificate-identity "${IDENTITY}"
|
||||
--certificate-oidc-issuer "${ISSUER}"
|
||||
--require-rekor
|
||||
--output json
|
||||
)
|
||||
|
||||
if [[ "${{ github.event.inputs.require_sbom }}" == "true" ]]; then
|
||||
VERIFY_ARGS+=(--require-sbom)
|
||||
fi
|
||||
|
||||
if [[ "${{ github.event.inputs.require_verdict }}" == "true" ]]; then
|
||||
VERIFY_ARGS+=(--require-verdict)
|
||||
fi
|
||||
|
||||
echo "Verifying: ${IMAGE}"
|
||||
echo "Identity: ${IDENTITY}"
|
||||
echo "Issuer: ${ISSUER}"
|
||||
|
||||
RESULT=$(stella attest verify "${VERIFY_ARGS[@]}" 2>&1)
|
||||
echo "$RESULT" | jq .
|
||||
|
||||
VERIFIED=$(echo "$RESULT" | jq -r '.valid')
|
||||
COUNT=$(echo "$RESULT" | jq -r '.attestationCount')
|
||||
|
||||
echo "verified=${VERIFIED}" >> $GITHUB_OUTPUT
|
||||
echo "count=${COUNT}" >> $GITHUB_OUTPUT
|
||||
|
||||
if [[ "$VERIFIED" != "true" ]]; then
|
||||
echo "::error::Verification failed"
|
||||
echo "$RESULT" | jq -r '.issues[]? | "::error::\(.code): \(.message)"'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Verification passed with ${COUNT} attestations"
|
||||
|
||||
verify-provenance:
|
||||
needs: pre-flight
|
||||
runs-on: ubuntu-22.04
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
outputs:
|
||||
valid: ${{ steps.verify.outputs.valid }}
|
||||
|
||||
steps:
|
||||
- name: Install StellaOps CLI
|
||||
run: |
|
||||
curl -sL https://get.stella-ops.org/cli | sh
|
||||
echo "$HOME/.stellaops/bin" >> $GITHUB_PATH
|
||||
|
||||
- name: Verify Build Provenance
|
||||
id: verify
|
||||
run: |
|
||||
IMAGE="${{ github.event.inputs.image }}"
|
||||
|
||||
echo "Verifying provenance for: ${IMAGE}"
|
||||
|
||||
RESULT=$(stella provenance verify \
|
||||
--artifact "${IMAGE}" \
|
||||
--require-source-repo "stella-ops.org/git.stella-ops.org" \
|
||||
--output json)
|
||||
|
||||
echo "$RESULT" | jq .
|
||||
|
||||
VALID=$(echo "$RESULT" | jq -r '.valid')
|
||||
echo "valid=${VALID}" >> $GITHUB_OUTPUT
|
||||
|
||||
if [[ "$VALID" != "true" ]]; then
|
||||
echo "::error::Provenance verification failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
create-audit-entry:
|
||||
needs: [verify-attestations, verify-provenance]
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
steps:
|
||||
- name: Install StellaOps CLI
|
||||
run: |
|
||||
curl -sL https://get.stella-ops.org/cli | sh
|
||||
echo "$HOME/.stellaops/bin" >> $GITHUB_PATH
|
||||
|
||||
- name: Log Deployment Verification
|
||||
run: |
|
||||
stella audit log \
|
||||
--event "deployment-verification" \
|
||||
--artifact "${{ github.event.inputs.image }}" \
|
||||
--environment "${{ github.event.inputs.environment }}" \
|
||||
--verified true \
|
||||
--attestations "${{ needs.verify-attestations.outputs.attestation-count }}" \
|
||||
--provenance-valid "${{ needs.verify-provenance.outputs.valid }}" \
|
||||
--actor "${{ github.actor }}" \
|
||||
--workflow "${{ github.workflow }}" \
|
||||
--run-id "${{ github.run_id }}"
|
||||
|
||||
approve-deployment:
|
||||
needs: [verify-attestations, verify-provenance, create-audit-entry]
|
||||
runs-on: ubuntu-22.04
|
||||
environment: ${{ github.event.inputs.environment }}
|
||||
|
||||
steps:
|
||||
- name: Deployment Approved
|
||||
run: |
|
||||
cat >> $GITHUB_STEP_SUMMARY << EOF
|
||||
## Deployment Approved
|
||||
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| **Image** | \`${{ github.event.inputs.image }}\` |
|
||||
| **Environment** | ${{ github.event.inputs.environment }} |
|
||||
| **Attestations** | ${{ needs.verify-attestations.outputs.attestation-count }} |
|
||||
| **Provenance Valid** | ${{ needs.verify-provenance.outputs.valid }} |
|
||||
| **Approved By** | @${{ github.actor }} |
|
||||
|
||||
Deployment can now proceed.
|
||||
EOF
|
||||
399
.gitea/workflows/release-keyless-sign.yml
Normal file
399
.gitea/workflows/release-keyless-sign.yml
Normal file
@@ -0,0 +1,399 @@
|
||||
# .gitea/workflows/release-keyless-sign.yml
|
||||
# Keyless signing for StellaOps release artifacts
|
||||
#
|
||||
# This workflow signs release artifacts using keyless signing (Fulcio).
|
||||
# It demonstrates dogfooding of the keyless signing feature.
|
||||
#
|
||||
# Triggers:
|
||||
# - After release bundle is published
|
||||
# - Manual trigger for re-signing
|
||||
#
|
||||
# Artifacts signed:
|
||||
# - Container images
|
||||
# - CLI binaries
|
||||
# - SBOM documents
|
||||
# - Release manifest
|
||||
|
||||
name: Release Keyless Signing
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: 'Release version to sign (e.g., 2025.12.0)'
|
||||
required: true
|
||||
type: string
|
||||
dry_run:
|
||||
description: 'Dry run (skip actual signing)'
|
||||
required: false
|
||||
default: false
|
||||
type: boolean
|
||||
|
||||
env:
|
||||
STELLAOPS_URL: "https://api.stella-ops.internal"
|
||||
REGISTRY: registry.stella-ops.org
|
||||
|
||||
jobs:
|
||||
sign-images:
|
||||
runs-on: ubuntu-22.04
|
||||
permissions:
|
||||
id-token: write
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
outputs:
|
||||
scanner-attestation: ${{ steps.sign-scanner.outputs.attestation-digest }}
|
||||
cli-attestation: ${{ steps.sign-cli.outputs.attestation-digest }}
|
||||
gateway-attestation: ${{ steps.sign-gateway.outputs.attestation-digest }}
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Determine Version
|
||||
id: version
|
||||
run: |
|
||||
if [[ -n "${{ github.event.inputs.version }}" ]]; then
|
||||
VERSION="${{ github.event.inputs.version }}"
|
||||
else
|
||||
VERSION="${{ github.event.release.tag_name }}"
|
||||
VERSION="${VERSION#v}"
|
||||
fi
|
||||
echo "version=${VERSION}" >> $GITHUB_OUTPUT
|
||||
echo "Release version: ${VERSION}"
|
||||
|
||||
- name: Install StellaOps CLI
|
||||
run: |
|
||||
curl -sL https://get.stella-ops.org/cli | sh
|
||||
echo "$HOME/.stellaops/bin" >> $GITHUB_PATH
|
||||
|
||||
- name: Log in to Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ secrets.REGISTRY_USERNAME }}
|
||||
password: ${{ secrets.REGISTRY_PASSWORD }}
|
||||
|
||||
- name: Get OIDC Token
|
||||
id: oidc
|
||||
run: |
|
||||
OIDC_TOKEN="${ACTIONS_ID_TOKEN}"
|
||||
if [[ -z "$OIDC_TOKEN" ]]; then
|
||||
echo "::error::OIDC token not available"
|
||||
exit 1
|
||||
fi
|
||||
echo "::add-mask::${OIDC_TOKEN}"
|
||||
echo "token=${OIDC_TOKEN}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Sign Scanner Image
|
||||
id: sign-scanner
|
||||
if: ${{ github.event.inputs.dry_run != 'true' }}
|
||||
env:
|
||||
STELLAOPS_OIDC_TOKEN: ${{ steps.oidc.outputs.token }}
|
||||
run: |
|
||||
VERSION="${{ steps.version.outputs.version }}"
|
||||
IMAGE="${REGISTRY}/stellaops/scanner:${VERSION}"
|
||||
|
||||
echo "Signing scanner image: ${IMAGE}"
|
||||
DIGEST=$(docker manifest inspect "${IMAGE}" -v | jq -r '.Descriptor.digest')
|
||||
|
||||
RESULT=$(stella attest sign \
|
||||
--keyless \
|
||||
--artifact "${DIGEST}" \
|
||||
--type image \
|
||||
--rekor \
|
||||
--output json)
|
||||
|
||||
ATTESTATION=$(echo "$RESULT" | jq -r '.attestationDigest')
|
||||
REKOR=$(echo "$RESULT" | jq -r '.rekorUuid')
|
||||
|
||||
echo "attestation-digest=${ATTESTATION}" >> $GITHUB_OUTPUT
|
||||
echo "rekor-uuid=${REKOR}" >> $GITHUB_OUTPUT
|
||||
|
||||
# Push attestation to registry
|
||||
stella attest push \
|
||||
--attestation "${ATTESTATION}" \
|
||||
--registry "stellaops/scanner"
|
||||
|
||||
- name: Sign CLI Image
|
||||
id: sign-cli
|
||||
if: ${{ github.event.inputs.dry_run != 'true' }}
|
||||
env:
|
||||
STELLAOPS_OIDC_TOKEN: ${{ steps.oidc.outputs.token }}
|
||||
run: |
|
||||
VERSION="${{ steps.version.outputs.version }}"
|
||||
IMAGE="${REGISTRY}/stellaops/cli:${VERSION}"
|
||||
|
||||
echo "Signing CLI image: ${IMAGE}"
|
||||
DIGEST=$(docker manifest inspect "${IMAGE}" -v | jq -r '.Descriptor.digest')
|
||||
|
||||
RESULT=$(stella attest sign \
|
||||
--keyless \
|
||||
--artifact "${DIGEST}" \
|
||||
--type image \
|
||||
--rekor \
|
||||
--output json)
|
||||
|
||||
ATTESTATION=$(echo "$RESULT" | jq -r '.attestationDigest')
|
||||
echo "attestation-digest=${ATTESTATION}" >> $GITHUB_OUTPUT
|
||||
|
||||
stella attest push \
|
||||
--attestation "${ATTESTATION}" \
|
||||
--registry "stellaops/cli"
|
||||
|
||||
- name: Sign Gateway Image
|
||||
id: sign-gateway
|
||||
if: ${{ github.event.inputs.dry_run != 'true' }}
|
||||
env:
|
||||
STELLAOPS_OIDC_TOKEN: ${{ steps.oidc.outputs.token }}
|
||||
run: |
|
||||
VERSION="${{ steps.version.outputs.version }}"
|
||||
IMAGE="${REGISTRY}/stellaops/gateway:${VERSION}"
|
||||
|
||||
echo "Signing gateway image: ${IMAGE}"
|
||||
DIGEST=$(docker manifest inspect "${IMAGE}" -v | jq -r '.Descriptor.digest')
|
||||
|
||||
RESULT=$(stella attest sign \
|
||||
--keyless \
|
||||
--artifact "${DIGEST}" \
|
||||
--type image \
|
||||
--rekor \
|
||||
--output json)
|
||||
|
||||
ATTESTATION=$(echo "$RESULT" | jq -r '.attestationDigest')
|
||||
echo "attestation-digest=${ATTESTATION}" >> $GITHUB_OUTPUT
|
||||
|
||||
stella attest push \
|
||||
--attestation "${ATTESTATION}" \
|
||||
--registry "stellaops/gateway"
|
||||
|
||||
sign-binaries:
|
||||
runs-on: ubuntu-22.04
|
||||
permissions:
|
||||
id-token: write
|
||||
contents: read
|
||||
|
||||
outputs:
|
||||
cli-linux-x64: ${{ steps.sign-cli-linux-x64.outputs.attestation-digest }}
|
||||
cli-linux-arm64: ${{ steps.sign-cli-linux-arm64.outputs.attestation-digest }}
|
||||
cli-darwin-x64: ${{ steps.sign-cli-darwin-x64.outputs.attestation-digest }}
|
||||
cli-darwin-arm64: ${{ steps.sign-cli-darwin-arm64.outputs.attestation-digest }}
|
||||
cli-windows-x64: ${{ steps.sign-cli-windows-x64.outputs.attestation-digest }}
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Determine Version
|
||||
id: version
|
||||
run: |
|
||||
if [[ -n "${{ github.event.inputs.version }}" ]]; then
|
||||
VERSION="${{ github.event.inputs.version }}"
|
||||
else
|
||||
VERSION="${{ github.event.release.tag_name }}"
|
||||
VERSION="${VERSION#v}"
|
||||
fi
|
||||
echo "version=${VERSION}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Install StellaOps CLI
|
||||
run: |
|
||||
curl -sL https://get.stella-ops.org/cli | sh
|
||||
echo "$HOME/.stellaops/bin" >> $GITHUB_PATH
|
||||
|
||||
- name: Download Release Artifacts
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
VERSION="${{ steps.version.outputs.version }}"
|
||||
mkdir -p artifacts
|
||||
|
||||
# Download CLI binaries
|
||||
gh release download "v${VERSION}" \
|
||||
--pattern "stellaops-cli-*" \
|
||||
--dir artifacts \
|
||||
|| echo "No CLI binaries found"
|
||||
|
||||
- name: Get OIDC Token
|
||||
id: oidc
|
||||
run: |
|
||||
OIDC_TOKEN="${ACTIONS_ID_TOKEN}"
|
||||
echo "::add-mask::${OIDC_TOKEN}"
|
||||
echo "token=${OIDC_TOKEN}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Sign CLI Binary (linux-x64)
|
||||
id: sign-cli-linux-x64
|
||||
if: ${{ github.event.inputs.dry_run != 'true' }}
|
||||
env:
|
||||
STELLAOPS_OIDC_TOKEN: ${{ steps.oidc.outputs.token }}
|
||||
run: |
|
||||
BINARY="artifacts/stellaops-cli-linux-x64"
|
||||
if [[ -f "$BINARY" ]]; then
|
||||
DIGEST="sha256:$(sha256sum "$BINARY" | cut -d' ' -f1)"
|
||||
|
||||
RESULT=$(stella attest sign \
|
||||
--keyless \
|
||||
--artifact "${DIGEST}" \
|
||||
--type binary \
|
||||
--rekor \
|
||||
--output json)
|
||||
|
||||
ATTESTATION=$(echo "$RESULT" | jq -r '.attestationDigest')
|
||||
echo "attestation-digest=${ATTESTATION}" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Sign CLI Binary (linux-arm64)
|
||||
id: sign-cli-linux-arm64
|
||||
if: ${{ github.event.inputs.dry_run != 'true' }}
|
||||
env:
|
||||
STELLAOPS_OIDC_TOKEN: ${{ steps.oidc.outputs.token }}
|
||||
run: |
|
||||
BINARY="artifacts/stellaops-cli-linux-arm64"
|
||||
if [[ -f "$BINARY" ]]; then
|
||||
DIGEST="sha256:$(sha256sum "$BINARY" | cut -d' ' -f1)"
|
||||
|
||||
RESULT=$(stella attest sign \
|
||||
--keyless \
|
||||
--artifact "${DIGEST}" \
|
||||
--type binary \
|
||||
--rekor \
|
||||
--output json)
|
||||
|
||||
ATTESTATION=$(echo "$RESULT" | jq -r '.attestationDigest')
|
||||
echo "attestation-digest=${ATTESTATION}" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Sign CLI Binary (darwin-x64)
|
||||
id: sign-cli-darwin-x64
|
||||
if: ${{ github.event.inputs.dry_run != 'true' }}
|
||||
env:
|
||||
STELLAOPS_OIDC_TOKEN: ${{ steps.oidc.outputs.token }}
|
||||
run: |
|
||||
BINARY="artifacts/stellaops-cli-darwin-x64"
|
||||
if [[ -f "$BINARY" ]]; then
|
||||
DIGEST="sha256:$(sha256sum "$BINARY" | cut -d' ' -f1)"
|
||||
|
||||
RESULT=$(stella attest sign \
|
||||
--keyless \
|
||||
--artifact "${DIGEST}" \
|
||||
--type binary \
|
||||
--rekor \
|
||||
--output json)
|
||||
|
||||
ATTESTATION=$(echo "$RESULT" | jq -r '.attestationDigest')
|
||||
echo "attestation-digest=${ATTESTATION}" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Sign CLI Binary (darwin-arm64)
|
||||
id: sign-cli-darwin-arm64
|
||||
if: ${{ github.event.inputs.dry_run != 'true' }}
|
||||
env:
|
||||
STELLAOPS_OIDC_TOKEN: ${{ steps.oidc.outputs.token }}
|
||||
run: |
|
||||
BINARY="artifacts/stellaops-cli-darwin-arm64"
|
||||
if [[ -f "$BINARY" ]]; then
|
||||
DIGEST="sha256:$(sha256sum "$BINARY" | cut -d' ' -f1)"
|
||||
|
||||
RESULT=$(stella attest sign \
|
||||
--keyless \
|
||||
--artifact "${DIGEST}" \
|
||||
--type binary \
|
||||
--rekor \
|
||||
--output json)
|
||||
|
||||
ATTESTATION=$(echo "$RESULT" | jq -r '.attestationDigest')
|
||||
echo "attestation-digest=${ATTESTATION}" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Sign CLI Binary (windows-x64)
|
||||
id: sign-cli-windows-x64
|
||||
if: ${{ github.event.inputs.dry_run != 'true' }}
|
||||
env:
|
||||
STELLAOPS_OIDC_TOKEN: ${{ steps.oidc.outputs.token }}
|
||||
run: |
|
||||
BINARY="artifacts/stellaops-cli-windows-x64.exe"
|
||||
if [[ -f "$BINARY" ]]; then
|
||||
DIGEST="sha256:$(sha256sum "$BINARY" | cut -d' ' -f1)"
|
||||
|
||||
RESULT=$(stella attest sign \
|
||||
--keyless \
|
||||
--artifact "${DIGEST}" \
|
||||
--type binary \
|
||||
--rekor \
|
||||
--output json)
|
||||
|
||||
ATTESTATION=$(echo "$RESULT" | jq -r '.attestationDigest')
|
||||
echo "attestation-digest=${ATTESTATION}" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
verify-signatures:
|
||||
needs: [sign-images, sign-binaries]
|
||||
runs-on: ubuntu-22.04
|
||||
permissions:
|
||||
contents: read
|
||||
packages: read
|
||||
|
||||
steps:
|
||||
- name: Install StellaOps CLI
|
||||
run: |
|
||||
curl -sL https://get.stella-ops.org/cli | sh
|
||||
echo "$HOME/.stellaops/bin" >> $GITHUB_PATH
|
||||
|
||||
- name: Determine Version
|
||||
id: version
|
||||
run: |
|
||||
if [[ -n "${{ github.event.inputs.version }}" ]]; then
|
||||
VERSION="${{ github.event.inputs.version }}"
|
||||
else
|
||||
VERSION="${{ github.event.release.tag_name }}"
|
||||
VERSION="${VERSION#v}"
|
||||
fi
|
||||
echo "version=${VERSION}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Verify Scanner Image
|
||||
if: ${{ github.event.inputs.dry_run != 'true' }}
|
||||
run: |
|
||||
VERSION="${{ steps.version.outputs.version }}"
|
||||
IMAGE="${REGISTRY}/stellaops/scanner:${VERSION}"
|
||||
DIGEST=$(docker manifest inspect "${IMAGE}" -v | jq -r '.Descriptor.digest')
|
||||
|
||||
stella attest verify \
|
||||
--artifact "${DIGEST}" \
|
||||
--certificate-identity "stella-ops.org/git.stella-ops.org:ref:refs/tags/v${VERSION}" \
|
||||
--certificate-oidc-issuer "https://git.stella-ops.org" \
|
||||
--require-rekor
|
||||
|
||||
- name: Summary
|
||||
run: |
|
||||
VERSION="${{ steps.version.outputs.version }}"
|
||||
cat >> $GITHUB_STEP_SUMMARY << EOF
|
||||
## Release v${VERSION} Signed
|
||||
|
||||
### Container Images
|
||||
|
||||
| Image | Attestation |
|
||||
|-------|-------------|
|
||||
| scanner | \`${{ needs.sign-images.outputs.scanner-attestation }}\` |
|
||||
| cli | \`${{ needs.sign-images.outputs.cli-attestation }}\` |
|
||||
| gateway | \`${{ needs.sign-images.outputs.gateway-attestation }}\` |
|
||||
|
||||
### CLI Binaries
|
||||
|
||||
| Platform | Attestation |
|
||||
|----------|-------------|
|
||||
| linux-x64 | \`${{ needs.sign-binaries.outputs.cli-linux-x64 }}\` |
|
||||
| linux-arm64 | \`${{ needs.sign-binaries.outputs.cli-linux-arm64 }}\` |
|
||||
| darwin-x64 | \`${{ needs.sign-binaries.outputs.cli-darwin-x64 }}\` |
|
||||
| darwin-arm64 | \`${{ needs.sign-binaries.outputs.cli-darwin-arm64 }}\` |
|
||||
| windows-x64 | \`${{ needs.sign-binaries.outputs.cli-windows-x64 }}\` |
|
||||
|
||||
### Verification
|
||||
|
||||
\`\`\`bash
|
||||
stella attest verify \\
|
||||
--artifact "sha256:..." \\
|
||||
--certificate-identity "stella-ops.org/git.stella-ops.org:ref:refs/tags/v${VERSION}" \\
|
||||
--certificate-oidc-issuer "https://git.stella-ops.org"
|
||||
\`\`\`
|
||||
EOF
|
||||
Reference in New Issue
Block a user