Skip to content

Commit 086bb60

Browse files
authored
fix: bundle install scripts in release archives (#504) (#1011)
* fix: bundle install scripts in release archives (#504) Release .tgz archives built by goreleaser only shipped plugin.yaml, the binary, README and LICENSE, but plugin.yaml defines install/update hooks that call install-binary.sh / install-binary.ps1. Extracting a release .tgz and running 'helm plugin install ./diff' therefore failed with 'install-binary.sh: not found'. - .goreleaser.yml: bundle install-binary.sh and install-binary.ps1 in every archive. - install-binary.sh / install-binary.ps1: skip the (now redundant) binary download when the platform binary is already staged in HELM_PLUGIN_DIR during install. Update mode always re-downloads. - release workflow: snapshot-only smoke test asserting every archive bundles plugin.yaml, both install scripts, and the binary. - README: note that extracting a release .tgz and running helm plugin install ./diff now works directly. Fixes #504 Signed-off-by: yxxhero <aiopsclub@163.com> * fix(ci): accept .exe suffix in archive smoke test Windows archives bundle diff/bin/diff.exe (not diff/bin/diff). The core fix is confirmed working: all archives (incl. Windows) now bundle the install scripts and binary. Signed-off-by: yxxhero <aiopsclub@163.com> * fix(install): retry binary download with backoff The git-clone install path resolves to releases/latest/download/<asset>, which can 404 during a release window when the new release is published but its assets are not fully uploaded yet. curl -sSf under 'set -e' failed immediately with no retry. Add a 5-attempt retry loop with linear backoff (3s,6s,9s,12s) around the curl/wget download in install-binary.sh, and equivalent try/catch retry in install-binary.ps1. This absorbs the release upload window and general transient network errors. The archive-install path is unaffected (it skips the download entirely via the existing guard). Signed-off-by: yxxhero <aiopsclub@163.com> * ci: add end-to-end archive install test for #504 Reproduces the issue #504 scenario in CI: extracts a snapshot linux archive, runs 'helm plugin install ./diff', asserts the install hook skips the network download (bundled binary already staged) and that 'helm diff version' runs. Prevents regressions in the archive-install path. Signed-off-by: yxxhero <aiopsclub@163.com> --------- Signed-off-by: yxxhero <aiopsclub@163.com>
1 parent bcb790d commit 086bb60

5 files changed

Lines changed: 139 additions & 5 deletions

File tree

.github/workflows/release.yaml

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,60 @@ jobs:
6666
args: release --clean ${{ env.flags }}
6767
env:
6868
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
69+
-
70+
name: Verify archives bundle plugin files (snapshot only)
71+
if: ${{ !startsWith(github.ref, 'refs/tags/v') }}
72+
run: |
73+
set -e
74+
missing=0
75+
for f in dist/helm-diff-*.tgz; do
76+
echo "== $f =="
77+
tar tzf "$f"
78+
for member in diff/plugin.yaml diff/install-binary.sh diff/install-binary.ps1; do
79+
if ! tar tzf "$f" | grep -q "^${member}$"; then
80+
echo "ERROR: ${member} missing from ${f}"
81+
missing=1
82+
fi
83+
done
84+
# the binary has a .exe suffix on windows archives
85+
if ! tar tzf "$f" | grep -qE '^diff/bin/diff(\.exe)?$'; then
86+
echo "ERROR: diff/bin/diff missing from ${f}"
87+
missing=1
88+
fi
89+
done
90+
if [ "$missing" -ne 0 ]; then
91+
echo "Smoke test failed: required plugin files missing from one or more archives"
92+
exit 1
93+
fi
94+
echo "Smoke test passed: all archives bundle plugin.yaml, install scripts, and binary"
95+
-
96+
name: Set up Helm
97+
if: ${{ !startsWith(github.ref, 'refs/tags/v') }}
98+
uses: azure/setup-helm@v5
99+
with:
100+
version: v3.18.6
101+
-
102+
name: End-to-end archive install test (snapshot only)
103+
if: ${{ !startsWith(github.ref, 'refs/tags/v') }}
104+
run: |
105+
set -e
106+
# Reproduce issue #504: extract a release archive and install from it.
107+
mkdir -p /tmp/archive-test
108+
tar xzf dist/helm-diff-linux-amd64.tgz -C /tmp/archive-test
109+
echo "Extracted archive layout:"
110+
find /tmp/archive-test/diff -maxdepth 2 -type f | sort
111+
112+
out="$(helm plugin install /tmp/archive-test/diff 2>&1)"
113+
echo "$out"
114+
# The install hook must find the bundled binary already staged in
115+
# HELM_PLUGIN_DIR and skip the network download.
116+
echo "$out" | grep -q "skipping download" || {
117+
echo "ERROR: install hook did not skip the download."
118+
echo "Archive install must not hit the network (binary is already bundled)."
119+
exit 1
120+
}
121+
helm diff version
122+
echo "End-to-end archive install test passed: installed from archive without downloading"
69123
-
70124
name: Export and upload public key
71125
if: ${{ startsWith(github.ref, 'refs/tags/v') }}

.goreleaser.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ archives:
5252
- README.md
5353
- plugin.yaml
5454
- LICENSE
55+
- install-binary.sh
56+
- install-binary.ps1
5557

5658
signs:
5759
- id: plugin-provenance

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,17 @@ helm plugin install https://github.com/databus23/helm-diff
2525

2626
If installing this in an offline/airgapped environment, download the platform-specific binary archive (e.g., `helm-diff-linux-amd64.tgz` or `helm-diff-windows-amd64.tgz`) from [releases](https://github.com/databus23/helm-diff/releases). Make sure to select the correct `.tgz` file for your operating system and architecture.
2727

28+
The release archives include everything needed to install the plugin (binary, `plugin.yaml`, and the install scripts). The simplest way to install offline is to extract the archive and point `helm plugin install` at the extracted directory:
29+
30+
```
31+
tar xzf helm-diff-linux-amd64.tgz # extracts into a ./diff directory
32+
helm plugin install ./diff
33+
```
34+
35+
The install script detects that the binary is already bundled and skips the GitHub download.
36+
37+
Alternatively, if you keep a separate local checkout of the plugin source, you can point the installer at a downloaded `.tgz` via the `HELM_DIFF_BIN_TGZ` environment variable.
38+
2839
Set `HELM_DIFF_BIN_TGZ` to the absolute path to the downloaded binary archive:
2940

3041
**POSIX shell:**

install-binary.ps1

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,22 @@ function Get-Url {
4040

4141
function Download-Plugin {
4242
param ([Parameter(Mandatory=$true)][string] $Url, [Parameter(Mandatory=$true)][string] $Output)
43-
Invoke-WebRequest -OutFile $Output $Url
43+
# Retry with backoff to absorb transient failures, e.g. a release window
44+
# where the "latest" asset is already published but not fully uploaded yet.
45+
$maxAttempts = 5
46+
for ($attempt = 1; $attempt -le $maxAttempts; $attempt++) {
47+
try {
48+
Invoke-WebRequest -OutFile $Output $Url
49+
return
50+
} catch {
51+
if ($attempt -eq $maxAttempts) {
52+
throw "Failed to download $Url after $maxAttempts attempts: $_"
53+
}
54+
$backoff = $attempt * 3
55+
Write-Host "Download failed (attempt $attempt/$maxAttempts), retrying in ${backoff}s..."
56+
Start-Sleep -Seconds $backoff
57+
}
58+
}
4459
}
4560

4661
function Install-Plugin {
@@ -56,6 +71,17 @@ $ErrorActionPreference = "Stop"
5671

5772
$archiveName = "helm-diff.tgz"
5873
$arch = Get-Architecture
74+
75+
# If installing (not updating) and the binary is already staged in the
76+
# plugin dir (e.g. installing from a release archive that bundles the
77+
# correct platform binary), skip the redundant download. Update mode
78+
# always re-downloads.
79+
$pluginBin = Join-Path $env:HELM_PLUGIN_DIR "bin" "diff.exe"
80+
if (-not $Update -and (Test-Path $pluginBin -PathType Leaf)) {
81+
Write-Host "Binary already present at $pluginBin, skipping download"
82+
exit 0
83+
}
84+
5985
$tmpDir = New-TemporaryDirectory
6086
trap { Remove-Item -path $tmpDir -Recurse -Force }
6187
$output = Join-Path $tmpDir $archiveName

install-binary.sh

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -123,10 +123,33 @@ downloadFile() {
123123
fi
124124

125125
echo "Downloading $DOWNLOAD_URL"
126-
if command -v curl >/dev/null 2>&1; then
127-
curl -sSf -L "$DOWNLOAD_URL" >"$PLUGIN_TMP_FILE"
128-
elif command -v wget >/dev/null 2>&1; then
129-
wget -q -O - "$DOWNLOAD_URL" >"$PLUGIN_TMP_FILE"
126+
# Retry with backoff to absorb transient failures, e.g. a release window
127+
# where the "latest" asset is already published but not fully uploaded yet.
128+
dl_attempts=5
129+
dl_attempt=1
130+
dl_ok=0
131+
while [ "$dl_attempt" -le "$dl_attempts" ]; do
132+
if command -v curl >/dev/null 2>&1; then
133+
if curl -sSfL "$DOWNLOAD_URL" >"$PLUGIN_TMP_FILE"; then
134+
dl_ok=1
135+
break
136+
fi
137+
elif command -v wget >/dev/null 2>&1; then
138+
if wget -q -O "$PLUGIN_TMP_FILE" "$DOWNLOAD_URL"; then
139+
dl_ok=1
140+
break
141+
fi
142+
else
143+
echo "Either curl or wget is required"
144+
exit 1
145+
fi
146+
echo "Download failed (attempt $dl_attempt/$dl_attempts), retrying in $((dl_attempt * 3))s..."
147+
sleep "$((dl_attempt * 3))"
148+
dl_attempt=$((dl_attempt + 1))
149+
done
150+
if [ "$dl_ok" -ne 1 ]; then
151+
echo "Error: failed to download $DOWNLOAD_URL after $dl_attempts attempts"
152+
exit 1
130153
fi
131154
}
132155

@@ -154,6 +177,17 @@ exit_trap() {
154177
exit $result
155178
}
156179

180+
# alreadyInstalled returns 0 when the platform binary is already staged in
181+
# the plugin dir. This is the case when installing from a release archive
182+
# (which bundles the correct platform binary), so the redundant download
183+
# can be skipped. Update mode always re-downloads.
184+
alreadyInstalled() {
185+
[ "$SCRIPT_MODE" = "install" ] || return 1
186+
bin="$HELM_PLUGIN_DIR/bin/diff"
187+
[ "$OS" = "windows" ] && bin="$bin.exe"
188+
[ -x "$bin" ]
189+
}
190+
157191
# --- Execution ---
158192
# Stop execution on any error
159193
trap "exit_trap" EXIT
@@ -162,6 +196,13 @@ set -e
162196
initArch
163197
initOS
164198
verifySupported
199+
200+
if alreadyInstalled; then
201+
echo "Binary already present at $HELM_PLUGIN_DIR/bin/diff, skipping download"
202+
trap - EXIT
203+
exit 0
204+
fi
205+
165206
getDownloadURL
166207
mkTempDir
167208
downloadFile

0 commit comments

Comments
 (0)