Skip to content

fix: retry latest version lookup and add latest-fallback opt-out - #303

Merged
Suneha Bose (bosesuneha) merged 5 commits into
Azure:mainfrom
thomastvedt:fix/retry-latest-version-lookup
Nov 4, 2026
Merged

Suneha Bose (bosesuneha) merged 5 commits into
Azure:mainfrom
thomastvedt:fix/retry-latest-version-lookup

Conversation

@thomastvedt

@thomastvedt Thomas Seljen Tvedt (thomastvedt) commented Sep 10, 2026 •

Copy link
Copy Markdown
Contributor

What happens today

When version is latest (the default), getLatestHelmVersion makes a single request to https://get.helm.sh/helm-latest-version. If that one request fails for any reason, the action logs a warning and installs the hard-coded stableHelmVersion (v3.18.4). The step stays green.

That URL now returns v4.3.1, so the fallback moves a job back an entire major version on a single transient network failure. The run then fails later on something that does not look related. In our case a deploy died on Error: unknown flag: --rollback-on-failure several steps after the silent downgrade, and it took a while to trace it back to this step.

The same file already takes the opposite position for the major.minor path. The comment on helmPatchExists says: "Any other status (403/405/429/5xx, ...) and genuine network errors are thrown, so transient failures, rate-limiting, or a host that disallows HEAD are never mistaken for a missing patch." Only the latest path guesses, and it guesses across a major.

This is the bug reported in #187 (closed after the reporter found the fallback documented in the README, with the unanswered request "It should at the very least be possible to opt out of the fallback"). The same symptom was reported in #136, #219, #221 and #226. #188 is about the constant going stale, which this PR does not change.

What this PR does

  1. Retries the lookup. fetchLatestHelmVersion makes up to 3 attempts with a 1 s, then 2 s wait between them. A non-2xx response is treated as a failure instead of installing the response body as a version, and its unread body is cancelled before the next attempt. A single transient failure no longer decides the outcome.
  2. Adds an opt-out of the fallback. A new latest-fallback input controls what happens once the retries are exhausted. It defaults to 'true', which keeps the documented v5 behavior: install stableHelmVersion with a warning. The warning now names the input. Setting it to 'false' fails the step with the underlying error instead.

Making failure the default is left for the next major release of the action, as suggested in review.

Out of scope: changing stableHelmVersion to a Helm 4 default. That would change what v3 users get when the fallback applies and belongs with #188 and #241.

Changes

  • src/run.ts: fetchLatestHelmVersion with retries and body cancellation on non-2xx; getLatestHelmVersion(fallbackToDefault = true); run() reads latest-fallback, and only an explicit 'false' turns the fallback off.
  • src/run.test.ts: tests for retry-then-succeed, cancelling the unread body before retrying, every attempt failing (falls back by default, throws with the fallback off), and two run() cases for the input. The retry backoff runs under fake timers so the suite stays fast.
  • action.yml: latest-fallback input, default 'true'.
  • README.md: the note about the fallback describes the retries and how to opt out.

npm test (58 passing), npm run typecheck and npm run format-check pass locally. lib/ is not touched.

🤖 Generated with Claude Code

Resolving 'version: latest' made a single request to
https://get.helm.sh/helm-latest-version and, on any failure, silently
installed the hard-coded default version. Since 'latest' resolves to
Helm 4, that fallback moves a job back a whole major version while the
step stays green.

Retry the lookup a few times with a short backoff, treat non-2xx
responses as failures instead of installing the response body, and add
a 'latest-fallback' input so callers can make the step fail instead of
installing the default version. The default keeps the existing
behavior.
Make 'latest-fallback' default to 'false', so an exhausted lookup fails
the step with the underlying error instead of installing the built-in
default version, which can be a major behind what 'latest' resolves to.
Setting the input to 'true' restores the previous behavior.
Comment thread action.yml Resolved
Comment thread src/run.ts Resolved
Installing the built-in default version when 'latest' cannot be
determined is documented behavior for v5, so 'latest-fallback' now
defaults to 'true' and callers opt out with 'false'. The warning
emitted on fallback names the input, so the downgrade is easy to trace
and to turn off.
A non-2xx response from the latest version URL is turned into an error
without reading its body. Cancel the body first, so its connection is
released before the next attempt instead of waiting for garbage
collection.

@bosesuneha Suneha Bose (bosesuneha) left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants