Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
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
104 changes: 104 additions & 0 deletions .github/workflows/build-publish-container-goreleaser.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# Copyright © 2026 OpenCHAMI a Series of LF Projects, LLC
# SPDX-License-Identifier: MIT
#
# Reusable workflow: builds and publishes a container image via GoReleaser,
# with multi-arch builds, build provenance attestation, and PR snapshot
# support.

name: Build and publish container using goreleaser
on:
workflow_call:
inputs:
cgo_enabled:
type: number
required: false
default: 0
is_pr_build:
type: boolean
required: false
default: false
pr_number:
type: number
required: false
default: ${{ github.event.number || 0 }}
registry_subject_name:
type: string
required: true
jobs:
container_build_publish:
runs-on: ubuntu-latest
steps:
- name: Set up latest stable Go
uses: actions/setup-go@v6.4.0
with:
go-version: stable
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
driver-opts: |
image=moby/buildkit:master
network=host
- name: Docker Login
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Checkout
uses: actions/checkout@v6.0.2
with:
fetch-tags: true
fetch-depth: 0
# Set environment variables required by GoReleaser
- name: Set build environment variables
run: |
{
GIT_STATE='dirty'
if git diff-index --quiet HEAD -- >/dev/null 2>&1; then
GIT_STATE='clean'
fi

echo "GIT_STATE=${GIT_STATE}"
echo "BUILD_HOST=$(hostname)"
echo "GO_VERSION=$(go version | awk '{print $3}')"
echo "BUILD_USER=$(whoami)"
echo "CGO_ENABLED=${{ inputs.cgo_enabled }}"
echo "IS_PR_BUILD=${{ inputs.is_pr_build }}"
} >> "${GITHUB_ENV}"
- name: Create Tag for PR
if: ${{ inputs.is_pr_build }}
run: |
git config --global user.name "github-actions[bot]"
git config --global user.email "github-actions[bot]@users.noreply.github.com"
git tag -f -a pr-${{ inputs.pr_number }} -m "PR Release"
- name: Build/Push/Release container with goreleaser
uses: goreleaser/goreleaser-action@v6
env:
GITHUB_TOKEN: ${{ github.token }}
with:
version: '~> 2'
args: release --clean ${{ inputs.is_pr_build && '--skip=announce,validate,archive' || '' }}
id: goreleaser
- name: Process goreleaser output
id: process_goreleaser_output
run: |
node - <<'EOF'
const fs = require('fs');
const artifacts = ${{ steps.goreleaser.outputs.artifacts }};
const firstNonNullDigest = artifacts.find(artifact => artifact.extra && artifact.extra.Digest != null)?.extra.Digest;
console.log(firstNonNullDigest);
fs.writeFileSync('digest.txt', firstNonNullDigest);
EOF
echo "digest=$(cat digest.txt)" >> "${GITHUB_OUTPUT}"
- name: Attest Binaries
uses: actions/attest-build-provenance@v4.1.0
with:
subject-path: dist/**
- name: generate build provenance
uses: actions/attest-build-provenance@v4.1.0
with:
subject-name: ${{ inputs.registry_subject_name }}
subject-digest: ${{ steps.process_goreleaser_output.outputs.digest }}
push-to-registry: true
41 changes: 41 additions & 0 deletions .github/workflows/build-rpm-quadlet.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Copyright © 2026 OpenCHAMI a Series of LF Projects, LLC
# SPDX-License-Identifier: MIT
#
# Reusable workflow: builds the caller repo's podman quadlet RPM and
# uploads it as an unsigned artifact for downstream signing.

name: Build RPM for Podman Quadlet Files
run-name: Create Podman Quadlet RPM for ${{ github.ref }}
on:
workflow_call:
inputs:
artifact-name-unsigned-rpms:
description: 'Artifact-name for unsigned RPM artifacts'
default: 'rpms-unsigned'
type: string
jobs:
rpmbuild:
runs-on: ubuntu-latest
container:
image: rockylinux:9
steps:
- name: Install build dependencies
run: dnf install -y -q git make rpm-build rpmlint tar gzip

- name: Mark workspace as a safe git directory
run: git config --global --add safe.directory "$GITHUB_WORKSPACE"

- name: Checkout
uses: actions/checkout@v6.0.2
with:
fetch-tags: true
fetch-depth: 0

- name: Build RPM
run: make rpm-build

- name: Upload RPM
uses: actions/upload-artifact@v7
with:
name: ${{ inputs.artifact-name-unsigned-rpms }}
path: '**/*.rpm'
114 changes: 114 additions & 0 deletions .github/workflows/gpg-sign-artifacts.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
# Copyright © 2026 OpenCHAMI a Series of LF Projects, LLC
# SPDX-License-Identifier: MIT
#
# Reusable workflow: intended as the common entry point for signing all
# release artifact types (RPMs today; other formats later) with a per-run
# ephemeral key certified through the repo's release key chain.
#
# Keep this file up to date and maintained as we add other package signing
# tasks (e.g. .deb, Arch packages).

name: GPG Sign artifacts
run-name: Create signed artifacts for ${{ github.ref }}
on:
workflow_call:
inputs:
artifact-name-unsigned-rpms:
description: 'Artifact-name for unsigned RPM artifacts'
default: 'rpms-unsigned'
type: string
artifact-name-signed-rpms:
description: 'Name for the signed RPM composite artifact'
type: string
default: 'rpms-signed'
artifact-name-public-keys:
description: 'Name for the public key composite artifact'
type: string
default: 'public-keys'
jobs:
artifacts-sign:
runs-on: ubuntu-latest
container:
image: rockylinux:9
steps:

- name: Install build dependencies
run: |
dnf install -y -q git make rpm-build rpmlint tar gzip

