diff --git a/install.sh b/install.sh index e249cb7..bbe6490 100755 --- a/install.sh +++ b/install.sh @@ -204,6 +204,24 @@ release_download_url() { fi } +release_latest_tag_download_url() { + if [ -n "$binary_url" ] || [ -n "$binary_base" ] || [ "$binary_version" != "latest" ] || [ -z "$repo" ]; then + return 1 + fi + web_base="$(repo_web_base "$repo")" || return 1 + latest_url="$(curl -fsSL -o /dev/null -w '%{url_effective}' "$web_base/releases/latest" 2>/dev/null || true)" + case "$latest_url" in + "$web_base"/releases/tag/*) + tag="${latest_url##"$web_base"/releases/tag/}" + [ -n "$tag" ] || return 1 + printf '%s/releases/download/%s/%s\n' "$web_base" "$tag" "$(release_artifact_name)" + ;; + *) + return 1 + ;; + esac +} + find_extracted_binary() { find "$1" -type f -name "$2" 2>/dev/null | sed -n '1p' } @@ -256,7 +274,12 @@ try_install_prebuilt() { need tar if ! curl -fL "$download_url" -o "$archive"; then echo "prebuilt unavailable: $download_url" >&2 - return 1 + alt_download_url="$(release_latest_tag_download_url || true)" + if [ -z "$alt_download_url" ] || ! curl -fL "$alt_download_url" -o "$archive"; then + [ -z "$alt_download_url" ] || echo "prebuilt unavailable: $alt_download_url" >&2 + return 1 + fi + download_url="$alt_download_url" fi verify_archive_checksum "$download_url" "$archive" "$checksum_file" || return 1 mkdir -p "$tmpdir/extract" diff --git a/scripts/upload-gitea-release.sh b/scripts/upload-gitea-release.sh index c530209..5212ddf 100755 --- a/scripts/upload-gitea-release.sh +++ b/scripts/upload-gitea-release.sh @@ -66,11 +66,29 @@ if [ -z "$release_id" ]; then exit 1 fi +delete_existing_asset() { + name="$1" + if ! command -v jq >/dev/null 2>&1; then + return 0 + fi + printf '%s\n' "$release_json" \ + | jq -r --arg name "$name" '.assets[]? | select(.name == $name) | .id' \ + | while IFS= read -r asset_id; do + [ -n "$asset_id" ] || continue + echo "replacing $name" + curl -fsS \ + -H "$auth_header" \ + -X DELETE \ + "$api/releases/$release_id/assets/$asset_id" >/dev/null + done +} + for artifact in "$@"; do if [ ! -f "$artifact" ]; then continue fi name="$(basename "$artifact")" + delete_existing_asset "$name" echo "uploading $name" curl -fsS \ -H "$auth_header" \ diff --git a/src/bin/dosh-client.rs b/src/bin/dosh-client.rs index 65c1c3a..30dd9f1 100644 --- a/src/bin/dosh-client.rs +++ b/src/bin/dosh-client.rs @@ -1110,6 +1110,38 @@ fn latest_release_download_url(repo: &str, artifact: &str) -> Option { Some(format!("{web}/releases/latest/download/{artifact}")) } +fn latest_release_tag_download_url(repo: &str, artifact: &str) -> Result> { + let web = repo + .strip_suffix(".git") + .unwrap_or(repo) + .trim_end_matches('/'); + if !web.starts_with("http://") && !web.starts_with("https://") { + return Ok(None); + } + let output = Command::new("curl") + .arg("-fsSL") + .arg("-o") + .arg("/dev/null") + .arg("-w") + .arg("%{url_effective}") + .arg(format!("{web}/releases/latest")) + .stdin(Stdio::null()) + .output() + .with_context(|| format!("resolve latest release for {web}"))?; + if !output.status.success() { + return Ok(None); + } + let effective = String::from_utf8_lossy(&output.stdout); + let prefix = format!("{web}/releases/tag/"); + let Some(tag) = effective.strip_prefix(&prefix) else { + return Ok(None); + }; + if tag.is_empty() { + return Ok(None); + } + Ok(Some(format!("{web}/releases/download/{tag}/{artifact}"))) +} + fn url_reachable(url: &str) -> Result { let status = Command::new("curl") .arg("-fsIL") @@ -1130,19 +1162,23 @@ fn run_update(config: &dosh::config::ClientConfig, check_only: bool) -> Result<( let raw_base = raw_base_from_repo(&repo); let installer = format!("{raw_base}/raw/branch/main/install.sh"); let artifact = release_artifact_name(); - let binary_url = latest_release_download_url(&repo, artifact); if check_only { println!("dosh {}", env!("CARGO_PKG_VERSION")); println!("repo: {repo}"); println!("installer: {installer}"); - if let Some(binary_url) = &binary_url { - let status = if url_reachable(binary_url)? { - "available" - } else { - "missing" - }; + if let Some(latest_url) = latest_release_download_url(&repo, artifact) { + let mut status = "missing"; + let mut display_url = latest_url.clone(); + if url_reachable(&latest_url)? { + status = "available"; + } else if let Some(tag_url) = latest_release_tag_download_url(&repo, artifact)? + && url_reachable(&tag_url)? + { + status = "available"; + display_url = tag_url; + } println!("prebuilt: {status} ({artifact})"); - println!("prebuilt_url: {binary_url}"); + println!("prebuilt_url: {display_url}"); } else { println!("prebuilt: unavailable for non-HTTP repo"); }