mirror-ublue-os-akmods/.github/workflows/reusable-build.yml

391 lines
16 KiB
YAML

name: build-akmod
run-name: "Build ${{ inputs.akmods_target != 'common' && format('akmods-{0}', inputs.akmods_target) || 'akmods' }}:${{ inputs.kernel_flavor }}-${{ inputs.version }}"
on:
workflow_call:
inputs:
akmods_target:
description: "The akmods to build: common, zfs, nvidia-lts, nvidia-open"
required: true
type: string
architecture:
description: "JSON string of architectures to build, '[aarch64, x86_64]'"
default: "['x86_64']"
required: false
type: string
kernel_flavor:
description: "The Kernel flavor: main, coreos-stable, longterm-6.12, etc"
required: true
type: string
version:
description: "Release Version: 10, 41, 42, etc"
required: true
type: string
kernel_cache_key:
description: "The Key for the Kernel Cache"
required: true
type: string
force:
description: "Force a build, ignoring the fingerprint guard"
default: false
required: false
type: boolean
workflow_dispatch:
inputs:
akmods_target:
description: "The akmods to build: common, zfs, nvidia-lts, nvidia-open"
required: true
type: string
architecture:
description: "JSON string of architectures to build, '[aarch64, x86_64]'"
default: "['x86_64']"
required: false
type: string
kernel_flavor:
description: "The Kernel flavor: main, coreos-stable, longterm-6.12, etc"
required: true
type: string
version:
description: "Release Version: 10, 41, 42, etc"
required: true
type: string
kernel_cache_key:
description: "The Key for the Kernel Cache"
required: true
type: string
force:
description: "Force a build, ignoring the fingerprint guard"
default: false
required: false
type: boolean
env:
AKMODS_KERNEL: ${{ inputs.kernel_flavor }}
AKMODS_TARGET: ${{ inputs.akmods_target }}
AKMODS_VERSION: ${{ inputs.version }}
KCKEY: ${{ inputs.kernel_cache_key }}
concurrency:
group: ${{ github.workflow }}-${{ github.ref || github.run_id }}-${{ inputs.version }}-${{ inputs.kernel_flavor }}-${{ inputs.akmods_target }}
cancel-in-progress: true
jobs:
build-akmods:
name: Build ${{ matrix.architecture }}
runs-on: ${{ matrix.architecture == 'x86_64' && 'ubuntu-26.04' || 'ubuntu-26.04-arm' }}
permissions:
actions: read
contents: read
packages: write
container:
image: "ghcr.io/ublue-os/devcontainer:latest@sha256:1c42755a3ab617139cc68e65248609eb2033de0e9ab5219746082ea192a182d8"
options: "--privileged --volume /:/host-sys --volume /var/lib/containers:/var/lib/containers --security-opt seccomp=unconfined --security-opt label=disable --user 0:0"
strategy:
fail-fast: false
matrix:
architecture: ${{ fromJson(inputs.architecture) }}
steps:
- name: Checkout Repo
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Mark Directory as Safe
run: git config --system --add safe.directory "$GITHUB_WORKSPACE"
- name: Export GITHUB_WORKSPACE
id: gh-workspace
run: echo "GITHUB_WORKSPACE=$GITHUB_WORKSPACE" >> "$GITHUB_OUTPUT"
- name: Cache Kernel RPMs
id: cache-kernel
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ${{ steps.gh-workspace.outputs.GITHUB_WORKSPACE }}/build/${{ env.AKMODS_KERNEL }}-${{ env.AKMODS_VERSION }}/KCWD/rpms
fail-on-cache-miss: true
key: ${{ env.KCKEY }}-${{ matrix.architecture }}
restore-keys: |
${{ matrix.architecture }}-${{ env.KCKEY }}
${{ matrix.architecture }}-${{ env.AKMODS_KERNEL }}
- name: Install Cosign
continue-on-error: true
uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2
with:
cosign-release: "v3.1.4"
# Every event prepares the snapshot, so test and build pin the same inputs
- name: Prepare Fingerprint
continue-on-error: true
run: just fingerprint || just fingerprint-clear
- name: Check if build needed
id: guard
continue-on-error: true
env:
AKMODS_FORCE_BUILD: ${{ inputs.force || github.event_name == 'pull_request' }}
AKMODS_MAX_AGE_DAYS: ${{ vars.AKMODS_MAX_AGE_DAYS }}
run: just build-needed
- name: Maximize build space
if: ${{ !(steps.guard.outcome == 'success' && steps.guard.outputs.skip == 'true') }}
shell: bash
run: |
set -xeuo pipefail
df -h
# Remove host toolchains, browsers
shopt -s nullglob
printf '/host-sys%s\0' /opt/PyPy /opt/az /opt/node/ /opt/pipx /opt/go /opt/Ruby \
/usr/lib/llvm-* /usr/local/julia* /usr/local/lib/android /usr/share/dotnet \
/usr/share/swift /usr/local/.ghcup /usr/lib/firefox /opt/google/chrome \
/opt/microsoft/msedge \
| xargs -0 -n1 -P4 rm -rf
df -h
- name: Retrieve Signing Key
if: contains(fromJson('["schedule", "workflow_dispatch", "merge_group"]'), github.event_name) && !(steps.guard.outcome == 'success' && steps.guard.outputs.skip == 'true')
shell: bash
run: |
mkdir -p certs
echo "${{ secrets.KERNEL_PRIVKEY }}" > certs/private_key.priv
echo "${{ secrets.AKMOD_PRIVKEY_20230518 }}" > certs/private_key_2.priv
# DEBUG: get character count of key
wc -c certs/private_key.priv
wc -c certs/private_key_2.priv
- name: Test Image
if: ${{ !(steps.guard.outcome == 'success' && steps.guard.outputs.skip == 'true') }}
run: just test
- name: Build Image
if: ${{ !(steps.guard.outcome == 'success' && steps.guard.outputs.skip == 'true') }}
run: just build
- name: Push and Sign Image
if: (contains(fromJson('["schedule", "merge_group"]'), github.event_name) || (github.event_name == 'workflow_dispatch' && github.ref == 'refs/heads/main')) && !(steps.guard.outcome == 'success' && steps.guard.outputs.skip == 'true')
run: |
just login
just push
env:
COSIGN_PRIVATE_KEY: ${{ secrets.SIGNING_SECRET }}
REGISTRY_ACTOR: ${{ github.actor }}
REGISTRY_TOKEN: ${{ github.token }}
manifest:
name: "Generate ${{ inputs.akmods_target != 'common' && format('akmods-{0}', inputs.akmods_target) || 'akmods' }}:${{ inputs.kernel_flavor }}-${{ inputs.version }} Manifest"
runs-on: ubuntu-26.04
needs: [build-akmods]
outputs:
image_name: ${{ steps.digest.outputs.image_name }}
manifest: ${{ steps.digest.outputs.manifest }}
manifest_kernel: ${{ steps.digest.outputs.manifest_kernel }}
container:
image: "ghcr.io/ublue-os/devcontainer:latest@sha256:1c42755a3ab617139cc68e65248609eb2033de0e9ab5219746082ea192a182d8"
options: "--privileged --volume /var/lib/containers:/var/lib/containers --security-opt seccomp=unconfined --security-opt label=disable --user 0:0"
permissions:
actions: read
attestations: write
contents: read
id-token: write
packages: write
steps:
- name: Exit on failure
if: needs.build-akmods.result != 'success'
run: exit 1
- name: Checkout Repo
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Mark Directory as Safe
run: git config --system --add safe.directory "$GITHUB_WORKSPACE"
- name: Export GITHUB_WORKSPACE
id: gh-workspace
run: echo "GITHUB_WORKSPACE=$GITHUB_WORKSPACE" >> "$GITHUB_OUTPUT"
- name: Cache Kernel RPMs
id: cache-kernel
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ${{ steps.gh-workspace.outputs.GITHUB_WORKSPACE }}/build/${{ env.AKMODS_KERNEL }}-${{ env.AKMODS_VERSION }}/KCWD/rpms
key: ${{ env.KCKEY }}-x86_64
restore-keys: |
${{ env.KCKEY }}-aarch64
- name: Install Cosign
if: contains(fromJson('["schedule", "workflow_dispatch", "merge_group"]'), github.event_name)
continue-on-error: true
uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2
with:
cosign-release: "v3.1.4"
# Attestations and digests always run, so provenance gets repaired even when this skips
- name: Check if manifest needed
id: manifest-guard
if: contains(fromJson('["schedule", "workflow_dispatch", "merge_group"]'), github.event_name)
continue-on-error: true
run: just manifest-needed
- name: Generate Manifest
if: contains(fromJson('["schedule", "workflow_dispatch", "merge_group"]'), github.event_name) && !(steps.manifest-guard.outcome == 'success' && steps.manifest-guard.outputs.skip == 'true')
run: |
just login
just manifest
env:
COSIGN_PRIVATE_KEY: ${{ secrets.SIGNING_SECRET }}
REGISTRY_ACTOR: ${{ github.actor }}
REGISTRY_TOKEN: ${{ github.token }}
- name: Get Manifest Digests
id: digest
if: contains(fromJson('["schedule", "workflow_dispatch", "merge_group"]'), github.event_name)
run: |
IMAGE_NAME="${{ inputs.akmods_target != 'common' && format('akmods-{0}', inputs.akmods_target) || 'akmods' }}"
MANIFEST_TAG="${{ inputs.kernel_flavor }}-${{ inputs.version }}"
KERNEL_RELEASE="$(jq -r '.kernel_release' < "$GITHUB_WORKSPACE/build/${{ env.AKMODS_KERNEL }}-${{ env.AKMODS_VERSION }}/KCWD/rpms/cache.json" | sed 's/\.x86_64\|\.aarch64//g')"
MANIFEST_TAG_KERNEL="${MANIFEST_TAG}-${KERNEL_RELEASE}"
DIGEST="$(skopeo inspect --format '{{.Digest}}' "docker://ghcr.io/${{ github.repository_owner }}/${IMAGE_NAME}:${MANIFEST_TAG}")"
echo "manifest=${DIGEST}" >> "$GITHUB_OUTPUT"
DIGEST_KERNEL="$(skopeo inspect --format '{{.Digest}}' "docker://ghcr.io/${{ github.repository_owner }}/${IMAGE_NAME}:${MANIFEST_TAG_KERNEL}")"
echo "manifest_kernel=${DIGEST_KERNEL}" >> "$GITHUB_OUTPUT"
echo "image_name=${IMAGE_NAME}" >> "$GITHUB_OUTPUT"
- name: Attest Manifest Image
if: contains(fromJson('["schedule", "workflow_dispatch", "merge_group"]'), github.event_name)
uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4
with:
subject-name: ghcr.io/${{ github.repository_owner }}/${{ steps.digest.outputs.image_name }}
subject-digest: ${{ steps.digest.outputs.manifest }}
push-to-registry: false
- name: Attest Manifest Kernel Image
if: contains(fromJson('["schedule", "workflow_dispatch", "merge_group"]'), github.event_name)
uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4
with:
subject-name: ghcr.io/${{ github.repository_owner }}/${{ steps.digest.outputs.image_name }}
subject-digest: ${{ steps.digest.outputs.manifest_kernel }}
push-to-registry: false
verify-publication:
name: Verify Published Signatures and Provenance
runs-on: ubuntu-26.04
needs: [manifest]
if: contains(fromJson('["schedule", "workflow_dispatch", "merge_group"]'), github.event_name)
permissions:
actions: read
attestations: write
contents: read
id-token: write
packages: read
steps:
- name: Checkout Repo
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install Skopeo
shell: bash
run: |
sudo apt-get update
sudo apt-get install --yes skopeo
command -v skopeo
skopeo --version
- name: Install Cosign
uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2
with:
cosign-release: "v3.1.4"
- name: Verify Published Subjects
env:
GH_TOKEN: ${{ github.token }}
IMAGE: ghcr.io/${{ github.repository_owner }}/${{ needs.manifest.outputs.image_name }}
MANIFEST_DIGEST: ${{ needs.manifest.outputs.manifest }}
MANIFEST_KERNEL_DIGEST: ${{ needs.manifest.outputs.manifest_kernel }}
shell: bash
run: |
set -euo pipefail
cosign version
command -v gh
gh --version
retry() {
local attempt
for attempt in 1 2 3 4 5; do
"$@" && return 0
if [[ "$attempt" -eq 5 ]]; then
return 1
fi
sleep $((attempt * 5))
done
}
verify_cosign() {
local image="$1"
retry cosign verify --key cosign.pub "$image"
retry cosign verify --new-bundle-format=false --key cosign.pub "$image"
}
policy_dir="$(mktemp -d)"
trap 'rm -rf "$policy_dir"' EXIT
cat > "$policy_dir/policy.json" <<'EOF'
{
"default": [{"type": "reject"}],
"transports": {
"docker": {
"ghcr.io/ublue-os/akmods": [{"type": "sigstoreSigned", "keyPath": "cosign.pub", "signedIdentity": {"type": "matchRepository"}}],
"ghcr.io/ublue-os/akmods-nvidia-lts": [{"type": "sigstoreSigned", "keyPath": "cosign.pub", "signedIdentity": {"type": "matchRepository"}}],
"ghcr.io/ublue-os/akmods-nvidia-open": [{"type": "sigstoreSigned", "keyPath": "cosign.pub", "signedIdentity": {"type": "matchRepository"}}],
"ghcr.io/ublue-os/akmods-zfs": [{"type": "sigstoreSigned", "keyPath": "cosign.pub", "signedIdentity": {"type": "matchRepository"}}],
"ghcr.io/ublue-os/akmods-extra": [{"type": "sigstoreSigned", "keyPath": "cosign.pub", "signedIdentity": {"type": "matchRepository"}}]
}
}
}
EOF
mkdir "$policy_dir/registries.d"
cat > "$policy_dir/registries.d/ghcr.yaml" <<'EOF'
docker:
ghcr.io:
use-sigstore-attachments: true
EOF
verify_policy() {
local image="$1"
local destination
destination="$(mktemp -d)"
retry skopeo copy --policy "$policy_dir/policy.json" --registries.d "$policy_dir/registries.d" "$image" "dir:$destination"
rm -rf "$destination"
}
manifest_tag="${{ inputs.kernel_flavor }}-${{ inputs.version }}"
subjects=(
"$IMAGE@$MANIFEST_DIGEST"
"$IMAGE@$MANIFEST_KERNEL_DIGEST"
)
for architecture in $(jq -r '.[]' <<'EOF'
${{ inputs.architecture }}
EOF
); do
release_digest="$(retry skopeo inspect --format '{{.Digest}}' "docker://$IMAGE:${manifest_tag}-${architecture}")"
subjects+=("$IMAGE@$release_digest")
done
for subject in "${subjects[@]}"; do
verify_cosign "$subject"
verify_policy "docker://$subject"
done
unsigned="quay.io/fedora/fedora-bootc@sha256:0cb32268d1a864801a6edc4032506f50f385d9f49c0b68a4eebecaaa555ecce6"
if cosign verify --key cosign.pub "$unsigned"; then
exit 1
fi
if cosign verify --new-bundle-format=false --key cosign.pub "$unsigned"; then
exit 1
fi
unsigned_destination="$(mktemp -d)"
if skopeo copy --policy "$policy_dir/policy.json" --registries.d "$policy_dir/registries.d" "docker://$unsigned" "dir:$unsigned_destination"; then
rm -rf "$unsigned_destination"
exit 1
fi
rm -rf "$unsigned_destination"
retry gh attestation verify "oci://$IMAGE@$MANIFEST_DIGEST" --repo ublue-os/akmods
retry gh attestation verify "oci://$IMAGE@$MANIFEST_KERNEL_DIGEST" --repo ublue-os/akmods