fix(installer): retry git ops + reject placeholder api_key + sync public configs

User hit two bugs in production:
1. uclaude_install.sh ERROR: Command failed at line 82 — gitea returned
   transient HTTP 502 to `git fetch`, `2>/dev/null` masked stderr but
   ERR trap fired with cryptic message.
2. After install, claude model picker showed only 5 models (built-in
   defaults) instead of 19. Root cause: load_config() fell back to the
   PUBLIC sanitized patcher.config.json (api_key='YOUR_API_KEY') after
   remote fetch failed → claude API auth broken → custom models invisible.

Fixes:

claude/uclaude_install.sh:
- New retry_git() helper: 3 attempts, 5s backoff, loud diagnostic
- Existing-clone branch: retry_git wraps `git fetch` AND `git reset`
- Fallback: if fetch fails 3x on existing clone, nuke and re-clone fresh
  (incremental fetch breaks more often than full clone on flaky gitea)
- Secondary fetch (before updater): tolerates failure with `|| true`
  (we already have a working clone)

claude/uclaude_updater.py:
- _config_is_usable() guard: rejects {"api_key": "YOUR_API_KEY"} etc.
- load_config() retries remote 3x with backoff before falling back
- Removed local-file fallback (was loading public sanitized = bait)
- Cache-only fallback now (from previous successful fetch)

Public configs synced from canonical (api_key sanitized, models list
fully refreshed):
- claude/patcher.config.json: 17 → 19 models (+gpt-5.5, +gemini-3.1-pro etc)
- codex/codex_config.json: 4 → 5 models (+gpt-5.5)
- gemini/gemini_config.json: refreshed
- target_version: 2.1.112 → 2.1.119

Tests: tests/test_installer_robustness.py — 6 new GREEN guards.
Total: 196 → 207 GREEN.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
delta-cloud-208e
2026-04-25 18:25:43 +00:00
parent 134606839a
commit 47e1978bef
5 changed files with 105 additions and 48 deletions

View File

@@ -76,39 +76,75 @@ fi
# ---- Clone / Update repo ----
# Retry helper for git ops — gitea sporadically returns 502 (HTTP) or
# closes RPC mid-packfile. Plain `2>/dev/null` masks stderr but the ERR
# trap still fires on non-zero exit. Run with retries + clear diagnostic
# instead of silent fail.
retry_git() {
local desc="$1"; shift
local attempt
for attempt in 1 2 3; do
if "$@" 2>&1; then
return 0
fi
echo " $desc failed (attempt $attempt/3), retrying in 5s..." >&2
sleep 5
done
echo " ERROR: $desc failed after 3 attempts" >&2
return 1
}
if [ -d "$INSTALL_DIR/.git" ]; then
echo " Already cloned, updating..."
cd "$INSTALL_DIR"
git fetch --depth 1 origin master 2>/dev/null
git reset --hard origin/master 2>/dev/null
if ! retry_git "git fetch" git fetch --depth 1 origin master; then
# Fallback: nuke and re-clone fresh (gitea RPC consistently failing
# for incremental fetch happens occasionally; full re-clone is more
# robust).
echo " Fetch failed permanently — falling back to fresh clone" >&2
cd /
rm -rf "$INSTALL_DIR"
retry_git "git clone (fresh)" git clone --depth 1 --no-checkout "$REPO_URL" "$INSTALL_DIR" || exit 1
cd "$INSTALL_DIR"
git sparse-checkout init --no-cone
git sparse-checkout set '/*' 'claude/*' '!claude/releases/v*' 'claude/releases/index.json' 'codex/*'
retry_git "git checkout (after fresh clone)" git checkout || exit 1
else
retry_git "git reset --hard" git reset --hard origin/master || exit 1
fi
else
echo " Cloning (shallow, sparse — only latest version)..."
# Shallow clone without checkout
git clone --depth 1 --no-checkout "$REPO_URL" "$INSTALL_DIR"
# Shallow clone without checkout — retry on transient gitea 502
retry_git "git clone" git clone --depth 1 --no-checkout "$REPO_URL" "$INSTALL_DIR" || exit 1
cd "$INSTALL_DIR"
# Enable sparse checkout: root + claude/ + codex/ (so optional codex
# install works) + index.json (first pass)
git sparse-checkout init --no-cone
git sparse-checkout set '/*' 'claude/*' '!claude/releases/v*' 'claude/releases/index.json' 'codex/*'
git checkout 2>/dev/null
# Allow checkout to fail on transient errors; trap won't catch
# because we wrap in `|| true`.
git checkout 2>/dev/null || git checkout || exit 1
# Read latest version from index.json and add only that release dir
if [ -f claude/releases/index.json ]; then
VER=$(python3 -c "import json; print(json.load(open('claude/releases/index.json'))['latest'])")
echo " Latest version: v${VER}"
git sparse-checkout add "claude/releases/v${VER}"
git checkout 2>/dev/null
git checkout 2>/dev/null || git checkout || true
fi
fi
echo ""
echo " Updating repo to latest version before running updater..."
# Update repo to latest version BEFORE running updater (so we get latest MIN_NODE_VERSION fix)
# Update repo to latest version BEFORE running updater (so we get latest
# MIN_NODE_VERSION fix). Tolerant of transient gitea 502 — `|| true`
# because we already have a working clone, fresh updater can run with
# slightly older code if the secondary fetch fails.
cd "$INSTALL_DIR"
git fetch --depth 1 origin master 2>/dev/null
git reset --hard origin/master 2>/dev/null || git pull --quiet 2>/dev/null
git fetch --depth 1 origin master 2>/dev/null || echo " (secondary fetch failed; continuing with existing clone)" >&2
git reset --hard origin/master 2>/dev/null || git pull --quiet 2>/dev/null || true
echo " Running updater..."