From 411e8daa4de3c0523dde374aec45fa68b60eb103 Mon Sep 17 00:00:00 2001 From: rcourtman Date: Fri, 1 May 2026 17:23:38 +0100 Subject: [PATCH] Port installer bundle fallback fix from v5 --- ...ga-installer-bundle-fallback-2026-05-01.md | 43 ++++++++++ docs/release-control/v6/internal/status.json | 11 +++ install.sh | 54 +++---------- scripts/installtests/install_sh_test.go | 81 +++++++++++++++++++ 4 files changed, 145 insertions(+), 44 deletions(-) create mode 100644 docs/release-control/v6/internal/records/known-rc-issue-closure-for-ga-installer-bundle-fallback-2026-05-01.md diff --git a/docs/release-control/v6/internal/records/known-rc-issue-closure-for-ga-installer-bundle-fallback-2026-05-01.md b/docs/release-control/v6/internal/records/known-rc-issue-closure-for-ga-installer-bundle-fallback-2026-05-01.md new file mode 100644 index 000000000..a166e1d46 --- /dev/null +++ b/docs/release-control/v6/internal/records/known-rc-issue-closure-for-ga-installer-bundle-fallback-2026-05-01.md @@ -0,0 +1,43 @@ +# Known RC Issue Closure For GA Installer Bundle Fallback Record + +- Date: `2026-05-01` +- Gate: `known-rc-issue-closure-for-ga` +- Lane: `L1` +- Result: `passed` + +## Context + +The v5 maintenance audit found that `release/5.1` had removed an eager +universal-bundle fallback from the root installer. The old fallback attempted +to download `pulse-${version}.tar.gz` during install when every cross-arch +agent binary was not already present locally. + +The v6 root installer still carried the same fallback for unified-agent +binaries. That made a normal install perform an unnecessary second network +lookup for a non-arch bundle and could produce a confusing warning even after +the actual platform archive had installed successfully. + +## Disposition + +The v6 installer now matches the maintenance-line behavior: + +- bundled unified-agent binaries are still copied from the extracted release + archive when present; +- the installer no longer tries to download a universal cross-architecture + agent bundle during the main install path; +- missing unified-agent binaries are left for the on-demand agent install path + instead of making the server installer depend on a fallback release asset. + +This is the canonical root fix for RC3 because the install-time dependency was +owned by the release installer, not by downstream agent setup commands. + +## Proof + +- `go test ./scripts/installtests -run 'TestInstallAdditionalAgentBinaries|TestResolveInstallScriptDownloadURL|TestInstallSHRequiresPinnedSignatureVerificationForReleaseDownloads' -count=1` +- `bash -n install.sh` +- `git diff --check` + +## Outcome + +The v5 installer bundle-fallback fix is ported to the current v6 candidate. The +`known-rc-issue-closure-for-ga` gate remains satisfied for this slice. diff --git a/docs/release-control/v6/internal/status.json b/docs/release-control/v6/internal/status.json index 431b55f19..e643e5fc7 100644 --- a/docs/release-control/v6/internal/status.json +++ b/docs/release-control/v6/internal/status.json @@ -2476,6 +2476,11 @@ "path": "docs/release-control/v6/internal/HIGH_RISK_RELEASE_VERIFICATION_MATRIX.md", "kind": "file" }, + { + "repo": "pulse", + "path": "docs/release-control/v6/internal/records/known-rc-issue-closure-for-ga-installer-bundle-fallback-2026-05-01.md", + "kind": "file" + }, { "repo": "pulse", "path": "docs/release-control/v6/internal/subsystems/deployment-installability.md", @@ -4388,6 +4393,12 @@ "kind": "file", "evidence_tier": "managed-runtime-exercise" }, + { + "repo": "pulse", + "path": "docs/release-control/v6/internal/records/known-rc-issue-closure-for-ga-installer-bundle-fallback-2026-05-01.md", + "kind": "file", + "evidence_tier": "managed-runtime-exercise" + }, { "repo": "pulse", "path": "docs/release-control/v6/internal/records/known-rc-issue-closure-for-ga-late-issue-intake-2026-05-01.md", diff --git a/install.sh b/install.sh index 091d81443..a458e166c 100755 --- a/install.sh +++ b/install.sh @@ -2960,6 +2960,7 @@ copy_unified_agent_binaries_from_dir() { install_additional_agent_binaries() { local version="$1" local source_dir="${2:-}" + local local_unified_installed=0 if [[ -z "$version" ]]; then return @@ -2968,7 +2969,9 @@ install_additional_agent_binaries() { local unified_targets=("linux-amd64" "linux-arm64" "linux-armv7" "linux-armv6" "linux-386" "darwin-amd64" "darwin-arm64" "windows-amd64" "windows-arm64" "windows-386") # Prefer locally available agents from the extracted archive to avoid network reliance - copy_unified_agent_binaries_from_dir "$source_dir" || true + if copy_unified_agent_binaries_from_dir "$source_dir"; then + local_unified_installed=1 + fi local unified_missing_targets=() for target in "${unified_targets[@]}"; do @@ -2984,55 +2987,18 @@ install_additional_agent_binaries() { done if [[ ${#unified_missing_targets[@]} -eq 0 ]]; then - return - fi - - local universal_url="https://github.com/$GITHUB_REPO/releases/download/$version/pulse-${version}.tar.gz" - local universal_tar="/tmp/pulse-universal-${version}.tar.gz" - - print_info "Downloading universal agent bundle for cross-architecture support..." - - if command -v curl >/dev/null 2>&1; then - if ! curl -fsSL --connect-timeout 10 --max-time 300 -o "$universal_tar" "$universal_url"; then - print_warn "Failed to download universal agent bundle" - rm -f "$universal_tar" - return + if [[ $local_unified_installed -eq 1 ]]; then + print_success "Unified agent binaries installed" fi - elif command -v wget >/dev/null 2>&1; then - if ! wget -q --timeout=300 -O "$universal_tar" "$universal_url"; then - print_warn "Failed to download universal agent bundle" - rm -f "$universal_tar" - return - fi - else - print_warn "Cannot download universal agent bundle (curl or wget not available)" return fi - local temp_dir - temp_dir=$(mktemp -d -t pulse-universal-XXXXXX) - if ! tar -xzf "$universal_tar" -C "$temp_dir"; then - print_warn "Failed to extract universal agent bundle" - rm -f "$universal_tar" - rm -rf "$temp_dir" - return - fi - - # Install unified agent binaries (preserve symlinks for Windows targets) - local unified_installed=0 - if copy_unified_agent_binaries_from_dir "$temp_dir"; then - unified_installed=1 - fi - - if [[ $unified_installed -eq 1 ]]; then + if [[ $local_unified_installed -eq 1 ]]; then print_success "Unified agent binaries installed" fi - if [[ $unified_installed -eq 0 ]]; then - print_warn "No agent binaries found in universal bundle" - fi - - rm -f "$universal_tar" - rm -rf "$temp_dir" + print_info "Skipping eager cross-architecture agent bundle download during install" + print_info "Missing unified agent binaries will be fetched on demand when requested" + return 0 } deploy_agent_scripts() { diff --git a/scripts/installtests/install_sh_test.go b/scripts/installtests/install_sh_test.go index 65d4b7089..34db74820 100644 --- a/scripts/installtests/install_sh_test.go +++ b/scripts/installtests/install_sh_test.go @@ -1332,6 +1332,87 @@ exit 1 } } +func TestInstallAdditionalAgentBinariesCopiesLocalExtrasWithoutNetwork(t *testing.T) { + tmpDir := t.TempDir() + sourceDir := filepath.Join(tmpDir, "source") + installDir := filepath.Join(tmpDir, "opt", "pulse") + if err := os.MkdirAll(filepath.Join(sourceDir, "bin"), 0755); err != nil { + t.Fatalf("mkdir source bin: %v", err) + } + if err := os.MkdirAll(filepath.Join(installDir, "bin"), 0755); err != nil { + t.Fatalf("mkdir install bin: %v", err) + } + agentPath := filepath.Join(sourceDir, "bin", "pulse-agent-linux-arm64") + if err := os.WriteFile(agentPath, []byte("unified-agent\n"), 0755); err != nil { + t.Fatalf("write agent: %v", err) + } + + script := ` + INSTALL_DIR="` + installDir + `" + GITHUB_REPO="rcourtman/Pulse" + curl_calls=0 + wget_calls=0 + chown() { :; } + print_info() { :; } + print_warn() { :; } + print_success() { :; } + curl() { curl_calls=$((curl_calls + 1)); return 99; } + wget() { wget_calls=$((wget_calls + 1)); return 99; } +` + extractRootInstallShellFunction(t, "copy_unified_agent_binaries_from_dir") + ` +` + extractRootInstallShellFunction(t, "install_additional_agent_binaries") + ` + install_additional_agent_binaries "v6.0.0-rc.3" "` + sourceDir + `" + printf 'curl=%s wget=%s\n' "$curl_calls" "$wget_calls" + ` + + out, err := exec.Command("bash", "-c", script).CombinedOutput() + if err != nil { + t.Fatalf("bash: %v\n%s", err, out) + } + if got := strings.TrimSpace(string(out)); got != "curl=0 wget=0" { + t.Fatalf("expected no network fallback calls, got %q", got) + } + if _, err := os.Stat(filepath.Join(installDir, "bin", "pulse-agent-linux-arm64")); err != nil { + t.Fatalf("expected local unified agent binary to be copied: %v", err) + } +} + +func TestInstallAdditionalAgentBinariesSkipsNetworkWhenLocalExtrasAreMissing(t *testing.T) { + tmpDir := t.TempDir() + sourceDir := filepath.Join(tmpDir, "source") + installDir := filepath.Join(tmpDir, "opt", "pulse") + if err := os.MkdirAll(filepath.Join(sourceDir, "bin"), 0755); err != nil { + t.Fatalf("mkdir source bin: %v", err) + } + if err := os.MkdirAll(filepath.Join(installDir, "bin"), 0755); err != nil { + t.Fatalf("mkdir install bin: %v", err) + } + + script := ` + INSTALL_DIR="` + installDir + `" + GITHUB_REPO="rcourtman/Pulse" + curl_calls=0 + wget_calls=0 + chown() { :; } + print_info() { :; } + print_warn() { :; } + print_success() { :; } + curl() { curl_calls=$((curl_calls + 1)); return 99; } + wget() { wget_calls=$((wget_calls + 1)); return 99; } +` + extractRootInstallShellFunction(t, "copy_unified_agent_binaries_from_dir") + ` +` + extractRootInstallShellFunction(t, "install_additional_agent_binaries") + ` + install_additional_agent_binaries "v6.0.0-rc.3" "` + sourceDir + `" + printf 'curl=%s wget=%s\n' "$curl_calls" "$wget_calls" + ` + + out, err := exec.Command("bash", "-c", script).CombinedOutput() + if err != nil { + t.Fatalf("bash: %v\n%s", err, out) + } + if got := strings.TrimSpace(string(out)); got != "curl=0 wget=0" { + t.Fatalf("expected missing local extras to skip network fallback, got %q", got) + } +} + func TestInstallSHRequiresPinnedSignatureVerificationForReleaseDownloads(t *testing.T) { content, err := os.ReadFile(repoFile("scripts", "install.sh")) if err != nil {