diff --git a/scripts/install_coyote.ps1 b/scripts/install_coyote.ps1 index 3995532..b222bd6 100644 --- a/scripts/install_coyote.ps1 +++ b/scripts/install_coyote.ps1 @@ -67,7 +67,13 @@ if ($os -eq 'windows') { try { getconf GNU_LIBC_VERSION *> $null; if ($LASTEXITCODE -eq 0) { $libc = 'gnu' } } catch { } try { if ((ldd --version 2>&1 | Out-String) -imatch 'glibc') { $libc = 'gnu' } } catch { } if ($libc -eq 'gnu') { - $candidates += 'coyote-x86_64-unknown-linux-gnu.tar.gz' + $libssl3 = $false + try { if ((ldconfig -p 2>&1 | Out-String) -match 'libssl\.so\.3') { $libssl3 = $true } } catch { } + if ($libssl3) { + $candidates += 'coyote-x86_64-unknown-linux-gnu.tar.gz' + } else { + Write-Info "glibc detected but OpenSSL 3 (libssl.so.3) not found; using musl build" + } } $candidates += 'coyote-x86_64-unknown-linux-musl.tar.gz' } else { @@ -77,54 +83,92 @@ if ($os -eq 'windows') { Fail "Unsupported OS for this installer: $os" } -$asset = $null +$tmp = New-Item -ItemType Directory -Force -Path ([IO.Path]::Combine([IO.Path]::GetTempPath(), "coyote-$(Get-Random)")) + +$exec = if ($isWin) { 'coyote.exe' } else { 'coyote' } +$dest = Join-Path $BinDir $exec + +$installed = $false +$tried = @() +$attempt = 0 foreach ($c in $candidates) { $asset = $release.assets | Where-Object { $_.name -eq $c } | Select-Object -First 1 - if ($asset) { break } + if (-not $asset) { + $tried += "${c}: no matching release asset" + continue + } + + $attempt++ + $work = New-Item -ItemType Directory -Force -Path (Join-Path $tmp.FullName "attempt-$attempt") + + Write-Info "Selected asset: $($asset.name)" + Write-Info "Download URL: $($asset.browser_download_url)" + + $archive = Join-Path $work.FullName 'asset' + try { + Invoke-WebRequest -UseBasicParsing -Headers @{ 'User-Agent' = 'coyote-installer' } -Uri $asset.browser_download_url -OutFile $archive + } catch { + Write-Info "Failed to download ${c}; trying next candidate. $_" + $tried += "${c}: download failed" + continue + } + + $extractDir = Join-Path $work.FullName 'extract'; New-Item -ItemType Directory -Force -Path $extractDir | Out-Null + + try { + if ($asset.name -match '\.zip$') { + Add-Type -AssemblyName System.IO.Compression.FileSystem + [System.IO.Compression.ZipFile]::ExtractToDirectory($archive, $extractDir) + } elseif ($asset.name -match '\.tar\.gz$' -or $asset.name -match '\.tgz$') { + $tar = Get-Command tar -ErrorAction SilentlyContinue + if ($tar) { & $tar.Source -xzf $archive -C $extractDir } + else { throw "Asset is tar archive but 'tar' is not available." } + } else { + try { Add-Type -AssemblyName System.IO.Compression.FileSystem; [System.IO.Compression.ZipFile]::ExtractToDirectory($archive, $extractDir) } + catch { + $tar = Get-Command tar -ErrorAction SilentlyContinue + if ($tar) { & $tar.Source -xf $archive -C $extractDir } else { throw "Unknown archive format; neither zip nor tar workable." } + } + } + } catch { + Write-Info "Failed to extract ${c}; trying next candidate. $_" + $tried += "${c}: extract failed" + continue + } + + $bin = $null + Get-ChildItem -Recurse -File $extractDir | ForEach-Object { + if ($isWin) { if ($_.Name -ieq 'coyote.exe') { $bin = $_.FullName } } + else { if ($_.Name -ieq 'coyote') { $bin = $_.FullName } } + } + if (-not $bin) { + Write-Info "Could not find coyote binary inside ${c}; trying next candidate" + $tried += "${c}: no coyote binary in archive" + continue + } + + if (-not $isWin) { try { & chmod +x -- $bin } catch {} } + + $works = $false + try { & $bin --version *> $null; if ($LASTEXITCODE -eq 0) { $works = $true } } catch { } + if (-not $works) { + Write-Info "Downloaded $c but it failed to run on this system; trying next candidate" + $tried += "${c}: binary failed to run on this system" + continue + } + + Copy-Item -Force $bin $dest + Write-Info "Installed: $dest" + $installed = $true + break } -if (-not $asset) { - Write-Error "No matching asset found for $os-$arch. Tried:"; $candidates | ForEach-Object { Write-Error " - $_" } + +if (-not $installed) { + Write-Error "No usable asset found for $os-$arch. Tried:" + $tried | ForEach-Object { Write-Error " - $_" } exit 1 } -Write-Info "Selected asset: $($asset.name)" -Write-Info "Download URL: $($asset.browser_download_url)" - -$tmp = New-Item -ItemType Directory -Force -Path ([IO.Path]::Combine([IO.Path]::GetTempPath(), "coyote-$(Get-Random)")) -$archive = Join-Path $tmp.FullName 'asset' -try { Invoke-WebRequest -UseBasicParsing -Headers @{ 'User-Agent' = 'coyote-installer' } -Uri $asset.browser_download_url -OutFile $archive } catch { Fail "Failed to download asset. $_" } - -$extractDir = Join-Path $tmp.FullName 'extract'; New-Item -ItemType Directory -Force -Path $extractDir | Out-Null - -if ($asset.name -match '\.zip$') { - Add-Type -AssemblyName System.IO.Compression.FileSystem - [System.IO.Compression.ZipFile]::ExtractToDirectory($archive, $extractDir) -} elseif ($asset.name -match '\.tar\.gz$' -or $asset.name -match '\.tgz$') { - $tar = Get-Command tar -ErrorAction SilentlyContinue - if ($tar) { & $tar.Source -xzf $archive -C $extractDir } - else { Fail "Asset is tar archive but 'tar' is not available." } -} else { - try { Add-Type -AssemblyName System.IO.Compression.FileSystem; [System.IO.Compression.ZipFile]::ExtractToDirectory($archive, $extractDir) } - catch { - $tar = Get-Command tar -ErrorAction SilentlyContinue - if ($tar) { & $tar.Source -xf $archive -C $extractDir } else { Fail "Unknown archive format; neither zip nor tar workable." } - } -} - -$bin = $null -Get-ChildItem -Recurse -File $extractDir | ForEach-Object { - if ($isWin) { if ($_.Name -ieq 'coyote.exe') { $bin = $_.FullName } } - else { if ($_.Name -ieq 'coyote') { $bin = $_.FullName } } -} -if (-not $bin) { Fail "Could not find coyote binary inside the archive." } - -if (-not $isWin) { try { & chmod +x -- $bin } catch {} } - -$exec = if ($isWin) { 'coyote.exe'} else { 'coyote' } -$dest = Join-Path $BinDir $exec -Copy-Item -Force $bin $dest -Write-Info "Installed: $dest" - if ($isWin) { $pathParts = ($env:Path -split ';') | Where-Object { $_ -ne '' } if ($pathParts -notcontains $BinDir) { diff --git a/scripts/install_coyote.sh b/scripts/install_coyote.sh index 7d72a8c..18ace5f 100755 --- a/scripts/install_coyote.sh +++ b/scripts/install_coyote.sh @@ -122,7 +122,11 @@ elif [[ "$OS" == "linux" ]]; then if ldd --version 2>&1 | grep -qi glibc; then LIBC="gnu"; fi if [[ "$LIBC" == "gnu" ]]; then - ASSET_CANDIDATES+=("coyote-x86_64-unknown-linux-gnu.tar.gz") + if ldconfig -p 2>/dev/null | grep -q 'libssl\.so\.3'; then + ASSET_CANDIDATES+=("coyote-x86_64-unknown-linux-gnu.tar.gz") + else + log "glibc detected but OpenSSL 3 (libssl.so.3) not found; using musl build" + fi fi ASSET_CANDIDATES+=("coyote-x86_64-unknown-linux-musl.tar.gz") @@ -137,66 +141,101 @@ DL_URLS=$(grep -oE '"browser_download_url":[[:space:]]*"[^"]+"' "$JSON" \ | sed -E 's/.*"browser_download_url":[[:space:]]*"//; s/"$//' \ || true) -ASSET_NAME=""; ASSET_URL="" +INSTALLED="" +TRIED=() +ATTEMPT=0 for candidate in "${ASSET_CANDIDATES[@]}"; do + ASSET_URL="" while IFS= read -r url; do [[ -z "$url" ]] && continue if [[ "$url" == */"$candidate" ]]; then - ASSET_NAME="$candidate" ASSET_URL="$url" break fi done <<< "$DL_URLS" - [[ -n "$ASSET_URL" ]] && break + + if [[ -z "$ASSET_URL" ]]; then + TRIED+=("$candidate: no matching release asset") + continue + fi + + ATTEMPT=$((ATTEMPT + 1)) + WORK="$TMPDIR/attempt-$ATTEMPT" + mkdir -p "$WORK" + + log "Selected asset: $candidate" + log "Download URL: $ASSET_URL" + + ARCHIVE="$WORK/asset" + if [[ "$DL" == "curl" ]]; then + if ! curl -fL -H 'User-Agent: coyote-installer' "$ASSET_URL" -o "$ARCHIVE"; then + log "Failed to download $candidate; trying next candidate" + TRIED+=("$candidate: download failed") + continue + fi + else + if ! wget -q --header='User-Agent: coyote-installer' "$ASSET_URL" -O "$ARCHIVE"; then + log "Failed to download $candidate; trying next candidate" + TRIED+=("$candidate: download failed") + continue + fi + fi + + EXTRACTED_DIR="$WORK/extracted"; mkdir -p "$EXTRACTED_DIR" + + if tar -tf "$ARCHIVE" >/dev/null 2>&1; then + if ! tar -xzf "$ARCHIVE" -C "$EXTRACTED_DIR"; then + log "Failed to extract $candidate; trying next candidate" + TRIED+=("$candidate: extract failed") + continue + fi + else + if command -v unzip >/dev/null 2>&1; then + if ! unzip -q "$ARCHIVE" -d "$EXTRACTED_DIR"; then + log "Failed to extract $candidate; trying next candidate" + TRIED+=("$candidate: extract failed") + continue + fi + else + log "Unknown archive format for $candidate and 'unzip' is not available; trying next candidate" + TRIED+=("$candidate: unknown archive format and 'unzip' unavailable") + continue + fi + fi + + BIN_PATH="" + while IFS= read -r -d '' f; do + base=$(basename "$f") + if [[ "$base" == "coyote" ]]; then + BIN_PATH="$f" + break + fi + done < <(find "$EXTRACTED_DIR" -type f -print0) + + if [[ -z "$BIN_PATH" ]]; then + log "Could not find 'coyote' binary in $candidate; trying next candidate" + TRIED+=("$candidate: no 'coyote' binary in archive") + continue + fi + + chmod +x "$BIN_PATH" + if ! "$BIN_PATH" --version >/dev/null 2>&1; then + log "Downloaded $candidate but it failed to run on this system; trying next candidate" + TRIED+=("$candidate: binary failed to run on this system") + continue + fi + + install -m 0755 "$BIN_PATH" "${BIN_DIR}/coyote" + INSTALLED="$candidate" + break done -if [[ -z "$ASSET_URL" ]]; then - echo "Error: no matching asset found for ${OS}-${ARCH}. Tried:" >&2 - for c in "${ASSET_CANDIDATES[@]}"; do echo " - $c" >&2; done +if [[ -z "$INSTALLED" ]]; then + echo "Error: no usable asset found for ${OS}-${ARCH}. Tried:" >&2 + for t in "${TRIED[@]}"; do echo " - $t" >&2; done exit 1 fi -log "Selected asset: $ASSET_NAME" -log "Download URL: $ASSET_URL" - -ARCHIVE="$TMPDIR/asset" -if [[ "$DL" == "curl" ]]; then - curl -fL -H 'User-Agent: coyote-installer' "$ASSET_URL" -o "$ARCHIVE" -else - wget -q --header='User-Agent: coyote-installer' "$ASSET_URL" -O "$ARCHIVE" -fi - -WORK="$TMPDIR/work"; mkdir -p "$WORK" -EXTRACTED_DIR="$WORK/extracted"; mkdir -p "$EXTRACTED_DIR" - -if tar -tf "$ARCHIVE" >/dev/null 2>&1; then - tar -xzf "$ARCHIVE" -C "$EXTRACTED_DIR" -else - if command -v unzip >/dev/null 2>&1; then - unzip -q "$ARCHIVE" -d "$EXTRACTED_DIR" - else - echo "Error: unknown archive format; install 'unzip'" >&2 - exit 1 - fi -fi - -BIN_PATH="" -while IFS= read -r -d '' f; do - base=$(basename "$f") - if [[ "$base" == "coyote" ]]; then - BIN_PATH="$f" - break - fi -done < <(find "$EXTRACTED_DIR" -type f -print0) - -if [[ -z "$BIN_PATH" ]]; then - echo "Error: could not find 'coyote' binary in the archive" >&2 - exit 1 -fi - -chmod +x "$BIN_PATH" -install -m 0755 "$BIN_PATH" "${BIN_DIR}/coyote" - log "Installed: ${BIN_DIR}/coyote" case ":$PATH:" in diff --git a/src/config/update.rs b/src/config/update.rs index ae7c4c3..9e074a7 100644 --- a/src/config/update.rs +++ b/src/config/update.rs @@ -68,30 +68,9 @@ fn normalize_version(requested: Option) -> Option { } } -fn host_has_glibc() -> bool { - if process::Command::new("getconf") - .arg("GNU_LIBC_VERSION") - .output() - .is_ok_and(|out| out.status.success()) - { - return true; - } - process::Command::new("ldd") - .arg("--version") - .output() - .is_ok_and(|out| { - let combined = format!( - "{}{}", - String::from_utf8_lossy(&out.stdout), - String::from_utf8_lossy(&out.stderr) - ); - combined.to_lowercase().contains("glibc") - }) -} - fn preferred_update_target() -> Option<&'static str> { match (env::consts::OS, env::consts::ARCH) { - ("linux", "x86_64") if host_has_glibc() => Some("x86_64-unknown-linux-gnu"), + ("linux", "x86_64") if cfg!(target_env = "gnu") => Some("x86_64-unknown-linux-gnu"), ("linux", "x86_64") => Some("x86_64-unknown-linux-musl"), // No aarch64 gnu asset is published; musl is the only option. ("linux", "aarch64") => Some("aarch64-unknown-linux-musl"),