Skip to content

ci(workflows): build Talos only when its sources changed - #4021

Merged
myasnikovdaniil merged 1 commit into
mainfrom
ci/build-talos-only-when-touched
Sep 21, 2026
Merged

myasnikovdaniil merged 1 commit into
mainfrom
ci/build-talos-only-when-touched

Conversation

@myasnikovdaniil

@myasnikovdaniil myasnikovdaniil commented Sep 2, 2026 •

Copy link
Copy Markdown
Contributor

Stacked on #4020, not independently mergeable.

Nothing on the same-repo PR path consumes either half of what build-talos produces. image-talos pushes ${REGISTRY}/talos:pr-N-sha, which no chart in the tree references and which is not even version-tagged here (PUBLISH_VERSIONED and PUBLISH_FLOATING are both 0). image-matchbox stamps packages/extra/bootbox/images/matchbox.tag, and that one line is the whole content of pr-patch-fragment-talos - checked against the artifact from a real run - but bootbox is opt-in and the E2E platform Package does not enable it, so no run pulls the image. Unstamped tag leaves installer artifact pointing at released matchbox, which is a valid ref nothing on this path resolves.

So the job ran a privileged siderolabs imager build on every PR for two artifacts with no reader. Now it runs when the PR touched packages/core/talos, and finalize accepts a skipped result as well as a successful one. Without that second half skipping it would block finalize, and therefore e2e, on nearly every PR.

Forks keep the unconditional build, and the reason is not about talos. e2e-fork.yaml publish job requires at least one OCI archive to exist, an invariant that holds today only because this job always ran, so a fork PR touching nothing under packages/ would arrive with an empty export and fail a legitimate run. Teaching that guard what plan expected to build is work in the fork allowlist path and belongs in its own change.

Saving is about 1.5-2 min of critical path. Main point is removing a privileged image build that nobody reads from every pull request.

Summary by CodeRabbit

  • CI Improvements

    • Pull request validation now runs Talos builds only when relevant changes require them, while retaining full validation for fork-based contributions.
    • Finalization correctly handles skipped Talos builds and preserves generated boot artifacts when builds succeed.
    • Changed-file detection now accounts for renames, deletions, and shared build inputs.
  • Documentation

    • Updated testing and build guidance to reflect the current validation behavior.
  • Tests

    • Added comprehensive coverage for build classification, workflow outputs, skipped jobs, and artifact handling.

@coderabbitai

coderabbitai Bot commented Sep 2, 2026 •

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: 4b078128-513a-4d2d-9655-839497ae2810

📥 Commits

Reviewing files that changed from the base of the PR and between f7cb9e7 and 975ea6a.

📒 Files selected for processing (6)
  • .github/workflows/pull-requests.yaml
  • docs/agents/e2e-testing.md
  • hack/build-matrix.sh
  • hack/build-talos-gating_test.bats
  • hack/build-talos-needed.sh
  • hack/overlay-main-images.sh

Included review availability: Your plan provides up to 8 included reviews per hour; 5 remain after this review.


📝 Walkthrough

Walkthrough

The workflow now classifies Talos-related changes from rename-blind paths. Same-repository Talos builds run only when required, fork builds remain unconditional, and finalization selects the correct bootbox overlay reference.

Changes

Talos build gating

Layer / File(s) Summary
Talos input classification and plan output
hack/build-talos-needed.sh, .github/workflows/pull-requests.yaml, hack/build-talos-gating_test.bats
The new classifier validates a readable changed-files list and detects direct or shared Talos inputs. The plan job publishes the result. Tests cover path matching, renames, deletions, invalid inputs, and unrelated changes.
Gated Talos execution and overlay selection
.github/workflows/pull-requests.yaml, hack/build-matrix.sh, hack/overlay-main-images.sh, docs/agents/e2e-testing.md, hack/build-talos-gating_test.bats
Same-repository Talos builds use the plan output, while fork builds remain unconditional. Finalization accepts skipped Talos jobs and preserves or overlays the bootbox reference based on Talos success. Tests verify workflow guards, overlay behavior, and removal of the nocloud artifact consumer.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant PlanJob
  participant TalosJob
  participant FinalizeJob
  participant Overlay
  PlanJob->>TalosJob: provide talos build requirement
  TalosJob->>FinalizeJob: report success or skipped status
  FinalizeJob->>Overlay: provide package exclusions
  TalosJob->>Overlay: preserve generated bootbox on success
  Overlay->>FinalizeJob: select generated or base bootbox reference
Loading

Suggested reviewers: lexfrei

Merge Risk: ⚪ Minimal · up to 975ea

The workflow gates unaffected same-repository Talos builds while preserving fork behavior and the correct bootbox reference. No merge-blocking correctness or availability risk is currently evidenced.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 3 functions across 4 files. (2 skipped: 2 … Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the primary change: gating Talos builds on source changes in CI workflows.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 3 functions across 4 files. (2 skipped: 2 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ci/build-talos-only-when-touched

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 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 labels Sep 2, 2026
@myasnikovdaniil
myasnikovdaniil force-pushed the feat/e2e-container-lane-v2 branch from c68f37c to 708b465 Compare September 3, 2026 06:38

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

NOT LGTM. The gate leans on a property of the plan job that the plan job does not promise and no test pins, and the branch sits on a base commit its base branch no longer contains.

Business context: skip the Talos build on same-repo PRs that did not touch its sources, since nothing on that path consumes either half of its output, and keep it unconditional for forks because the fork publish job needs at least one OCI archive.

Blockers

  1. The trailing space in contains(needs.plan.outputs.touched, 'packages/core/talos ') is load-bearing and pinned by nothing.

The plan job builds that value at .github/workflows/pull-requests.yaml:190 with sed ... | sort -u | tr '\n' ' ', so today every element carries a trailing space, the last one included, and the match works wherever packages/core/talos sorts. That space is a side effect of tr, not a contract. The producer's own comment on line 189 describes the value as "Whitespace-separated, no trailing slash", and the only consumer that existed before this change, hack/overlay-main-images.sh:50, takes it as "whitespace-separated package dirs" and word-splits, which cannot tell a trailing space from its absence. Switching that pipeline to paste -sd' ', or appending a sed 's/ *$//' tidy-up, keeps both of those contracts and quietly changes this gate: contains stops matching whenever packages/core/talos sorts last, and the build is skipped on precisely the PRs the gate exists to catch.

hack/build-talos-gating_test.bats pins the consumer side only. Its own comment states the producer's behaviour ("The touched list is space-joined by the plan job, so the trailing space is what keeps this from matching a future packages/core/talos-something") without checking it, so the coupling is asserted in a place that cannot fail. The failure is silent in the direction that costs: no build, nothing red, and a broken packages/core/talos reaches main and first shows up on the release path, where nightly.yaml:286 and e2e-tag.yaml:181 still run make prepare-env. A case in the same file that runs the plan pipeline over a fixture and asserts the last element keeps its space would close it, or the producer can emit the delimiter deliberately instead of inheriting it.

  1. The branch is built on c68f37c70, which feat/e2e-container-lane-v2 no longer contains after being force-pushed to 708b4652e.

mergeable is CONFLICTING against its own base, and the API reports 71 changed files and +3989/-4276 for what is a two-file change. Both numbers are artifacts of the stale base: measured over c68f37c70..0ac677bd6 the contribution is 2 files, +81/-1. Rebase onto 708b4652e before this can be read or merged as a stacked change.

Non-blocking

The comment's opening line, "Runs when this PR could have changed what it builds", is wider than the condition below it. packages/core/talos/Makefile:6 includes hack/common-envs.mk, which supplies REGISTRY, IMAGE_TAG and the image-tags/cache-args helpers the recipe calls, and the job hands hack/buildkitd.toml to buildx. An edit to either changes what the build produces or how it runs, and neither puts packages/core/talos into touched, so the build is skipped. The practical cost is small because nothing on this path consumes the output, but the sentence is permanent and checkable, so it is worth narrowing to what the gate actually keys on.

One coordination point for the rebase. This branch carries 24 references to the full-e2e label in pull-requests.yaml, and they are not all prose: line 51 compares github.event.label.name != 'full-e2e' inside the concurrency key. If the in-flight rename of that label lands first, whoever merges second reinstates a name that no longer exists, and a string comparison against a label does not fail a test when the label stops existing. Worth settling the merge order rather than finding out afterwards.

@myasnikovdaniil
myasnikovdaniil force-pushed the feat/e2e-container-lane-v2 branch from 708b465 to 00c6eb1 Compare September 5, 2026 10:11
Base automatically changed from feat/e2e-container-lane-v2 to main September 7, 2026 14:33
Classify the rename-blind changed-path list against direct Talos
sources and shared build inputs. This lets same-repository pull
requests skip the heavy build while removals, renames, and build
configuration changes still select it. Fork builds remain
unconditional.

Preserve the matchbox digest produced by a successful Talos build when
finalize overlays base-branch image references. When the job is
skipped, bootbox continues to receive the current base reference.

Assisted-By: LLM
Signed-off-by: Myasnikov Daniil <[email protected]>
@myasnikovdaniil
myasnikovdaniil force-pushed the ci/build-talos-only-when-touched branch from 0ac677b to 975ea6a Compare September 10, 2026 17:10
@github-actions github-actions Bot added size/L This PR changes 100-499 lines, ignoring generated files and removed size/M This PR changes 30-99 lines, ignoring generated files labels Sep 10, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM. Both blockers from my last pass are closed, and the paths the gate keys on match what the build actually reads.

The trailing-space coupling is gone because the gate no longer reads touched at all. plan hands hack/build-talos-needed.sh the rename-blind /tmp/changed_norenames.txt, the script matches whole lines, and hack/build-talos-gating_test.bats goes red if build-talos's if ever mentions needs.plan.outputs.touched again. Base is main now and the PR is mergeable, so the stale-base blocker went with it. hack/common-envs.mk and hack/buildkitd.toml, the two shared inputs I raised as non-blocking last time, are both in the pattern.

I enumerated the inputs from the recipe instead of from the pattern, and they line up. make -C packages/core/talos image reads images/talos/profiles/{installer,kernel,initramfs}.yaml and takes TALOS_VERSION out of installer.yaml. Matchbox builds from images/matchbox/Dockerfile with the repo root as context, and that Dockerfile copies only _out/assets/* plus packages/core/talos/images/matchbox/{groups,profiles}. Registry and tag macros come from hack/common-envs.mk, the buildx config from hack/buildkitd.toml, the job env from pull-requests.yaml. I found no input outside the five entries.

Nothing on this path reads either output, so the skip is safe downstream. hack/e2e-compose.yaml:28 boots ghcr.io/siderolabs/talos:v1.13.5 by digest rather than the built image, and no chart in the tree references ${REGISTRY}/talos. The only cross-job handoff is pr-patch-fragment-talos; with it absent, finalize's apply loop skips the unmatched glob and the overlay hands bootbox the base-branch matchbox ref. No stale artifact gets substituted anywhere, because there is no consumer.

The overlay half fixes a real bug rather than only accommodating the skip. Before this, a successful Talos build stamped packages/extra/bootbox/images/matchbox.tag and the overlay then put the base-branch ref back over it, since bootbox was in neither BUILD_MATRIX nor TOUCHED. Adding it to BUILT_JSON on success keeps the fresh digest. Skipping the whole package costs nothing here: bootbox's values.yaml carries no image reference, so images/matchbox.tag is the only file covered.

I ran the bats file and mutated three things to check the tests can actually fail: dropped hack/common-envs.mk from the pattern, removed skipped from finalize's Talos condition, and made the overlay treat bootbox as rebuilt regardless of TALOS_RESULT. Each one turned a different test red.

What I could not observe from this PR's own runs. It never exercises its own skip path, because it edits .github/workflows/pull-requests.yaml, which the classifier counts as a Talos input, so Build Talos ran and succeeded on run 34506559966. Any PR touching the workflow behaves the same, so the first run with talos == 'false' lands after merge. Job-result propagation is CI-only too: the test pins the text of finalize's condition, not what GitHub does when a needed job skips. The red E2E on this head is chainsaw/redis-2-backup-roundtrip and nothing else, which is #4218; it cannot be this change, since Talos built on that run and e2e got the same artifacts it would have without the gate.

Non-blocking

The description's reason for keeping forks unconditional does not hold. It says e2e-fork.yaml's archive check works "only because this job always ran", but finalize uploads pr-oci-installer for every fork PR (pull-requests.yaml:736-742) and finalize runs on every non-docs PR, so the -ge 1 check at e2e-fork.yaml:480 is satisfied without Talos. The comment you added to the job says exactly that and contradicts the description. Keeping forks unconditional is still the right call as a scope boundary. It is the stated reason that is wrong, and the description is what a reader reaches for first.

Also stale: the description opens with "Stacked on #4020, not independently mergeable". #4020 merged on 7 September, base is main, and GitHub reports the PR mergeable.

hack/build-talos-needed.sh is not among the paths it matches, so a PR editing only the classifier skips the build it governs. It cannot ship a wrong image, since the classifier is not a build input, but a change to the gate gets no run under the gate. .dockerignore is in the pattern and no such file exists in the tree, which is harmless and will do the right thing if one is added.

@myasnikovdaniil
myasnikovdaniil merged commit a1b32f2 into main Sep 21, 2026
45 of 47 checks passed
@myasnikovdaniil
myasnikovdaniil deleted the ci/build-talos-only-when-touched branch September 21, 2026 09:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/uncategorized PR auto-labeler could not map title scope to a known area/*; please review size/L This PR changes 100-499 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants