feat: install Helm v4 plugins from .tgz release assets#648
Merged
yxxhero merged 13 commits intohelmfile:mainfrom Feb 18, 2026
Merged
feat: install Helm v4 plugins from .tgz release assets#648yxxhero merged 13 commits intohelmfile:mainfrom
yxxhero merged 13 commits intohelmfile:mainfrom
Conversation
Helm v4 requires plugins to be distributed as .tgz packages to register as subcommands. Installing from a repo URL registers them as "legacy" plugins that don't expose subcommands (e.g., "helm secrets" won't work). When Helm v4+ is detected, the action now queries the plugin's GitHub release for .tgz assets with companion .prov (provenance) files and installs those natively. Plugins without v4 packages (e.g., helm-diff) fall back to the existing --verify=false legacy install. Fixes helmfile#604
…ication Helm v4 verifies .tgz plugin signatures against .prov provenance files by default. The runner doesn't have plugin authors' GPG keys, causing "plugin verification failed: open ~/.gnupg/pubring.gpg: no such file" errors. Before installing .tgz assets, the action now fetches the plugin author's GPG public key from https://github.com/<owner>.gpg and imports it via `gpg --import`. If the key import fails, a warning is logged but the install still proceeds.
Helm v4's plugin verification looks for pubring.gpg (GnuPG v1 format), but modern gpg stores imported keys in pubring.kbx (v2 format). After importing the plugin author's GPG key, export the keyring to the legacy format so Helm v4 can find it for signature verification.
There was a problem hiding this comment.
Pull request overview
This PR updates the action’s Helm plugin installation logic to support Helm v4’s new plugin distribution/verification model by preferring signed .tgz release assets (with .prov files) and importing GPG keys needed for verification, while retaining a legacy fallback path.
Changes:
- Add Helm v4 detection and GitHub Releases asset resolution for
.tgz+.provplugin packages. - Import a plugin owner’s GPG public key and export a legacy
pubring.gpgkeyring for Helm v4 verification. - Extend Jest coverage for the new Helm v4 install paths and fallbacks.
Reviewed changes
Copilot reviewed 2 out of 4 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
src/helm.ts |
Adds Helm v4 plugin asset discovery + GPG key import/export and updates plugin install strategy. |
dist/index.js |
Updates the bundled action output to include the new logic. |
__tests__/helm.test.ts |
Adds/updates tests to cover Helm v4 asset install behavior and fallbacks. |
Member
|
@saipavan9 please take a look. |
The `~` shorthand doesn't expand on Windows, causing the gpg --export command to fail with "No such file or directory". Use os.homedir() and path.join() for cross-platform path resolution. Also quote the output path to handle spaces in Windows paths.
- Pass GPG arguments as arrays to exec() instead of string interpolation to handle paths with spaces correctly on Windows - Use os.homedir() and path.join() for cross-platform path resolution instead of relying on ~ expansion - Try both vX.Y.Z and X.Y.Z tag formats when querying GitHub releases API, since users may omit the v prefix but repos tag with it
Contributor
Author
|
@yxxhero Fixed all the issues |
When GPG verification fails for a .tgz plugin install (e.g., missing key, wrong signer, gpg not available), retry with --verify=false instead of hard-failing the action. The .tgz still registers as a proper v4 plugin even without verification.
When a user provides a direct .tgz URL (e.g., a specific release download), install it directly instead of querying the GitHub releases API, which could resolve different assets than what was requested.
- Refactor all exec() calls to pass command and arguments separately instead of string interpolation, preventing potential issues with special characters in URLs or paths - Validate HTTP status code from GPG key endpoint before attempting import, avoiding feeding HTML error pages to gpg --import - Remove unused variables from tests
- Replace == with === for exit code comparisons in legacy install path - Pass 'helm plugin list' as argument array instead of command string
Extract GITHUB_REPO_REGEX constant to unify the GitHub URL parsing pattern used in both resolveHelmV4PluginAssets and installHelmPlugins. Add core.debug() message when GITHUB_TOKEN is not set to help users understand potential rate limiting on GitHub API requests.
Replace GITHUB_REPO_REGEX with parseGitHubRepo() that uses new URL() to properly validate hostname and extract clean owner/repo from pathname segments. Only retry with --verify=false when stderr indicates an actual verification failure (verification/pubring/openpgp keywords), not for unrelated errors like 404 or corrupt archives.
Reset lastError after a successful API response so that an expected 404 on an alternate tag format (e.g. v3.15.0 vs 3.15.0) does not produce a spurious warning when the other candidate succeeds.
Reject owners and repos with invalid characters (e.g. user@domain.com) to prevent pointless API calls and confusing warnings for malformed URLs that happen to have github.com as their hostname.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
APIVERSION: legacyand don't expose subcommands (e.g.,helm secretsreturns "unknown command").tgzassets with companion.provprovenance files and installs those nativelyhttps://github.com/<owner>.gpgbefore install so Helm v4's signature verification succeedspubring.gpgformat since Helm v4 looks for GnuPG v1 keyring, not the modernpubring.kbx--verify=falselegacy install for plugins without v4 packages (e.g., helm-diff)Problem
Helm v4 overhauled its plugin system. Installing plugins via the repo URL (
helm plugin install https://github.com/jkroepke/helm-secrets) registers them aslegacyplugins:Legacy plugins don't register as subcommands, so
helm secrets decryptfails withError: unknown command "secrets".Helm v4 plugins must be installed from
.tgzrelease archives (per helm-secrets installation docs):Additionally, Helm v4 verifies
.tgzsignatures against.provprovenance files by default. Without the plugin author's GPG public key in the runner's keyring, install fails with:This happens because modern
gpg(v2.x) imports keys intopubring.kbx(new keybox format), but Helm v4's verification code looks forpubring.gpg(old GnuPG v1 format).Solution
The action now automatically handles Helm v4 plugin installation:
helm version --short.tgzassets with companion.provfiles (distinguishes v4 plugin packages from platform-specific binaries)https://github.com/<owner>.gpgfor signature verificationpubring.gpgformat — Helm v4 reads the old GnuPG v1 keyring, but modern gpg stores keys inpubring.kbx;gpg --export --output pubring.gpgcreates the legacy file.tgzpackage natively — plugin registers as a proper v4 subcommand--verify=falselegacy install if no.tgzassets are found (e.g., helm-diff) or if the API call failsNo workflow changes required — existing configs work as-is:
Test plan
npm test)npm run all(build + format + lint + package) succeedshelm secretssubcommand worksFixes #604
🤖 Generated with Claude Code