name: Build veilor-os OCI (BlueBuild) # v0.7 spike — builds the bootable OCI image used by the bootstrap # kickstart's `ostreecontainer` directive. Runs on the Forgejo # self-hosted runner (label `nullstone`); GitHub-side cosign/SBOM/ # attest steps are gated off because Forgejo has no Sigstore Fulcio- # trusted OIDC issuer (see docs/PROOF-OF-WORK.md, build-iso.yml fix). # # Reference: https://blue-build.org/how-to/setup-build-action/ on: push: branches: [v0.7-bluebuild-spike] paths: - 'bluebuild/**' - 'overlay/**' - 'assets/**' - 'scripts/**' - '.github/workflows/build-bluebuild.yml' pull_request: branches: [main, v0.7-bluebuild-spike] schedule: # Rebuild weekly so we pick up upstream secureblue + Fedora updates. - cron: '0 6 * * 1' workflow_dispatch: permissions: contents: read jobs: build: name: Build + push OCI # nullstone label resolves to veilor-build:43 (fedora43 + nodejs) # via runner config. Privileged + userns=host + sock pass-through # already wired in the runner config (see infra/forgejo/). runs-on: nullstone timeout-minutes: 60 permissions: contents: read packages: write id-token: write # for GH-only cosign keyless (skipped on Forgejo) attestations: write env: # Forgejo container registry path. PAT in FORGEJO_REGISTRY_TOKEN # secret has package:write on veilor-org. FORGEJO_REGISTRY: git.s8n.ru FORGEJO_IMAGE: git.s8n.ru/veilor-org/veilor-os OCI_TAG: "43" # GH parallel target — only used when run on github.com. GHCR_IMAGE: ghcr.io/${{ github.repository_owner }}/veilor-os steps: - name: Checkout # Pinned to last v4 tag confirmed to ship on node20. uses: actions/checkout@v4.1.7 - name: Install build tooling (Fedora) run: | set -euxo pipefail dnf -y upgrade --refresh # veilor-build:43 already ships git, curl, tar, sudo, nodejs. # cosign is not packaged in Fedora 43; we install it from the # upstream release tarball below in a separate step. dnf -y install --skip-unavailable \ podman \ buildah \ skopeo \ jq - name: Install cosign binary (upstream release) run: | set -euxo pipefail # Fedora 43 has no cosign rpm. Pull static x86_64 binary # from sigstore/cosign GitHub releases. Pinned to v2.4.1. COSIGN_VERSION="2.4.1" curl -fsSL \ "https://github.com/sigstore/cosign/releases/download/v${COSIGN_VERSION}/cosign-linux-amd64" \ -o /usr/local/bin/cosign chmod +x /usr/local/bin/cosign cosign version - name: Build OCI image with BlueBuild action id: bluebuild # Composite action — runs podman + buildah inside; works on # Forgejo runner same as GH-hosted. Pinned to commit SHA per # the v0.5 CI hardening pass. uses: blue-build/github-action@24d146df25adc2cf579e918efe2d9bff6adea408 # v1 with: recipe: bluebuild/recipe.yml # registry_token is consumed by the action to publish; for # the Forgejo path we re-tag + push manually below, so this # token only matters for the GH-mirror path. registry_token: ${{ secrets.GITHUB_TOKEN }} pr_event_number: ${{ github.event.number }} maximize_build_space: false - name: Re-tag local OCI for Forgejo + GHCR run: | set -euxo pipefail # The action emits the local image as `:`. # Confirm it landed, then add registry-prefixed tags. podman images podman tag localhost/veilor-os:latest "${FORGEJO_IMAGE}:${OCI_TAG}" || true podman tag localhost/veilor-os:latest "${FORGEJO_IMAGE}:latest" || true - name: Push to Forgejo registry (primary) if: success() && github.event_name != 'pull_request' && github.server_url != 'https://github.com' env: FORGEJO_REGISTRY_TOKEN: ${{ secrets.FORGEJO_REGISTRY_TOKEN }} FORGEJO_REGISTRY_USER: ${{ secrets.FORGEJO_REGISTRY_USER }} run: | set -euo pipefail if [ -z "${FORGEJO_REGISTRY_TOKEN:-}" ]; then echo "[WARN] FORGEJO_REGISTRY_TOKEN secret is empty; skipping push" exit 0 fi echo "$FORGEJO_REGISTRY_TOKEN" | podman login \ --username "${FORGEJO_REGISTRY_USER:-veilor-org}" \ --password-stdin "$FORGEJO_REGISTRY" podman push "${FORGEJO_IMAGE}:${OCI_TAG}" podman push "${FORGEJO_IMAGE}:latest" echo "[OK] pushed ${FORGEJO_IMAGE}:{${OCI_TAG},latest}" - name: Push to GHCR (mirror, GitHub-only) if: success() && github.event_name != 'pull_request' && github.server_url == 'https://github.com' run: | set -euo pipefail podman tag localhost/veilor-os:latest "${GHCR_IMAGE}:${OCI_TAG}" podman tag localhost/veilor-os:latest "${GHCR_IMAGE}:latest" echo "${{ secrets.GITHUB_TOKEN }}" | podman login \ --username "${{ github.repository_owner }}" \ --password-stdin ghcr.io podman push "${GHCR_IMAGE}:${OCI_TAG}" podman push "${GHCR_IMAGE}:latest" - name: Smoke-test OCI image if: github.event_name == 'push' || github.event_name == 'workflow_dispatch' run: | set -euxo pipefail podman run --rm "localhost/veilor-os:latest" /bin/bash -c ' set -e echo "-- os-release" head -5 /etc/os-release echo "-- sudo present"; which sudo echo "-- mullvad-browser path"; rpm -q mullvad-browser || echo "not installed" echo "-- yggdrasil"; rpm -q yggdrasil || echo "not installed" echo "-- tailscale"; rpm -q tailscale || echo "not installed" echo "-- veilor-firstboot unit"; ls -la /etc/systemd/system/veilor-firstboot.service 2>&1 || true ' # ── GitHub-only signing/SBOM/attest ──────────────────────────── # cosign keyless needs Sigstore Fulcio-trusted OIDC. Forgejo # has none, so these are GH-only. v0.7+ TODO: cosign key-pair # signing for Forgejo using a stored secret. - name: SBOM (SPDX, GitHub-only) if: github.event_name == 'push' && github.server_url == 'https://github.com' # Pinned to last v0.17 release that ships node20. uses: anchore/sbom-action@v0.17.2 with: image: ${{ env.GHCR_IMAGE }}:${{ env.OCI_TAG }} format: spdx-json output-file: veilor-os-oci.spdx.json - name: Build provenance attestation (GitHub-only) if: github.event_name == 'push' && github.server_url == 'https://github.com' # Pinned to last v2.2 release that ships node20. uses: actions/attest-build-provenance@v2.2.3 with: subject-name: ${{ env.GHCR_IMAGE }} subject-digest: ${{ steps.bluebuild.outputs.digest }}