- name: Mark workspace as a safe git directory
run: git config --global --add safe.directory "$GITHUB_WORKSPACE"

- name: Checkout
uses: actions/checkout@v6.0.2
with:
fetch-tags: true
fetch-depth: 0

- name: Check for repo key expiry
uses: OpenCHAMI/github-actions/actions/gpg-check-key-expiration@v3.5
with:
repo-key-armored-b64: ${{ secrets.GPG_REPO_KEY_B64 }}
warn-days: '30'

- name: Configure GPG release keys
id: gpg
uses: OpenCHAMI/github-actions/actions/gpg-configure-release-keys@v3.5
with:
repo-cert-key-armored-b64: ${{ secrets.GPG_REPO_CERT_KEY_B64 }}
master-public-key-asc: ${{ secrets.MASTER_PUBLIC_ASC }}
master-fpr: ${{ secrets.MASTER_FPR }}
name: '${{ github.repository }} Release'
comment: 'ephemeral key for ${{ github.ref_name }}'
email: 'release@packages.openchami.org'
expire-days: '1'

- name: Download RPM artifacts requested for release
uses: actions/download-artifact@v8
with:
name: ${{ inputs.artifact-name-unsigned-rpms }}
path: dist

- name: Sign rpms
id: rpmsign
uses: OpenCHAMI/github-actions/actions/gpg-sign-rpm@v3.5
with:
resign: true
gnupg-home: ${{ steps.gpg.outputs.gnupg-home }}
gpg-fingerprint: ${{ steps.gpg.outputs.ephemeral-fingerprint }}

- name: Verify trust chain
uses: OpenCHAMI/github-actions/actions/gpg-verify-trust-chain@v3.5
with:
master-public-key: ${{ secrets.MASTER_PUBLIC_ASC }}
require-master: true
repo-public-key-file: ${{ steps.gpg.outputs.repo-cert-public-key-file }}
ephemeral-public-key-file: ${{ steps.gpg.outputs.ephemeral-public-key-file }}
rpm-dir: .

- name: rpmlint
run: rpmlint "$(find . -name '*.rpm')" || true

- name: Upload signed RPMs
uses: actions/upload-artifact@v7
with:
name: ${{ inputs.artifact-name-signed-rpms }}
path: '**/*.rpm'
overwrite: true

- name: Upload public signing keys
uses: actions/upload-artifact@v7
with:
name: ${{ inputs.artifact-name-public-keys }}
path: '**/*.pub.asc'
overwrite: true

- name: Cleanup GNUPGHOME
if: always()
env:
GNUPGHOME_PATH: ${{ steps.gpg.outputs.gnupg-home }}
run: |
set -euo pipefail
[ -n "$GNUPGHOME_PATH" ] && [ -d "$GNUPGHOME_PATH" ] || exit 0
find "$GNUPGHOME_PATH" -type f -exec shred -u {} + 2>/dev/null || true
rm -rf "$GNUPGHOME_PATH"
95 changes: 95 additions & 0 deletions .github/workflows/release-signed-artifacts.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# Copyright © 2026 OpenCHAMI a Series of LF Projects, LLC
# SPDX-License-Identifier: MIT
#
# Reusable workflow: publishes a GitHub Release for a tag, attaching signed
# RPMs and public keys, with trust-chain verification instructions in the
# release body.
name: Release signed artifacts
run-name: Generate release with signed artifacts for ${{ github.ref }}
permissions:
contents: write
on:
workflow_call:
inputs:
artifact-name-signed-rpms:
description: 'Name for the signed RPM composite artifact'
type: string
default: 'rpms-signed'
required: false
artifact-name-public-keys:
description: 'Name for the public key composite artifact'
type: string
default: 'public-keys'
required: false
jobs:
artifacts-release:
runs-on: ubuntu-latest
container:
image: rockylinux:9
steps:
- name: Install dependencies
run: |
dnf install -y -q git tar gzip zip
- name: Mark workspace as a safe git directory
run: git config --global --add safe.directory "$GITHUB_WORKSPACE"
- name: Checkout
uses: actions/checkout@v6.0.2
with:
fetch-tags: true
fetch-depth: 0
- name: Download signed RPM artifacts
uses: actions/download-artifact@v8
with:
name: ${{ inputs.artifact-name-signed-rpms }}
path: dist/rpms
- name: Download public key artifacts
uses: actions/download-artifact@v8
with:
name: ${{ inputs.artifact-name-public-keys }}
path: dist/keys
- name: Create GitHub Release
uses: softprops/action-gh-release@v3.0.2
with:
tag_name: ${{ github.ref_name }}
name: Release ${{ github.ref_name }}
fail_on_unmatched_files: true
files: |
dist/rpms/**/*.rpm
dist/keys/**/*.asc
body: |-
## GPG Signature Verification

Each RPM in this release is signed with a short-lived ephemeral key that
is certified by the repository signing key, which is itself certified by
the OpenCHAMI offline master key.

### Trust chain

```
offline master key
└─[certifies]─> repo key
└─[certifies]─> ephemeral key (${{ github.ref_name }})
└─[signs]─> RPM files
```

### How to verify

1. Download `repo-cert.pub.asc` and `ephemeral.pub.asc` from this release.
2. Import both keys:
```bash
gpg --import repo-public.asc ephemeral-public.asc
```
3. Verify each RPM:
```bash
rpm --checksig *.rpm
```
4. For full chain verification (requires the master public key):
```bash
curl -LO \
https://raw.githubusercontent.com/OpenCHAMI/gpg-signing-manager/main/scripts/verify-chain.sh
bash verify-chain.sh \
--master master.pub.asc \
--repo repo-cert.pub.asc \
--ephemeral ephemeral.pub.asc \
--rpm *.rpm
```
Loading