@@ -52,8 +52,24 @@ ARG AGENT_RUNTIME_REF=v2026.8.18
5252ARG AGENT_API_REPO=https://github.com/nesquena/hermes-webui.git
5353ARG AGENT_API_REF=v0.52.76
5454
55- # Runtime engine (Python agent)
56- RUN git clone --depth 1 --branch "${AGENT_RUNTIME_REF}" "${AGENT_RUNTIME_REPO}" runtime \
55+ # Runtime engine (Python agent).
56+ #
57+ # The clone retries with backoff. github.com rate-limits *unauthenticated*
58+ # requests per source IP, and CI runners share pooled egress addresses, so a
59+ # cold clone here intermittently returns HTTP 429 and took the whole image
60+ # build down with it (`fatal: unable to access ...: error: 429`, exit 128 --
61+ # 2 of 15 `docker compose build` runs). This is not CI-only defensiveness: a
62+ # self-host user behind a shared NAT hits the identical refusal. Retry is the
63+ # right layer precisely because the condition is transient; the loop still
64+ # exits non-zero and loudly on the last attempt rather than continuing with no
65+ # runtime cloned.
66+ RUN n=0; rm -rf runtime; \
67+ until git clone --depth 1 --branch "${AGENT_RUNTIME_REF}" "${AGENT_RUNTIME_REPO}" runtime; do \
68+ n=$((n+1)); \
69+ if [ "$n" -ge 5 ]; then echo "FATAL: clone of ${AGENT_RUNTIME_REPO} failed after 5 attempts" >&2; exit 1; fi; \
70+ echo "clone attempt $n failed (HTTP 429 / transient network); retrying in $((n*15))s" >&2; \
71+ rm -rf runtime; sleep $((n*15)); \
72+ done \
5773 && cd runtime \
5874 && UV_NO_CONFIG=1 uv sync \
5975 && if [ -d .venv ] && [ ! -d venv ]; then ln -sfn .venv venv; fi \
@@ -64,8 +80,18 @@ RUN git clone --depth 1 --branch "${AGENT_RUNTIME_REF}" "${AGENT_RUNTIME_REPO}"
6480
6581# HTTP API surface the frontend / backend talk to (:8787).
6682# Default branch is master (not main). Fall back to HEAD if the ref moves.
67- RUN (git clone --depth 1 --branch "${AGENT_API_REF}" "${AGENT_API_REPO}" api \
68- || git clone --depth 1 "${AGENT_API_REPO}" api) \
83+ #
84+ # Same 429 retry as the runtime clone above. Note the inner `||` fallback
85+ # handles only a *moved ref*; on a rate-limit it just re-issues the same
86+ # refused request, so it needed the outer retry to be any use at all.
87+ RUN n=0; rm -rf api; \
88+ until (git clone --depth 1 --branch "${AGENT_API_REF}" "${AGENT_API_REPO}" api \
89+ || git clone --depth 1 "${AGENT_API_REPO}" api); do \
90+ n=$((n+1)); \
91+ if [ "$n" -ge 5 ]; then echo "FATAL: clone of ${AGENT_API_REPO} failed after 5 attempts" >&2; exit 1; fi; \
92+ echo "clone attempt $n failed (HTTP 429 / transient network); retrying in $((n*15))s" >&2; \
93+ rm -rf api; sleep $((n*15)); \
94+ done \
6995 && cd api \
7096 && if [ -f requirements.txt ]; then \
7197 UV_NO_CONFIG=1 uv pip install --python /opt/deepsql-agent/runtime/venv/bin/python -r requirements.txt; \
0 commit comments