Skip to content

Commit 9a03e44

Browse files
authored
fix(setup): retry-with-backoff git clones, force HTTPS for setup deps (#84)
Two failure modes hit during a fresh end-to-end install: 1. `gh repo clone` constructs the underlying `git clone` URL based on the user's gh `Git operations protocol` setting. Users with `ssh` set (often by older interactive logins) and no SSH key registered hit a cryptic `Permission denied (publickey)` on every plugin clone. 2. GitHub HTTP/2 returns transient 500s during setup runs ("error: RPC failed; HTTP 500"). A single attempt with no retry leaves the user to delete partial clones and re-run. The same URL succeeds seconds later, and HTTP/1.1 dodges the issue entirely. Add `git_clone_with_retry` (lib/common.sh) that: - Pins `http.version=HTTP/1.1` on the per-invocation git config. - Sets `url.https://github.com/.insteadOf=git@github.com:` so SSH-style URLs (whether explicit or constructed by gh) get rewritten transparently. - Retries with exponential backoff (3 attempts: 2s, 4s, 8s), cleaning up the partial directory between attempts. - Forwards extra args (used for `--depth 1` from the skills installer). Wire it into: - `install_plugin` (lib/wordpress.sh) — drops the gh-vs-git branching; plain `git_clone_with_retry` covers public github.com, GitHub Enterprise, and any HTTPS git host. (gh remains used elsewhere for issue/PR work where its auth helper actually matters.) - `install_skills_from_repo` (lib/skills.sh) — same path, same resilience for the WordPress + Data Machine skills bundles cloned during Phase 8.5. Closes #83
1 parent 9c7ff4b commit 9a03e44

3 files changed

Lines changed: 45 additions & 11 deletions

File tree

lib/common.sh

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,44 @@ write_file() {
2929
echo "$content" > "$file_path"
3030
fi
3131
}
32+
33+
# Robust git clone for setup-time plugin/skill deps:
34+
# - Pins HTTP/1.1 to dodge intermittent GitHub HTTP/2 500s seen during
35+
# fresh setup runs.
36+
# - Rewrites SSH-style URLs (git@github.com:…) to HTTPS so users with
37+
# `gh auth status` reporting `Git operations protocol: ssh` but no SSH
38+
# key registered don't hit cryptic `Permission denied (publickey)`.
39+
# - Retries with exponential backoff (default 3 attempts: 2s, 4s, 8s)
40+
# and cleans up partial directories between attempts.
41+
#
42+
# Usage: git_clone_with_retry <url> <dir> [extra git-clone args…]
43+
git_clone_with_retry() {
44+
local url="$1"
45+
local dir="$2"
46+
shift 2 || true
47+
local max_attempts=3
48+
local delay=2
49+
local attempt
50+
51+
if [ "$DRY_RUN" = true ]; then
52+
echo -e "${BLUE}[dry-run]${NC} git clone $url $dir $* (with HTTPS rewrite + HTTP/1.1 + retry)"
53+
return 0
54+
fi
55+
56+
for attempt in $(seq 1 "$max_attempts"); do
57+
if git \
58+
-c http.version=HTTP/1.1 \
59+
-c "url.https://github.com/.insteadOf=git@github.com:" \
60+
clone "$@" "$url" "$dir"; then
61+
return 0
62+
fi
63+
if [ "$attempt" -lt "$max_attempts" ]; then
64+
warn "Clone of $url failed (attempt $attempt/$max_attempts); retrying in ${delay}s..."
65+
rm -rf "$dir"
66+
sleep "$delay"
67+
delay=$((delay * 2))
68+
fi
69+
done
70+
warn "Clone of $url failed after $max_attempts attempts."
71+
return 1
72+
}

lib/skills.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ install_skills_from_repo() {
1414

1515
local tmp_dir
1616
tmp_dir=$(mktemp -d)
17-
if git clone --depth 1 "$repo_url" "$tmp_dir" 2>/dev/null; then
17+
rmdir "$tmp_dir" 2>/dev/null || true # git_clone_with_retry needs a non-existent target on retry.
18+
if git_clone_with_retry "$repo_url" "$tmp_dir" --depth 1; then
1819
for skill_dir in "$tmp_dir"/*/; do
1920
local skill_name
2021
skill_name=$(basename "$skill_dir")

lib/wordpress.sh

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -31,16 +31,8 @@ install_plugin() {
3131
local plugin_dir="$SITE_PATH/wp-content/plugins/$slug"
3232

3333
if [ ! -d "$plugin_dir" ] || [ "$DRY_RUN" = true ]; then
34-
# Prefer gh CLI for GitHub repos (avoids proxy/auth issues).
35-
# Strip .git suffix first, then extract owner/repo — macOS sed doesn't
36-
# handle the optional \(\.git\)\{0,1\} correctly (greedy [^/]* eats it).
37-
local gh_nwo
38-
gh_nwo=$(echo "$repo_url" | sed 's|\.git$||' | sed -n 's|^https://github\.com/\([^/]*/[^/]*\)$|\1|p')
39-
if [ -n "$gh_nwo" ] && command -v gh &>/dev/null; then
40-
run_cmd gh repo clone "$gh_nwo" "$plugin_dir"
41-
else
42-
run_cmd git clone "$repo_url" "$plugin_dir"
43-
fi
34+
git_clone_with_retry "$repo_url" "$plugin_dir" || \
35+
warn "Clone failed for $slug — install will continue without it"
4436
elif [ -d "$plugin_dir/.git" ]; then
4537
log "Plugin $slug already exists — pulling latest..."
4638
run_cmd git -C "$plugin_dir" pull --ff-only 2>/dev/null || \

0 commit comments

Comments
 (0)