Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
20 changes: 14 additions & 6 deletions .github/workflows/compat.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ name: Compat
# On the KVM fleet (spin-stack/ci), because it boots: the newest release is unpacked as the machine
# before, and the machine here is that tree with this change's QEMU, firmware and kernel built over
# it - the base image is the release's, since a checkpoint resumes with the disks it was saved
# with. The builds read the BuildKit cache main's lanes export (CACHE_BACKEND=gha), so a change
# that leaves QEMU and the kernel alone compiles neither.
# with. The builds read the BuildKit cache main's lanes export (CACHE_BACKEND=registry), so a
# change that leaves QEMU and the kernel alone compiles neither.
#
# The test holds the promise only where it is made: when the two fingerprints differ the contract
# moved on purpose, the test says so and passes, and the release notes say it for the fleet.
Expand Down Expand Up @@ -39,6 +39,8 @@ on:

permissions:
contents: read
# The BuildKit cache main's lanes write (Taskfile.yml, CACHE_REGISTRY).
packages: read

# Per ref and never cancelled: a run on main is a commit somebody needs the answer for, and a
# release's waits on it. Called from release.yml it is the release's job and takes its group.
Expand Down Expand Up @@ -71,17 +73,23 @@ jobs:
- name: A BuildKit that reads the lanes' cache
run: go tool task boot:buildkit

# The tokens `type=gha` needs in a `run:` step; qemu.yml says why.
- uses: crazy-max/ghaction-github-runtime@04d248b84655b509d8c44dc1d6f990c879747487 # v4.0.0
# The cache is one of this repository's packages, and reading it takes a login: without one
# BuildKit carries on without the cache and compiles QEMU and the kernel cold.
- name: Log in to GitHub Packages
uses: docker/login-action@8aa5831fb8d48d8f76594c7ddde61ae58019f550 # v4.8.0
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

# The guests' vsock transport, loaded and made this user's: a module on the fleet's kernel
# that nothing loads before the first QEMU opens /dev/vhost-vsock.
- name: Load vhost_vsock
run: go tool task vsock

# Reads the lanes' scopes and writes none back: main's lanes keep them current.
# Reads the lanes' caches and writes none back: main's lanes keep them current.
- name: Build this change's QEMU and kernel, and the tools
run: go tool task qemu:build kernel:build tools CACHE_BACKEND=gha CACHE_EXPORT=false
run: go tool task qemu:build kernel:build tools CACHE_BACKEND=registry CACHE_EXPORT=false

# The newest release, checked against its sha256 and its signature, as the machine before;
# this change's machine is the same tree with what was just built in place of its own. The
Expand Down
23 changes: 15 additions & 8 deletions .github/workflows/image.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ on:

permissions:
contents: read
# The BuildKit cache: main writes it, everything else reads it (Taskfile.yml, CACHE_REGISTRY).
packages: write

concurrency:
group: image-${{ github.head_ref || github.ref_name }}
Expand Down Expand Up @@ -84,20 +86,25 @@ jobs:
cache: true

# A builder of its own (the docker-container driver): Docker's default one cannot export its
# cache. What it reads and writes is GitHub's cache, in the scopes Taskfile.yml decides
# (CACHE_BACKEND=gha), so the runner itself keeps nothing between runs.
# cache. What it reads and writes is the cache image in GitHub's registry that Taskfile.yml
# names (CACHE_BACKEND=registry), so the runner itself keeps nothing between runs.
- uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1

# Without this, `--cache-to type=gha` below is a silent no-op: the tokens BuildKit
# needs reach JavaScript actions and not `run:` steps. See the long note in qemu.yml.
- name: Expose the Actions cache to buildx
uses: crazy-max/ghaction-github-runtime@04d248b84655b509d8c44dc1d6f990c879747487 # v4.0.0
# The cache is one of this repository's packages, and reading it takes a login as much as
# writing it does. Without one BuildKit says it could not import the cache, carries on
# without it, and the build passes at the cold time.
- name: Log in to GitHub Packages
uses: docker/login-action@8aa5831fb8d48d8f76594c7ddde61ae58019f550 # v4.8.0
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

# The image is made and checked with the static e2fsprogs this repository builds, not
# the build container's, and image/build.sh stops when it is not there.
- name: Build e2fsprogs
env:
CACHE_BACKEND: gha
CACHE_BACKEND: registry
CACHE_EXPORT: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
run: go tool task e2fsprogs:build

Expand All @@ -113,7 +120,7 @@ jobs:
# and a cache entry large enough to push others out of the repository's 10 GB.
- name: Build the base image
env:
CACHE_BACKEND: gha
CACHE_BACKEND: registry
CACHE_EXPORT: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
run: go tool task image:build

Expand Down
21 changes: 14 additions & 7 deletions .github/workflows/kernel.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ on:

permissions:
contents: read
# The BuildKit cache: main writes it, everything else reads it (Taskfile.yml, CACHE_REGISTRY).
packages: write

concurrency:
group: kernel-${{ github.head_ref || github.ref_name }}
Expand Down Expand Up @@ -80,21 +82,26 @@ jobs:
cache: true

# A builder of its own (the docker-container driver): Docker's default one cannot export its
# cache. What it reads and writes is GitHub's cache, in the scopes Taskfile.yml decides
# (CACHE_BACKEND=gha), so the runner itself keeps nothing between runs.
# cache. What it reads and writes is the cache image in GitHub's registry that Taskfile.yml
# names (CACHE_BACKEND=registry), so the runner itself keeps nothing between runs.
- uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1

# Without this, `--cache-to type=gha` below is a silent no-op: the tokens BuildKit
# needs reach JavaScript actions and not `run:` steps. See the long note in qemu.yml.
- name: Expose the Actions cache to buildx
uses: crazy-max/ghaction-github-runtime@04d248b84655b509d8c44dc1d6f990c879747487 # v4.0.0
# The cache is one of this repository's packages, and reading it takes a login as much as
# writing it does. Without one BuildKit says it could not import the cache, carries on
# without it, and the build passes at the cold time.
- name: Log in to GitHub Packages
uses: docker/login-action@8aa5831fb8d48d8f76594c7ddde61ae58019f550 # v4.8.0
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

# Ends in `task kernel:verify`, which opens the ELF and checks the Xen PVH notes
# survived the strip. Without them QEMU has no entry point into this kernel and the
# failure is a VM that does not start, in whichever lane boots one next.
- name: Build the kernel
env:
CACHE_BACKEND: gha
CACHE_BACKEND: registry
CACHE_EXPORT: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
run: go tool task kernel:build KERNEL_NPROC=4

Expand Down
47 changes: 16 additions & 31 deletions .github/workflows/qemu.yml
Original file line number Diff line number Diff line change
Expand Up @@ -91,27 +91,19 @@ jobs:
cache: true

# A builder of its own (the docker-container driver): Docker's default one cannot export its
# cache. What it reads and writes is GitHub's cache, in the scopes Taskfile.yml decides
# (CACHE_BACKEND=gha), so the runner itself keeps nothing between runs.
# cache. What it reads and writes is the cache image in GitHub's registry that Taskfile.yml
# names (CACHE_BACKEND=registry), so the runner itself keeps nothing between runs.
- uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1

# What makes `--cache-to type=gha` do anything at all.
#
# BuildKit writes to GitHub's cache service with $ACTIONS_RUNTIME_TOKEN and
# $ACTIONS_RESULTS_URL, and those are handed to JavaScript actions, not to `run:`
# steps. Docker's documentation is explicit — "If you invoke the `docker buildx`
# command manually from an inline step, then the variables must be manually exposed"
# — and names this action for it. Every build here runs through `task`, which is a
# `run:` step.
#
# Without it the flags are accepted, nothing is stored, and nothing says so: the
# builds pass and take the cold time, every run. That failure is invisible in a log.
# Where it shows is `gh api repos/{owner}/{repo}/actions/cache/usage` — a repository
# that builds QEMU and a kernel and reports a handful of megabytes is caching neither.
#
# Remove this step and nothing breaks; everything just gets slow again.
- name: Expose the Actions cache to buildx
uses: crazy-max/ghaction-github-runtime@04d248b84655b509d8c44dc1d6f990c879747487 # v4.0.0
# The cache is one of this repository's packages, and reading it takes a login as much as
# writing it does. Without one BuildKit says it could not import the cache, carries on
# without it, and the build passes at the cold time.
- name: Log in to GitHub Packages
uses: docker/login-action@8aa5831fb8d48d8f76594c7ddde61ae58019f550 # v4.8.0
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

# The pinned version comes from versions.yaml, never from a literal here: two places
# to bump is how CI and production end up on different QEMUs.
Expand All @@ -121,7 +113,7 @@ jobs:

- name: Build QEMU and verify what came out
env:
CACHE_BACKEND: gha
CACHE_BACKEND: registry
CACHE_EXPORT: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
run: go tool task qemu:build QEMU_JOBS=4

Expand Down Expand Up @@ -166,20 +158,13 @@ jobs:

# Publishing is for main only: a fork's token cannot write packages, and an unreviewed
# branch - a pull request, or one dispatched to build a QEMU for the lab - has no
# business claiming the version tag.
- name: Log in to GitHub Packages
if: github.ref == 'refs/heads/main' && github.event_name != 'pull_request'
uses: docker/login-action@8aa5831fb8d48d8f76594c7ddde61ae58019f550 # v4.8.0
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

# business claiming the version tag. The login is the one made for the cache.
#
# The runtime image, tagged with the pinned QEMU version: what `task qemu:fetch` pulls,
# so CI's argument check runs main's QEMU in seconds instead of compiling it. The
# binaries are static, so the image around them carries nothing they need. It reads the
# builder stage the step above compiled from the `qemu` scope, and writes no cache: that
# scope holds these layers already.
# builder stage the step above compiled from the `qemu` cache, and writes no cache: that
# cache holds these layers already.
- name: Publish the runtime image
if: github.ref == 'refs/heads/main' && github.event_name != 'pull_request'
run: go tool task qemu:publish
27 changes: 18 additions & 9 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ permissions:
# Keyless signing: cosign asks Sigstore for a short-lived certificate bound to this workflow at
# this ref, which is what a machine that takes the release holds its files to.
id-token: write
# The BuildKit cache main's lanes write (Taskfile.yml, CACHE_REGISTRY), read here and by compat,
# which a called workflow can be granted no more than its caller holds.
packages: read

# One release at a time. The version is decided at the start of a run by counting the tags
# already taken today and pushed at the end, so two runs at once both generate .01 and the
Expand Down Expand Up @@ -71,18 +74,24 @@ jobs:
go-version-file: go.mod
cache: true

# What main's lanes already compiled is read, not compiled again: the BuildKit scopes are the
# What main's lanes already compiled is read, not compiled again: the BuildKit caches are the
# lanes' (CACHE_BACKEND in Taskfile.yml); a cold release compiles QEMU and the kernel, each
# tens of minutes, which the ceiling above is for.
#
# A builder of its own (the docker-container driver): Docker's default one cannot export its
# cache. What it reads and writes is GitHub's cache, in the scopes Taskfile.yml decides
# (CACHE_BACKEND=gha), so the runner itself keeps nothing between runs.
# cache. What it reads and writes is the cache image in GitHub's registry that Taskfile.yml
# names (CACHE_BACKEND=registry), so the runner itself keeps nothing between runs.
- uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1

# Without this, `--cache-to type=gha` below is a silent no-op: the tokens BuildKit
# needs reach JavaScript actions and not `run:` steps. See the long note in qemu.yml.
- name: Expose the Actions cache to buildx
uses: crazy-max/ghaction-github-runtime@04d248b84655b509d8c44dc1d6f990c879747487 # v4.0.0
# The cache is one of this repository's packages, and reading it takes a login as much as
# writing it does. Without one BuildKit says it could not import the cache, carries on
# without it, and the build passes at the cold time.
- name: Log in to GitHub Packages
uses: docker/login-action@8aa5831fb8d48d8f76594c7ddde61ae58019f550 # v4.8.0
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

# Two sources, in order: what the caller typed, or a new one for today. The generated form is deliberately not `date +%s` or a commit
# hash — it has to be readable, sortable, and the same shape as one typed by hand.
Expand All @@ -103,11 +112,11 @@ jobs:
- name: Build the release
env:
VERSION: ${{ steps.version.outputs.version }}
# The same scopes the per-artefact workflows write on main, decided in
# The same caches the per-artefact workflows write on main, decided in
# Taskfile.yml. That is the whole point of them being named there: a release
# reads what the lane on main already paid to compile, instead of starting cold
# because a workflow file spelled the scope differently.
CACHE_BACKEND: gha
CACHE_BACKEND: registry
# Reads what main's lanes exported and writes nothing back: a release is built once, and
# the next one reads main's newer cache, not this. See CACHE_EXPORT in Taskfile.yml.
CACHE_EXPORT: "false"
Expand Down
Loading
Loading