ci(publish): compose the registry URL from a base and the repository owner #64

Merged
jklein merged 2 commits from ci/publish-charts-url-owner into main 2026-09-30 20:54:21 +02:00
Member

Fixes #63

publish-helm-chart.yml composed the upload target as "${REGISTRY_CHARTS_URL%/}/api/charts", so op://GitHub-Actions/jlab-registry-access/charts-url had to hold the full registry URL including the owner and the package type.

It now holds only the base up to /api/packages, and the owner comes from ${{ github.repository_owner }}:

op://GitHub-Actions/jlab-registry-access/charts-url  =  https://<host>/api/packages
target                                              =  <base>/<owner>/helm/api/charts

No caller changes: the 26 thin publish-<chart>.yml are untouched, and no value pointing at the registry — not even the owner path segment — enters the repository.

Measured, not assumed: is github.repository_owner populated on this forge?

Yes. Observed value: jlab-cluster, 12 characters.

How: a temporary step at the head of detect-changed-charts in validate-charts.yml printed the context value on the pull_request run of this PR. Read off run 282:

github.repository_owner = [jlab-cluster]
github.repository       = [jlab-cluster/helm-charts]
length = 12

The step has since been removed again (second commit on this branch — validate-charts.yml is now byte-identical to main), and the measured value is recorded in the comment above the upload step so the fact outlives the step that produced it.

Guards

Guard Rejects
charts-url must not end in /api/charts the old, full URL; the error names /api/packages as the new shape
charts-url must not contain a /helm segment the old URL's package-type segment
charts-url must end in /api/packages everything else, including a base that already carries the owner (…/api/packages/<owner>) or no path at all
derived owner non-empty (:?) an empty github.repository_owner, which would build <base>//helm/api/charts
derived owner matches [A-Za-z0-9._-] only a /, a space or a ? in the owner, which would build a different URL entirely

Each of the three charts-url errors says what to change in the vault item, because the only other signal is a 404 against a URL 1Password has masked.

Verified locally against 12 inputs (good base with and without a trailing slash, both owners of the instance, the old full URL in three stages of truncation, an owner-bearing base, a bare host, an empty owner, an owner with / and with a space) — every case lands in the intended branch.

What deliberately stayed

Everything the upload step already had, each paid for once: the per-field :? guards, the whitespace check on the credentials (curl --config truncates an unquoted value at the first space), the host masking via ::add-mask::, the built file name <chart>-<version>.tgz instead of a glob, and --fail-with-body. The long comment block above the step keeps explaining all of them; it gained a paragraph on the composition and lost nothing.

Migration — hard cut, and in this order

Vault item and workflow have to match. Both mismatches fail loudly with a 404; neither publishes to a wrong place.

  1. The maintainer shortens op://GitHub-Actions/jlab-registry-access/charts-url to the base, i.e. everything up to and including /api/packages.
  2. Then merge.

Why this PR cannot prove itself, and what does

Merging triggers no publish — the PR touches no chart directory, so the new path stays unproven until the next chart change.

The end-to-end check is one manual workflow_dispatch of publish-helm-chart.yml for any chart. The registry accepts a re-upload of an already published version (HTTP 201) and replaces the artifact (#160), so it is a safe test of the whole path.

Only the maintainer can trigger it — the bot account has no write permission on the repository.

Docs

  • README, the publish-helm-chart.yml bullet: "the workflow appends /api/charts to charts-url" is gone. "No registry address ever appears in this repository" stays, and now carries the reason — the owner is derived, not stored.
  • Wiki, CI-CD → Publishing: new section "The target URL is composed, not stored" with the same correction, the guard table, the order of the cut and the manual-dispatch proof. Already pushed (d7ce205).

Scope

Two files: publish-helm-chart.yml and README.md. No chart directory — no version bump, no install-test, no publish from this PR.

Fixes #63 `publish-helm-chart.yml` composed the upload target as `"${REGISTRY_CHARTS_URL%/}/api/charts"`, so `op://GitHub-Actions/jlab-registry-access/charts-url` had to hold the full registry URL including the owner and the package type. It now holds only the **base up to `/api/packages`**, and the owner comes from `${{ github.repository_owner }}`: ``` op://GitHub-Actions/jlab-registry-access/charts-url = https://<host>/api/packages target = <base>/<owner>/helm/api/charts ``` No caller changes: the 26 thin `publish-<chart>.yml` are untouched, and no value pointing at the registry — not even the owner path segment — enters the repository. ## Measured, not assumed: is `github.repository_owner` populated on this forge? **Yes.** Observed value: **`jlab-cluster`**, 12 characters. How: a temporary step at the head of `detect-changed-charts` in `validate-charts.yml` printed the context value on the `pull_request` run of this PR. Read off **run 282**: ``` github.repository_owner = [jlab-cluster] github.repository = [jlab-cluster/helm-charts] length = 12 ``` The step has since been **removed again** (second commit on this branch — `validate-charts.yml` is now byte-identical to `main`), and the measured value is recorded in the comment above the upload step so the fact outlives the step that produced it. ## Guards | Guard | Rejects | | --- | --- | | `charts-url` must not end in `/api/charts` | the old, full URL; the error names `/api/packages` as the new shape | | `charts-url` must not contain a `/helm` segment | the old URL's package-type segment | | `charts-url` must end in `/api/packages` | everything else, including a base that already carries the owner (`…/api/packages/<owner>`) or no path at all | | derived owner non-empty (`:?`) | an empty `github.repository_owner`, which would build `<base>//helm/api/charts` | | derived owner matches `[A-Za-z0-9._-]` only | a `/`, a space or a `?` in the owner, which would build a different URL entirely | Each of the three `charts-url` errors says what to change in the vault item, because the only other signal is a 404 against a URL 1Password has masked. Verified locally against 12 inputs (good base with and without a trailing slash, both owners of the instance, the old full URL in three stages of truncation, an owner-bearing base, a bare host, an empty owner, an owner with `/` and with a space) — every case lands in the intended branch. ## What deliberately stayed Everything the upload step already had, each paid for once: the per-field `:?` guards, the whitespace check on the credentials (`curl --config` truncates an unquoted value at the first space), the host masking via `::add-mask::`, the **built** file name `<chart>-<version>.tgz` instead of a glob, and `--fail-with-body`. The long comment block above the step keeps explaining all of them; it gained a paragraph on the composition and lost nothing. ## Migration — hard cut, and in this order Vault item and workflow have to match. Both mismatches fail loudly with a 404; neither publishes to a wrong place. 1. **The maintainer shortens `op://GitHub-Actions/jlab-registry-access/charts-url` to the base**, i.e. everything up to and including `/api/packages`. 2. Then merge. ## Why this PR cannot prove itself, and what does **Merging triggers no publish** — the PR touches no chart directory, so the new path stays unproven until the next chart change. The end-to-end check is one manual **`workflow_dispatch` of `publish-helm-chart.yml`** for any chart. The registry accepts a re-upload of an already published version (HTTP 201) and replaces the artifact (#160), so it is a safe test of the whole path. **Only the maintainer can trigger it** — the bot account has no write permission on the repository. ## Docs - **README**, the `publish-helm-chart.yml` bullet: "the workflow appends `/api/charts` to `charts-url`" is gone. "No registry address ever appears in this repository" stays, and now carries the reason — the owner is derived, not stored. - **Wiki, `CI-CD` → Publishing**: new section "The target URL is composed, not stored" with the same correction, the guard table, the order of the cut and the manual-dispatch proof. Already pushed (`d7ce205`). ## Scope Two files: `publish-helm-chart.yml` and `README.md`. **No chart directory** — no version bump, no install-test, no publish from this PR.
jlab-bot added 1 commit 2026-09-30 20:50:27 +02:00
ci(publish): compose the registry URL from a base and the repository owner
Validate Helm Charts / Detect changed charts (pull_request) Successful in 3s
Validate Helm Charts / Build ${{ fromJSON(needs.detect-changed-charts.outputs.charts) }} (pull_request) Skipped
Validate Helm Charts / Install-test (pull_request) Skipped
Validate Helm Charts / Resource quantities (pull_request) Successful in 8s
Validate Helm Charts / Trivy config ${{ fromJSON(needs.detect-changed-charts.outputs.scan-charts) }} (pull_request) Successful in 15s
a12983fbea
charts-url in op://GitHub-Actions/jlab-registry-access held the full
registry URL and publish-helm-chart.yml appended /api/charts to it. It
now holds only the base up to /api/packages, and the owner segment comes
from github.repository_owner, so the target is composed as
<base>/<owner>/helm/api/charts.

Nothing in this repository points at the registry any more, not even a
path segment, and the second owner on the same instance needs no second
setting. The 26 thin publish-<chart>.yml callers are unchanged.

Two guards keep the switch from the old shape loud rather than a 404
against a masked URL: charts-url is rejected unless it ends in
/api/packages, naming /helm, /api/charts or an included owner
explicitly; and the derived owner is rejected when empty or when it is
not a usable URL path segment. Everything the upload step already had
is untouched - the per-field :? guards, the whitespace check on the
credentials, the host masking, the built file name, --fail-with-body.

The probe step in validate-charts.yml is temporary and measures whether
this forge populates github.repository_owner at all; it is removed again
once the value has been read off the run.
jklein added 1 commit 2026-09-30 20:52:09 +02:00
ci: remove the temporary repository_owner probe, record the measurement
Validate Helm Charts / Detect changed charts (pull_request) Successful in 2s
Validate Helm Charts / Build ${{ fromJSON(needs.detect-changed-charts.outputs.charts) }} (pull_request) Skipped
Validate Helm Charts / Trivy config ${{ fromJSON(needs.detect-changed-charts.outputs.scan-charts) }} (pull_request) Skipped
Validate Helm Charts / Install-test (pull_request) Skipped
Validate Helm Charts / Resource quantities (pull_request) Successful in 7s
b614b53602
github.repository_owner is populated on this Gitea instance: measured as
"jlab-cluster" (12 characters) in run 282 of validate-charts.yml. The
value is now in the comment above the upload step, so the fact survives
without the step that produced it.
jklein approved these changes 2026-09-30 20:54:18 +02:00
jklein merged commit b614b53602 into main 2026-09-30 20:54:21 +02:00
jklein deleted branch ci/publish-charts-url-owner 2026-09-30 20:54:22 +02:00
Sign in to join this conversation.
No Reviewers
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: jlab-cluster/helm-charts#64