fix(helm): correct adminPwd secret key in NOTES.txt + add chart-publish workflow#83
Conversation
…sh workflow Two fixes from TKE deploy testing (reported by guosong): 1. NOTES.txt adminPwd key mismatch (fix/P0) NOTES.txt instructed users to fetch the admin password with key 'adminPwd', but secret.yaml stores it under 'OCTO_ADMIN_PWD'. Running the documented command returned an empty string, making the initial login impossible to automate. Fix: update NOTES.txt to use the correct key name OCTO_ADMIN_PWD and show the full kubectl command to retrieve it. 2. Add chart-publish workflow (feat) helm/octo chart was merged in PR Mininglamp-OSS#68 but no publish pipeline existed. Installing via 'helm install octo oci://ghcr.io/mininglamp-oss/octo' failed with 403 because the package was never pushed to ghcr.io. Fix: add .github/workflows/chart-publish.yml that: - triggers on GitHub Release publish (automatic) or workflow_dispatch (for backfills) - verifies Chart.yaml version matches the release tag - logs in to ghcr.io with GITHUB_TOKEN (packages: write) - runs helm package + helm push oci://ghcr.io/mininglamp-oss - smoke-tests the push with helm show chart Refs: TKE deploy notes 2026-05-22 (guosong)
Jerry-Xin
left a comment
There was a problem hiding this comment.
The PR is relevant to octo-deployment, but the new chart publish workflow does not reliably integrate with the repository’s existing release flow.
🔴 Blocking
🔴 Critical — .github/workflows/chart-publish.yml:3-6: the workflow depends on release.published to auto-publish charts, but this repo’s release path is .github/workflows/release-publish.yml, which delegates release creation from a GitHub Actions workflow using the repository token permissions. GitHub does not create new workflow runs for most events triggered by GITHUB_TOKEN; only workflow_dispatch and repository_dispatch are exceptions. That means releases published by the existing release-publish.yml path are not expected to trigger this workflow automatically, so future chart releases can still fail to be pushed unless maintainers remember to run the manual dispatch. (docs.github.com)
Suggested fix: call chart publishing from the release workflow itself, trigger this workflow via workflow_dispatch/repository_dispatch, or publish with a GitHub App/PAT-backed release event if that is the intended architecture.
💬 Non-blocking
🟡 Warning — .github/workflows/chart-publish.yml:10-11: the workflow_dispatch input says the version “Must match helm/octo/Chart.yaml appVersion”, but the workflow actually checks Chart.yaml version at lines 45-54. This should say version, not appVersion.
🟡 Warning — .github/workflows/chart-publish.yml:36-49 and .github/workflows/chart-publish.yml:72-81: the manually supplied version is interpolated directly into shell scripts. Since dispatch inputs are maintainer-controlled this is not a public exploit path, but it is still safer to pass it through env: and quote shell references consistently.
✅ Highlights
🔵 Suggestion — helm/octo/templates/NOTES.txt:39-43: the secret key fix is correct. secret.yaml stores OCTO_ADMIN_PWD, and the rendered secret name matches {{ include "octo.fullname" . }}-secrets.
Validation note: I attempted helm lint and helm template, but helm is not installed in this review environment. git diff --check main...HEAD passed.
yujiawei
left a comment
There was a problem hiding this comment.
Code Review — PR #83 (octo-deployment)
Summary
Two small, well-scoped fixes:
- NOTES.txt — corrects the documented secret key for the initial superAdmin password (
adminPwd→OCTO_ADMIN_PWD). .github/workflows/chart-publish.yml— new workflow to package and push the Helm chart tooci://ghcr.io/mininglamp-osson release publish (or manual dispatch).
No production code changes; no security regressions identified. Approving with a few suggestions to consider in a follow-up.
1. Verification
| Item | Status | Evidence |
|---|---|---|
secret.yaml stores the key as OCTO_ADMIN_PWD (not adminPwd) |
✅ | helm/octo/templates/secret.yaml:24 — OCTO_ADMIN_PWD: {{ .Values.secrets.adminPwd | default "" | quote }} |
| NOTES.txt uses correct key name | ✅ | helm/octo/templates/NOTES.txt:43 |
{{ include "octo.fullname" . }}-secrets matches the actual Secret name |
✅ | helm/octo/templates/_helpers.tpl:122-124 defines octo.secretName as printf "%s-secrets" (include "octo.fullname" .), which is what the rendered kubectl command will resolve to |
| Workflow action SHAs pinned with version comments | ✅ | actions/checkout@11bd71… (v4.2.2), azure/setup-helm@b9e51… (v4.3.0) |
| Workflow permissions minimally scoped | ✅ | contents: read + packages: write only |
set -euo pipefail in shell steps |
✅ | All run: blocks |
GITHUB_TOKEN piped via --password-stdin, never echoed |
✅ | chart-publish.yml:56-61 |
| Version cross-check before push | ✅ | chart-publish.yml:45-54 |
2. Findings
P0 / P1
None.
P2 — suggestions (non-blocking)
S1. NOTES.txt should reuse the octo.secretName helper
helm/octo/templates/NOTES.txt:42 writes {{ include "octo.fullname" . }}-secrets, duplicating the formula from _helpers.tpl:122-124. If the secret naming convention ever changes (e.g. adds a suffix variant), this line will silently drift. Prefer:
kubectl get secret -n {{ .Release.Namespace }} {{ include "octo.secretName" . }} \
-o jsonpath='{.data.OCTO_ADMIN_PWD}' | base64 -d
S2. workflow_dispatch backfill only works while Chart.yaml.version equals the requested version
The "Verify Chart.yaml version matches" step (chart-publish.yml:45-54) reads Chart.yaml from whatever ref actions/checkout resolved — which for workflow_dispatch is the default branch (main). That means:
- Backfilling the current chart version works (today: 0.2.4 ✅).
- Once
Chart.yamlis bumped to0.2.5onmain, a maintainer can no longer dispatch a backfill of0.2.4from this workflow — the version check will fail.
Two options if you want true backfill:
- Add a
refinput toworkflow_dispatchand passwith: { ref: ${{ inputs.ref }} }toactions/checkout, or - Resolve the version from a tag instead of an input when a tag is supplied.
If backfill-of-current-only is the intended scope, please document it in the input description so a future maintainer doesn't get burned.
S3. helm push to OCI overwrites existing tags by default
ghcr.io OCI tags are mutable. A double-trigger (release republish + a manual dispatch) would silently overwrite a previously-published chart of the same version. Consider either:
- Guarding with a
helm show chart oci://… --version <v>probe before push and failing if it already exists (matching the spirit of an "immutable release"), or - Adding a
forceboolean input gated behind an explicit opt-in.
S4. Missing concurrency: group
Add to prevent overlapping runs of the same version if two events fire near-simultaneously:
concurrency:
group: chart-publish-${{ github.event.release.tag_name || inputs.version }}
cancel-in-progress: falseS5. Brittle Chart.yaml parser
grep '^version:' helm/octo/Chart.yaml | awk '{print $2}' (chart-publish.yml:48) works today because the current version: 0.2.4 is unquoted. If anyone ever quotes it (version: "0.2.4") or adds a comment on the same line, the captured string will include the quotes/comment and the equality check will fail with a confusing message. yq (already a common helm-CI tool) would be more robust:
CHART_VERSION="$(yq '.version' helm/octo/Chart.yaml)"S6. (Optional) Consider provenance / cosign signing
The reusable release-publish flow already exists; chart publishing is a good place to layer in cosign sign for the OCI chart artifact in a future PR. Not in scope here.
3. Security review (PR was flagged needs-human-review)
-
No new secret material is introduced in code; the workflow consumes the built-in
GITHUB_TOKENonly. -
packages: writeis required for ghcr.io chart push and is the correct minimal scope. -
No
pull_request_target, no untrusted-inputrun:interpolation that could enable script injection.inputs.versionflows into a shell variable (VERSION="${{ inputs.version }}"); since this isworkflow_dispatchonly triggerable by repo maintainers, the attack surface is acceptable, but a future hardening pass could quote-then-printenvthe inputs viaenv:mapping to fully neutralise injection vectors:- env: VERSION_INPUT: ${{ inputs.version }} run: | VERSION="${VERSION_INPUT#v}" …
-
The NOTES.txt change is a docs-only fix and discloses no new sensitive info; the password it points to was already user-supplied via
secrets.adminPwd.
Suggest a human verify (per the security_sensitive classification) that:
- The ghcr package settings for
mininglamp-oss/octoallowGITHUB_TOKENwrites from this repo (org Package settings → "Manage Actions access"). If not pre-configured, the first push will 403 even withpackages: writegranted at the workflow level. - There's no organisational requirement to sign chart artifacts before publish (cosign / sigstore policy).
4. Additional observations (out of scope)
release-publish.ymlruns as aworkflow_dispatchonly and calls a centralised reusable workflow. The newchart-publish.ymlauto-fires onrelease: published, which is the right linkage point — together they form a clean two-step (manual release → automatic chart push). Worth a one-line comment inrelease-publish.ymlcross-referencingchart-publish.ymlso future maintainers see the chain.Chart.yamlappVersion: "latest"is a separate quality issue: it prevents downstream consumers from pinning to a concrete server image release. Not introduced by this PR, but flagging while we're here.
Verdict
Approve. The two fixes are correct, minimal, and address real reported issues. Suggestions above are quality-of-life and supply-chain hardening, none of which block merge.
) PR Mininglamp-OSS#82 adds .yamllint to ignore helm/octo/templates/. That PR is not yet merged, so this branch (which rebases from main) is missing the config and yamllint fails on Helm template files. Cherry-pick the .yamllint file here so this PR's CI is self-contained and green. Once PR Mininglamp-OSS#82 merges, the .yamllint will already be in main and this commit becomes a no-op.
Jerry-Xin
left a comment
There was a problem hiding this comment.
Relevant to this repository, but the PR leaves the YAML CI job broken.
🔴 Blocking
- 🔴 Critical —
.yamllintis not used by CI. The repo’s CI still runsyamllintwith an inline config at.github/workflows/ci.yml:49, so the new ignore rule in.yamllint:2is bypassed. I reproduced the CI command locally and it still lints Helm templates, failing with syntax errors such ashelm/octo/templates/statefulset-redis.yaml:1andhelm/octo/templates/secret.yaml:5. Update CI to use the repository config, e.g.yamllint -f github ., or add thehelm/octo/templates/ignore to the inline-dconfig.
💬 Non-blocking
- 🔵 Suggestion —
.github/workflows/chart-publish.yml:11says the manualversionmust matchChart.yaml appVersion, but the workflow checksChart.yaml versionat lines 45-54. The logic is correct for chart publishing; the input description should sayversion.
✅ Highlights
- The NOTES change correctly uses
OCTO_ADMIN_PWD, matchinghelm/octo/templates/secret.yaml:24. - The secret name in
helm/octo/templates/NOTES.txt:42matches the helper-defined<fullname>-secretsconvention inhelm/octo/templates/_helpers.tpl:122.
lml2468
left a comment
There was a problem hiding this comment.
Verdict
CHANGES_REQUESTED — The NOTES.txt fix is correct, but the chart-publish workflow has an architectural trigger issue (confirmed independently from the existing review), and CI is red due to a missing rebase.
Blocking Items
1. CI RED — yamllint still scans Helm templates (.github/workflows/ci.yml not updated)
The PR adds .yamllint with the correct ignore: | block, but the CI workflow (.github/workflows/ci.yml) on this branch still uses the old inline -d config that contains the broken \n escape. yamllint ignores .yamllint when -d is passed explicitly.
PR #82 (still open) fixes ci.yml to use yamllint -f github . (which reads .yamllint). This PR should be rebased on top of PR #82 once it merges — otherwise CI will remain red and the .yamllint file in this PR is dead code.
Additionally, both PRs add an identical .yamllint file, which will cause a merge conflict (or at minimum a confusing add/add resolution). After rebase, this PR's .yamllint addition should be dropped since PR #82 already provides it.
2. release.published auto-trigger will not fire (chart-publish.yml:3-6)
Confirming Allen's finding independently. release-publish.yml delegates to a reusable workflow with permissions: contents: write — meaning it uses GITHUB_TOKEN to create releases. Per GitHub docs:
Events triggered by the GITHUB_TOKEN will not create a new workflow run.
The release: published event in chart-publish.yml will therefore never fire from the automated release path. The workflow_dispatch fallback works but requires manual intervention every release — defeating the purpose of automation.
Suggested fix: either trigger chart-publish.yml via workflow_call from the release workflow itself, or use a PAT/GitHub App token for release creation so downstream events fire.
Non-blocking Notes
.github/workflows/chart-publish.yml:10-11: input description says "Must match helm/octo/Chart.yaml appVersion" but the actual check (line 48) validatesversion:, notappVersion. Misleading.chart-publish.yml:36-49:${{ inputs.version }}interpolated directly in shell. Since it's maintainer-controlled viaworkflow_dispatch, not a public exploit, but passing throughenv:would be cleaner.Chart.yamlappVersion: "latest"means the chart doesn't pin a concrete server image version — separate concern, not introduced here.- Missing
concurrency:group to prevent overlapping publishes of the same version.
Highlights
- The NOTES.txt fix is correct and well-documented.
secret.yaml:24storesOCTO_ADMIN_PWD, and the kubectl command in NOTES.txt correctly references it with the right secret name template. - Actions are SHA-pinned with version comments — good supply-chain practice.
set -euo pipefailin all shell steps — correct.helm show chartsmoke-test after push is a nice verification step.
yujiawei
left a comment
There was a problem hiding this comment.
Code Review — PR #83 (octo-deployment)
Verification Summary
| Item | Result | Evidence |
|---|---|---|
| NOTES.txt key now matches actual secret data key | ✅ | helm/octo/templates/secret.yaml:24 stores OCTO_ADMIN_PWD; helm/octo/templates/NOTES.txt:43 retrieves the same key. Cross-checked against helm/octo/templates/deployment-octo-server.yaml:189,193,249 which also reads OCTO_ADMIN_PWD from the secret. |
| Pre-fix bug is real | ✅ | On main, helm/octo/templates/NOTES.txt says If secrets.adminPwd was set … but never told users how to retrieve it; the legacy command (-o jsonpath='{.data.adminPwd}') returns empty because that key doesn't exist on the rendered Secret. |
Inlined secret name {{ include "octo.fullname" . }}-secrets matches the actual secret resource name |
✅ | helm/octo/templates/_helpers.tpl:122-124 defines octo.secretName as printf "%s-secrets" (include "octo.fullname" .), so the inlined form produces the same string. |
| Workflow action versions pinned to commit SHA | ✅ | actions/checkout@11bd719… (v4.2.2), azure/setup-helm@b9e5190… (v4.3.0). Helm CLI pinned to 3.15.3. |
permissions: block follows least-privilege |
✅ | contents: read + packages: write only — minimum needed for helm push to ghcr.io. |
Chart version verification step matches Chart.yaml to release/input version |
✅ | .github/workflows/chart-publish.yml:45-54 greps version: from Chart.yaml and exit 1s on mismatch. |
Smoke test (helm show chart oci://…) runs after push |
✅ | chart-publish.yml:76-81. |
GHCR login uses GITHUB_TOKEN with stdin (no token leakage in logs) |
✅ | chart-publish.yml:56-61 pipes via --password-stdin. |
release: published trigger pairs correctly with the existing release-publish.yml flow |
✅ | release-publish.yml calls the reusable Mininglamp-OSS/.github/.../reusable-release-publish.yml@main and supports draft=false → publishes a real release that fires the release.published event. |
Default values still produce a valid render (secrets.adminPwd: "" path) |
✅ | helm/octo/templates/secret.yaml:24 uses default "" so the key always exists; NOTES.txt only mentions the key — it doesn't crash if the value is empty. |
.yamllint correctly excludes Helm templates so chart {{ ... }} doesn't break CI |
✅ | .yamllint:2-3 ignores helm/octo/templates/. |
P0 / P1 Issues
None. The two fixes are correct, scoped, and verifiable from the diff alone.
Suggestions / Nits (non-blocking)
Nit 1 — Prefer the existing octo.secretName helper in NOTES.txt
helm/octo/templates/NOTES.txt:42 reconstructs the secret name inline:
kubectl get secret -n {{ .Release.Namespace }} {{ include "octo.fullname" . }}-secrets \The chart already has octo.secretName (_helpers.tpl:122-124), which secret.yaml:8 uses as the source of truth. Inlining the suffix risks drift if someone ever changes the secret naming convention via that helper. Suggested:
kubectl get secret -n {{ .Release.Namespace }} {{ include "octo.secretName" . }} \Functionally identical today; just keeps the rendered name behind a single helper.
Nit 2 — Harden against template injection in shell run: blocks (security_sensitive context)
chart-publish.yml interpolates ${{ inputs.version }}, ${{ steps.version.outputs.version }}, and ${{ github.actor }} directly inside run: shell scripts (lines 37, 49, 72, 74, 81, 60). workflow_dispatch is restricted to actors with write permission so the blast radius is bounded, but GitHub's own hardening guidance is to bind these through env: first to neutralize shell metacharacters:
- name: Resolve chart version
id: version
env:
EVENT_NAME: ${{ github.event_name }}
INPUT_VERSION: ${{ inputs.version }}
run: |
set -euo pipefail
if [[ "$EVENT_NAME" == "workflow_dispatch" ]]; then
VERSION="$INPUT_VERSION"
else
VERSION="${GITHUB_REF_NAME#v}"
fi
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"Same pattern for the verify / push / smoke-test / login steps. Not blocking for this PR, but flagging because the routing tag is security_sensitive and this is the kind of thing that's easier to add now than retrofit. Reference: https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions#good-practices-for-mitigating-script-injection-attacks
Nit 3 — set -euo pipefail is missing in the GHCR login step
chart-publish.yml:56-61 is the one run: block without set -euo pipefail. A failing helm registry login inside a single-pipe command can still surface as success in some shells. Cheap to fix:
- name: Log in to ghcr.io
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ACTOR: ${{ github.actor }}
run: |
set -euo pipefail
echo "$GH_TOKEN" | helm registry login ghcr.io --username "$ACTOR" --password-stdinNit 4 — PR description doesn't mention the .yamllint change
The diff includes a new .yamllint (cherry-pick from PR #82, per the second commit message), but the PR body only describes the two named fixes. Not a blocker — the file is benign and its purpose is obvious from CI context — but for the audit trail it's worth one line in the description so future archeology doesn't have to guess.
Nit 5 (info) — appVersion is "latest" while chart version is pinned
helm/octo/Chart.yaml has version: 0.2.4 (immutable) and appVersion: "latest" (floating). The verify step only checks version, which is the correct field for OCI publish. Just noting that appVersion: latest means the chart isn't pinned to a reproducible app build — outside the scope of this PR but worth pinning in a follow-up before public-facing GA.
Security-Sensitive Review Notes (for human verifier)
Items I'd want a human to eyeball even though I see no concrete defect:
- First-time
packages: writeuse on this repo.permissions:is correctly scoped to the workflow file (no global change), and the only network destination isghcr.io. No third-party registry, no extra secrets. Safe. GITHUB_TOKENis the only credential. No long-lived PAT; the token is repo-scoped and ephemeral. Confirmed nosecrets.*reference other thanGITHUB_TOKEN.- First push to
oci://ghcr.io/mininglamp-oss/octowill create the package. Maintainer should confirm the org has GHCR enabled and thatInherit access from source repositoryis the desired visibility default after the first publish — package visibility is not yet set in the repo settings as far as the diff shows. Recommend setting the new package to public post-first-push if the chart is meant to be installed without auth (the PR description'shelm install … oci://ghcr.io/mininglamp-oss/octoexample assumes public). - Shell-injection vector noted in Nit 2. Bounded by
workflow_dispatchwrite-permission gating; not a P1 because GitHub's permission model already limits the attacker set, but a defense-in-depth fix is cheap.
Recommendation
Approve. Two fixes are correct, verifiable, and minimal. The only changes I'd push for are non-blocking polish (helper reuse + workflow hardening) that can ship in a follow-up.
lml2468
left a comment
There was a problem hiding this comment.
Re-review — 📖 齐静春 (incremental check at 403fa0d)
Status: 🔴 CI still red — both P0s from previous review remain unresolved
Previous P0 #1 — yamllint CI failure:
- New commit added
.yamllintconfig (extends: relaxed) — correct content ✓ - But CI still uses inline
-dconfig fromci.ymlon main, so the.yamllintfile is ignored at runtime - Fix: depends on PR #82 being merged first (which updates ci.yml to read
.yamllint). PR #82 is still OPEN.
Previous P0 #2 — release.published trigger won't fire:
chart-publish.ymlstill useson: release: types: [published]butrelease-publish.ymlcreates releases withGITHUB_TOKEN, which does not trigger downstream workflows- The incremental diff shows no changes to
chart-publish.yml— this is unresolved. - Fix: use a PAT or
workflow_dispatchtrigger fromrelease-publish.ymlto chain the chart publish.
Gate
Per review policy: CI red → cannot approve or do deep review. Please resolve both P0s (likely: rebase on PR #82 once merged + fix the trigger chain), then request re-review.
Summary
Two fixes identified during TKE Serverless deploy testing (reported by @guosong).
Fix 1 — NOTES.txt adminPwd key mismatch (P0 usability bug)
Problem
NOTES.txt instructed users to retrieve the initial superAdmin password with:
This returns an empty string. The actual key stored by
secret.yamlisOCTO_ADMIN_PWD, notadminPwd.Fix
Updated NOTES.txt to use the correct key name and show the full
kubectlcommand with namespace:Fix 2 — Add
chart-publish.ymlworkflowProblem
The chart was merged in PR #68 but no publish pipeline existed. Installing via OCI:
failed with 403 because the package was never pushed to ghcr.io.
Fix
Added
.github/workflows/chart-publish.ymlthat:workflow_dispatch(for backfills)Chart.yamlversion matches the release tag before pushingGITHUB_TOKEN(no extra secrets needed, usespackages: writepermission)helm package+helm push oci://ghcr.io/mininglamp-osshelm show chartAfter merge, a maintainer can trigger the workflow manually with
version=0.2.4to backfill the current chart.Testing
secret.yamlstores key asOCTO_ADMIN_PWD(line 24)release-publish.yml; actual push requirespackages: writewhich ActionsGITHUB_TOKENprovides for org reposRefs: TKE Serverless deploy notes 2026-05-22