Skip to content

fix: Use shared workflow for builder cleanup #605

fix: Use shared workflow for builder cleanup

fix: Use shared workflow for builder cleanup #605

Workflow file for this run

name: Build Image Variants
on:
repository_dispatch:
types:
- build
push:
branches: [main]
pull_request:
branches: [main]
workflow_dispatch:
permissions: {}
env:
REPO_NAME: snosi
concurrency:
group: ${{ github.workflow }}-${{ github.ref || github.run_id }}
cancel-in-progress: ${{ github.event_name == 'push' }}
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
id-token: write
attestations: write
strategy:
matrix:
include:
- profile: cayo
description: "Cayo Linux Server Image"
- profile: cayoloaded
description: "Cayo Loaded Linux Server Image"
- profile: snow
description: "Snow Linux OS Image"
- profile: snowloaded
description: "Snow Loaded Linux OS Image"
- profile: snowfield
description: "Snowfield Linux OS Image"
- profile: snowfieldloaded
description: "Snow Field Loaded Linux OS Image"
steps:
- name: Show disk space (before)
run: sudo df -h
- name: Free Disk Space (Ubuntu)
uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be # v1.3.1
- name: Mount BTRFS for podman storage
uses: ublue-os/container-storage-action@main
with:
target-dir: /var/lib/containers
- name: Show disk space (after)
run: sudo df -h
- name: Checkout repository
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
persist-credentials: false
- name: Check mkosi package duplicates
run: ./check-duplicate-packages.sh
- name: Generate build date
id: date
run: echo "date=$(date -u +%Y-%m-%dT%H:%M:%SZ)" >> $GITHUB_OUTPUT
- name: Generate version tag
id: version
run: echo "tag=$(date +%Y%m%d%H%M%S)" >> $GITHUB_OUTPUT
- name: setup-mkosi
uses: systemd/mkosi@3c3a08fb07d27fbe473625aa0725655cfb2c68bf
- name: Build Image
run: |
sudo mkosi --profile ${{ matrix.profile }} \
--image-version "${{ steps.version.outputs.tag }}" \
build
- name: Package image
env:
IMAGE: ghcr.io/${{ github.repository_owner }}/${{ matrix.profile }}
run: |
sudo ./shared/outformat/image/buildah-package.sh \
output/${{ matrix.profile }} \
"$IMAGE:${{ steps.version.outputs.tag }}" \
"org.opencontainers.image.title=${{ matrix.profile }}" \
"org.opencontainers.image.description=${{ matrix.description }}" \
"org.opencontainers.image.version=${{ steps.version.outputs.tag }}" \
"org.opencontainers.image.created=${{ steps.date.outputs.date }}" \
"org.opencontainers.image.source=https://github.com/${{ github.repository_owner }}/${{ env.REPO_NAME }}/blob/${{ github.sha }}/mkosi.conf" \
"org.opencontainers.image.url=https://github.com/${{ github.repository_owner }}/${{ env.REPO_NAME }}/tree/${{ github.sha }}" \
"org.opencontainers.image.documentation=https://raw.githubusercontent.com/${{ github.repository_owner }}/${{ env.REPO_NAME }}/${{ github.sha }}/README.md"
- name: Chunk image
if: github.event_name != 'pull_request'
env:
IMAGE: ghcr.io/${{ github.repository_owner }}/${{ matrix.profile }}
MAX_LAYERS: 128
run: |
sudo ./shared/outformat/image/chunkah-package.sh \
"$IMAGE:${{ steps.version.outputs.tag }}" \
$(date -d '${{ github.event.head_commit.timestamp }}' +%s)
- name: Smoke test - verify SUID bit on sudo
env:
IMAGE: ghcr.io/${{ github.repository_owner }}/${{ matrix.profile }}
run: |
mode=$(sudo podman run --rm "$IMAGE:${{ steps.version.outputs.tag }}" stat -c '%a' /usr/bin/sudo)
echo "sudo permissions: $mode"
if [[ "$mode" != "4755" ]]; then
echo "::error::SUID bit not set on /usr/bin/sudo (expected 4755, got $mode)"
exit 1
fi
- name: Setup Syft
id: setup-syft
if: github.event_name != 'pull_request'
uses: anchore/sbom-action/download-syft@e22c389904149dbc22b58101806040fa8d37a610 # v0.24.0
with:
syft-version: v1.39.0
- name: Generate SBOM
if: github.event_name != 'pull_request'
id: generate-sbom
env:
PROFILE: ${{ matrix.profile }}
SYFT_CMD: ${{ steps.setup-syft.outputs.cmd }}
run: |
SBOM="$(mktemp -d)/sbom.json"
export SYFT_PARALLELISM=$(($(nproc)*2))
sudo "${SYFT_CMD}" --source-name "${PROFILE}" "output/${PROFILE}" -o "syft-json=${SBOM}"
du -sh "${SBOM}"
echo "SBOM=${SBOM}" >> "$GITHUB_OUTPUT"
- name: Push image
if: github.event_name != 'pull_request'
id: push
env:
IMAGE: ghcr.io/${{ github.repository_owner }}/${{ matrix.profile }}
run: |
sudo buildah push \
--compression-format=zstd:chunked \
--digestfile=/tmp/image-digest \
--creds="${{ github.actor }}:${{ secrets.GHCR_PAT }}" \
"$IMAGE:${{ steps.version.outputs.tag }}" \
"docker://$IMAGE:${{ steps.version.outputs.tag }}"
DIGEST=$(sudo cat /tmp/image-digest)
if [[ ! "$DIGEST" =~ ^sha256: ]]; then
echo "::error::Failed to extract valid digest: $DIGEST"
exit 1
fi
echo "digest=$DIGEST" >> $GITHUB_OUTPUT
sudo buildah tag \
"$IMAGE:${{ steps.version.outputs.tag }}" \
"$IMAGE:latest"
sudo buildah push \
--compression-format=zstd:chunked \
--creds="${{ github.actor }}:${{ secrets.GHCR_PAT }}" \
"$IMAGE:latest" \
"docker://$IMAGE:latest"
- name: Log in to ghcr.io
if: github.event_name != 'pull_request'
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GHCR_PAT }}
- name: Install ORAS
if: github.event_name != 'pull_request'
uses: oras-project/setup-oras@38de303aac69abb66f3e6255b7198bff35f323e3 # v2
- name: Login to GHCR with ORAS
if: github.event_name != 'pull_request'
env:
GHCR_PAT: ${{ secrets.GHCR_PAT }}
run: |
echo "${GHCR_PAT}" | oras login ghcr.io -u ${{ github.actor }} --password-stdin
- name: Install Cosign
if: github.event_name != 'pull_request'
uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0
with:
cosign-release: "v2.6.1"
- name: Upload SBOM
if: github.event_name != 'pull_request'
id: upload-sbom
env:
IMAGE: ghcr.io/${{ github.repository_owner }}/${{ matrix.profile }}
DIGEST: ${{ steps.push.outputs.digest }}
SBOM: ${{ steps.generate-sbom.outputs.SBOM }}
run: |
cd "$(dirname "${SBOM}")"
oras attach \
--artifact-type application/vnd.syft+json \
--annotation "filename=$(basename "${SBOM}")" \
"${IMAGE}@${DIGEST}" \
"$(basename "${SBOM}")"
sbom_digest=$(oras discover --format json "${IMAGE}@${DIGEST}" | jq -r '[.referrers[] | select(.artifactType == "application/vnd.syft+json")] | last | .digest')
if [[ -z "${sbom_digest}" || "${sbom_digest}" == "null" ]]; then
echo "::error::Failed to discover SBOM artifact digest"
exit 1
fi
echo "sbom_digest=${sbom_digest}" >> "$GITHUB_OUTPUT"
- name: Sign SBOM
if: github.event_name != 'pull_request'
env:
COSIGN_EXPERIMENTAL: "false"
COSIGN_PRIVATE_KEY: ${{ secrets.SIGNING_SECRET }}
IMAGE: ghcr.io/${{ github.repository_owner }}/${{ matrix.profile }}
SBOM_DIGEST: ${{ steps.upload-sbom.outputs.sbom_digest }}
run: |
cosign sign --key env://COSIGN_PRIVATE_KEY --yes \
"${IMAGE}@${SBOM_DIGEST}"
- name: Sign image
if: github.event_name != 'pull_request'
run: |
cosign login ghcr.io -u "${{ github.actor }}" -p "${{ secrets.GHCR_PAT }}"
cosign sign --key env://COSIGN_PRIVATE_KEY --yes \
"$IMAGE@${{ steps.push.outputs.digest }}"
env:
IMAGE: ghcr.io/${{ github.repository_owner }}/${{ matrix.profile }}
COSIGN_PRIVATE_KEY: ${{ secrets.SIGNING_SECRET }}
- name: Attest build provenance
if: github.event_name != 'pull_request'
uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0
with:
subject-name: ghcr.io/${{ github.repository_owner }}/${{ matrix.profile }}
subject-digest: ${{ steps.push.outputs.digest }}
push-to-registry: true
- name: Move manifests
if: github.event_name != 'pull_request'
run: |
sudo ./manifestmv.sh
- name: Upload manifests to R2
if: github.event_name != 'pull_request'
uses: ryand56/r2-upload-action@b801a390acbdeb034c5e684ff5e1361c06639e7c
with:
r2-account-id: ${{ secrets.R2_ACCOUNT_ID }}
r2-access-key-id: ${{ secrets.R2_ACCESS_KEY_ID }}
r2-secret-access-key: ${{ secrets.R2_SECRET_ACCESS_KEY }}
r2-bucket: frostyardrepo
source-dir: output/manifests
destination-dir: manifests/
- name: cleanup build output
run: |
sudo -E mkosi clean
release:
runs-on: ubuntu-latest
needs: build
if: github.event_name == 'push' && github.ref == 'refs/heads/main' && needs.build.result == 'success'
continue-on-error: true
permissions:
contents: write
steps:
- name: Install ORAS
uses: oras-project/setup-oras@38de303aac69abb66f3e6255b7198bff35f323e3 # v2
- name: Login to GHCR with ORAS
env:
GHCR_PAT: ${{ secrets.GHCR_PAT }}
run: echo "${GHCR_PAT}" | oras login ghcr.io -u ${{ github.actor }} --password-stdin
- name: Resolve previous and current snowloaded tags
id: versions
env:
IMAGE: ghcr.io/${{ github.repository_owner }}/snowloaded
run: |
set -euo pipefail
tags=$(oras repo tags "$IMAGE" | grep -E '^[0-9]{14}$' | sort -r || true)
if [[ -z "$tags" ]]; then
echo "::warning::No timestamped tags found on $IMAGE; skipping release"
echo "skip=true" >> "$GITHUB_OUTPUT"
exit 0
fi
current=$(echo "$tags" | head -n1)
previous=$(echo "$tags" | sed -n '2p')
if [[ -z "$previous" ]]; then
echo "::warning::Only one timestamped tag on $IMAGE; nothing to diff against; skipping release"
echo "skip=true" >> "$GITHUB_OUTPUT"
exit 0
fi
echo "previous=$previous" >> "$GITHUB_OUTPUT"
echo "current=$current" >> "$GITHUB_OUTPUT"
- name: Generate changelog
id: changelog
if: steps.versions.outputs.skip != 'true'
uses: frostyard/changelog-generator@main
with:
image: ghcr.io/${{ github.repository_owner }}/snowloaded
previous-tag: ${{ steps.versions.outputs.previous }}
current-tag: ${{ steps.versions.outputs.current }}
output-file: changelog.md
- name: Compute release tag and title
id: release-meta
if: steps.versions.outputs.skip != 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CURRENT: ${{ steps.versions.outputs.current }}
run: |
set -euo pipefail
date_part="${CURRENT:0:4}-${CURRENT:4:2}-${CURRENT:6:2}"
hh="${CURRENT:8:2}"; mm="${CURRENT:10:2}"; ss="${CURRENT:12:2}"
# Daily counter: find highest existing N in releases matching "<date>.N"
existing=$(gh release list --repo "$GITHUB_REPOSITORY" --limit 200 \
--json tagName --jq \
"[.[] | .tagName | select(startswith(\"${date_part}.\")) \
| sub(\"^${date_part}\\\\.\"; \"\") | select(test(\"^[0-9]+$\")) | tonumber] | max // 0")
next=$(( existing + 1 ))
tag="${date_part}.${next}"
title="Build ${date_part} ${hh}:${mm}:${ss} UTC"
echo "tag=$tag" >> "$GITHUB_OUTPUT"
echo "title=$title" >> "$GITHUB_OUTPUT"
- name: Create GitHub Release
if: steps.versions.outputs.skip != 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG: ${{ steps.release-meta.outputs.tag }}
TITLE: ${{ steps.release-meta.outputs.title }}
run: |
gh release create "$TAG" \
--repo "$GITHUB_REPOSITORY" \
--target "$GITHUB_SHA" \
--title "$TITLE" \
--notes-file changelog.md