Skip to content

ci: warm the build cache on release-1.5 pushes - #3469

Merged
myasnikovdaniil merged 3 commits into
release-1.5from
ci/release-1.5-cache-warmer
Jul 29, 2026
Merged

myasnikovdaniil merged 3 commits into
release-1.5from
ci/release-1.5-cache-warmer

Conversation

@myasnikovdaniil

Copy link
Copy Markdown
Contributor

What this PR does

Nothing has ever warmed release-1.5's build cache, so every PR build on this branch runs cold end to end and exceeds its 30-minute timeout.

This branch's copy of build-main.yaml is dead code. It triggers on push: branches: [main], which never happens here, and it targets [self-hosted] — the decommissioned cozy-runner-1 (#2937) — so it could not have run even if it fired. This PR points it at this branch instead.

Both halves of the cold build trace back to 39cdc88 (feat(kubernetes): bootstrap tenant workers with Talos via CABPT) landing on main on 2026-06-16, six days before release-1.5 was cut on 2026-06-22:

  • That commit dropped ubuntu-container-disk from make image, so main stopped writing its six :<minor>-buildcache refs. release-1.5 is now the only branch that still builds the image, and the CI log shows all six importers failing with not found. Serially that was ~28min of the 30min budget; fix(ci, kubernetes): build container disks concurrently to unblock Build #3457 parallelises the target down to ~10min.
  • Every other cache ref carries main's sources, which have diverged from this branch, so most of the remaining 28 packages miss too. Measured on the fix(ci, kubernetes): build container disks concurrently to unblock Build #3457 run: ~38min across the full build, and no single hot spot left to attack — apps/kubernetes at 15m07s and system/linstor at 5m39s, everything else 20-60s each.

Changes

before after
trigger push: [main] push: [release-1.5]
runner [self-hosted] (decommissioned) oracle-vm-24cpu-96gb-x86-64
cache namespace shared cozystack/ cozystack-cache-release-1.5/
IMAGE_TAG main release-1.5
warmer timeout 90 120

pull-requests.yaml gets the same CACHE_REGISTRY so PR builds read what the warmer writes, and its Build timeout goes to 75 minutes.

The cache namespace is branch-scoped on purpose. Writing the shared refs from here would replace main's layers with this branch's, so main's next PR build would miss on everything and blow its own 30-minute timeout until the next main push re-warmed it. CACHE_REGISTRY already exists as a knob in hack/common-envs.mk, so this is one env var per job rather than new machinery. Image tags are unaffected — they still resolve to cozystack/<image>, only cache refs move.

The timeout raise is not optional. The new namespace starts empty, so this PR and the next one still build cold at ~45min, and anything that later invalidates the cache broadly — a Dockerfile edit, a go.mod bump — costs the same again. 30 minutes only ever fit a warm build, and when it did not fit, the job reported a timeout rather than whatever actually went wrong.

Verification

  • Both workflows are asserted to agree on the cache namespace, rather than eyeballed.
  • Confirmed the env split renders as intended: --tag iad.ocir.io/…/cozystack/ubuntu-container-disk:v1.35-release-1.5 alongside --cache-from/--cache-to on …/cozystack-cache-release-1.5/ubuntu-container-disk:v1.35-buildcache.
  • WRITE_CACHE stays unset in PR builds, so they remain read-only against the cache and never race on a manifest.

