diff --git a/scripts/install.ps1 b/scripts/install.ps1 index 3a8c803..1f43285 100644 --- a/scripts/install.ps1 +++ b/scripts/install.ps1 @@ -35,8 +35,13 @@ $arch = switch ($procArch) { # --- resolve version ------------------------------------------------------ -$version = $env:EMAILABLE_VERSION -if (-not $version) { +# Resolve the latest version, validating against semver so a prerelease-only +# repo (whose /releases/latest has no /tag/) fails here instead of 404ing on a +# bogus download URL. Prereleases aren't auto-selected; set $env:EMAILABLE_VERSION. +$SemVer = '^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$' + +function Get-LatestVersion { + # Prefer the redirect over the API to dodge unauthenticated rate limits. try { $resp = Invoke-WebRequest -UseBasicParsing -MaximumRedirection 0 ` -Uri "https://github.com/$Repo/releases/latest" -ErrorAction SilentlyContinue @@ -45,8 +50,29 @@ if (-not $version) { } $location = $null if ($resp -and $resp.Headers) { $location = $resp.Headers['Location'] } - if (-not $location) { Abort "could not determine latest version" } - $version = ($location -split '/tag/')[-1] + if ($location) { + $v = (($location -split '/')[-1]).TrimStart('v') + if ($v -match $SemVer) { return $v } + } + + # Fall back to the API if redirect parsing fails. + try { + $json = Invoke-RestMethod -UseBasicParsing ` + -Headers @{ 'Accept' = 'application/vnd.github+json'; 'User-Agent' = 'emailable-cli-installer' } ` + -Uri "https://api.github.com/repos/$Repo/releases/latest" + $v = ([string]$json.tag_name).TrimStart('v') + if ($v -match $SemVer) { return $v } + } catch {} + + return $null +} + +$version = $env:EMAILABLE_VERSION +if (-not $version) { + $version = Get-LatestVersion + if (-not $version) { + Abort "could not determine the latest version; set `$env:EMAILABLE_VERSION to install a specific release" + } } $version = $version.TrimStart('v') $tag = "v$version" diff --git a/scripts/install.sh b/scripts/install.sh index 10a944c..82bf13f 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -58,13 +58,42 @@ esac # --- resolve version -------------------------------------------------------- +# Resolve the latest version, validating against semver so a prerelease-only +# repo (whose /releases/latest has no /tag/) fails here instead of 404ing on a +# bogus download URL. Prereleases aren't auto-selected; set EMAILABLE_VERSION. +latest_version() { + local url version json + + # Prefer the redirect over the API to dodge unauthenticated rate limits. + if url=$(curl -fsSLI -o /dev/null -w '%{url_effective}' \ + "https://github.com/${REPO}/releases/latest" 2>/dev/null); then + version="${url##*/}" + version="${version#v}" + if [[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$ ]]; then + printf '%s\n' "$version" + return 0 + fi + fi + + if json=$(curl -fsSL -H 'Accept: application/vnd.github+json' \ + -H 'User-Agent: emailable-cli-installer' \ + "https://api.github.com/repos/${REPO}/releases/latest" 2>/dev/null); then + if [[ "$json" =~ \"tag_name\"[[:space:]]*:[[:space:]]*\"v?([^\"]+)\" ]]; then + version="${BASH_REMATCH[1]}" + if [[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$ ]]; then + printf '%s\n' "$version" + return 0 + fi + fi + fi + + return 1 +} + version="${EMAILABLE_VERSION:-}" if [ -z "$version" ]; then - # Use the redirect target of /releases/latest rather than the API to avoid - # unauthenticated rate limits. - version=$(curl -fsSLI -o /dev/null -w '%{url_effective}' \ - "https://github.com/${REPO}/releases/latest" | sed 's#.*/tag/##') - [ -n "$version" ] || abort "could not determine latest version" + version=$(latest_version) || \ + abort "could not determine the latest version; set EMAILABLE_VERSION to install a specific release" fi version="${version#v}" tag="v${version}"