Skip to content

Commit e0efe9d

Browse files
geekypunkclaude
andcommitted
fix(agent): retry upstream clones so a GitHub 429 stops failing the image build
`docker compose build` failed on 2 of the last 15 CI runs, always in the deepsql-agent image, always the same way: Cloning into 'runtime'... remote: This request was rate-limited due to too many requests. fatal: unable to access 'https://github.com/NousResearch/hermes-agent.git/': The requested URL returned error: 429 exit code: 128 Both clones in agent/Dockerfile are unauthenticated requests to github.com, issued once with no retry. GitHub rate-limits unauthenticated traffic per source IP, and Actions runners share pooled egress addresses, so under load the clone is refused and the whole image build dies with it. Nothing about the pull request under test is involved, which is why the failure looked random and landed on unrelated branches. Both clones now retry up to five times with escalating backoff (15/30/45/60s). The condition is genuinely transient, so retry is the correct layer rather than a mask: on the final attempt the loop still prints FATAL and exits 1, so an upstream that is actually gone fails the build loudly instead of producing an image with no runtime in it. This is not CI-only defensiveness. `scripts/self-host/install.sh` runs the same build, so a self-host user behind a shared NAT hits the identical refusal. Note the webui clone's existing `||` fallback only ever handled a *moved ref*; against a rate-limit it re-issued the same refused request, so it needed the outer retry to be of any use. Verified: both RUN blocks pass `sh -n` as the shell receives them; the wrapper clones hermes-agent for real (exit 0), and against an unreachable repo it retries 5x and exits 1. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent 1fbaaf1 commit e0efe9d

1 file changed

Lines changed: 30 additions & 4 deletions

File tree

agent/Dockerfile

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,24 @@ ARG AGENT_RUNTIME_REF=main
4444
ARG AGENT_API_REPO=https://github.com/nesquena/hermes-webui.git
4545
ARG AGENT_API_REF=master
4646

47-
# Runtime engine (Python agent)
48-
RUN git clone --depth 1 --branch "${AGENT_RUNTIME_REF}" "${AGENT_RUNTIME_REPO}" runtime \
47+
# Runtime engine (Python agent).
48+
#
49+
# The clone retries with backoff. github.com rate-limits *unauthenticated*
50+
# requests per source IP, and CI runners share pooled egress addresses, so a
51+
# cold clone here intermittently returns HTTP 429 and took the whole image
52+
# build down with it (`fatal: unable to access ...: error: 429`, exit 128 --
53+
# 2 of 15 `docker compose build` runs). This is not CI-only defensiveness: a
54+
# self-host user behind a shared NAT hits the identical refusal. Retry is the
55+
# right layer precisely because the condition is transient; the loop still
56+
# exits non-zero and loudly on the last attempt rather than continuing with no
57+
# runtime cloned.
58+
RUN n=0; rm -rf runtime; \
59+
until git clone --depth 1 --branch "${AGENT_RUNTIME_REF}" "${AGENT_RUNTIME_REPO}" runtime; do \
60+
n=$((n+1)); \
61+
if [ "$n" -ge 5 ]; then echo "FATAL: clone of ${AGENT_RUNTIME_REPO} failed after 5 attempts" >&2; exit 1; fi; \
62+
echo "clone attempt $n failed (HTTP 429 / transient network); retrying in $((n*15))s" >&2; \
63+
rm -rf runtime; sleep $((n*15)); \
64+
done \
4965
&& cd runtime \
5066
&& UV_NO_CONFIG=1 uv sync \
5167
&& if [ -d .venv ] && [ ! -d venv ]; then ln -sfn .venv venv; fi \
@@ -56,8 +72,18 @@ RUN git clone --depth 1 --branch "${AGENT_RUNTIME_REF}" "${AGENT_RUNTIME_REPO}"
5672

5773
# HTTP API surface the frontend / backend talk to (:8787).
5874
# Default branch is master (not main). Fall back to HEAD if the ref moves.
59-
RUN (git clone --depth 1 --branch "${AGENT_API_REF}" "${AGENT_API_REPO}" api \
60-
|| git clone --depth 1 "${AGENT_API_REPO}" api) \
75+
#
76+
# Same 429 retry as the runtime clone above. Note the inner `||` fallback
77+
# handles only a *moved ref*; on a rate-limit it just re-issues the same
78+
# refused request, so it needed the outer retry to be any use at all.
79+
RUN n=0; rm -rf api; \
80+
until (git clone --depth 1 --branch "${AGENT_API_REF}" "${AGENT_API_REPO}" api \
81+
|| git clone --depth 1 "${AGENT_API_REPO}" api); do \
82+
n=$((n+1)); \
83+
if [ "$n" -ge 5 ]; then echo "FATAL: clone of ${AGENT_API_REPO} failed after 5 attempts" >&2; exit 1; fi; \
84+
echo "clone attempt $n failed (HTTP 429 / transient network); retrying in $((n*15))s" >&2; \
85+
rm -rf api; sleep $((n*15)); \
86+
done \
6187
&& cd api \
6288
&& if [ -f requirements.txt ]; then \
6389
UV_NO_CONFIG=1 uv pip install --python /opt/deepsql-agent/runtime/venv/bin/python -r requirements.txt; \

0 commit comments

Comments
 (0)