diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index d1c8009e..1e4c39e8 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -66,6 +66,60 @@ jobs: args: release --clean ${{ env.flags }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - + name: Verify archives bundle plugin files (snapshot only) + if: ${{ !startsWith(github.ref, 'refs/tags/v') }} + run: | + set -e + missing=0 + for f in dist/helm-diff-*.tgz; do + echo "== $f ==" + tar tzf "$f" + for member in diff/plugin.yaml diff/install-binary.sh diff/install-binary.ps1; do + if ! tar tzf "$f" | grep -q "^${member}$"; then + echo "ERROR: ${member} missing from ${f}" + missing=1 + fi + done + # the binary has a .exe suffix on windows archives + if ! tar tzf "$f" | grep -qE '^diff/bin/diff(\.exe)?$'; then + echo "ERROR: diff/bin/diff missing from ${f}" + missing=1 + fi + done + if [ "$missing" -ne 0 ]; then + echo "Smoke test failed: required plugin files missing from one or more archives" + exit 1 + fi + echo "Smoke test passed: all archives bundle plugin.yaml, install scripts, and binary" + - + name: Set up Helm + if: ${{ !startsWith(github.ref, 'refs/tags/v') }} + uses: azure/setup-helm@v5 + with: + version: v3.18.6 + - + name: End-to-end archive install test (snapshot only) + if: ${{ !startsWith(github.ref, 'refs/tags/v') }} + run: | + set -e + # Reproduce issue #504: extract a release archive and install from it. + mkdir -p /tmp/archive-test + tar xzf dist/helm-diff-linux-amd64.tgz -C /tmp/archive-test + echo "Extracted archive layout:" + find /tmp/archive-test/diff -maxdepth 2 -type f | sort + + out="$(helm plugin install /tmp/archive-test/diff 2>&1)" + echo "$out" + # The install hook must find the bundled binary already staged in + # HELM_PLUGIN_DIR and skip the network download. + echo "$out" | grep -q "skipping download" || { + echo "ERROR: install hook did not skip the download." + echo "Archive install must not hit the network (binary is already bundled)." + exit 1 + } + helm diff version + echo "End-to-end archive install test passed: installed from archive without downloading" - name: Export and upload public key if: ${{ startsWith(github.ref, 'refs/tags/v') }} diff --git a/.goreleaser.yml b/.goreleaser.yml index c0f53c1e..2c91cfd5 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -52,6 +52,8 @@ archives: - README.md - plugin.yaml - LICENSE + - install-binary.sh + - install-binary.ps1 signs: - id: plugin-provenance diff --git a/README.md b/README.md index a3241623..fb11d451 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,17 @@ helm plugin install https://github.com/databus23/helm-diff 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. +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: + +``` +tar xzf helm-diff-linux-amd64.tgz # extracts into a ./diff directory +helm plugin install ./diff +``` + +The install script detects that the binary is already bundled and skips the GitHub download. + +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. + Set `HELM_DIFF_BIN_TGZ` to the absolute path to the downloaded binary archive: **POSIX shell:** diff --git a/install-binary.ps1 b/install-binary.ps1 index 90e92261..56e33874 100644 --- a/install-binary.ps1 +++ b/install-binary.ps1 @@ -40,7 +40,22 @@ function Get-Url { function Download-Plugin { param ([Parameter(Mandatory=$true)][string] $Url, [Parameter(Mandatory=$true)][string] $Output) - Invoke-WebRequest -OutFile $Output $Url + # Retry with backoff to absorb transient failures, e.g. a release window + # where the "latest" asset is already published but not fully uploaded yet. + $maxAttempts = 5 + for ($attempt = 1; $attempt -le $maxAttempts; $attempt++) { + try { + Invoke-WebRequest -OutFile $Output $Url + return + } catch { + if ($attempt -eq $maxAttempts) { + throw "Failed to download $Url after $maxAttempts attempts: $_" + } + $backoff = $attempt * 3 + Write-Host "Download failed (attempt $attempt/$maxAttempts), retrying in ${backoff}s..." + Start-Sleep -Seconds $backoff + } + } } function Install-Plugin { @@ -56,6 +71,17 @@ $ErrorActionPreference = "Stop" $archiveName = "helm-diff.tgz" $arch = Get-Architecture + +# If installing (not updating) and the binary is already staged in the +# plugin dir (e.g. installing from a release archive that bundles the +# correct platform binary), skip the redundant download. Update mode +# always re-downloads. +$pluginBin = Join-Path $env:HELM_PLUGIN_DIR "bin" "diff.exe" +if (-not $Update -and (Test-Path $pluginBin -PathType Leaf)) { + Write-Host "Binary already present at $pluginBin, skipping download" + exit 0 +} + $tmpDir = New-TemporaryDirectory trap { Remove-Item -path $tmpDir -Recurse -Force } $output = Join-Path $tmpDir $archiveName diff --git a/install-binary.sh b/install-binary.sh index 3cbbc6e4..1e4de20f 100755 --- a/install-binary.sh +++ b/install-binary.sh @@ -123,10 +123,33 @@ downloadFile() { fi echo "Downloading $DOWNLOAD_URL" - if command -v curl >/dev/null 2>&1; then - curl -sSf -L "$DOWNLOAD_URL" >"$PLUGIN_TMP_FILE" - elif command -v wget >/dev/null 2>&1; then - wget -q -O - "$DOWNLOAD_URL" >"$PLUGIN_TMP_FILE" + # Retry with backoff to absorb transient failures, e.g. a release window + # where the "latest" asset is already published but not fully uploaded yet. + dl_attempts=5 + dl_attempt=1 + dl_ok=0 + while [ "$dl_attempt" -le "$dl_attempts" ]; do + if command -v curl >/dev/null 2>&1; then + if curl -sSfL "$DOWNLOAD_URL" >"$PLUGIN_TMP_FILE"; then + dl_ok=1 + break + fi + elif command -v wget >/dev/null 2>&1; then + if wget -q -O "$PLUGIN_TMP_FILE" "$DOWNLOAD_URL"; then + dl_ok=1 + break + fi + else + echo "Either curl or wget is required" + exit 1 + fi + echo "Download failed (attempt $dl_attempt/$dl_attempts), retrying in $((dl_attempt * 3))s..." + sleep "$((dl_attempt * 3))" + dl_attempt=$((dl_attempt + 1)) + done + if [ "$dl_ok" -ne 1 ]; then + echo "Error: failed to download $DOWNLOAD_URL after $dl_attempts attempts" + exit 1 fi } @@ -154,6 +177,17 @@ exit_trap() { exit $result } +# alreadyInstalled returns 0 when the platform binary is already staged in +# the plugin dir. This is the case when installing from a release archive +# (which bundles the correct platform binary), so the redundant download +# can be skipped. Update mode always re-downloads. +alreadyInstalled() { + [ "$SCRIPT_MODE" = "install" ] || return 1 + bin="$HELM_PLUGIN_DIR/bin/diff" + [ "$OS" = "windows" ] && bin="$bin.exe" + [ -x "$bin" ] +} + # --- Execution --- # Stop execution on any error trap "exit_trap" EXIT @@ -162,6 +196,13 @@ set -e initArch initOS verifySupported + +if alreadyInstalled; then + echo "Binary already present at $HELM_PLUGIN_DIR/bin/diff, skipping download" + trap - EXIT + exit 0 +fi + getDownloadURL mkTempDir downloadFile