Notes for the reviewer

  • Stacked on fix(ci, kubernetes): build container disks concurrently to unblock Build #3457. Both PRs edit adjacent lines of pull-requests.yaml, so keeping them independent guaranteed a conflict. GitHub retargets this to release-1.5 automatically once fix(ci, kubernetes): build container disks concurrently to unblock Build #3457 merges.
  • The filename stays build-main.yaml even though it no longer warms main. Renaming it would make any later backport touching main's copy arrive as a whole new file rather than a modification, which is the failure mode the backport bot already hits on this branch.
  • One assumption I could not test locally: the registry credentials need repo-create permission for the new cozystack-cache-release-1.5/* path. It is the same tenancy namespace that already auto-creates repos as packages get added, so it should hold — but if it does not, --cache-to fails the warmer, and the fallback is a cache-tag suffix in hack/common-envs.mk instead of a separate namespace.
  • Sequencing: the merge commit is itself a push to release-1.5, so the warmer fires on merge and takes ~50-60min cold. PR builds after that should be ~15min. The warmer cannot be triggered manually — workflow_dispatch is only available for workflows present on the default branch, and this one exists only here.
  • Out of scope, but adjacent: tags.yaml, release-e2e.yaml and pull-requests-release.yaml on this branch all still say runs-on: [self-hosted], so the release and release-e2e lanes are broken the same way this warmer was.

Release note

NONE

Every release-1.5 PR Build job dies at the 30-minute timeout, and all of
it is spent in one target. `image-ubuntu-container-disk` builds a disk
per Kubernetes minor -- six of them -- and each spends ~4-5min inside a
libguestfs appliance installing that minor's kubelet/kubeadm into its own
copy of the cloud image. Serially that is ~28min of the 30min budget:

  v1.35 6:00, v1.34 4:21, v1.33 4:27, v1.32 4:39, v1.31 4:42, v1.30 cancelled

None of it is cached, and no warmer will fix that on its own. Only
build-main.yaml sets WRITE_CACHE=1, and main dropped this image from
`make image` in 39cdc88 six days before release-1.5 was cut, so the
:*-buildcache refs it reads have never been written -- the CI log shows
all six importers failing with "not found". release-1.5 is now the only
branch that builds this image at all.

The six builds share no per-version work, so run them concurrently
instead. A sub-make carries its own -j, keeping the root `make build`
serial, and buildkit dedups the shared guestfish and cloud-image stages
across the concurrent solves. --output-sync=target groups each version's
log under its target name; six interleaved buildx progress streams have
no per-solve identifier and are unreadable. Verified the per-version
buildx/echo/rm command set is byte-identical to the loop it replaces
across PUBLISH_VERSIONED, PUBLISH_FLOATING and WRITE_CACHE combinations.

Moving off the 4cpu/16gb shape because six concurrent libguestfs
appliances want ~9gb of RAM and a core apiece. The comment justifying
the small shape assumed a warm cache, which no longer holds here.

This also stops swallowing failures. The loop separated versions with
`;`, so make saw only the last version's exit status: a failed build for
any other version left its committed .tag file stale and the job green.
Under the sub-make a failure in any version fails the target.

Assisted-By: Claude <[email protected]>
Signed-off-by: Myasnikov Daniil <[email protected]>
Nothing has ever warmed this branch's build cache. This workflow is a
copy of main's warmer that still triggers on `push: branches: [main]`, so
on release-1.5 it has never fired once -- and it targets [self-hosted],
the decommissioned cozy-runner-1 (#2937), so it could not have run even
if it did fire.

The consequence is a PR Build that runs cold end to end and exceeds its
30-minute timeout. Two separate reasons, both traceable to 39cdc88
("bootstrap tenant workers with Talos via CABPT") landing on main six
days before this branch was cut:

  * that commit dropped ubuntu-container-disk from `make image`, so main
    stopped writing its six :<minor>-buildcache refs. release-1.5 is the
    only branch that still builds the image, and the CI log shows all six
    importers failing with "not found" -- ~28min serial, cut to ~10min by
    parallelising the target in #3457.
  * every other ref carries main's sources, which have diverged from this
    branch, so most of the remaining 28 packages miss too -- ~38min across
    the full build, with no single hot spot left to fix.

Point it at this branch, move it to the ephemeral shape main's warmer
already uses, and write to a branch-scoped CACHE_REGISTRY namespace.
The namespace matters: writing the shared refs from here would replace
main's layers with release-1.5's, so main's next PR build would miss on
everything and blow its own 30-min timeout until the next main push
re-warmed it. pull-requests.yaml has to read the same namespace, which
the following commit does.

The filename stays build-main.yaml even though it no longer warms main.
Renaming it would make any later backport touching main's copy arrive as
a whole new file rather than a modification, which is the failure mode
the backport bot already hits on this branch.

Assisted-By: Claude <[email protected]>
Signed-off-by: Myasnikov Daniil <[email protected]>
Point the Build job at the namespace build-main.yaml now writes for this
branch. Without this the warmer would run and nothing would read it: the
cache-from refs would still resolve to the shared namespace main writes,
whose layers carry main's sources and miss on most of this branch's 28
packages. Builds only read the cache -- WRITE_CACHE defaults to 0, so
concurrent PR builds still never race on a cache manifest, and a miss
degrades to a cold build rather than an error.

Raise the timeout to 75 minutes at the same time. A warm build is ~15min,
but the namespace starts empty, so this PR and the next one run cold at
~45min: six ubuntu-container-disk images plus 28 packages from scratch.
Anything that later invalidates the cache broadly -- a Dockerfile edit, a
go.mod bump -- costs the same again. 30 minutes only ever fit a warm
build, and when it did not fit, the job reported a timeout rather than
whatever actually went wrong.

Assisted-By: Claude <[email protected]>
Signed-off-by: Myasnikov Daniil <[email protected]>
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 9f33ffa7-0eb5-4fde-b068-dcf06871c452

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ci/release-1.5-cache-warmer

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added size/M This PR changes 30-99 lines, ignoring generated files area/uncategorized PR auto-labeler could not map title scope to a known area/*; please review labels Jul 28, 2026
@dosubot dosubot Bot added area/build Issues or PRs related to image build infrastructure, multi-arch support area/ci Issues or PRs related to CI workflows, GitHub Actions, automation labels Jul 28, 2026
@myasnikovdaniil
myasnikovdaniil changed the base branch from ci/parallel-container-disk to release-1.5 July 28, 2026 09:30

@IvanHunters IvanHunters left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. CI/build-infra only, no runtime chart/CRD/RBAC/migration surface. Cache wiring verified end-to-end: both workflows set CACHE_REGISTRY at workflow level to the same branch-scoped ref; the warmer writes (WRITE_CACHE=1) and PR builds read-only, so writer/reader target identical refs with no manifest race. Runner label oracle-vm-24cpu-96gb-x86-64 is a real in-use custom label; the actionlint "unknown runner" note is a false positive.

Non-blocking notes:

  • Stacked on #3457 — merge after it lands (or confirm pulling its Makefile parallelization into release-1.5 here is intended), otherwise the merge-base diff drags #3457's commit in.
  • The warmer only fires on push to release-1.5, i.e. post-merge, so the merging PR and near-term PRs still build cold (~45m) — consistent with the raised 75m timeout.
  • Adjacent lanes (tags.yaml, release-e2e.yaml, pull-requests-release.yaml) still target the decommissioned [self-hosted] runner — follow-up, out of scope here.

@myasnikovdaniil
myasnikovdaniil merged commit 5f2bea1 into release-1.5 Jul 29, 2026
12 of 13 checks passed
@myasnikovdaniil
myasnikovdaniil deleted the ci/release-1.5-cache-warmer branch July 29, 2026 07:11
myasnikovdaniil added a commit that referenced this pull request Jul 31, 2026
## What this PR does

Adds the flux CLI to the release-1.5 cache warmer, which #3469 moved
onto the ephemeral runner shape without carrying over the toolchain step
that main's warmer and this branch's `pull-requests.yaml` both have.

`make build` finishes at `packages/core/installer`, whose
`image-packages` target shells out to `flux push artifact`. The
self-hosted runner the warmer used to target had flux baked in; the
ephemeral shape does not.

Confirmed on the first warmer run after #3469 merged ([run
30430753152](https://github.com/cozystack/cozystack/actions/runs/30430753152)):
it failed at 49 minutes with `/bin/sh: 1: flux: not found` on
`Makefile:35`, the last of 28 packages.

The cache itself came out warm, because every package exports its cache
as it builds and installer is last:

| | |
| --- | --- |
| cache export operations | 87 |
| cache manifests written | 70 |
| repos created under `CACHE_REGISTRY` | 34 |
| `ubuntu-container-disk` refs (`v1.30`–`v1.35`, `mode=max`) | all 6 |
| real auth or export failures | 0 |

So the only loss was the `cozystack-packages:release-1.5` artifact push,
and nothing downstream depends on it — PR builds build the installer
themselves with flux present. This also settles the open question from
#3469: the registry credentials can create repositories under the new
`cozystack-cache-release-1.5/*` namespace, so no fallback to a cache-tag
suffix is needed.

Fixing it still matters. A permanently red warmer means a genuine
breakage is indistinguishable from this known-broken tail, and nothing
else reports on that workflow.

The alternative would be teaching the warmer to run the 28 image builds
without the artifact push, but no such target exists and it would
diverge from main's warmer for no gain. Installing the CLI keeps the two
warmers identical in shape, and matches main publishing
`cozystack-packages:main` from its own warmer.

### Release note

```release-note
NONE
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/build Issues or PRs related to image build infrastructure, multi-arch support area/ci Issues or PRs related to CI workflows, GitHub Actions, automation area/uncategorized PR auto-labeler could not map title scope to a known area/*; please review size/M This PR changes 30-99 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants