diff --git a/.github/workflows/mirror-nofire.yml b/.github/workflows/mirror-nofire.yml new file mode 100644 index 0000000..381ab2a --- /dev/null +++ b/.github/workflows/mirror-nofire.yml @@ -0,0 +1,61 @@ +name: Mirror guest images (nofire) + +# Copies the images bunny injects into every build (urunit, the Cloud +# Hypervisor kernel, libarchive) from harbor.nbfc.io into ghcr.io/nofireai, by +# digest. hops/parse_file.go and hops/llb.go reference the copies, so a build +# no longer depends on harbor.nbfc.io being up or on what its `latest` points +# at today. +# +# skopeo --preserve-digests keeps each copy byte-identical to its source, so +# the digest pinned in the Go code is the upstream digest. Changing one means +# editing it both here and there in the same PR. +# +# Runs on push to nofire when this file changes, and on demand. The mirror has +# to exist before a frontend built from the new defaults is used; both run on +# the same push, and the frontend build takes longer. + +on: + push: + branches: ["nofire"] + paths: [".github/workflows/mirror-nofire.yml"] + workflow_dispatch: + +permissions: + contents: read + packages: write # create/update ghcr.io/nofireai/* packages + +jobs: + mirror: + runs-on: ubuntu-24.04 + strategy: + fail-fast: false + matrix: + include: + - src: harbor.nbfc.io/nubificus/urunit + dst: ghcr.io/nofireai/urunit + digest: sha256:ae7553fcf81489da20c34e8ec57f64f549a6db8aa9c48636e343c271d6a7b2d2 + - src: harbor.nbfc.io/nubificus/bunny/linux-kernel-cloud-hypervisor + dst: ghcr.io/nofireai/bunny/linux-kernel-cloud-hypervisor + digest: sha256:a9638a1ddb2e780247ce49cc5f6175b032a9ddd7b533f53e1bc5e7d31016c930 + - src: harbor.nbfc.io/nubificus/bunny/libarchive + dst: ghcr.io/nofireai/bunny/libarchive + digest: sha256:5244491f1afc2ee646b5a6b576598902580f64b9c00e61448512fa8723dcde7b + steps: + - name: Copy ${{ matrix.src }}@${{ matrix.digest }} + env: + SRC: ${{ matrix.src }} + DST: ${{ matrix.dst }} + DIGEST: ${{ matrix.digest }} + GHCR_USER: ${{ github.actor }} + GHCR_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + # Tagged with the digest's first 12 hex characters so the package + # page shows which upstream build it holds; the Go code pulls by + # digest and never reads the tag. + tag="sha-${DIGEST#sha256:}"; tag="${tag:0:16}" + skopeo copy --all --preserve-digests \ + --dest-creds "${GHCR_USER}:${GHCR_TOKEN}" \ + "docker://${SRC}@${DIGEST}" "docker://${DST}:${tag}" + # Fail loudly if the copy is not byte-identical. + got="$(skopeo inspect --raw --creds "${GHCR_USER}:${GHCR_TOKEN}" "docker://${DST}:${tag}" | sha256sum | cut -d' ' -f1)" + test "sha256:${got}" = "${DIGEST}" || { echo "::error::${DST}:${tag} is sha256:${got}, expected ${DIGEST}"; exit 1; } diff --git a/hops/llb.go b/hops/llb.go index cad5a29..12b6add 100644 --- a/hops/llb.go +++ b/hops/llb.go @@ -22,8 +22,9 @@ import ( ocispecs "github.com/opencontainers/image-spec/specs-go/v1" ) +// Mirrored and pinned like the defaults in parse_file.go. const ( - defaultBsdcpioImage string = "harbor.nbfc.io/nubificus/bunny/libarchive:latest" + defaultBsdcpioImage string = "ghcr.io/nofireai/bunny/libarchive@sha256:5244491f1afc2ee646b5a6b576598902580f64b9c00e61448512fa8723dcde7b" ) // Create a LLB State that simply copies all the files in the include list inside diff --git a/hops/parse_file.go b/hops/parse_file.go index 8860112..6bd234b 100644 --- a/hops/parse_file.go +++ b/hops/parse_file.go @@ -27,12 +27,20 @@ import ( "gopkg.in/yaml.v3" ) +// urunit and the Cloud Hypervisor kernel are the two artifacts every +// Containerfile build injects, so they come from our own registry, pinned by +// digest: an upstream `latest` could change the guest kernel under a build +// without anyone noticing, and a harbor.nbfc.io outage (it served a certificate +// for the wrong name on 2026-08-26) would fail every build. The copies are +// made by .github/workflows/mirror-nofire.yml with digests preserved, so each +// digest here is the upstream one. To move to a newer upstream build, bump the +// digest there and here together. const ( - defaultUrunitImage string = "harbor.nbfc.io/nubificus/urunit:latest" + defaultUrunitImage string = "ghcr.io/nofireai/urunit@sha256:ae7553fcf81489da20c34e8ec57f64f549a6db8aa9c48636e343c271d6a7b2d2" defaultUrunitPath string = "/urunit" defaultQemuKernelImage string = "harbor.nbfc.io/nubificus/bunny/linux-kernel-qemu:latest" defaultFirecrackerKernelImage string = "harbor.nbfc.io/nubificus/bunny/linux-kernel-firecracker:latest" - defaultCLHKernelImage string = "harbor.nbfc.io/nubificus/bunny/linux-kernel-cloud-hypervisor:latest" + defaultCLHKernelImage string = "ghcr.io/nofireai/bunny/linux-kernel-cloud-hypervisor@sha256:a9638a1ddb2e780247ce49cc5f6175b032a9ddd7b533f53e1bc5e7d31016c930" ) var (