Repository navigation
ci(release): publish the hosted controller image and chart, pinned by digest - #748
Conversation
petr-sandbox
left a comment
There was a problem hiding this comment.
Review notes (AI-assisted, not verified by running the workflows). Two findings are on lines outside the diff:
scripts/prune_dev_packages.py~L103: the movingmaintag isn't protected from pruning.doomed()goes by age and position only. Ifmainstops moving (say the hosted push keeps failing after the switch push succeeds), the version it points to ages past 30 days and outside the 20 newest, gets deleted, and the dev ArgoCD Application can no longer resolvemain.- Stale docs. The
switch-release.ymlheader still describes three images and one chart.RELEASING.mdsays the hosted chart is pinned "the same way", but its refs arerepository@digestwith no:<version>tag.
The first two inline comments (partial push, GHCR missing-package response) look most important before merge.
| for chart in $(jq -r '.[].chart' "$RUNNER_TEMP/charts.json"); do | ||
| # prep refused a published version, but a re-run of this job alone | ||
| # skips prep, so ask again right before pushing. | ||
| scripts/refuse_published_chart.sh "oci://$REGISTRY/$NAMESPACE/charts/$chart" "$VERSION" |
There was a problem hiding this comment.
A failed push leaves the version half-published, with no way to finish it. Each chart is checked and pushed in turn. If charts/switch:X is pushed and the switch-hosted-controller push then fails, re-running the job stops here on switch, because X is already published. The hosted chart is never pushed, release-pins is never recorded, and dev main never moves; the only way out is a new version. Suggest checking every chart before pushing any, and treating "every chart already present" as done.
There was a problem hiding this comment.
Fixed in 303ea6e. scripts/release_charts.sh publish checks every chart before pushing any. A chart that already has the version is kept only if it renders exactly this build's image refs, and its registry digest is used for the pins. Anything else at that version fails the run. So re-running the failed chart job finishes the version, and "all present with these images" counts as done. I tested it against a local registry: switch pushed first and the hosted push missing → the re-run pushes the hosted chart; everything present → no-op; different images at the same version → fails. A full re-run still stops at prep, since it rebuilds the images.
| echo "::error::$chart:$version is already published. A version is published once; cut a new one." | ||
| exit 1 | ||
| fi | ||
| if ! grep -q ': not found$' <<<"$out"; then |
There was a problem hiding this comment.
A brand-new package may never pass this check (unconfirmed). Only helm's : not found counts as unpublished. charts/switch-hosted-controller doesn't exist yet under either <owner>/ or <owner>/dev/, and GHCR can answer a missing repository with denied / unauthorized instead. If it does, this exits 1 and blocks every dev build and release, and the package can never be created. Worth checking what GHCR actually returns for a missing package before merge.
There was a problem hiding this comment.
Checked against GHCR: a package that doesn't exist answers : not found, with credentials and anonymously. That covers both charts/switch-hosted-controller and dev/charts/switch-hosted-controller, and a namespace that doesn't exist too. The check now lives in release_charts.sh (status), with that finding noted. Any other answer still fails loudly; I confirmed that with an HTTP/HTTPS mismatch.
| for f in $(jq -r '.[]' <<<"$files"); do args+=(-f "$f"); done | ||
| # Linted with the check values: a chart with a values schema | ||
| # does not lint without the values it requires. | ||
| helm lint "$path" "${args[@]}" |
There was a problem hiding this comment.
The switch chart is no longer linted with its default values. The bare helm lint deploy/remote/helm/switch was removed, so lint always runs with render-check-values.yaml (plus overlays). A template that fails lint with the defaults but passes with the placeholder secrets now gets through. switch-release.yml made the same change.
There was a problem hiding this comment.
Fixed: Chart.lint_defaults. The switch chart is linted on its defaults again, then with each render's values. The hosted chart has a schema that requires values, so it's linted with its render values only. Both workflows go through the same build step.
| if not re.search(r"@sha256:[0-9a-f]{64}$", ref): | ||
| problems.append(f"{ref}: not pinned by digest") | ||
| elif ref.startswith(prefix): | ||
| elif ref.startswith(prefix) or name in IMAGES: |
There was a problem hiding this comment.
name in IMAGES matches by basename from any registry. A third-party image with a generic name, for example docker.io/envoyproxy/gateway:v1 as a sidecar, would fail verify --chart switch-hosted-controller with "add it to IMAGES". The other-chart check should only match refs from the pinning registry.
There was a problem hiding this comment.
Fixed: another chart's image is flagged only when it comes from the pinning registry. Added a test with docker.io/envoyproxy/gateway:v1 in the hosted render.
| lines, image.values_key, "image", f"{name}:{version}@{digests[name]}" | ||
| ) | ||
| expected: dict[tuple[str, str], str] = {} | ||
| if chart == "switch": |
There was a problem hiding this comment.
Nit: the pin style is picked with a hard-coded chart name. The docstring says adding a chart to CHARTS is the whole change, but a third chart using the <key>.image style would get the repository/digest shape, or fail with "expected one image.repository line". Suggest making the style a field on Chart.
There was a problem hiding this comment.
Fixed: Chart.pin_style (tagged / repository-digest). Nothing in pin keys off the chart name anymore.
| for f in $(jq -r '.[]' <<<"$files"); do args+=(-f "$f"); done | ||
| # Linted with the check values: a chart with a values schema | ||
| # does not lint without the values it requires. | ||
| helm lint "$path" "${args[@]}" |
There was a problem hiding this comment.
Nit: this pin, package, lint and verify loop is nearly a copy of pr-ci.yml 280-295, and the two already differ: one renders the .tgz, the other the source tree. Moving it into a script, as was done for refuse_published_chart.sh, would keep PR CI testing what the release runs.
Also, helm lint runs once per render, so the source chart is linted again for every overlay. It lints the source tree, not the packaged .tgz that gets pushed.
There was a problem hiding this comment.
Fixed: pin, package, lint and verify moved to scripts/release_charts.sh build, and PR CI runs that same step with placeholder digests. Lint and template now both run on the packaged .tgz. Lint still runs once per value set (defaults, then each render), because with different values it catches different template errors.
| uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 | ||
| with: | ||
| # To ask, before moving the dev `main` tag, whether this is still main's head. | ||
| fetch-depth: 0 |
There was a problem hiding this comment.
Nit: fetch-depth: 0 seems to be needed only for the git ls-remote in the main tag step, which needs no history. A shallow checkout would avoid fetching full history on every chart job.
There was a problem hiding this comment.
Fixed: dropped it. git ls-remote needs no history.
|
Thanks. Both summary points are fixed in 303ea6e:
Inline replies cover the rest. Nothing has run on Actions yet; the first merge to main exercises the publish path. |
cd2f15d to
d56ef39
Compare
… digest The hosted controller is built and published beside the switch images on every release, RC and dev build, and its chart is pinned to its image by digest the same way the switch chart is. The pinner now knows which chart each image belongs to, and PR CI checks both charts. Dev chart builds also move a `main` tag, only while the build is still the tip of main, so an environment can follow main and resolve it to a digest. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…build with PR CI - scripts/release_charts.sh packages, lints and checks the charts for both the release and PR CI, and publishes them: every chart is checked before any push, and a chart already at the version is kept only if it renders this build's images, so re-running a failed chart job completes the version. - The switch chart is linted on its defaults again, and lint and render run on the packaged .tgz. - The pin style is a field on the chart; verify flags another chart's image only from the pinning registry. - The dev cleanup never deletes the build the `main` tag points at. - No full-history checkout in the chart job; header and RELEASING updated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Rebasing onto the hosted-controller image main had already started publishing under a hardcoded matrix entry (image: hosted-controller) replaced the dynamic include this PR relies on. The pin/digest/release steps all key off scripts/pin_chart_images.py's IMAGES dict, which names it switch-hosted-controller — a static matrix using the other name would have built an image whose digest the pin step could never find. Also fixes two leftover "hosted-controller" (singular-chart-era) mentions in RELEASING.md to match. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
303ea6e to
78f6f85
Compare
…e rebase Main had independently added its own hosted-controller artifact entry (published_as: hosted-controller) while this PR was adding its own (published_as: switch-hosted-controller, the name the release workflow's pin/digest machinery actually uses). The rebase's line-based YAML merge kept both, under the same key, which the generator wrote out as a literal duplicate dict key — exactly what "Registry, generated modules and declared versions agree" exists to catch. Drops the stale one and regenerates. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Part of the deployment pipeline work (phase 4b), stacked on #745.
What
switch-hosted-controller(image) andcharts/switch-hosted-controller(chart) on every release, RC and dev build, versioned with switch-core.scripts/pin_chart_images.pynow knows which chart each image belongs to (CHARTS,IMAGES[*].chart). The hosted chart is pinned throughimage.repositoryandimage.digest.verifyis scoped per chart and rejects another chart's first-party image.scripts/refuse_published_chart.sh).maintag ondev/charts/*, only while the build is still the tip of main. ArgoCD can follow it for development and resolve it to a digest.Chart.yamlnow uses the same0.0.0-devplaceholder as the switch chart. Napoleon installs it from source today, so that path still works.artifacts.yaml, RELEASING and CHANGELOG are updated.Testing
pytest core/tests/test_pin_chart_images.py core/tests/test_gen_artifacts.py: all pass.actionlintis clean. The PR CI chart step, run locally, pins, lints and verifies both charts.maintag step usesoras tagand has only been checked by reading it.🤖 Generated with Claude Code