Skip to content
Merged
72 changes: 72 additions & 0 deletions tools/setup/common/curl-fetch.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#!/usr/bin/env bash
#
# tools/setup/common/curl-fetch.sh — sourceable helper for
# fetching URLs with uniform retry behaviour during install.
#
# WHY
# ===
# Aaron 2026-04-28: *"curl 502 pattern i mean why should a PR
# ever fail for this? our code does not handle the retries
# already?"* — exactly: external-infra failures (upstream
# package mirrors returning 5xx, transient curl-22 / network
# blips) should be absorbed by retry-with-backoff inside the
Comment thread
AceHack marked this conversation as resolved.
Outdated
# install path, not kicked out to a workflow-rerun discipline.
#
# This file centralises the retry policy so every call site
# uses the same flags. Previously the policy was inlined in
# `tools/setup/common/verifiers.sh` and missing entirely from
# `linux.sh` (mise install), `macos.sh` (Homebrew install),
# and `elan.sh` (Lean toolchain install). Aaron 2026-04-28
# follow-up: *"sounds like a common helper would help too
# rather than copy/paste."*
#
# USAGE
# =====
# Source this file, then call `curl_fetch` with the same
# args you'd pass to curl. The function prepends the retry
# flags transparently:
#
# # shellcheck source=/dev/null
# source "$REPO_ROOT/tools/setup/common/curl-fetch.sh"
# curl_fetch https://mise.run | sh
# /bin/bash -c "$(curl_fetch https://example.com/install.sh)"
Comment thread
AceHack marked this conversation as resolved.
Outdated
# curl_fetch --output "$path" "$url"
#
# RETRY POLICY (rationale)
# ========================
# --retry 5 — five attempts total. Empirically
# covers all transient upstream blips
# this install path has hit during
# 2026-04 sessions.
# --retry-delay 2 — 2-second base delay between retries.
# Short enough to not penalise CI when
# the retry succeeds; long enough to
# let upstream recover from a brief
# surge.
# --retry-all-errors — retry on ALL transient errors,
# including HTTP 4xx that curl
# would otherwise pass through.
# (Default `--retry` only retries on
# select transient errors; setup
# installers benefit from the broader
# surface.)
# -fsSL — original flags preserved:
# -f: fail on HTTP errors
# -s: silent (no progress meter)
# -S: show errors when silent
# -L: follow redirects
#
# IDEMPOTENCE
# ===========
# Repeated source of this file overwrites the function body
# (no append, no accumulation). Safe to source from multiple
# scripts within the same install run.
Comment thread
AceHack marked this conversation as resolved.
Outdated

# Guard: only define once per shell to avoid noise on multi-source.
if ! declare -F curl_fetch >/dev/null 2>&1; then
Comment thread
AceHack marked this conversation as resolved.
Outdated

curl_fetch() {
curl -fsSL --retry 5 --retry-delay 2 --retry-all-errors "$@"
Comment thread
AceHack marked this conversation as resolved.
}

fi
5 changes: 4 additions & 1 deletion tools/setup/common/elan.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,12 @@

set -euo pipefail

# shellcheck source=curl-fetch.sh
source "$(dirname "${BASH_SOURCE[0]}")/curl-fetch.sh"

if ! command -v elan >/dev/null 2>&1; then
echo "↓ installing elan (Lean 4 toolchain manager)..."
curl -fsSL https://raw.githubusercontent.com/leanprover/elan/master/elan-init.sh \
curl_fetch https://raw.githubusercontent.com/leanprover/elan/master/elan-init.sh \
| sh -s -- -y --default-toolchain none
Comment thread
AceHack marked this conversation as resolved.
Outdated
fi

Expand Down
18 changes: 10 additions & 8 deletions tools/setup/common/verifiers.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@

set -euo pipefail

# shellcheck source=curl-fetch.sh
source "$(dirname "${BASH_SOURCE[0]}")/curl-fetch.sh"

REPO_ROOT="$(cd "$(dirname "$0")/../../.." && pwd)"
MANIFEST="$REPO_ROOT/tools/setup/manifests/verifiers"

Expand Down Expand Up @@ -47,15 +50,14 @@ grep -vE '^(#|$)' "$MANIFEST" | while IFS= read -r line; do
# ~13:52 UTC, hit PR #481 CodeQL csharp + PR #482 markdownlint
# CI runs). Per Otto-285 (don't use determinism to avoid
# edge-case handling — handle the network-non-determinism
# algorithmically), curl handles the retry: `--retry 5` attempts,
# exponential backoff (2/4/8/16/32 s default), `--retry-all-errors`
# so 4xx/5xx server errors retry too (curl's default only retries
# connect / dns / 408 / 429 / 5xx-with-Retry-After). Keeps
# `-fsSL` semantics — fail at the end if all 5 attempts hit
# the same transient.
# algorithmically), curl_fetch (from common/curl-fetch.sh)
# handles the retry: 5 attempts, 2-4-8-16-32 s exponential
# backoff, --retry-all-errors so 4xx/5xx errors retry too.
# Keeps -fsSL semantics — fail at the end if all 5 attempts
# hit the same transient. (Aaron 2026-04-28: helper extracted
# from copy-pasted call sites; was previously inline here.)
Comment thread
AceHack marked this conversation as resolved.
Outdated
echo "↓ downloading $target from $url"
curl -fsSL --retry 5 --retry-delay 2 --retry-all-errors \
-o "$dest.part" "$url"
curl_fetch -o "$dest.part" "$url"
mv "$dest.part" "$dest"
echo "✓ $target"
fi
Expand Down
5 changes: 4 additions & 1 deletion tools/setup/linux.sh
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ set -euo pipefail
REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
SETUP_DIR="$REPO_ROOT/tools/setup"

# shellcheck source=common/curl-fetch.sh
source "$SETUP_DIR/common/curl-fetch.sh"

# ── Detect apt availability (Debian/Ubuntu) ─────────────────────────
if ! command -v apt-get >/dev/null 2>&1; then
echo "error: this script currently supports Debian/Ubuntu only"
Expand Down Expand Up @@ -55,7 +58,7 @@ echo "✓ apt packages up to date"
# ── 2. mise ─────────────────────────────────────────────────────────
if ! command -v mise >/dev/null 2>&1; then
echo "↓ installing mise via the official installer..."
curl -fsSL https://mise.run | sh
curl_fetch https://mise.run | sh
Comment thread
AceHack marked this conversation as resolved.
Outdated
# The installer puts mise at $HOME/.local/bin/mise; ensure we can
# invoke it for the remainder of this script run.
export PATH="$HOME/.local/bin:$PATH"
Expand Down
5 changes: 4 additions & 1 deletion tools/setup/macos.sh
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ set -euo pipefail
REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
SETUP_DIR="$REPO_ROOT/tools/setup"

# shellcheck source=common/curl-fetch.sh
source "$SETUP_DIR/common/curl-fetch.sh"

# ── 1. Xcode Command Line Tools ─────────────────────────────────────
if ! xcode-select -p >/dev/null 2>&1; then
echo "↓ installing Xcode Command Line Tools (non-interactive)..."
Expand All @@ -37,7 +40,7 @@ echo "✓ Xcode CLT at $(xcode-select -p 2>/dev/null || echo 'pending user confi
# ── 2. Homebrew ─────────────────────────────────────────────────────
if ! command -v brew >/dev/null 2>&1; then
echo "↓ installing Homebrew..."
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
/bin/bash -c "$(curl_fetch https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
Comment thread
AceHack marked this conversation as resolved.
Outdated
# Ensure brew is on PATH for the remainder of this script run.
if [ -x /opt/homebrew/bin/brew ]; then
eval "$(/opt/homebrew/bin/brew shellenv)"
Expand Down
Loading