Skip to content

chore(release): don't activate kubernetes-nodes on the release-1.6 line - #3437

Merged
myasnikovdaniil merged 1 commit into
release-1.6from
cleanup/release-1.6-drop-features
Aug 4, 2026
Merged

myasnikovdaniil merged 1 commit into
release-1.6from
cleanup/release-1.6-drop-features

Conversation

@myasnikovdaniil

@myasnikovdaniil myasnikovdaniil commented Jul 23, 2026 •

Copy link
Copy Markdown
Contributor

What this changes

kubernetes-nodes is not ready to launch on the 1.6 line yet, so this removes its single include from the iaas platform bundle (packages/core/platform/templates/bundles/iaas.yaml). The platform stops activating the package while it still ships in the release; the line comes back when the feature is ready.

Scope

One line removed, nothing else. The kubernetes-nodes app package, its API types (api/apps/v1alpha1/kubernetesnodes), and the kubernetes-nodes-rd system package are all kept intact — this deactivates the feature, it does not delete it.

NONE

@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 23, 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: c44478df-1059-4f3e-9dae-a82eb4cafe0e

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 cleanup/release-1.6-drop-features

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/release Issues or PRs related to release tooling (changelog, backport, release pipeline) kind/cleanup Categorizes issue or PR as related to cleanup of code, process, or technical debt size/XXL This PR changes 1000+ lines, ignoring generated files labels Jul 23, 2026
@myasnikovdaniil
myasnikovdaniil marked this pull request as ready for review July 23, 2026 08:24
@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.

@kvaps
Andrei Kvapil (kvaps) self-requested a review July 23, 2026 08:37
{{include "cozystack.platform.package.default" (list "cozystack.capi-provider-infra-kubevirt" $) }}
{{include "cozystack.platform.package.default" (list "cozystack.bucket-application" $) }}
{{include "cozystack.platform.package" (list "cozystack.kubernetes-application" "kubevirt" $) }}
{{include "cozystack.platform.package" (list "cozystack.kubernetes-nodes-application" "kubevirt" $) }}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This single-line-removal is the only thing in the PR that needs to stay. Simply don't activate a feature that isn't ready yet and bring this line back when the feature is ready for launch.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done — reduced to just this one-line removal. The kubernetes-nodes app package, its API types, and the kubernetes-nodes-rd package are all restored; the feature is only deactivated in the iaas bundle now, and the line comes back when it's ready to launch. Thanks!

@myasnikovdaniil
myasnikovdaniil force-pushed the cleanup/release-1.6-drop-features branch from dbe28d0 to 6af47b4 Compare July 24, 2026 04:49
@github-actions github-actions Bot added size/XS This PR changes 0-9 lines, ignoring generated files and removed size/XXL This PR changes 1000+ lines, ignoring generated files labels Jul 24, 2026
@myasnikovdaniil myasnikovdaniil changed the title chore(release): drop kubernetes-nodes from the release-1.6 line chore(release): don't activate kubernetes-nodes on the release-1.6 line Jul 24, 2026
myasnikovdaniil added a commit that referenced this pull request Jul 29, 2026
… artifacts

Supersedes the interim guard in this branch`s first commit, which skipped the
overlay entirely for release-line PRs. Skipping fixed the wrong images but left
those PRs testing their line`s last release: for any package the PR did not
rebuild, the committed ref is the released digest, so a component changed by an
earlier backport was exercised as its pre-backport binary until the next rc.

The overlay now reads `cozystack-packages:<base branch>` instead of always
`:main`, and build-release.yaml publishes that artifact for every maintained
`release-<major>.<minor>` branch the way build-main.yaml does for main: images
tagged with the branch, and the whole packages tree pushed with each reference
digest-pinned to what the run just built. Each base branch therefore has its own
generation to overlay from, which is what the original bug was really about —
#3437 failed install because main`s cozystack-controller served an aggregated
OpenAPI release-1.6`s charts could not validate against.

Three deliberate choices:

  * The trigger matches line branches only (`release-[0-9]+.[0-9]+`). The
    per-release and rc staging branches promote-rc.yaml and tags.yaml create
    (release-1.6.1, release-1.6.0-rc.4) must not trigger a full rebuild — their
    images come from the tag build, and rebuilding them would be waste.
  * WRITE_CACHE stays 0. CACHE_REGISTRY/<img>:buildcache is a single ref per
    image and build-main.yaml is deliberately its only, serialized writer so
    concurrent builds cannot race on the cache manifest (the 409 class #2711
    fixed for image tags). A line build can overlap a main build, so writing here
    would reintroduce that race. Line builds read the cache.
  * A missing artifact still degrades to committed refs, but on a release line it
    now emits a ::warning:: naming the branch. Silent degradation is
    indistinguishable from a working overlay, which is how a mis-specified branch
    filter would hide for a whole release cycle.

Cost: one `make build` per push to a maintained line, i.e. per merged backport.

hack/overlay-main-images_test.bats pins the artifact tag to the base branch,
rejects a hardcoded :main in either overlay step, and pins build-release.yaml`s
branch filter, image tag and WRITE_CACHE=0. Mutation-checked: restoring :main,
setting WRITE_CACHE=1, and broadening the filter to release-* each fail a test.
13/13 green; actionlint and zizmor clean.

Assisted-By: Claude <[email protected]>
Signed-off-by: Myasnikov Daniil <[email protected]>
myasnikovdaniil added a commit that referenced this pull request Aug 3, 2026
… artifacts (#3471)

## What this PR does

The PR finalize job overlays image refs for every package a PR did not
rebuild, so e2e and the installer do not test last-release images for
everything outside the PR's build matrix. It could only ever read
`cozystack-packages:main`, which meant a **release-line PR was handed
main's binaries to run against its own line's charts**.

#3437 is the demonstration: a one-line change on
`release-1.6` deactivating an app failed install deterministically,
twice, with

```
helmrelease/backupstrategy-controller: Helm install failed …
error validating data: SchemaError(github.com/cozystack/cozystack/pkg/apis/core/v1alpha1.Option.spec):
unknown model in reference: "github.com~1cozystack~1cozystack~1pkg~1apis~1core~1v1alpha1.OptionSpec"
```

main's `cozystack-controller` served an aggregated OpenAPI that
`release-1.6`'s charts could not validate against. Nothing in that PR
was broken; the lane was. Both branches carry `option_types.go` and key
`OptionSpec` identically in the committed generated OpenAPI, so this is
a generation mismatch at runtime, not a codegen drift.

This PR fixes it by giving every base branch its own artifact to overlay
from, rather than by turning the overlay off.

### The change

1. **`pull-requests.yaml`** reads `cozystack-packages:${BASE_REF}`
(`github.base_ref`) instead of a hardcoded `:main`.
2. **`build-release.yaml`** (new) publishes that artifact for maintained
`release-<major>.<minor>` branches exactly as `build-main.yaml` does for
main: every image tagged with the branch, and the whole packages tree
pushed with each reference digest-pinned to what the run just built.

An earlier revision of this branch simply skipped the overlay for
non-main bases. That fixed the wrong-images problem but left
release-line PRs testing their line's *last release*: for any package
the PR did not rebuild, the committed ref is the released digest, so a
component changed by an earlier backport was still exercised as its
pre-backport binary until the next rc. Per-line artifacts remove that
gap too, which is why the guard was replaced rather than kept.

### Three deliberate choices

**The trigger matches line branches only** (`release-[0-9]+.[0-9]+`).
The per-release and rc staging branches `promote-rc.yaml` and
`tags.yaml` create — `release-1.6.1`, `release-1.6.0-rc.4` — must not
trigger a full rebuild; their images come from the tag build and
rebuilding them is waste.

**`WRITE_CACHE` stays `0`.** `CACHE_REGISTRY/<img>:buildcache` is a
single ref per image and `build-main.yaml` is deliberately its only,
serialized writer so concurrent builds cannot race on the cache manifest
— the 409 class #2711 fixed for image tags. A line build can overlap a
main build, so writing here would reintroduce that race. Line builds
read the cache.

**A missing artifact still degrades to committed refs, but says so.** On
a release line it emits a `::warning::` naming the branch. Silent
degradation is indistinguishable from a working overlay, which is how a
mis-specified branch filter could hide for a whole release cycle.

### Cost

One `make build` per push to a maintained line — in practice per merged
backport. That is the price of release-line PRs testing their line's tip
instead of its last release.

### Verification

`hack/overlay-main-images_test.bats` pins the artifact tag to the base
branch, rejects a hardcoded `:main` in either overlay step, and pins
`build-release.yaml`'s branch filter, image tag and `WRITE_CACHE: '0'`.
Mutation-checked: restoring `:main`, setting `WRITE_CACHE: '1'`, and
broadening the filter to `release-*` each fail a test. 13/13 green;
`actionlint` and `zizmor` clean.

Worth an explicit ack in review: the branch-filter pattern is the one
thing no local test can prove, since only GitHub evaluates it. If it
does not match, `build-release` never runs and the new `::warning::` is
what surfaces it on the next release-line PR.

### Backport

`release-1.6` needs this too — for `pull_request` events GitHub builds
the workflow from the merge ref, so a release-line PR only stops
receiving main's images once the change is on its base branch.
#3472 carried the interim guard and is closed in
favour of backporting this instead.

### Release note

```release-note
NONE
```


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Added a “Build release line” workflow that builds and publishes images
and the packages artifact for maintained `release-<major>.<minor>`
branches.

* **Bug Fixes**
* Updated PR workflow finalization to use base-branch–specific package
overlays, avoiding incorrect main-branch image references when targeting
release branches.
* Improved fallback behavior when the base-branch packages artifact is
unavailable.

* **Tests**
* Added workflow wiring tests to verify base-branch artifact usage and
that each maintained release line publishes its own packages artifact.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
myasnikovdaniil added a commit that referenced this pull request Aug 3, 2026
…h, and publish per-line artifacts (#3514)

## What this PR does

Hand backport of #3471 to `release-1.6`. Two clean cherry-picks, each
carrying its `-x` reference; the resulting tree is byte-identical to
#3471's merged state for all three files.

The overlay must be fixed **on this branch** to have any effect here.
For `pull_request` events GitHub builds the workflow from the merge ref,
and for `push` events it reads the workflow from the pushed ref — so
neither half of #3471 reaches this line while it lives only on `main`.
Concretely, `build-release.yaml` on `main` never fires for a push to
`release-1.6`, and a `release-1.6` PR keeps using this branch's copy of
`pull-requests.yaml`.

## Why the bot could not do this

The automatic backport reported success on every job and opened nothing
(run 30817682877). It is not a conflict — #3471 carries an empty commit,
`007d0b1a` (`chore(ci): re-trigger CI after a label event produced a
no-op run`, zero files). `korthout/backport-action` cherry-picks
commit-by-commit without `--allow-empty`, so that commit fails with `The
previous cherry-pick is now empty`; the `draft_commit_conflicts`
fallback then runs `git commit --all -m BACKPORT-CONFLICT`, which also
has nothing to commit, and the action aborts:

```
git cherry-pick -x 007d0b1
The previous cherry-pick is now empty, possibly due to conflict resolution.
git commit --all -m BACKPORT-CONFLICT
The previous cherry-pick is now empty, possibly due to conflict resolution.
git cherry-pick --abort
Backport failed for `release-1.6`, because it was unable to cherry-pick the commit(s).
```

This is a general gap, not specific to this PR: any labelled PR
containing an empty commit backports to nothing, and the failure
surfaces as three green jobs. Worth a follow-up on the workflow —
skipping empty commits, or having the action's failure fail the job —
but that is out of scope here.

## What this unblocks

#3437 fails install deterministically on this line today, because the
unguarded overlay hands it main's `cozystack-controller` to run against
`release-1.6`'s charts. The binary indexes
`internal.cozystack.io/v1alpha1 TenantProjection` at startup, this
branch ships no such CRD, so it fatals and every release that depends on
it stalls. With this merged, the overlay reads
`cozystack-packages:release-1.6` instead.

That works even before the first `build-release.yaml` run publishes the
artifact: a missing artifact degrades to the committed refs, which on
this branch are the v1.6.0 digests its charts are written against. The
degraded path is also no longer silent — it emits a warning naming the
branch.

#3437 will need a new head commit rather than a re-run, since the
finalize job is reused on re-run and its existing `pr.patch` already has
main's refs baked in.

## Note on cost

Merging this is itself a push to `release-1.6`, so `build-release.yaml`
fires on it and runs a full `make build` (up to 2h on a 24-cpu runner)
to publish the line's images and `cozystack-packages:release-1.6`. That
recurs per push to this line; `concurrency` with `cancel-in-progress`
collapses bursts, and `paths-ignore` only exempts `docs/**`.

## Verification

`hack/overlay-main-images_test.bats` 13/13 green on this branch,
including the two wiring tests the backport brings (`the overlay reads
the artifact for the PR's own base branch`, `every maintained release
line publishes its own packages artifact`). `actionlint` clean on both
workflows, and both parse as YAML. `hack/common-envs.mk` is
byte-identical between `main` and `release-1.6`, so `IMAGE_TAG`,
`WRITE_CACHE`, `PUBLISH_VERSIONED` and `PUBLISH_FLOATING` behave here
exactly as they do on main.

### Release note

```release-note
NONE
```
kubernetes-nodes isn't ready to launch on 1.6 yet. Remove only its include
from the iaas platform bundle so the package still ships but stays inactive;
restore the line when the feature is ready. The package, API types and RD
package are kept intact.

Assisted-By: Claude <[email protected]>
Signed-off-by: Myasnikov Daniil <[email protected]>
pull Bot pushed a commit to medampudi/cozystack that referenced this pull request Aug 3, 2026
The PR finalize job pulls cozystack-packages:main and repoints every package
the PR did not rebuild at current-main images. On a main-based PR that is the
whole point: it stops e2e from testing last-release images for everything
outside the build matrix. On a release-line PR it is wrong — the committed refs
there are not stale, they are that line`s released digests, and the charts are
written against exactly those.

So a release-line PR was installing main`s binaries onto its own charts, and the
mismatch grows with every commit main gains. cozystack#3437 is the demonstration: a
one-line change on release-1.6 that deactivates an app failed install
deterministically, twice, with

  SchemaError(...core/v1alpha1.Option.spec): unknown model in reference:
  "...core~1v1alpha1.OptionSpec"

from main`s cozystack-controller serving an aggregated OpenAPI that branch`s
charts cannot validate against. Nothing in the PR was broken; the lane was.
Left alone this makes every 1.6 backport look red, which is when release-branch
PRs are busiest.

Both overlay steps are now gated on `github.base_ref == main`, so a release-line
PR keeps its committed digests — the behaviour that predates the overlay.
Retargeting the overlay at a per-line artifact would be better but is not
possible today: build-main.yaml publishes only cozystack-packages:main, and the
registry carries no release-* equivalent (verified against the packages repo`s
tag list: `main` plus per-PR tags, nothing else).

hack/overlay-main-images_test.bats pins the wiring per step, so adding a third
overlay step without the guard fails the suite. Mutation-checked by removing one
guard. 12/12 green; actionlint clean.

Assisted-By: Claude <[email protected]>
Signed-off-by: Myasnikov Daniil <[email protected]>
myasnikovdaniil added a commit that referenced this pull request Aug 4, 2026
build-release.yaml publishes `cozystack-packages:<line>` on push to a
release line, and pull-requests.yaml's overlay reads it so a release-line
PR tests its own line's binaries instead of main's (#3471, #3437).

It cannot fire for the push that creates the line. The freeze points
release-X.Y at a commit that is already on main, so the push carries no
new commits, and GitHub does not run a workflow whose paths/paths-ignore
filter finds no changed files ("If there are no files changed, the
workflow will not run"). The line would therefore have no artifact until
its first cherry-pick merged, and in that window the overlay finds
nothing to pull and leaves every package on its committed ref — at
freeze time the previous release's, which is exactly the
cross-generation mix #3437 fixed. The old flow had no such window: it
branched at the promote merge commit, which carried real commits and its
own release's refs.

Add workflow_dispatch to build-release.yaml and have the freeze step
dispatch it for the branch it just created, so the artifact exists from
the moment the line does. The dispatch is non-fatal: without it the
overlay no-ops and early cherry-pick PRs test their committed refs,
which is where they were before #3471. The tag is pushed and the line is
frozen by that point, so failing there would misreport both.

The trigger also gives a line build a re-run button, which previously
needed an empty commit pushed to the line.

Assisted-By: Claude <[email protected]>
Signed-off-by: Myasnikov Daniil <[email protected]>
@myasnikovdaniil
myasnikovdaniil merged commit 3b96ef3 into release-1.6 Aug 4, 2026
12 of 13 checks passed
@myasnikovdaniil
myasnikovdaniil deleted the cleanup/release-1.6-drop-features branch August 4, 2026 06:48
myasnikovdaniil added a commit that referenced this pull request Sep 7, 2026
pull_request_review does not populate github.base_ref or
github.head_ref: GitHub sets those for pull_request and
pull_request_target only. So `git diff origin/${BASE_REF}...HEAD` in
plan resolved as `origin/...HEAD`, died as an ambiguous revision under
bash -e, and every approving review failed plan and posted E2E Tests =
failure instead of starting the suite. finalize's packages-artifact pull
and the TIA selection read the same value, and verify-release-candidate
derived STABLE_VERSION from head_ref, which would have verified a
version that does not exist. All four now read the ref out of the
payload, which every event here carries.

e2e-report, verify-release-candidate and resolve_assets did not get the
review clause plan got, and none of them reaches plan through `needs`.
!cancelled() is a status-check function, so a skipped plan does not hold
e2e-report back on its own: a `commented` or `changes_requested`
submission ran it with plan skipped, read PLAN_RESULT != 'success', and
posted failure over an already-green status. That happened on this
branch (run 33750114876) rather than being read off the docs.

The concurrency key gets the complement of that clause, which the
comment above it had argued was unnecessary. A review submission is not
a `labeled` action, so without its own term a non-approving submission
landed in the main group, cancelled the live suite, and then published
nothing - the same wedge the label split exists to prevent, with a
176-minute run on the line instead of a build. The suffix is -noop
rather than -label now that two kinds of event earn it.

review.state == 'approved' does not say who approved. Anyone with read
access can submit a review on a public repository and each submission
re-fires the event, so that guard let any account start the big job on
any open same-repo pull request as often as it liked. The clause now
also requires author_association in OWNER/MEMBER/COLLABORATOR, matching
the write access the ruleset counts approvals from, and
pull_request.state == 'open', since the event fires on closed pull
requests too.

heavy stops being a pure expression. plan decides it in a step that
first reads the E2E Tests status for the head SHA, so an approving
review earns the lane only when that SHA has no green to reuse. Two
approvals are normal here and the second one cancelled the live suite
and paid for another ~176 minutes on the same answer, which is the cost
this whole change is about. Only success dedupes, so re-running a red
suite still works. That reading is published as `prior` and the opener
consults it: a run that will neither re-verify the head nor conclude the
context must not overwrite the green with a pending that e2e-report
deliberately never concludes, or the gate wedges itself on the good
path. The pending it does write now says which of the two states it is.

e2e/run reaches the release arm alongside e2e/full. Without that it set
heavy, could not reach the suite, and the reporter concluded "e2e not
repeated" - a label named "run" reporting a run that did not happen. The
reporter's release branch answers to either label for the same reason.

The contract tests could not tell the broken workflow from the fixed
one, because the whole review axis was invisible to an extraction that
matches github.event.label.name. gate-concurrency now normalises the
review predicates to the facts they assert, requires all four of plan,
verify-release-candidate, resolve_assets and e2e-report to carry them
the same way round, requires the key to carry them the other way round,
and requires every remaining job to either carry them or name plan in
its needs - which closes the enumeration hole the label loop documents.
It also pins that e2e reads plan's heavy output and that the step
computes it, so replacing the decision with a constant fails. Every
mutation above was run against the suite, including the two that used to
leave it green.

promote-gate-contract stops pinning plan's whole `if:` line. It broke on
every unrelated edit to that guard while proving nothing beyond text
identity, and the axis it was accidentally covering is now covered by
meaning next door. overlay-main-images_test pinned BASE_REF to
github.base_ref by text; it now requires the payload form and rejects
the other, since reading github.base_ref there degrades the overlay to
committed refs on every approving review - #3437's generation mismatch,
reached silently.

docs/release.md still told maintainers to add full-e2e in six places, on
the one path where skipping the suite is a deliberate release-risk call;
the label sync renames it away, so following that would have created a
fresh label no workflow reads. docs/agents/e2e-testing.md said the
e2e/full label forces the whole suite, that each lane concludes the
status when the suite is done, and that the key is the complement of
plan's guard - all three false after this change, on lines the rename
pass had already touched. It now also states the new resting state,
which is what a contributor needs from that file: E2E Tests pending
until an approval or e2e/run.

Assisted-By: LLM
Signed-off-by: Myasnikov Daniil <[email protected]>
myasnikovdaniil added a commit that referenced this pull request Sep 8, 2026
pull_request_review does not populate github.base_ref or
github.head_ref: GitHub sets those for pull_request and
pull_request_target only. So `git diff origin/${BASE_REF}...HEAD` in
plan resolved as `origin/...HEAD`, died as an ambiguous revision under
bash -e, and every approving review failed plan and posted E2E Tests =
failure instead of starting the suite. finalize's packages-artifact pull
and the TIA selection read the same value, and verify-release-candidate
derived STABLE_VERSION from head_ref, which would have verified a
version that does not exist. All four now read the ref out of the
payload, which every event here carries.

e2e-report, verify-release-candidate and resolve_assets did not get the
review clause plan got, and none of them reaches plan through `needs`.
!cancelled() is a status-check function, so a skipped plan does not hold
e2e-report back on its own: a `commented` or `changes_requested`
submission ran it with plan skipped, read PLAN_RESULT != 'success', and
posted failure over an already-green status. That happened on this
branch (run 33750114876) rather than being read off the docs.

The concurrency key gets the complement of that clause, which the
comment above it had argued was unnecessary. A review submission is not
a `labeled` action, so without its own term a non-approving submission
landed in the main group, cancelled the live suite, and then published
nothing - the same wedge the label split exists to prevent, with a
176-minute run on the line instead of a build. The suffix is -noop
rather than -label now that two kinds of event earn it.

review.state == 'approved' does not say who approved. Anyone with read
access can submit a review on a public repository and each submission
re-fires the event, so that guard let any account start the big job on
any open same-repo pull request as often as it liked. The clause now
also requires author_association in OWNER/MEMBER/COLLABORATOR, matching
the write access the ruleset counts approvals from, and
pull_request.state == 'open', since the event fires on closed pull
requests too.

heavy stops being a pure expression. plan decides it in a step that
first reads the E2E Tests status for the head SHA, so an approving
review earns the lane only when that SHA has no green to reuse. Two
approvals are normal here and the second one cancelled the live suite
and paid for another ~176 minutes on the same answer, which is the cost
this whole change is about. Only success dedupes, so re-running a red
suite still works. That reading is published as `prior` and the opener
consults it: a run that will neither re-verify the head nor conclude the
context must not overwrite the green with a pending that e2e-report
deliberately never concludes, or the gate wedges itself on the good
path. The pending it does write now says which of the two states it is.

e2e/run reaches the release arm alongside e2e/full. Without that it set
heavy, could not reach the suite, and the reporter concluded "e2e not
repeated" - a label named "run" reporting a run that did not happen. The
reporter's release branch answers to either label for the same reason.

The contract tests could not tell the broken workflow from the fixed
one, because the whole review axis was invisible to an extraction that
matches github.event.label.name. gate-concurrency now normalises the
review predicates to the facts they assert, requires all four of plan,
verify-release-candidate, resolve_assets and e2e-report to carry them
the same way round, requires the key to carry them the other way round,
and requires every remaining job to either carry them or name plan in
its needs - which closes the enumeration hole the label loop documents.
It also pins that e2e reads plan's heavy output and that the step
computes it, so replacing the decision with a constant fails. Every
mutation above was run against the suite, including the two that used to
leave it green.

promote-gate-contract stops pinning plan's whole `if:` line. It broke on
every unrelated edit to that guard while proving nothing beyond text
identity, and the axis it was accidentally covering is now covered by
meaning next door. overlay-main-images_test pinned BASE_REF to
github.base_ref by text; it now requires the payload form and rejects
the other, since reading github.base_ref there degrades the overlay to
committed refs on every approving review - #3437's generation mismatch,
reached silently.

docs/release.md still told maintainers to add full-e2e in six places, on
the one path where skipping the suite is a deliberate release-risk call;
the label sync renames it away, so following that would have created a
fresh label no workflow reads. docs/agents/e2e-testing.md said the
e2e/full label forces the whole suite, that each lane concludes the
status when the suite is done, and that the key is the complement of
plan's guard - all three false after this change, on lines the rename
pass had already touched. It now also states the new resting state,
which is what a contributor needs from that file: E2E Tests pending
until an approval or e2e/run.

Assisted-By: LLM
Signed-off-by: Myasnikov Daniil <[email protected]>
Aleksei Sviridkin (lexfrei) added a commit that referenced this pull request Sep 24, 2026
…4460)

<!-- Thank you for making a contribution! Here are some tips for you:
- Use Conventional Commits for the PR title: `type(scope): description`
- Types: feat, fix, docs, style, refactor, perf, test, build, ci, chore
- Scopes are not an exhaustive list — pick the most specific scope for
the change and extend the list when a genuinely new area appears.
Examples:
- System components: dashboard, platform, operator, cilium, kube-ovn,
linstor, fluxcd, cluster-api
- Managed apps: postgres, mariadb, redis, kafka, clickhouse,
virtual-machine, kubernetes
- Development and maintenance: api, hack, tests, ci, docs, maintenance
- Breaking changes: append `!` after type/scope (`feat(api)!: ...`) or
add a `BREAKING CHANGE:` footer
- If it's a work in progress, consider creating this PR as a draft.
- Don't hesistate to ask for opinion and review in the community chats,
even if it's still a draft.
- Add the label `kind/backport` if it's a bugfix that needs to be
backported to a previous version.
-->

## What this PR does

A PR stacked on a feature branch now overlays the image refs of the line
it grew from, so its E2E no longer falls back to the refs committed in
the tree.

The Finalize job pulls `cozystack-packages:<base branch>` and overlays
its image refs onto the packages the PR did not rebuild. For a stacked
PR the base is something like `fix/foo`. The slash is not valid in an
OCI tag, and no workflow publishes an artifact for that branch anyway.
So the pull failed, the step only printed a warning, and every unbuilt
package ran its committed release ref. That is how stacked PRs got the
Grafana plugin 404 described in #4391.

Now `main` and `release-X.Y` bases read their own artifact. Any other
base reads the artifact of whichever of main and the release lines it
has the fewest commits on top of, and a notice says which one. A stack
on a release line keeps that line's images, for example #3895, which
sits on `release-1.5`. On a tie main wins, so a stack on a line cut from
main reads main's artifact until the stack contains a commit of the
line's own. Always taking main there would put main's images under the
line's charts, and that is the install failure from #3437. The new bats
case builds a small git fixture and checks the tag pulled for six bases,
one of them a deleted branch.

The other half of #4391, the stale `grafana-dashboards.tag`, is a
release-managed digest pin and is not touched here. It gets re-stamped
at release prep.

Part of #4391

### Screenshots

<!-- REQUIRED for UI changes: attach screenshots or screen recordings
demonstrating
the visual impact of your changes. PRs with UI changes without
screenshots will not be merged. -->

### Downstream repositories

<!--
Cozystack is upstream for repositories that are NOT kept in sync with it
automatically, and nothing in CI compares the two sides, so a change
here can break them silently.

Which change forces what, and which file to touch, is the trigger map in
docs/agents/contributing.md under "Downstream Repositories". Walk it
before you tick anything.

IF YOU ARE AN AI AGENT OR AN LLM, THIS IS PART OF THE TASK, NOT A FORM
TO FILL IN:

1. Walk the trigger map against the actual diff of this PR, file by
file. Do not decide from the PR title or from your memory of what you
changed.
2. For every repository you tick, open the follow-up PR there and link
it on the same line. A ticked box with no link claims work that does not
exist, which is worse than an empty box. Search that repository for an
open PR or issue covering it first, and link that instead of filing a
duplicate.
3. When the follow-up is out of scope, or needs a decision that is not
yours to make, open an issue in that repository instead, link that, and
say so in the PR body. Do not push a speculative PR into another
repository just to fill a line here.
4. Do not tick "No downstream repository is affected" to make the
checklist go away. If you are unsure, leave every box empty and say so
in the PR body, so a human decides.
-->

- [x] No downstream repository is affected by this change
- [ ] [cozystack/website](https://github.com/cozystack/website) -
follow-up:
- [ ]
[cozystack/terraform-provider-cozystack](https://github.com/cozystack/terraform-provider-cozystack)
- follow-up:
- [ ]
[cozystack/ansible-cozystack](https://github.com/cozystack/ansible-cozystack)
- follow-up:
- [ ] [cozystack/ccp](https://github.com/cozystack/ccp) - follow-up:
- [ ] [cozystack/talm](https://github.com/cozystack/talm) - follow-up:
- [ ] [cozystack/cozyhr](https://github.com/cozystack/cozyhr) -
follow-up:
- [ ] [cozystack/cozy-proxy](https://github.com/cozystack/cozy-proxy) -
follow-up:
- [ ]
[cozystack/cozystack-telemetry-server](https://github.com/cozystack/cozystack-telemetry-server)
- follow-up:
- [ ]
[cozystack/external-apps-example](https://github.com/cozystack/external-apps-example)
- follow-up:
- [ ] [cozystack/examples](https://github.com/cozystack/examples) -
follow-up:
- [ ] [cozystack/community](https://github.com/cozystack/community) -
follow-up:

### Release note

<!--  Write a release note:
- Explain what has changed internally and for users.
- Start with the same `type(scope):` prefix as in the PR title
- Follow the guidelines at
https://github.com/kubernetes/community/blob/master/contributors/guide/release-notes.md.
-->

```release-note
fix(ci): E2E for a PR stacked on a feature branch overlays the image refs of the line the stack grew from, instead of falling back to the committed release refs.
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/release Issues or PRs related to release tooling (changelog, backport, release pipeline) kind/cleanup Categorizes issue or PR as related to cleanup of code, process, or technical debt size/XS This PR changes 0-9 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants