mirror of
https://github.com/cozystack/cozystack.git
synced 2026-10-30 16:47:20 +09:00
release-1.6
294
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
f3223c33d3 |
ci(e2e): run nightly and release-tag e2e on the container lane
Nightly and the rc gate still booted the management nodes as QEMU
guests, the substrate the merge-gating lanes left behind. On it the
tenant Kubernetes suites have missed their node-join deadline on most
nights since 2026-10-02, each failure spends the whole deadline, and
the job reaches its timeout before the alphabetically later suites
run. Over the same days those suites passed on every PR run that
selected them, where the nodes are Talos containers and a tenant
worker sits one virtualization level shallower.
Both lanes now use the PR lanes' container substrate. Nightly's e2e no
longer waits for the nocloud disk, which it does not boot; the disk is
still built and published. A manual e2e-tag dispatch against a tag
that predates the container lane fails early and names the ref to
dispatch from instead.
DRBD, the replicated StorageClass and the Cozystack Talos node image
lose their last e2e coverage with this. The docs and comments that
pointed at nightly and the rc gate as that coverage now say so.
Assisted-by: LLM
Signed-off-by: Aleksei Sviridkin <f@lex.la>
(cherry picked from commit
|
||
|
|
f3104a9067 |
ci(e2e): give the nightly e2e runner headroom over its nodes
The nightly e2e job runs on a 24-vCPU runner while its three
management nodes take 8 vCPU each. On QEMU that is a 1:1 commit with
nothing left for the host. On the container lane the job would not
start at all: hack/e2e-container-up.sh refuses a host that does not
have strictly more CPUs than the nodes take, and nightly checks out
main's tree, which carries the same check.
The next commit moves nightly onto the container lane, so move the job
first to the 32-vCPU class the pull request and tag lanes on this
branch already use. main made the same move in
|
||
|
|
975c26b3a5 |
test(e2e): drop the DRBD sidecar from container-lane satellites
The linstor chart adds a drbd-logger sidecar to every satellite, and on the container lane's shared kernel there is no DRBD for it to tail, so it exits at once. The satellite pod is then never Ready, piraeus never registers the node, LINSTOR reports no nodes and no PVC binds. main drops the sidecar through a chart value; the chart on this branch has none, and the lane must not change what the chart ships. So post-install prep applies a LinstorSatelliteConfiguration of its own on the container lane, ahead of the linstor waits, with the same DaemonSet patch main renders: delete drbd-logger from the generated satellite DaemonSet. The two DRBD-only initContainers are already gone through the chart's Talos satellite configuration. Once all three nodes are Online, prep reads the live DaemonSets back and fails if a satellite still carries the sidecar or none was found at all, so a configuration that silently stopped applying cannot pass for one that worked. The QEMU lane applies nothing and keeps the chart as shipped. Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> Assisted-by: LLM |
||
|
|
3c8d7df331 |
fix(e2e): stop a failed teardown posting a failed test verdict
The teardown ends both merge-gating lanes under `if: always()`, and this
branch made it `exit "$cleanup_rc"`. So a `docker compose down` hiccup, a
busy zpool or a failed rmdir after a fully passing suite posts the
required "E2E Tests" status as a failure, and nothing downstream can tell
that apart from a test that actually broke. The runner is discarded when
the job ends, so there is no next run for the strictness to protect; a
reused host is a developer machine, and an annotation serves it. Loud,
not fatal.
Two comments described mechanisms that are gone. The breakpoint step
still explained reading a soft-red output that this branch removed, so a
maintainer would go looking for it; the node-join deadline now fails its
suite like any other, which is what makes `failure()` sufficient. The
build-talos header still described building the nocloud disk and e2e
needing it, when the job builds neither and the container lane downloads
no disk -- the profile is compiled by nightly and by the tag build's
`make assets` instead, and what a PR still compiles is installer.yaml,
which shares every input with it bar the platform and output format.
And the platform-package override now states its consequence for both
packages it replaces, not only for CDI: the merge-gating lane exercises
neither the shipped CDI default nor `drbd.enabled: true`.
Adapted for release-1.6: the gate here is the "E2E Tests" job itself
rather than a commit status, so the teardown comment says so; the
breakpoint step never had the soft-red condition on this branch and is
left alone; the build-talos header names only the tag build, since
nightly.yaml does not run from this branch; and the e2e-fork.yaml and
platform-packages hunks have nothing to apply to.
Assisted-By: LLM
(cherry picked from commit
|
||
|
|
d8b13f6b81 |
test(e2e): correct two comments that describe the opposite of the code
pull-requests.yaml said the QEMU substrate "is not wired into any
workflow now" and that prepare-cluster and hack/e2e-prepare-cluster.bats
stay in the tree for a nightly lane that will carry the coverage. Both
halves are wrong in the direction that costs the most: nightly.yaml and
e2e-tag.yaml already run `make prepare-env`, which calls prepare-cluster,
so anyone trusting that comment deletes live code and takes nightly and
every release-candidate run with it. docs/agents/e2e-testing.md states it
correctly, so the tree disagreed with itself.
hack/e2e-platform-packages.sh said the lane replaces "only LINSTOR with
an otherwise-identical Package". It replaces two, and the kubevirt-cdi
one is not identical: it raises the importer memory limit from CDI's
600M to 4Gi and the request from 60M to 256Mi. The consequence belongs
next to the override -- the merge-gating lane does not exercise the
shipped CDI default, and whether a tenant worker disk imports at 600M is
answered by the QEMU lanes, which take no override.
Also drops the dead half of `resolve_assets`. It resolved
nocloud-amd64.raw.xz and exported disk_id; the container lane boots no
disk and nothing in this workflow reads that output any more, so all the
lookup could still do was fail a release-labelled PR over an asset it
does not use. The job stays -- e2e's release arm gates on its result --
and now proves what is still worth proving, that the draft release
exists. e2e-tag.yaml keeps resolving the asset, because it consumes it.
Adapted for release-1.6: nightly.yaml never runs from this branch, so
the corrected comment names e2e-tag.yaml alone as the lane that still
boots QEMU. The platform-packages comment is dropped because the
generator here replaces no Package, and resolve_assets keeps this
branch's own trigger condition.
Assisted-By: LLM
(cherry picked from commit
|
||
|
|
f75153ac0a |
test(e2e): run the merge-gating lanes on Talos containers
The srv1-srv3 nodes of both lanes that gate a merge -- the same-repo one
in pull-requests.yaml and the fork one in e2e-fork.yaml -- run as Talos
containers instead of QEMU guests, so a tenant worker sits at L2 rather
than L3. Measured in CI on commits where both substrates ran the same
tree: whole-job wall clock down 24-49 minutes, essentially all of it in
Chainsaw, while the two tenant-Kubernetes suites go from 1143-2040s to
466-809s and stop depending on the runner's vgif flag.
The node-join soft-red gate goes with the substrate, which is what it
was for. It existed because nested virtualisation on the shared runners
degraded far enough that a worker registering in minutes elsewhere could
miss any deadline this test could afford; #3513 established that the
discriminator was the host's kvm_amd vgif flag, and removing a nesting
level removes what it acted on. So hack/e2e-node-join-soft-red.sh, the
SOFT-RED-node-join.txt marker and the soft_red job outputs are gone from
all four workflows, and a missed deadline is an ordinary red again. The
::warning annotation survives, because the reason a run went red should
be legible without opening the log, and so does the 124-versus-everything
-else test, because "the workers were slow" and "the wait never ran"
send a reader to different places.
Done by swapping the substrate inside the existing e2e jobs rather than
by promoting a separate container job. Those jobs carry more than a
substrate -- the GitHub App token, the report-overrun guard, the images
list, the SSH breakpoint -- and promoting a job that never had them
would have dropped four features without saying so.
Three properties of container mode drive the design, and each fails
quietly rather than loudly. machine.kernel.modules is a silent no-op --
kernel_module_spec.go returns early on ModeContainer with no error and
no event -- so the host loads openvswitch and zfs before the nodes start
and the workflow step asserts both rather than trusting them; a missing
module surfaces an hour later looking like a CNI or a storage
regression. Docker gives a privileged container its own tmpfs /dev
seeded once at creation, so /dev/kvm works but ZFS zvols created later
never appear and linstor-csi fails ControllerPublishVolume; the compose
file binds the host devtmpfs, which is also why talosctl cluster create
docker can never carry LINSTOR. And there is no ARP VIP, so
192.168.123.11 stands in for the QEMU lane's .10.
The QEMU machinery stays in the tree and keeps running: nightly.yaml and
e2e-tag.yaml still call `make prepare-env`, so DRBD, the replicated
StorageClass, its Immediate binding mode, the tenant StorageClass
propagation that only applies to remotely-accessible classes, and the
Cozystack Talos node image with its extensions all keep nightly and
release-candidate coverage. None of them is exercised per pull request
any more. That is the trade.
Nothing on the PR path builds or downloads a nocloud disk now, so
build-talos drops the talos-nocloud step and the talos-image artifact.
build-talos itself stays: finalize consumes its digest fragment.
One property this does not fix, recorded beside the runner class because
it will be read off a red run eventually: a container node reports the
HOST's CPU and memory as capacity, and hack/e2e-container-up.sh counters
that with kubelet systemReserved. That corrects the scheduler and not
what a pod reads from /proc, so a workload sizing itself from
/proc/cpuinfo or /proc/meminfo is configured for the whole runner.
Also carried here because the same files carry them: the ghcr.io
pull-through mirror removal (see #4007 -- whichever lands first makes the
other a no-op), the authoritative HelmRelease readiness gate, the
backup-credential preflight, the prepull guard against an image-less
container fragment, and folding the two standalone OIDC suites into the
latest-version tenant cluster.
Adapted for release-1.6: this branch has no e2e-fork.yaml, no soft-red
gate, no ghcr.io mirror and no backup e2e, so those hunks are dropped
together with the tests for helpers that exist only on main; e2e-tag.yaml
stays on QEMU. The platform Package generator carries the API endpoint
and nothing else, because the linstor drbd.enabled and kubevirt-cdi
importerResources overrides need chart values this branch does not ship;
the container lane gets e2e-side substitutes for them in later commits.
run-kubernetes.sh takes the storage-class helper, the local-lane pool
baseline, the scoped drain and the OIDC fold, while cleanup keeps its
best-effort `|| true`. The OIDC suite references in select-install.sh and
the chainsaw README go with the suites here rather than in a later commit.
(cherry picked from commit
|
||
|
|
5673249d47 |
ci(release): validate the tag-time changelog and port it from the tag
Since the promote PR is based on release-1.6, the changelog it carries merges onto the maintenance line and never reaches main. The backstop job in tags.yaml only asks whether the file is on origin/main, so it sees "absent", spends an AI run, and produces text nobody reviewed — which update-releasenotes.yaml then pushes into the published release body, overwriting the reviewed notes the release actually shipped with. Ask the second question too. `at_tag` is whether the changelog is present in this checkout, which is the tag commit and therefore the promote merge commit. When it is, port that reviewed file to main verbatim and skip generation entirely; the AI now runs only when the changelog is genuinely absent on both sides. Add hack/validate-changelog.sh, byte-identical to main, and gate the commit on it. A ported file came from the tag and a generated one came from a step that is deliberately allowed to fail, so neither had been checked: the old guard only asked whether the file was non-empty, which a truncated Copilot stream satisfies. The validator asserts the H1, the release-link comment, the trailing compare link and at least one section, all carrying the expected version, so a fragment or a changelog generated against the wrong tag cannot reach a release body. Verified against this branch's own shipped changelogs: v1.6.1, v1.5.2 and v1.5.1 all pass, and v1.6.1 checked against 1.6.2 fails as it should. This takes effect for tags cut after it lands — tags.yaml and the script it calls both come from the tag's own tree. Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |
||
|
|
7df35017b5 |
ci(release): run the full e2e suite on rc tag pushes from release-1.6
Workflows run from the ref they fire on, so a tag pushed on this branch
executes THIS tags.yaml, not main's. main grew an rc-e2e job on
2026-07-24; release-1.6 never did. The v1.6.1-rc.1 push therefore
produced only Prepare Release, a skipped changelog and skipped website
docs — no E2E Release Candidate job at all. Promote RC looks for its
evidence by exact job name, found none, and v1.6.1 was promoted with the
gate bypassed. That is the hole this closes.
Add e2e-tag.yaml byte-identical to main and call it from a new rc-e2e
job. The suite step works as written here because this branch already
carries hack/e2e-chainsaw/ and a test-chainsaw target, and the sandbox
image already installs chainsaw 0.2.15.
Two things make the wiring load-bearing rather than cosmetic:
* the called job's name must stay "E2E ${{ inputs.tag }} (full suite)".
Promote RC composes the string it searches for from the rc tag, and
GitHub renders a called job as "<caller> / <called>", which the
gate's suffix matcher accepts. Rename either side and the gate fails
closed.
* the caller must grant contents: read AND checks: write. A caller's
permissions are the ceiling for every job in the called workflow and
GitHub validates that ceiling statically, at run creation, before any
`if:` is evaluated. e2e-tag.yaml's e2e job declares checks: write for
a dispatch-only breakpoint workflow_call can never reach, so omitting
the grant would fail the entire tags.yaml run for EVERY tag push, rc
and stable alike, building nothing.
prepare-release already publishes the rc as a non-draft prerelease with
the nocloud-amd64.raw.xz asset and pushes the release-X.Y.Z-rc.N staging
branch before rc-e2e can start, which is exactly what e2e-tag.yaml's
resolve job requires.
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
|
||
|
|
e85482bacc |
ci(release): complete the candidate-aware promotion pipeline on release-1.6
Promote RC refuses to promote a release whose target base cannot verify
the packages candidate. It checks five markers on the base branch before
its first registry write and fails closed with "Target base
'release-1.6' lacks '<path>'". This branch satisfied one of the five
(hack/lib/image-refs.sh), so the next patch release off release-1.6
could not be dispatched at all.
Add the two halves the base has to carry:
* hack/verify-promoted-packages.sh and hack/lib/promoted-packages.sh,
byte-identical to main. The verifier pulls the candidate by its
committed digest and proves it matches the merge tree apart from the
artifact's impossible self-reference, that promotion rewrote every
version-line image tag, and that the container repo@digest set is
unchanged from the rc artifact.
* the two jobs that run it. verify-release-candidate in
pull-requests.yaml checks GitHub's prospective merge tree so a
packages/ edit pushed to the promote PR shows up as a failed check,
and a Verify stable packages candidate step in the finalize job of
pull-requests-release.yaml repeats it against the real merge commit.
Position is the whole point of the finalize half: it runs before the
write-once stable tag, the draft-release publish and the retag, so a bad
candidate is refused while nothing stable-named exists yet. The former
"Set up toolchain (skopeo, yq, helm)" and "Login to registry (GHCR)"
steps moved up with it and are no longer duplicated later in the job;
the toolchain now also installs flux and pins yq by version and sha256,
because an irreversible step must not depend on whatever
releases/latest/download resolves to today.
verify-release-candidate is keyed on the promote PR's author and its
head-branch shape rather than on the `release` label main matches. This
branch's on.pull_request.types has no `labeled` event, and the label is
applied about a second after the PR opens (a separate call in
gh pr create), so the `opened` payload carries no labels and a
label-keyed guard would never fire here.
.release-tooling is gitignored, matching main, so the trusted-base
checkout can never be staged.
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
|
||
|
|
6fe15b0bf5 |
ci(release): carry the finalize fixes onto the 1.6 line
release-1.6 was cut at v1.6.0 on 2026-07-22, and the finalize fixes landed on main afterwards, so shipping v1.6.1 from this line would repeat two defects v1.6.0 hit. Workflows run from the ref they fire on: a promote PR based on release-1.6 runs THIS file, not main's. Carries three upstream commits, after which this file is byte-identical to main, so future backports touching it will not conflict: |
||
|
|
e226b85f86 |
fix(ci): overlay images from the PR base branch, and publish per-line 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 <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
(cherry picked from commit
|
||
|
|
09f4cd10da |
fix(ci): only overlay current-main images on main-based PRs
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. #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 <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
(cherry picked from commit
|
||
|
|
baf0158d81 |
fix(release): reuse only an OPEN promote PR, not one in any state
The promote step guarded PR creation with `gh pr view <branch>`, which resolves a pull request by head branch in any state. A closed one satisfied the guard, so creation was skipped while the step still exited 0 — a promotion that reports success and leaves nothing to merge, the same silent-skip shape as the enumeration bug fixed in #3404. STABLE_BRANCH is derived from the stable version, so every rc promoted to that version shares it. Both re-dispatch paths this workflow documents as supported are therefore wedged once any promote PR for that version has been closed: re-dispatching the same rc, and promoting a newer rc to the same version. Neither recovers by reopening the old PR, because the step's own force-push makes its head unreachable and GitHub refuses with "state cannot be changed. The <branch> branch was force-pushed or recreated." Hit live while promoting v1.6.0-rc.4 after #3397 had been closed: the run went green and opened no PR. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |
||
|
|
d0c0f399f8 |
fix(ci): cover every image-ref storage shape in promote, retag and mirror (#3404)
## The bug Promotion rewrites the rc version substring to the stable version across the vendored image references. That rewrite lived inline in `promote-rc.yaml` and globbed the depth-2 package `values.yaml` plus `packages/apps/kubernetes/images/*.tag` **only**, on the premise that the kubernetes app was the only one whose `.tag` files carry the cozystack version. Nine other `.tag` files do, and `system/multus` seds its reference straight into a vendored upstream daemonset manifest. #3397 (promote `v1.6.0-rc.4` → `v1.6.0`) is therefore staged with **33 of 55 first-party references still reading `v1.6.0-rc.4`**. `hack/promote-retag.sh` and `hack/nightly-mirror.sh` had the same blind spot from the other direction — both scanned the depth-2 `values.yaml` alone, so those images were never retagged to the stable version (**30 references selected before, 42 after**) and never mirrored to the public registry for a nightly. A retag miss leaves an image short of a tag, since the digest still resolves inside one registry. A mirror miss is worse: the host rewrite walks the same file list, so a reference that is neither mirrored nor rewritten leaves the published tree pointing at the private build registry. `v1.6.0` is the first release to go through promotion — every earlier release was a full rebuild from the tag, which stamped every file uniformly. The glob has always been wrong; the old path just made it unobservable. ## Why it could not be caught before The rewrite was workflow-inline, so there was nothing to unit test. The only way to observe the miss was to cut a release. It is a script now — `hack/promote-rewrite-tags.sh` — and `hack/promote-rewrite-tags_test.bats` round-trips it against the real tree under `make unit-tests`, with no cluster, registry or release involved. ## The fix The three call sites each carried their own idea of where a reference can live, which is how they drifted apart. That knowledge moves into `hack/lib/image-refs.sh` as the single enumeration — `image_ref_files()` for the files, `collect_image_refs()` for the references inside them — and all three source it, so a new storage location is declared once and reaches every consumer. The rewrite also asserts its own postcondition: it scans **wider** than it rewrites and fails the promotion when a reference survives in a location the enumeration does not know about, rather than shipping it quietly. The wide scan is shape-filtered to image-reference position, because `X.Y.Z-rc.N` is an ordinary version string that other things legitimately contain — `kubevirt-csi-driver/go.sum` pins `github.com/golang/protobuf v1.4.0-rc.2`, and `v1.4.0-rc.2` is a cozystack rc that was actually cut. Narrowing it to `${RC}@sha256:` instead would have been wrong: `system/cilium/values.yaml` keeps `tag:` and `digest:` under separate keys. `docs/agents/image-refs.md` records the contract — three tag classes, three storage shapes, the invariants, and which consumer does what. ## Not a bug: kamaji `cluster-api-control-plane-provider-kamaji` stays at `v0.19.0-cozystack.0` across the promotion. It is a first-party rebuild of an upstream component, versioned by that component rather than by the cozystack version line, so no rewrite is owed. A test pins this so a future fix here cannot over-reach. ## Documented known gaps, deliberately not fixed here Both are pre-existing and in the **host** dimension; the version dimension this PR owns is complete, because the version always lives in a `tag` key regardless of where the host sits. - **The nightly host rewrite cannot reach a split host.** It is a literal `<src-registry>/` substring replace, so `keycloak-operator` (host in a sibling `registry:` key) and `kubeovn` (`global.registry.address`, no trailing slash) are mirrored but keep pointing at the build registry in the published tree. Fixing it needs structure-aware rewriting. - **A reference inside a gzip is invisible.** `capi-providers-cpprovider` ships `files/components.gz`, and the promote postcondition cannot catch this class at all since `grep -rIl` skips binaries. Nothing is owed today — kamaji is component-versioned and the image is still mirrored and retagged via its `.tag` — so the residual is that a nightly's kamaji ConfigMap keeps the build-registry host. ## Testing - 17 tests in the new suite; `make bats-unit-tests` green (312 tests, exit 0); `shellcheck -x` clean; `zizmor` clean on the workflow. - Verified against four real rc versions (`1.4.0-rc.2`, `1.4.0-rc.4`, `1.5.0-rc.2`, `1.6.0-rc.4`) in both directions — no false positives, no false negatives. - Every test is mutation-proven. Reverting the enumeration to the old glob, deleting any of the five YAML shapes, corrupting emitted digests, reverting extras to parse-only, dropping the unreadable-file guard, broadening shape 4 beyond `global.images`, or planting an unenumerated first-party reference in any storage shape each fail a named test. ## Reviewer note, out of scope `platform-migrations` is pinned at two different digests — `core/platform/values.yaml` at `v1.5.0` and `backupstrategy-controller/values.yaml` at `v1.4.0-rc.2` (an unowned pin; no Makefile writes that key, tracked in #3143). A `promote-retag.sh` dry-run emits **two copies to the same destination tag**, so the write-once guard will fail the promotion. This is independent of this PR and needs a decision about the backup-client image version, so it is left alone here — but it blocks the 1.6.0 promote and wants fixing before the re-cut. ## Release note ```release-note Promotion, retagging and nightly mirroring now cover every image reference the build stamps, including those stored in `images/*.tag` files and in vendored manifests. Previously a promoted release could ship images still tagged with the release-candidate version, and those images received no stable tag in the registry. ``` Fixes the promote half of #3143. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Image promotion and nightly mirroring now detect digest-pinned image references across values files, standalone tag files, and supported templates. - Stable release preparation supports RC→stable tag rewriting via a dedicated promotion script, with broader verification to ensure no RC references remain. - **Bug Fixes** - Improved completeness of ref discovery and scanning, including previously missed template/tag locations. - **Documentation** - Added and linked guidance documenting image-reference invariants and how tooling enumerates supported storage shapes. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
09323fff13 |
fix(ci): cover every image-ref storage shape in promote, retag and mirror
Promotion rewrote the rc version substring across the depth-2 package values.yaml plus packages/apps/kubernetes/images/*.tag alone, on the premise that the kubernetes app was the only one whose .tag files carry the cozystack version. Nine other .tag files do, and system/multus seds its ref straight into a vendored upstream manifest. v1.6.0 was therefore staged with 33 of 55 first-party refs still reading v1.6.0-rc.4. promote-retag.sh and nightly-mirror.sh had the same blind spot from the other direction: both scanned only the depth-2 values.yaml, so those images were never retagged to the stable version (30 refs selected before, 42 after) and never mirrored to the public registry for a nightly. A retag miss merely leaves an image short of a tag, since the digest still resolves inside one registry; a mirror miss is worse, because the host rewrite walks the same file list and a ref that is neither mirrored nor rewritten leaves the published tree pointing at the private build registry. The three call sites each carried their own idea of where a ref can live, which is how they drifted apart. Move that knowledge into hack/lib/image-refs.sh as the single enumeration -- image_ref_files() for the files, collect_image_refs() for the refs inside them -- and have all three source it, so a new storage location is declared once and reaches every consumer. The rewrite itself moves out of promote-rc.yaml into hack/promote-rewrite-tags.sh. Being workflow-inline is why this was only observable by cutting a release: there was nothing to unit test. It now also asserts its own postcondition, scanning wider than it rewrites and failing the promotion when any ref survives in a location the enumeration does not know about, rather than shipping it quietly. Component-versioned images (kamaji's v0.19.0-cozystack.N) and third-party pass-through images do not ride the cozystack version line and are deliberately left alone; a test pins that so a future fix here cannot over-reach. capi-providers-cpprovider is a documented known gap: its ref is also embedded in a gzipped copy that the chart actually ships, and rewriting only the readable copy would diverge the two. docs/agents/image-refs.md records the contract -- three tag classes, three storage shapes, the invariants, and which consumer does what. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |
||
|
|
792951b9c6 |
fix(ci): make workflow cancellation effective
Assisted-By: GPT-5 <noreply@openai.com> Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |
||
|
|
8020656725 |
fix(ci): resolve nightly packages digest without image-specific skopeo ops (#3332)
## What this PR does
The Nightly workflow has **never completed a run**. Every scheduled run
since `NIGHTLY_ENABLED` was set has failed in `mirror` at "Resolve
source commit and version", ~80s in, with:
```
level=fatal msg="unsupported image-specific operation on artifact with type \"application/vnd.cncf.flux.config.v1+json\""
```
Nothing has ever been published — `cozy-installer:nightly` and
`cozystack-packages:nightly` both 404 on GHCR, and `cozystack-nocloud`
does not exist.
This fixes that failure and the next two behind it.
### 1. The digest lookup could never have worked
`cozystack-packages` is a **Flux OCI artifact** (config mediaType
`application/vnd.cncf.flux.config.v1+json`), not a container image. A
plain `skopeo inspect` parses the config blob as an image config and
fatals on it, so `skopeo inspect --format '{{.Digest}}'` was never
viable against this ref — the `--raw` call on the very next line already
acknowledged this.
Replaced with `skopeo inspect --raw` + `skopeo manifest-digest`, which
hashes the manifest bytes — exactly what the registry addresses the
artifact by, with no image-specific parsing.
This also collapses two registry fetches into one: the digest and the
`org.opencontainers.image.revision` annotation now come from the **same
bytes**, which strengthens the race the original comment describes —
there is no longer any window between pinning the tag and reading it.
### 2. `flux pull artifact` rejects a non-existent output path (×2)
`flux pull artifact --output _baked` fails with `invalid output path
"_baked": no such file or directory`. `_baked` is neither tracked nor
created by anything. `pull-requests.yaml` does `rm -rf` + `mkdir -p`
first; `nightly.yaml` omitted it. Since the workflow always died at step
1, this was never reached.
Same bug for `_rw` in the e2e staging step. Both now create the
directory first.
## Verification
- Reproduced the original fatal against the live artifact with skopeo
1.22.2.
- Ran the fixed resolve step end-to-end against the real registry:
returns the digest the registry reports, and a `sha` that matches the
current `main` HEAD.
- Reproduced the `_baked` failure and confirmed the fix extracts the
tree.
- `actionlint` clean; `zizmor --offline` clean; `shellcheck` clean on
changed lines.
Also checked and **not** changed, each disproven rather than assumed:
- `skopeo copy --multi-arch all` on a Flux artifact — works; `copy` is
mediaType-agnostic.
- The `awk -F@ '/artifact successfully pushed/'` digest capture —
matches current flux CLI output, and is the same idiom
`packages/core/installer/Makefile` already uses in production.
- `nightly-mirror.sh`'s `inspect --format Digest` — all mirrored refs
there are genuine image manifests, so it is correct as written.
- `make -C packages/core/talos talos-nocloud` — exists and is CI-proven
via `tags.yaml`.
## Honest scope
This fixes the `mirror` job's blockers. It does **not** promise a green
nightly on the first run: every step after `mirror` is still unproven,
having never executed. The most likely next wall is `build-disk` —
`cozystack-nocloud` is a brand-new package, so the first `oras push`
creates it, and the follow-up `oras tag` can hit `403 permission_denied`
until the package is linked (the risk the workflow already documents
in-line). The first-ever full e2e run may well surface its own issues.
Reviewing that follow-on separately seems better than growing this PR.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Improved nightly artifact mirroring and staging reliability.
* Prevented artifact pull failures by ensuring required output
directories are created before use.
* Enhanced retrieval of artifact version and source revision metadata.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
|
||
|
|
0f4a2715ba |
ci(labeler): map cert-manager, external-secrets and metrics-server scopes
PRs scoped to these three components fell through to area/uncategorized because the scope map had no entry for them. metrics-server serves the monitoring pipeline; cert-manager and external-secrets are platform services. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Aleksei Sviridkin <f@lex.la> |
||
|
|
90254efa6d |
fix(ci): resolve nightly packages digest without image-specific skopeo ops
The Nightly workflow has never completed a run. Every scheduled run since
NIGHTLY_ENABLED was set has failed in `mirror` at "Resolve source commit
and version", ~80s in:
level=fatal msg="unsupported image-specific operation on artifact with
type \"application/vnd.cncf.flux.config.v1+json\""
cozystack-packages is a Flux OCI artifact, not a container image: its
config mediaType is application/vnd.cncf.flux.config.v1+json. Plain
`skopeo inspect` parses the config blob as an image config and fatals on
any non-image artifact, so resolving the digest with
`inspect --format '{{.Digest}}'` could never work.
Fetch the :main manifest once with `inspect --raw` and derive both the
digest (`skopeo manifest-digest`, which just hashes the manifest bytes —
no image-specific parsing) and the revision annotation from those exact
bytes. This also tightens the race the original comment describes: the
digest and the annotation now come from a single fetch, so there is no
window at all between pinning the tag and reading its revision, and one
registry round-trip is saved.
Because the workflow never ran past this step, the rest was unproven.
Also fix both `flux pull artifact` calls, which passed an --output
directory that does not exist and would have failed with "invalid output
path": create it first, matching the established idiom in
pull-requests.yaml ("Pull current-main packages tree").
Verified against the live OCIR artifact: the fixed step resolves
packages_ref to the same digest the registry reports and yields main's
current HEAD sha; the fixed pull extracts the tree. skopeo copy on Flux
artifacts, the flux push digest awk parse, the cozy-installer chart name
and nightly-mirror.sh's per-image digest verification were each checked
and are correct as-is.
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
|
||
|
|
90342063c0 |
chore(github): add area/keycloak, area/tenant, area/kamaji labels
Three scopes recur across PR history with no fitting area/* label (keycloak, tenant, kamaji). Add the labels and map their scopes in the auto-labeler, and fill two gaps in existing mappings (mongodb -> area/database, csi -> area/storage) so these stop landing in area/uncategorized. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Aleksei Sviridkin <f@lex.la> |
||
|
|
a269eb3965 |
chore(deps): update actions/checkout action to v6.0.3 (#3133)
This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [actions/checkout](https://redirect.github.com/actions/checkout) | action | patch | `v6.0.2` → `v6.0.3` | --- ### Release Notes <details> <summary>actions/checkout (actions/checkout)</summary> ### [`v6.0.3`](https://redirect.github.com/actions/checkout/blob/HEAD/CHANGELOG.md#v603) [Compare Source](https://redirect.github.com/actions/checkout/compare/v6.0.2...v6.0.3) - Fix checkout init for SHA-256 repositories by [@​yaananth](https://redirect.github.com/yaananth) in [#​2439](https://redirect.github.com/actions/checkout/pull/2439) - fix: expand merge commit SHA regex and add SHA-256 test cases by [@​yaananth](https://redirect.github.com/yaananth) in [#​2414](https://redirect.github.com/actions/checkout/pull/2414) </details> --- ### Configuration 📅 **Schedule**: (UTC) - Branch creation - At any time (no schedule defined) - Automerge - At any time (no schedule defined) 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/cozystack/cozystack). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yNDIuMiIsInVwZGF0ZWRJblZlciI6IjQzLjI0Mi4yIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJhdXRvbWF0ZWQiLCJkby1ub3QtbWVyZ2UvaG9sZCJdfQ==--> |
||
|
|
44507d2cd0 |
docs(agents): add downstream repository checklist to the PR template
Cozystack is upstream for repositories that nothing keeps in sync with it, and no CI job compares the two sides, so a change here breaks them in silence. Only two narrow paths are automated, and both fire on a release rather than on a PR. It has already happened. The website docs generator works from an app list hardcoded in its own Makefile, and packages/apps/opensearch was never added to it, so an application we ship has no reference page at all. The Terraform provider still offers a Kubernetes version this repo removed. The plugin repo still sends operators to hack/ scripts that were deleted, one of them rewritten as a Go command. Add a Downstream Repositories trigger map to the contributing guide, naming per repository which change here forces a change over there and which file to touch, and a checklist in the PR template that links to it. The template addresses AI agents directly: they author a large share of PRs and will otherwise tick a box without opening the follow-up it claims. The coupling is wider than the obvious ones, and mostly invisible. talm restates the node contract, overlapping ours but already diverged, so it must be diffed rather than overwritten. cozyhr reads an annotation the operator writes and silently merges the wrong values if it is renamed. cozy-proxy is vendored in from its own repo, yet consumes labels produced here and quietly stops handling external VM services if they move. The telemetry server queries metric names emitted here, and a rename just empties its dashboards. None of these fail loudly. Guard the map with a unit test, because a map about silent drift must not drift silently itself. It pins every in-repo path the map cites to a file that exists, pins the map and the checklist to the same repository list, and pins the workflow carve-out below to the map's location, to the job that reads the flag, and to the output that carries it: cutting any one of those three leaves the other two looking wired up. The header states plainly what the suite cannot check, so a green tick is not mistaken for a correct map: whether a coupling is real, and whether it still holds over there, stays a human's job. Editing the map alone is a docs-only PR, which skips the unit tests and would exempt the map from its own guard. Rather than widen the docs-only rule, which also gates the Talos image build, the plan step exports a separate flag that only the unit-test job reads: a docs PR now runs the tests and still builds nothing. It detects the map by its old path too, since a rename is reported as the new path alone. Document that gh pr create --body and --body-file replace the body wholesale, which would otherwise drop the checklist from every CLI-authored PR. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Aleksei Sviridkin <f@lex.la> |
||
|
|
8a5bc68b43 | chore(deps): update actions/checkout action to v6.0.3 | ||
|
|
99a28d82c2 |
test(e2e): migrate app suite from BATS to Chainsaw and wire into CI (#2826)
## What this PR does Migrates the E2E **app** test suite from BATS to [Kyverno Chainsaw](https://github.com/kyverno/chainsaw) — a declarative, Kubernetes-native E2E framework (CNCF, part of the Kyverno project; successor to KUTTL) — and wires it into CI as the replacement for the per-app BATS loop. This grows the original pilot (postgres + bucket) into the full suite. All 22 app suites are ported under `hack/e2e-chainsaw/` and the `hack/e2e-apps/*.bats` files are removed: `postgres`, `bucket`, `mariadb`, `mongodb`, `redis`, `qdrant`, `clickhouse`, `kafka`, `etcd`, `openbao`, `harbor`, `foundationdb`, `external-dns`, `kuberture`, `vminstance`, `gateway`, `kubernetes-latest`, `kubernetes-previous`, `kubernetes-oidc-system`, `kubernetes-oidc-customconfig`, `securitygroup`, `serviceexposure`. **Approach** - **Declarative** suites assert on `status.conditions` and concrete fields. The `timeout N sh -ec "until kubectl get ..."` + `kubectl wait` pair that appeared ~100 times collapses into a single `assert` that polls existence and state together, with a structured diff on failure and automatic `events`/`describe`/`podLogs` capture via `catch` (previously only `harbor.bats` did this, by hand). - **Imperative** suites keep their logic in `script` steps: `openbao` init/unseal, `kuberture` external-dns split-horizon probes, `vminstance`, the `gateway` admission/impersonation cases, and `kubernetes-latest`/`previous`, which wrap the relocated `hack/e2e-chainsaw/_lib/run-kubernetes.sh` verbatim (Kamaji bring-up, LB/NFS/ouroboros checks). - `gateway` tests derive the tenant apex from the namespace `namespace.cozystack.io/host` label at runtime, so they are host-independent. **CI** - The `e2e` job now runs `chainsaw test hack/e2e-chainsaw/` via a new `test-chainsaw` target and uploads the JUnit `chainsaw-report.xml`. - The `chainsaw` binary is added to the e2e-sandbox image. - `install-cozystack` and `test-openapi` stay BATS (cluster bootstrap + OpenAPI checks). - The per-app 3-retry loop is dropped — assertion polling replaces the fixed-timeout flakiness it papered over. **Validation** Ran against a development cluster: the DB/app, storage, and VM suites pass; the three suites that depend on platform features not present on that cluster (`gateway`, `kuberture`, `external-dns`) and the two heavyweight `kubernetes-*` suites are exercised by this PR's CI run on a freshly installed platform. Note for reviewers: service-port asserts use the `(ports[*].port)` projection form rather than a number-literal filter (`` ports[?port == `N`] ``), because Chainsaw v0.2.15 mis-evaluates JMESPath number-literal comparisons. **2026-07-09: reconciled with main (~520 commits of drift)** - etcd suite re-ported to the v1alpha2 operator contracts (#2859): `readyMembers=3` gate, pod-label/`/scale`/WorkloadMonitor/metrics/defrag contracts, plus the `ETCD_E2E_S3_ROUNDTRIP`-gated backup round-trip driving `examples/backups/etcd` - 12 new `ingress-hostname-policy` gateway cases ported (apex derived at runtime from the tenant-root namespace label) - post-branch bats drift folded in: `mariadb-single` webhook guard, kafka `c1.small` presets, bucket/harbor 2m BucketClaim fail-fast, bucket readonly-denial promoted to a hard fail (cosi-driver v0.3.1), qdrant PVC-reclaim guard (#3059), kuberture fail-loud negations, SC-fallback-default test (#2872 B1) - 4 suites that only exist on main since the branch was cut are ported: `kubernetes-oidc-system`, `kubernetes-oidc-customconfig`, `securitygroup`, `serviceexposure` - CI seams: `nightly.yaml` converted to the chainsaw invocation, `run-kubernetes.sh` reconciled (Talos/CABPT waits, tenant drain, LINSTOR pool wait, talos-image-cache under `_lib/`), `e2e-capture-dataplane.sh` replicated in the Chainsaw global catch ### Release note ```release-note fix(kubernetes): tenant Kubernetes teardown no longer hangs when the cluster has no working nodes — the pre-delete hook now bounds its wait for the in-tenant HelmReleases and force-clears their Flux finalizers on timeout (#3271) ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * End-to-end testing is now driven by a unified Chainsaw suite (platform apps, Kubernetes, networking, security groups, service exposure). * Test Impact Analysis selects affected suites, with an option to run the full suite. * CI produces a JUnit report artifact and collects suite-scoped diagnostics on failure. * **Bug Fixes** * CI E2E now fails fast and emits clearer Kubernetes/HelmRelease and sandbox bucket state diagnostics. * Improved teardown reliability with bounded wait + finalizer handling to avoid hangs. * **Documentation** * Updated E2E testing docs and local guidance to reflect the new Chainsaw-based workflow and suite paths. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
302a72f14a |
Merge origin/main into feat/e2e-chainsaw-example
Reconciles the talos-image-cache conflicts created when #3254 merged to main: keep the Chainsaw _lib/ port (hack/e2e-chainsaw/_lib/talos-image-cache.sh, a superset of #3254's logic + the node-join diagnose helper), drop the BATS-era hack/e2e-apps/talos-image-cache.sh, and keep the _lib/-path versions of the mirror manifest, install bats, and unit test. Also picks up main's zizmor action-pinning (#3223, unblocks pre-commit/zizmor) and Talos v1.13.6 (#3240). Assisted-By: Claude <noreply@anthropic.com> Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |
||
|
|
5b9b420ffe | chore(deps): update container image & action digests | ||
|
|
814a47f23e |
ci: move remaining workflows off the persistent self-hosted runner
Finishes the runner decommission tracked in #2937. PR builds, the main cache warmer, nightly, promote-rc and pull-requests-release already run on the ephemeral pool; this moves the stragglers so no workflow depends on the persistent runner anymore: - tags.yaml prepare-release: large ephemeral shape (same as the cache warmer), flux CLI bootstrap, and read-only OCIR access so release builds warm-start from the shared mode=max build cache while still pushing images to GHCR. Without the cache wiring every tag build on an ephemeral runner would be 100% cold. - tags.yaml generate-changelog / update-website-docs, backport.yaml, update-releasenotes.yaml: GitHub-hosted runners — these jobs only need git and the GitHub API, no docker or repo toolchain. - pull-requests.yaml: the debug label no longer reroutes jobs to the self-hosted runner (that path would queue forever once the runner is gone). The label keeps gating the SSH breakpoint on e2e failure, which works from ephemeral runners too. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Andrei Kvapil <andrei.kvapil@aenix.io> |
||
|
|
4a2e43969b |
fix(ci): polish cut-prerelease per re-review nits (#3253)
Cosmetic follow-ups from the re-review (all fail-closed, no behavior change): - Write-access gate: drop the inert 'maintain' from the allowed list. getCollaboratorPermissionLevel's `permission` field returns only the legacy roles admin/write/read/none (maintain collapses to write), so admin+write is the full "can push" set; 'maintain' never matched. - Stale-tip guard: resolve the remote tip without a pipe masking ls-remote's exit code, mirroring the write-once query — a transport failure now reports a distinct error instead of "branch not found". - tags.yaml: the hand-pushed-stable rejection message now says stable tags are finalized by the promote flow at the merge commit (pull-requests-release.yaml), not "come from promote-rc.yaml" — promote-rc opens the PR; finalize cuts the tag. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |
||
|
|
31cda39dcd |
fix(ci): address review findings on cut-prerelease (#3253)
Non-blocking review notes from #3253: - Require write access to dispatch. workflow_dispatch already needs write access to trigger, but add an explicit getCollaboratorPermissionLevel gate (admin/maintain/write) as auditable defense-in-depth that fails closed. - Constrain a `main` dispatch to a new minor's pre-release (patch Z == 0). A patch-line pre-release built from main content would produce a wrong-tree release-X.Y.Z-rc.N staging branch; it must be cut from its release-X.Y line. - Guard against a stale checkout tip. workflow_dispatch pins the dispatch-time SHA, not the push-time branch tip (the gap includes the concurrency-queue wait); if the branch advanced, the push would tag a stale commit and yield an empty base_ref, failing tags.yaml after burning the write-once name. Compare local HEAD to the remote branch tip and fail with a re-run hint instead. - Distinguish ls-remote "tag absent" (exit 2) from a transport/auth failure (any other non-zero), so an infra blip cannot read as "free to create". - Shallow checkout (fetch-depth: 1, no fetch-tags) instead of a full-history clone to push one ref; drop the redundant local tag pre-check. - Run pure-input validation (tag + dispatch branch) before minting the token or checking out, so a bad dispatch fails in seconds. actionlint + zizmor clean; run blocks bash -n checked. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |
||
|
|
bece285678 |
ci: pin action refs flagged by the zizmor gate (#3223)
The zizmor "Audit workflows" gate (added in #3223) is red on main: five actions/* references in the release workflows from #3017 are pinned to a floating tag instead of a commit SHA (blanket unpinned-uses policy, High severity): - nightly.yaml (x3) actions/checkout@v4 - promote-rc.yaml actions/checkout@v4 - retention.yaml actions/create-github-app-token@v1 Pin each to the SHA the rest of the repo already standardizes on: - actions/checkout -> 34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - actions/create-github-app-token -> d72941d797fd3113feb6b93fd0dec494b13a2547 # v1 `zizmor --offline .github/workflows/` now reports no findings. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |
||
|
|
47eec0157f |
feat(release): add Cut Pre-release Tag workflow (#2677)
Pre-release tags (vX.Y.Z-alpha.N / -beta.N / -rc.N) were cut by a manual `git push origin HEAD:refs/tags/<tag>`. That leaves the immutable-tag work in #2677 only partly enforceable: a repo ruleset cannot restrict `v*` tag *creation* to CI while still letting maintainers hand-push pre-releases (fnmatch has no way to match "v* except v*-rc.*"), so "do not hand-cut stable tags" stays a matter of discipline backed only by an in-workflow guard. Add cut-prerelease.yaml — a workflow_dispatch that becomes the sole entry point for creating a pre-release tag: - Generates the CozyStack CI app token (same app as promote-rc.yaml) and pushes the tag as the app, so tags.yaml fires (a tag pushed with the default GITHUB_TOKEN would not trigger it) and the push-event base_ref is populated (tags.yaml's Get-base-branch step needs it). - Validates ^v\d+\.\d+\.\d+-(alpha|beta|rc)\.\d+$ and rejects stable vX.Y.Z — stable tags are cut write-once by the promote flow at a PR merge commit. - Enforces dispatch from main or the matching release-X.Y branch, and refuses to move an existing tag (write-once). - Pushes HEAD:refs/tags/<tag> from the branch tip — the exact form docs/release.md prescribes — on ubuntu-latest (no build runner needed). With every pre-release cut by CI, repo admins can then enable a "Restrict creations" tag ruleset on `v*` with the CI app as the sole bypass actor, turning tag-creation discipline into hard enforcement. Also update tags.yaml's two human-facing error messages and docs/release.md (the RC and patch flows, the workflow list, and a new repo-side tag-ruleset enforcement note) to point at the workflow instead of a manual push. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |
||
|
|
1f84d2d49e |
Merge origin/main into feat/e2e-chainsaw-example (post-#3244)
Fold the in-sandbox Talos image cache work into the Chainsaw layout: the new shared helper relocates to hack/e2e-chainsaw/_lib/ talos-image-cache.sh and run-kubernetes.sh sources it from there (the ouroboros DNS-check no-retry rework merges clean). The OIDC bats are resolved as deleted again; their Chainsaw fixtures deliberately skip the talos.imageFactoryURL injection — the helper only emits it when the cache is up, and the zero-worker render-side tests instantiate no DataVolume imports for it to redirect. Assisted-By: Claude <noreply@anthropic.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |
||
|
|
08ac063d6d |
ci(e2e): move install gate to 32vCPU runner to end CPU oversubscription
The three sandbox QEMU guests each run with `-smp 8`, demanding 24 vCPU in total. On the 24-vCPU runner that is a 1:1 CPU commit with no host headroom for QEMU's emulation/IO threads, vhost-net, the host NAT every guest image pull traverses, containerd and the kernel. Under the peak concurrency of "Install Cozystack" the guests lose cycles to CPU steal, so in-guest readiness/liveness probes miss their deadlines and a HelmRelease intermittently flips InProgress -> Failed with dependents cascading to DependencyNotReady — on a different component each run, the signature of resource thrash rather than a product bug. Move the job to the 32-vCPU / 128 GiB runner class. Guest sizing is unchanged (24 vCPU / 72 GiB), so the install does not slow down, while the host keeps 8 vCPU and 56 GiB of headroom that absorbs the emulation and network overhead the probes were starving against. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Aleksei Sviridkin <f@lex.la> |
||
|
|
efbf372d25 |
test(e2e): fix post-merge seams the conflict markers missed
- nightly.yaml still looped over hack/e2e-apps/*.bats with the removed test-apps-% target; switch it to the single chainsaw invocation (empty CHAINSAW_SUITES = full suite) and upload the JUnit report - pin the chainsaw-report upload-artifact step to the same SHA the rest of the workflows use (scorecard/zizmor gate) - repoint hack/run-kubernetes-drain_test.bats at the relocated hack/e2e-chainsaw/_lib/run-kubernetes.sh - replicate cozytest.sh's NotReady-pod data-plane capture (hack/e2e-capture-dataplane.sh) in the Chainsaw global error.catch so chainsaw-suite failures keep diagnostic parity with the BATS lanes - refresh stale bats path comments Assisted-By: Claude <noreply@anthropic.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |
||
|
|
9297e51170 |
Merge origin/main into feat/e2e-chainsaw-example
Reconcile the Chainsaw e2e migration with ~500 commits of main drift: - keep main's run-kubernetes.sh helpers (kubectl_wait_retry, tenant drain, LINSTOR pool-free wait, SC-fallback probe) under the branch's _lib/ rename with the Chainsaw finally/snapshot deltas re-applied - resolve modify/delete on 10 drifted hack/e2e-apps/*.bats as deleted; their post-branch assertion drift is re-ported into the Chainsaw suites in follow-up commits - drop the 4 new main-side bats (kubernetes-oidc-*, securitygroup, serviceexposure) from _lib/ relocation; ported as Chainsaw suites in follow-up commits - docs/agents/e2e-testing.md: merge main's plan-job TIA wording with the Chainsaw suite phrasing Assisted-By: Claude <noreply@anthropic.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |
||
|
|
f0e0cec64c |
ci: harden GitHub Actions workflows for OpenSSF Scorecard (pin, permissions, zizmor gate) (#3223)
## What this PR does Restores the two OpenSSF Scorecard checks that dropped the aggregate score below our target, and adds a durable gate so they cannot silently regress again. **Root cause.** 15 of 17 workflows already declare a read-only top-level token and pin their actions by SHA. Two did not — `build-main.yaml` and `ui-test.yaml` (the latter added by the in-tree console move) — and that was enough to zero out two checks: - **Token-Permissions** is scored on the weakest workflow, so a single workflow with an undeclared (default-write) top-level token drops the whole check to `0`. - **Pinned-Dependencies** counted the six tag-pinned action refs as unpinned. **Fixes** - `build-main.yaml`, `ui-test.yaml`: add a top-level read-only `permissions` block (job-level `packages: write` kept where the build cache needs it) and pin every action to the SHA already used elsewhere in the tree. - `.github/zizmor.yml` + `.github/workflows/zizmor.yml` + `.pre-commit-config.yaml`: add [zizmor](https://docs.zizmor.sh/) as a gate. It runs on the PR diff (pre-commit) and tree-wide on every workflow-touching PR, failing on any reintroduced unpinned action or missing/overly-broad permissions block. Scope is intentionally limited to the two Scorecard-relevant audits (`unpinned-uses`, `excessive-permissions`), which are clean tree-wide as of this PR; the remaining audits carry pre-existing debt and are disabled with a documented TODO to burn them down and re-enable one at a time. - `scorecard.yml`: wire an optional `SCORECARD_TOKEN` secret. The default `GITHUB_TOKEN` cannot read branch-protection settings, so the Branch-Protection check is currently scored on partial data. The token is a no-op until the secret is added by a maintainer. - `internal/fluxshardoperator/config_fuzz_test.go`: native Go fuzz targets for the shard config parsers (round-trip and range invariants, not just no-panic), giving the Scorecard Fuzzing check a real signal on a hot-path parser. **Verification** - `zizmor --offline .github/workflows/` → *No findings to report* (gate green). - `go test ./internal/fluxshardoperator/ -run '^Fuzz'` passes; a 15s live fuzz of `FuzzParsePinnedTenants` ran 10.5k execs with no crashers. Follow-ups (tracked separately, not in this PR): merge Renovate's digest-pinning PRs to lift Pinned-Dependencies further, tighten branch protection on `main` and release branches, upgrade the OpenSSF Best Practices badge, and sign releases. ### Release note ```release-note ci: pin GitHub Actions by SHA and scope workflow token permissions to restore the OpenSSF Scorecard Token-Permissions and Pinned-Dependencies checks, and add a zizmor gate (pre-commit + PR workflow) that blocks future regressions ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added automated security scanning for workflow changes via a dedicated CI workflow, along with a matching pre-commit hook. * **Bug Fixes** * Hardened CI workflows with workflow-level read-only token permissions and pinned GitHub Action versions for more reliable execution. * Improved OpenSSF Scorecard configuration to better handle branch-protection checks when a token secret is available. * **Tests** * Added Go fuzz tests to ensure shard parsing/formatting remains non-panicking and preserves round-trip/contract behavior. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
cece5c23b5 |
feat(release): immutable tags & rc→stable promotion (#2677) (#3017)
## What this PR does Implements the immutable-tag + rc-promotion flow from #2677. A **stable release becomes a renamed release-candidate**: the bytes shipped as `vX.Y.Z` are bit-for-bit the bytes built and e2e-tested as `vX.Y.Z-rc.N`. No tag is ever force-moved, and stable is never rebuilt — it is *promoted* by retagging the rc's existing images. Layered onto the build matrix from #2937/#2983 (this PR is **stacked on `refactor/build-matrix-2937`** and must merge after it). ## The five force-retag sites, removed | Site | Before | After | |---|---|---| | `tags.yaml` api/apps/v1alpha1 tag | `git tag -f` / `push -f` | write-once (create-if-absent, fail if it would move) | | `tags.yaml` release-X.Y.Z branch | `git branch -f` / `push -f` | compare-before-force (no-op if unchanged; staging branch only) | | `pull-requests-release.yaml` stable tag | `git tag -f` / `push -f` | write-once at the PR merge commit (force impossible by construction) | | `pull-requests-release.yaml` maintenance branch | `updateRef force:true` | fast-forward-only | | `auto-release.yaml` patch tags | delete-recreate (cron) | **workflow deleted** — stable only via explicit promote | ## Version decoupling (the enabler) The operator baked its version into the image at build time, so an rc image self-reported the rc string — blocking retag-promotion. It now reads `COZYSTACK_VERSION` from the environment (threaded via `cozystackOperator.platformVersion` → Deployment env, stamped by `make manifests`), falling back to the build-time value when unset. The same image bits can report any release name. The only runtime reader is the telemetry metric `cozy_cluster_info{cozystack_version=...}`. ## Promotion flow `promote-rc.yaml` (`workflow_dispatch`, `rc_tag=vX.Y.Z-rc.N`): 1. Validate the rc release exists and the stable tag does not. 2. `hack/promote-retag.sh` reads the rc's digest-pinned image refs from `packages/*/*/values.yaml` and `skopeo copy`s each — **by digest** — to `:vX.Y.Z` and `:latest`, verifying with `skopeo inspect`. 3. Rewrite the cosmetic `-rc.N` substring in vendored tags to the stable version (the `@sha256` wins regardless), restamp the version-stamped assets (operator manifests, cozypkg, openapi; the heavy Talos assets are copied verbatim from the rc draft), open a `release-X.Y.Z` PR. 4. Merging the PR reuses the existing release-PR e2e and `pull-requests-release.yaml` finalize to cut the **write-once** stable tag at the merge commit and publish the release. Squash is disallowed (the tag needs a real merge commit). `nightly.yaml` cuts write-once `*-nightly.<date>` tags (gated by `NIGHTLY_ENABLED`); `retention.yaml` keeps the newest 14 per line (dry-run by default). ## Validation **Local end-to-end — the core claim is proven, not asserted.** The whole design rests on "copy-by-digest to a new tag preserves the digest, so stable == the e2e-tested rc, bit-for-bit." This was exercised against a real registry (`ttl.sh`) with real `skopeo`: - Pushed a multi-arch image under a `:v1.4.0-rc.2` tag, built a temp tree exercising all three `values.yaml` digest shapes (single `image:` string, split `repository`/`tag`/`digest` map, and the `platformSourceRef` OCI artifact), then ran the actual `hack/promote-retag.sh v1.4.0`. - **Result: `:v1.4.0-rc.2`, `:v1.4.0`, and `:latest` all resolve to the identical digest** (`sha256:fd8d9aa6…`), across all 17 platform manifests. The script's own `skopeo inspect` post-check passed, and an independent re-inspection confirmed it. Local validation caught (and this PR fixes) two bugs that only surface against a real registry — never in static lint: 1. `skopeo copy --multi-arch all --all` — mutually exclusive flags, fatal error; every retag would have failed on CI. Now `--multi-arch all`. 2. The same digest was retagged twice when an image appeared in two value shapes; the ref set is now deduped on the canonical `repo@digest`. **Other local checks:** `go test ./pkg/version` + `go build`/`vet`; `helm template` renders `COZYSTACK_VERSION` in all 3 operator variants (omitted when unset); `make manifests` stamps the version into the install assets; `shellcheck` clean on `promote-retag.sh`; `actionlint` clean and `act -l` resolves the job graph on all workflows; the rc-tag parser and nightly version-math unit-tested (accept/reject + minor-vs-patch bumps). **Still CI-only (cannot be exercised offline):** the full workflow runtime — github-script API calls, OCIR auth, the rc-release / staging-branch preconditions, the nightly `base_ref` push mechanics, and self-hosted runners. The *logic* inside the steps is unit-tested; the *orchestration* is not. Treat it as unproven until the workflows run. ## Out of scope / required follow-up (needs repo admin — not doable from a PR) - Tag-protection rules: `v*` and `api/apps/v1alpha1/*` create-only by the CI app, no delete/update; "limit branches/tags updated in a single push" = 1. - Enable `NIGHTLY_ENABLED` (and `RETENTION_APPLY` when ready) repo variables. - Registry-side pruning of `*-nightly.*` image tags (no OCIR delete parity yet — tracked TODO in `retention.yaml`). ## Release note ```release-note NONE ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added nightly publishing (mirror-by-digest, disk build, e2e validation) and nightly retention pruning. * Added RC-to-stable promotion via digest retagging (no rebuild). * Operator/installer and console now expose version/platform metadata, including runtime override via `COZYSTACK_VERSION`. * **Bug Fixes** * Enforced write-once tag behavior and fast-forward-only maintenance updates across release workflows. * Made nightly mirroring/selection and retention pruning more selective and safer. * **Tests** * Added Bats coverage for nightly mirroring and RC retagging, plus new Helm/unit checks. * **Documentation** * Updated the release model to center RC promotion and tag immutability. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
48a3cfd341 |
Merge branch 'main' into ci/scorecard-hardening
Signed-off-by: myasnikovdaniil <60174387+myasnikovdaniil@users.noreply.github.com> |
||
|
|
75452f36a3 |
ci(actions): pin third-party GitHub Actions to commit SHAs
Pin the remaining floating version tags in the build-main and ui-test workflows to immutable commit SHAs, preserving the original tag in a trailing comment so Renovate can keep the pins up to date. Supply-chain hardening against tag hijacking/mutation; no functional change — each SHA is the commit the tag currently resolves to. Assisted-By: Claude Signed-off-by: Andrei Kvapil <andrei.kvapil@aenix.io> |
||
|
|
a3f97e5a0c |
ci: align pre-commit zizmor with CI and set persist-credentials on ui-test
Review feedback (non-blocking): - .pre-commit-config.yaml pinned zizmor at v1.22.0 while the CI gate installs 1.26.1; the zizmorcore/zizmor-pre-commit rev is the zizmor version 1:1, so the gap meant local and CI runs could differ. Align the hook to v1.26.1. - ui-test.yaml's checkout did not set persist-credentials: false, unlike the other two workflows hardened here (build-main.yaml, zizmor.yml). Set it so the token is not left in .git/config for the later pnpm install step to read. Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> Assisted-By: Claude <noreply@anthropic.com> Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
a0bd53f84d |
ci: pin workflow actions, scope token permissions, add zizmor gate
build-main.yaml and ui-test.yaml (the latter added by the in-tree console
move) landed without following the repo's workflow conventions: no top-level
permissions block and actions referenced by moving tags. Together they zero out
two OpenSSF Scorecard checks the rest of the tree already satisfies:
- Token-Permissions: scored on the weakest workflow, so one workflow with an
undeclared (default-write) top-level token drops the whole check to 0.
- Pinned-Dependencies: the six tag-pinned action refs count as unpinned.
Fix both files (top-level read-only token; job-level packages:write kept where
needed; all action refs pinned to the SHA already used elsewhere in the tree)
and add a durable gate so it cannot recur:
- .github/zizmor.yml enables the two Scorecard-relevant audits (unpinned-uses,
excessive-permissions), clean tree-wide as of this commit. The remaining
audits carry pre-existing debt and are disabled with a TODO to burn down and
re-enable one at a time.
- .pre-commit-config.yaml runs zizmor (offline) on changed workflows.
- .github/workflows/zizmor.yml runs the same audit tree-wide on every PR that
touches a workflow, failing on any reintroduced regression.
Also wire scorecard.yml to an optional SCORECARD_TOKEN secret: the default
GITHUB_TOKEN cannot read branch-protection settings, so the Branch-Protection
check is scored on partial data. The token is a no-op until the secret is added.
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
Assisted-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
||
|
|
8aebc34250 |
ci(release): compare-before-force the promote staging-branch push
Address review feedback from lexfrei on .github/workflows/promote-rc.yaml:200. The "Prepare stable branch" step built the release-X.Y.Z branch with `git checkout -B` + `git commit -s` (a fresh, timestamp-dependent SHA every run) and pushed it with a plain non-force push. On a re-dispatch — retrying after a later step failed, or promoting a newer rc to the same version — the new commit is a sibling of the already-pushed one (same parent, different SHA), so the push is rejected non-fast-forward and promotion wedges until the branch is deleted by hand. That contradicts both the validate step (which tolerates a leftover draft precisely so a newer rc can be re-promoted) and the workflow header's "overwritable branch, nothing to wedge a later re-promotion". Reuse the compare-before-force pattern from tags.yaml's Create release branch step: skip when the remote already matches, force-push + log when the branch genuinely moves. It is a mutable staging branch, so overwriting it is correct. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |
||
|
|
70adc01e79 |
ci(release): nits — skopeo login via stdin; tighten dashboard version regex
- nightly.yaml: pass the registry tokens to `skopeo login` over stdin (--password-stdin) instead of `-p` on argv, matching promote-rc/finalize and keeping the token out of the process list. - dashboard configmap: require digits-then-dot (^v?[0-9]+\.) when deriving the displayed platform version from the console image tag. The old ^v?[0-9] let a tagless, port-carrying ref (ghcr.io:443/.../console, whose last ":"-segment is "443/.../console") pass as a version. Verified: v1.5.0 / 1.5.0 still surface; the port ref and :latest no longer do. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |
||
|
|
8acd7f7d6b |
ci(release): harden tags.yaml — publish rcs, gate stable-only jobs, reject hand-pushed stables
The tag workflow assumed the old hand-pushed-stable flow in several places that break (or misbehave) under rc-promotion: - Drop PUBLISH_FLOATING from the Build step. This job now only ever builds a prerelease (a stable tag skips via its pre-existing draft, or is rejected — below), so moving :latest here would track the newest rc, and an rc on an old line would drag :latest backwards. :latest belongs to promotion (finalize). - Publish rc releases. tags.yaml created the rc release as a draft and nothing ever published it, so the community could not see the rc page or download its nocloud-amd64.raw.xz — defeating the rc's stated purpose. Publish it (prerelease:true, make_latest:false) once assets are uploaded; rc tags are write-once, nothing to protect with draft status. - Reject hand-pushed stable tags. A stable vX.Y.Z with no pre-existing draft is a hand-push down the legacy full-rebuild path (full rebuild, moved :latest, rogue release PR). Fail fast with a message pointing at promote-rc.yaml. This replaces two now-unreachable steps that shared the same gate: the submodule Go-module tag (moved to finalize in the previous commit) and "Create pull request if not exists" (promote-rc owns PR creation). - Gate generate-changelog and update-website-docs on stable (ref_name has no '-'), so each rc no longer opens a changelog-vX.Y.Z-rc.N PR that never merges and an "update managed apps for vX.Y.Z-rc.N" website PR. - Paginate the release lookups (check_release, draft-reuse): a promote-created draft can sit past the default first 30 and be missed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |
||
|
|
d252fc93a0 |
ci(release): make rc promotion transactional; defer registry writes to finalize
Promotion mutated the registry at dispatch time — retagging ~37 images to vX.Y.Z (+:latest), publishing the stable cozy-installer chart — before the promote PR's e2e ran and before any merge. An abandoned promotion then left stable-named bytes for a version that never shipped, and the (correct) image write-once check wedged any later re-promotion to the same version. Move every irreversible registry side effect into pull-requests-release.yaml finalize, which runs only after the promote PR merged (= after its e2e passed), making promotion atomic. promote-rc.yaml now only stages the release-X.Y.Z branch, drafts the stable release, and opens the PR; an abandoned promotion leaves nothing but an overwritable draft + branch. Folded into the same restructure: - Cut the api/apps/v1alpha1/vX.Y.Z Go-module tag in finalize, at the stable tag's commit. It was gated in tags.yaml on `release_exists==false && is_stable`, unsatisfiable under promotion (the draft always pre-exists), so promoted stables never got their submodule tag — a regression for Go consumers. Finalize is where the stable tag is born, so it cuts the matching submodule tag write-once (stable only). - Gate :latest by max-semver. promote-retag.sh now moves :latest only when MOVE_LATEST=1 (default off); finalize passes it the same make_latest decision the release gets, so a patch on an older line moves neither the release's `latest` nor the images' :latest. Added MOVE_LATEST bats coverage. - Stamp platformVersion into the published stable chart's default values (uncommitted, at package time) so `helm --install --version X.Y.Z` reports the stable version in telemetry instead of the rc version baked into the operator image. Not committed to the branch — that would leak platformVersion=vX.Y.Z onto main and mis-stamp the next rc. - promote-rc: serialize all promotions (static concurrency group, not per-rc); label the PR full-e2e so the full suite runs; scope the rc->stable sed to the depth-2 values.yaml glob plus the kubernetes .tag files (the build's own write set); tolerate a leftover draft so re-promotion is not wedged. - finalize: assert the PR author is cozystack-ci[bot], and run on an ephemeral oracle-vm runner (needs skopeo/helm). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |
||
|
|
c69f0f61c7 |
ci(release): annotate the nightly nocloud push with image.source
The first-ever `oras push` of cozystack-nocloud creates the GHCR package, but without org.opencontainers.image.source it is created unlinked to the repo, and the follow-up `oras tag … nightly` can 403 permission_denied: write_package until someone links it by hand. Set the annotation on push so the package is linked from the start (matches the --label the image builds already carry). Assisted-By: Claude <noreply@anthropic.com> Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |
||
|
|
846626ba12 |
fix(ci): promote against GHCR and publish the stable installer chart
Addresses two blockers that stop a stable release from being cut by the promote flow (both reproduced against a real registry in review). B1 — wrong registry: the workflow forced env.REGISTRY to the OCIR CI registry and logged in only to OCIR, but release-tag images (rc and stable) are built and pushed to GHCR by tags.yaml (hack/common-envs.mk default). promote-retag.sh filters refs by the $REGISTRY/ prefix, so with REGISTRY=OCIR every GHCR ref was dropped, the selection went empty, and the retag step exited 1 before doing anything. Point the job at ghcr.io/cozystack/cozystack (matching the script default) and log in to GHCR via --password-stdin (also resolves the token-on-argv nit). B2 — missing chart: promote-retag.sh only retags image refs found in values.yaml, and the cozy-installer OCI chart is a separate artifact in no values.yaml. make build (which pushes the chart) is gated on a not-yet-existing release, so a promoted stable never published cozy-installer:X.Y.Z — the documented `helm --install … --version X.Y.Z` 404'd with manifest-unknown. Repackage the chart from the restamped stable tree (no rebuild — it references the retagged images + packages artifact by digest) and helm push it, mirroring the nightly flow. Adds helm to the job. Assisted-By: Claude <noreply@anthropic.com> Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |
||
|
|
e82c03a715 |
ci(pr-labeler): drop 'edited' trigger to stop bot PR re-run churn
The PR Auto-Label workflow triggered on pull_request_target 'edited', which fires on every PR title *and body* edit. Long-lived bot PRs such as Renovate repeatedly edit their description on schedule, causing the label job to re-run dozens of times against an unchanged commit (e.g. ~48 runs on #2998, all on the same head SHA). Drop 'edited' so the job only runs on opened/reopened/synchronize. Labels derived from the PR title are re-applied on the next push or on reopen. Title-only edits between pushes no longer trigger a relabel, which is an acceptable trade for eliminating the churn. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |
||
|
|
d51a053a8b |
fix(ci): harden promote-rc validation and asset restamping
Two issues flagged in review on promote-rc.yaml:
- The "Validate rc is promotable" github-script called core.setFailed()
but kept running: a missing rc release then hit `rcRel.assets` and a
missing disk asset hit `disk.id`, throwing a TypeError that masked the
intended failure message. Return immediately after each setFailed so the
clean diagnostic is what surfaces.
- "Create draft stable release and upload assets" ran `make upload_assets`
without COZYSTACK_VERSION. upload_assets depends on `manifests`, which
re-renders the operator manifests; with no version it blanks the
platformVersion, silently undoing the stable stamp the Restamp step just
applied. Pass COZYSTACK_VERSION=${STABLE_VERSION} so the re-render
re-stamps the stable version.
Assisted-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
|
||
|
|
64f2d7ebee |
ci(release): don't let the rebuild guard skip stable follow-up jobs
Address review feedback from coderabbitai on .github/workflows/tags.yaml: the single `skip` output meant 'a release already exists' but was reused to gate update-website-docs, so a promoted stable tag (whose draft promote-rc already created) skipped the website-docs job, not just the rebuild. Rename the output to release_exists to reflect its real meaning and drop it from the update-website-docs gate so a promotion still publishes docs (the rebuild steps inside prepare-release stay gated on it). Assisted-By: Claude <noreply@anthropic.com> Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |