Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
dcf95d9
rename action:sign-rpm to action:gpg-sign-rpm
seantronsen Jul 29, 2026
cdf71fe
add action:gpg-check-key-expiration based on gpg-signing-manager
seantronsen Jul 29, 2026
434ace2
add action:gpg-configure-release-keys to simplify downstream keys
seantronsen Jul 29, 2026
a383cce
add action:gpg-verify-trust-chain/action.yml to validate ephemeral ke…
seantronsen Jul 29, 2026
b43bc35
add generic reusable workflow for gpg signing artifacts before release
seantronsen Jul 29, 2026
0fc1769
wip: rename signing workflow and apply band-aids
seantronsen Jul 30, 2026
b6094de
add workflow for generating a release with signed artifacts
seantronsen Jul 30, 2026
3767c83
add small reusable workflow for building rpm quadlets
seantronsen Jul 30, 2026
81bf04e
add abstracted workflow for using goreleaser to build/publish containers
seantronsen Jul 30, 2026
4b838a6
add abstract workflow for RPM validation
seantronsen Jul 30, 2026
d83604f
wip: spotfix check expiry script call
seantronsen Jul 30, 2026
ae3478a
wip: adjust action versions
seantronsen Jul 30, 2026
bd3786b
wip: spotfix unbound var
seantronsen Jul 30, 2026
17edf25
wip: add ignore for blank lines in rpm validation workflow
seantronsen Jul 30, 2026
635cf3c
wip: fix 'sudo: command not found' in keygen
seantronsen Jul 30, 2026
e3f509d
update gpg sign to include signing deps for rhel envs
seantronsen Jul 30, 2026
ffa0a7e
wip: remove rpm-paths input var in favor of generalized behaviors
seantronsen Jul 30, 2026
e125b35
wip: fix diff in rpm validation workflow
seantronsen Jul 30, 2026
22d9008
wip: fix diff checker in rpm validation workflow
seantronsen Jul 30, 2026
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
93 changes: 93 additions & 0 deletions .github/workflows/build-publish-container-goreleaser.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# Copyright © 2026 OpenCHAMI a Series of LF Projects, LLC
# SPDX-License-Identifier: MIT

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: |

Check failure on line 52 in .github/workflows/build-publish-container-goreleaser.yml

View workflow job for this annotation

GitHub Actions / lint

shellcheck reported issue in this script: SC2129:style:1:1: Consider using { cmd1; cmd2; } >> file instead of individual redirects

Check failure on line 52 in .github/workflows/build-publish-container-goreleaser.yml

View workflow job for this annotation

GitHub Actions / lint

shellcheck reported issue in this script: SC2086:info:6:49: Double quote to prevent globbing and word splitting

Check failure on line 52 in .github/workflows/build-publish-container-goreleaser.yml

View workflow job for this annotation

GitHub Actions / lint

shellcheck reported issue in this script: SC2086:info:5:49: Double quote to prevent globbing and word splitting

Check failure on line 52 in .github/workflows/build-publish-container-goreleaser.yml

View workflow job for this annotation

GitHub Actions / lint

shellcheck reported issue in this script: SC2086:info:4:32: Double quote to prevent globbing and word splitting

Check failure on line 52 in .github/workflows/build-publish-container-goreleaser.yml

View workflow job for this annotation

GitHub Actions / lint

shellcheck reported issue in this script: SC2086:info:3:55: Double quote to prevent globbing and word splitting

Check failure on line 52 in .github/workflows/build-publish-container-goreleaser.yml

View workflow job for this annotation

GitHub Actions / lint

shellcheck reported issue in this script: SC2086:info:2:34: Double quote to prevent globbing and word splitting

Check failure on line 52 in .github/workflows/build-publish-container-goreleaser.yml

View workflow job for this annotation

GitHub Actions / lint

shellcheck reported issue in this script: SC2086:info:1:100: Double quote to prevent globbing and word splitting
echo "GIT_STATE=$(if git diff-index --quiet HEAD --; then echo 'clean'; else echo 'dirty'; fi)" >> $GITHUB_ENV
echo "BUILD_HOST=$(hostname)" >> $GITHUB_ENV
echo "GO_VERSION=$(go version | awk '{print $3}')" >> $GITHUB_ENV
echo "BUILD_USER=$(whoami)" >> $GITHUB_ENV
echo "CGO_ENABLED=${{ inputs.cgo_enabled }}" >> $GITHUB_ENV
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: |

Check failure on line 76 in .github/workflows/build-publish-container-goreleaser.yml

View workflow job for this annotation

GitHub Actions / lint

shellcheck reported issue in this script: SC2129:style:2:1: Consider using { cmd1; cmd2; } >> file instead of individual redirects

Check failure on line 76 in .github/workflows/build-publish-container-goreleaser.yml

View workflow job for this annotation

GitHub Actions / lint

shellcheck reported issue in this script: SC2086:info:7:36: Double quote to prevent globbing and word splitting
echo "const fs = require('fs');" > process.js
echo 'const artifacts = ${{ steps.goreleaser.outputs.artifacts }}' >> process.js
echo "const firstNonNullDigest = artifacts.find(artifact => artifact.extra && artifact.extra.Digest != null)?.extra.Digest;" >> process.js
echo "console.log(firstNonNullDigest);" >> process.js
echo "fs.writeFileSync('digest.txt', firstNonNullDigest);" >> process.js
node process.js
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
39 changes: 39 additions & 0 deletions .github/workflows/build-rpm-quadlet.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Copyright © 2026 OpenCHAMI a Series of LF Projects, LLC
# SPDX-License-Identifier: MIT

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'

Check failure on line 11 in .github/workflows/build-rpm-quadlet.yml

View workflow job for this annotation

GitHub Actions / lint

input "artifact-name-unsigned-rpms" of workflow_call event has the default value "rpms-unsigned", but it is also required. if an input is marked as required, its default value will never be used
type: string
required: true
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'
104 changes: 104 additions & 0 deletions .github/workflows/gpg-sign-artifacts.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
# In the future, to ensure consistent release behaviors and proper artifact
# signing, all release artifacts should travel through this workflow. We'll
# update it as the need arises to ensure artifacts of all types (distro
# packages, source tarballs, etc.) are properly signed.
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
required: true
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-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@dev-rpm-quadlets
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@dev-rpm-quadlets
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@dev-rpm-quadlets
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@dev-rpm-quadlets
with:
master-public-key: ${{ secrets.MASTER_PUBLIC_ASC }}
require-master: false
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
91 changes: 91 additions & 0 deletions .github/workflows/release-signed-artifacts.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# Copyright © 2026 OpenCHAMI a Series of LF Projects, LLC
# SPDX-License-Identifier: MIT
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
```
64 changes: 64 additions & 0 deletions .github/workflows/validate-rpm-quadlet.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Copyright © 2026 OpenCHAMI a Series of LF Projects, LLC
# SPDX-License-Identifier: MIT

name: Validate Podman Quadlet RPM
run-name: Validate Podman Quadlet RPM for ${{ github.ref }}
on:
workflow_call:
inputs:
artifact-name-signed-rpms:
description: 'Artifact-name for signed RPM artifacts'
default: 'rpms-signed'
required: true
type: string
expected-files:
description: 'a list of files the RPM is expected to install (newline delimited or multiline yaml string)'
required: true
type: string

jobs:
rpmvalidate:
runs-on: ubuntu-latest
container:
image: rockylinux:9
steps:

- name: Install build dependencies
run: dnf install -y -q git rpmlint tar gzip diffutils

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

- 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: Find Quadlet RPM
run: |
set -euo pipefail

quadlet_rpm=$(find . -type f -iname "*.rpm" | head -n 1)
if [ -z "${quadlet_rpm}" ]; then
echo "could not locate quadlet rpm file"
exit 1
fi

echo "using QUADLET_RPM_PATH=${quadlet_rpm}"
echo "QUADLET_RPM_PATH=${quadlet_rpm}" >> "$GITHUB_ENV"

- name: Verify installed file list is exactly what's expected
shell: bash
env:
EXPECTED_FILES: ${{ inputs.expected-files }}
run: |
set -euo pipefail
rpm -qlp "${QUADLET_RPM_PATH}" | sort | grep -v '^$' > /tmp/actual-files.txt
printf '%s\n' "${EXPECTED_FILES}" | sort | grep -v '^$' > /tmp/expected-files.txt
diff /tmp/expected-files.txt /tmp/actual-files.txt
Loading