ci(helm-release): revive a workflow that has never once started (17/17 startup_failure) - #1098
ci(helm-release): revive a workflow that has never once started (17/17 startup_failure)#1098mdheller wants to merge 1 commit into
Conversation
All 17 runs of helm-release.yml (2026-06-23 -> 2026-07-29) are `startup_failure`. The run is rejected at creation, before any job starts, so there is nothing in the logs to point at the cause. `helm/chart-releaser-action@v1.6.0` is the reason. This repo sets `allowed_actions: selected` with github_owned_allowed + verified_allowed + patterns_allowed (dtolnay/*, Swatinem/*, oven-sh/*, tauri-apps/*). The action is on the Marketplace, which is what makes this one easy to get wrong -- but `verified_allowed` gates on the publisher's verified-creator badge, and `helm` has none. Fetching the listing finds zero `Verified<` markers, against 2 for azure/login. It matches no pattern either. Isolated by elimination: every other action in the file demonstrably runs today. azure/setup-helm@v4 succeeds in deploy-tests.yml (88 successes), google-github-actions/auth@v2 in provision-secrets.yml, and actions/* are github-owned. Replaced with a pinned, checksum-verified install of the `cr` binary plus the three `cr` calls the action wrapped, matching the direction already taken for opentofu/setup-opentofu in #1090 and #1097. Adding `helm/*` to patterns_allowed would also work, but that is a settings change with no diff to review, and it was rejected for the OpenTofu case. The workflow had no pull_request trigger, so a PR touching it could not run it -- the same blind spot #1097 closed for tofu-plan.yml. It now triggers on itself and on the scripts, and splits into two jobs: a read-only `package` job that runs on pull_request, and the publishing `release` job that does not. The policy check is whole-file at run creation, so the PR-time run reaching its steps proves every `uses:` in the file is permitted, including the one only the release job uses. Verified locally: checksum mismatch and unknown version both fail hard and install nothing; --package-only packages all four charts and exits 0; a missing CR_TOKEN refuses to publish.
Verification: the run is no longer rejected at creationTotal run count went 17 → 18, and run 18 was accepted. The defect was rejection before startup, so the thing that had to be proven is admission, not a green tick. Here is the contrast:
A It also confirms the trigger change did its job — before this PR the workflow had no What is not yet verifiedWhether the steps pass. The run is sitting in a deep queue — 343 queued, 5 in progress repo-wide at the time of writing. It is Specifically still unproven end-to-end on a real runner:
Not exercised by this PR at all, and only reachable on
I will not infer success from valid YAML. The admission claim above is evidenced; the step-level claim is not, yet. |
There was a problem hiding this comment.
Pull request overview
Note
Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.
Revives the helm-release.yml workflow by removing a GitHub Actions policy-blocked third-party action and replacing it with pinned, checksum-verified installation plus local scripts so the workflow can actually start and be validated on PRs.
Changes:
- Replace
helm/chart-releaser-actionwithscripts/ci/install-chart-releaser.sh(pinned + sha256-verified) andscripts/ci/release-charts.sh. - Split workflow into a PR-safe
packagejob and a gatedreleasejob, and addpull_requesttriggers for workflow/script changes. - Bootstrap
gh-pageson first publish so initial releases don’t fail on a missing branch.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
scripts/ci/release-charts.sh |
Implements packaging + publishing logic previously wrapped by the blocked action, including gh-pages bootstrap. |
scripts/ci/install-chart-releaser.sh |
Installs cr from upstream releases with pinned version + committed checksums. |
.github/workflows/helm-release.yml |
Adds PR trigger + a read-only packaging job; switches publish job to run the new scripts. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| echo "Branch '${PAGES_BRANCH}' does not exist yet; creating it with an empty index." | ||
| wt="$(mktemp -d)/${PAGES_BRANCH}" | ||
| git worktree add --detach "$wt" | ||
| git -C "$wt" checkout --orphan "$PAGES_BRANCH" | ||
| git -C "$wt" rm -rq --cached . || true | ||
| find "$wt" -mindepth 1 -maxdepth 1 ! -name .git -exec rm -rf {} + | ||
| : > "$wt/index.yaml" | ||
| git -C "$wt" add index.yaml | ||
| git -C "$wt" commit -m "chore(charts): initialise Helm chart repository index" | ||
| git -C "$wt" push origin "$PAGES_BRANCH" |
| cr index \ | ||
| --owner "$OWNER" \ | ||
| --git-repo "$REPO" \ | ||
| --package-path "$PKG_DIR" \ | ||
| --pages-branch "$PAGES_BRANCH" \ | ||
| --index-path .cr-index/index.yaml \ | ||
| --token "$CR_TOKEN" \ | ||
| --push |
| tar -xzf "${TMP}/${TGZ}" -C "$TMP" cr | ||
| install -m 0755 "${TMP}/cr" "${DEST}/cr" |
| steps: | ||
| - uses: actions/checkout@v4 | ||
|
|
||
| - uses: azure/setup-helm@v4 |
All 17 runs of
helm-release.yml(2026-06-23 → 2026-07-29) arestartup_failure. It has never once succeeded — never once started. The run is rejected at creation, before any job exists, so there are no logs to point at the cause.Root cause
helm/chart-releaser-action@v1.6.0.The repo sets
allowed_actions: selected:{"github_owned_allowed":true,"verified_allowed":true, "patterns_allowed":["dtolnay/*","Swatinem/*","oven-sh/*","tauri-apps/*"]}The action is on the Marketplace —
https://github.com/marketplace/actions/helm-chart-releaserreturns 200 — which is what makes this one easy to get wrong. Butverified_allowedgates on the publisher's verified-creator badge, not on Marketplace presence, and the publisherhelmhas none. Fetching the listing finds zeroVerified<markers, against 2 forazure/login. It is not github-owned and matches no pattern.Isolated by elimination — every other action in the file demonstrably runs today:
helm-release.ymlactions/checkout@v4azure/setup-helm@v4deploy-tests.yml= 88 successesgoogle-github-actions/auth@v2provision-secrets.ymlsucceedshelm/chart-releaser-action@v1.6.0The fix — option 1, for consistency
Replaced with a pinned, checksum-verified install of the
crbinary (scripts/ci/install-chart-releaser.sh) plus the threecrcalls the action wrapped (scripts/ci/release-charts.sh). This matchesscripts/ci/install-opentofu.shfrom #1090 / #1097 — same shape: committed sha256, hard refusal on an unpinned version, hard failure on checksum mismatch.The committed checksums were verified by downloading both linux artifacts and recomputing:
cris pinned at v1.8.1 rather than the v1.6.0 the action defaulted to. There is no behaviour to preserve — the step has never executed once.Option 2 — an admin adds
helm/*topatterns_allowed— would also work. Not done deliberately: it is a settings change with no diff to review, and it was rejected for the OpenTofu case for that reason. Noting it so the choice reads as deliberate rather than overlooked.Deliberate behaviour differences from the action
cr upload --skip-existingdrops the already-released ones. The action diffed charts against the latest tag. Same outcome, no dependence on tag history being intact, and re-running is a no-op rather than a failure.gh-pagesis bootstrapped if absent.cr index --pushpublishes viagit worktree add --detach <dir> origin/gh-pages, so the branch must already exist.gh-pagesdoes not exist in this repo (git ls-remote --heads origin gh-pagesis empty), so even with the policy fixed the first run would have died on a prerequisite nobody is watching for. Creating it empty is the one-time manual setup chart-releaser documents; it is guarded byls-remote, so it happens at most once.Making the fix verifiable pre-merge
The workflow had no
pull_requesttrigger at all — onlypush: branches:[main]andworkflow_dispatch. A PR touching it could not run it. That is the same blind spot #1097 closed fortofu-plan.yml, and it is a large part of why 17 consecutive failures went unnoticed.It now triggers on itself and on the two scripts, and splits into two jobs:
package— runs onpull_request,permissions: contents: read. Installs the pinnedcr, packages every chart, publishes nothing.release—if: github.event_name != 'pull_request'. Cuts releases, pushesgh-pages, pushes OCI.The policy check is whole-file at run creation, so a PR-time run that reaches its steps proves every
uses:in the file is permitted — includinggoogle-github-actions/auth@v2, which only the skippedreleasejob uses.Verified locally before pushing:
OK, installsFAILED/did NOT match, exit 1, nothing installedCR_VERSION=9.9.9release-charts.sh --package-onlyCR_TOKENThis is the third instance — and there is a fourth
infra-drift-detect(26startup_failure),tofu-plan.yml(25),helm-release.yml(17). I enumerated everyuses:across the repo — all 104 files in.github/workflows/, plus a whole-tree sweep confirming there are no composite actions (.github/actions/is empty, noaction.ymlexists anywhere) — and checked each against the policy — github-owned, publisher verified-creator badge (fetched and counted, not assumed from Marketplace presence, which is exactly what fooled this case), or pattern match.Blocked
helm/chart-releaser-action@v1.6.0helm-release.ymlstartup_failureopentofu/setup-opentofu@v1,@9d84900ftofu-plan.yml,infra-drift-detect.ymlstartup_failurepeter-evans/create-pull-request@v6search-orchestrator-image-pin.ymlstartup_failuresince 2026-07-03The fourth is
search-orchestrator-image-pin.yml. It last succeeded 2026-04-26; every run from 2026-07-03 onward is astartup_failure. It isworkflow_run-triggered off the image build, so it fires rarely and fails invisibly — the automated image-digest pinning it exists to do has not happened in nearly four months. Same root cause, same failure mode. Not fixed here — it needs its own PR, sincecreate-pull-requesthas no one-linerun:equivalent (likelygh pr create).Allowed — verified, no action needed
azure/setup-helm@v4,azure/login,azure/setup-kubectl@v3,docker/login-action@v3,docker/setup-buildx-action@v3,docker/build-push-action@v5,v6,hashicorp/setup-terraform@v3,aws-actions/configure-aws-credentials,google-github-actions/auth@v2,google-github-actions/get-gke-credentials@v2— all show 2Verified<markers, and all butazure/loginandaws-actions/*are independently confirmed by live successful runs.SocioProphet/.github/.github/workflows/build-image.yml@mainis a same-org reusable workflow (images.yml, 68 successes). Allactions/*are github-owned.Worth noting: none of the four allowlisted patterns is used by any workflow in this repo.
dtolnay/*,Swatinem/*,oven-sh/*,tauri-apps/*are Rust/Tauri patterns belonging to Noetica and BearBrowser. The pattern allowlist is dead weight here.Recommendation: make this mechanical
Four workflows have now been silently dead for weeks to months on the same policy, and the failure mode is specifically one that produces nothing to read — no logs, no annotation, and a
paths:filter that often means no PR can surface it either. Manual discovery does not scale.A check should fail a PR when a workflow references an action the policy cannot run. Sketch:
GET /repos/{owner}/{repo}/actions/permissions/selected-actions.uses:under.github/workflows/.actions/*andgithub/*whengithub_owned_allowed; same-repo / same-org reusable workflows; anything matchingpatterns_allowed.verified_allowed, resolve the action's Marketplace listing and require the verified-creator badge — not merely a 200. Fetching the listing and getting 200 is precisely what makes this bug look fine.Natural home:
tools/check_workflow_path_filters.py, run byci-path-filter-audit.yml. That workflow already triggers on.github/workflows/**, already parses every workflow file, and is already framed around exactly this doctrine — "a wrong promise is worse than no filter, because the validator stops running pre-merge and nobody notices." A policy-blocked action is the same failure with a harsher edge: the validator does not merely stop being asked, it is rejected outright. (The siblingscripts/verify_workflow_branches.pyin socioprophet is the other candidate, but it is in the wrong repo and does not already read this repo's workflows.)Step 4 needs a network call, which that tool deliberately does not make today — so it wants either a committed, reviewed allowlist of verified publishers or an
--offlinemode. That is a real design question, which is why it is not built in this PR. Proposing it here rather than bundling it.Not touched
.cr-release-packages/and.cr-index/are runner-local build output and are not in.gitignore. Nothing commits from the main worktree —cr index --pushoperates in its own temp worktree — so this is cosmetic. Left alone to keep the diff insidehelm-release.yml+scripts/ci/.Verification status
See the first comment below for whether the
packagejob actually reached its steps. A workflow that merely parses proves nothing here — the entire defect is rejection before startup.🤖 Generated with Claude Code