Files
coder/.github
Garrett DelfosseandMcKayla はな 54bd34d8e3 ci: guard helm index.yaml publish with a GCS generation precondition (#29793)
The `Publish Helm Chart` step does an unguarded read-modify-write of
`gs://helm.coder.com/v2/index.yaml`: download, `helm repo index --merge`
with only this run's three charts, upload. The workflow `concurrency`
group is keyed on `github.ref`, so releases for different tags run in
parallel. When two of them reach this step at about the same time, both
merge into the same old index and the second upload silently drops the
first release's entries. This is how 2.36.6 (and, going by the current
index, probably 2.36.2) went missing from the `coder-v2` chart repo
after back-to-back manual re-runs.

## Fix

Treat the index like a compare-and-swap:

- Read the object's GCS `generation` before and after downloading
`index.yaml`, and fail if it changed underneath us.
- Upload with `x-goog-if-generation-match:<generation>` so a concurrent
writer causes a `412` instead of a clobber.
- On either failure, exit with a `::error::` telling the operator to
re-run the job. No automatic retry; a re-run merges against the fresh
index.

Chart tarballs are still uploaded before the index, so an index entry is
never published ahead of its tarball. The OCI pushes to
`ghcr.io/coder/chart` are unchanged; they have no shared index and were
never affected.

Serializing all release runs through a single `concurrency` group was
considered and rejected: GitHub keeps only the newest pending run per
group, so a third dispatch would silently cancel a queued release.

## Verification

- `actionlint` and `shellcheck` on the step script pass.
- Exercised the step locally with a fake `gsutil`: the happy path
uploads and bumps the generation; bumping the generation between read
and upload makes the precondition fail and the step exit 1 with the
re-run message.
- Not exercised against the real bucket. The
`x-goog-if-generation-match` header and `gsutil stat` `Generation:`
field are standard GCS behaviour, but a dry-run cannot cover this
because the step is skipped when `dry_run` is set.

This does not repair the already-missing entries; #29794 adds a one-off
workflow for that.

---
_Generated by Coder Agents on behalf of @f0ssel._

---------

Co-authored-by: McKayla はな <kayla@tree.camp>
2026-09-23 15:22:56 -04:00
..