From e646a329af3e9dcf3d0efc3d45fe6d2026fcb0d8 Mon Sep 17 00:00:00 2001 From: ulises-jeremias Date: Sat, 18 Jul 2026 13:14:42 -0300 Subject: [PATCH 1/2] fix(ci): wait for PyPI simple index before Docker build JSON API readiness is not enough for pip inside the image build; also verify simple-index listing and wheel URL reachability, and retry pip install in the Dockerfile when CDN edges lag (#217). Co-authored-by: Cursor --- .github/workflows/publish-docker.yml | 28 ++++++++++++++++++++++++---- Dockerfile | 15 ++++++++++++--- 2 files changed, 36 insertions(+), 7 deletions(-) diff --git a/.github/workflows/publish-docker.yml b/.github/workflows/publish-docker.yml index a47af66..df5aa62 100644 --- a/.github/workflows/publish-docker.yml +++ b/.github/workflows/publish-docker.yml @@ -54,24 +54,44 @@ jobs: echo "minor=$MINOR" } >> "$GITHUB_OUTPUT" + # JSON API can return 200 before the simple index / CDN edges that pip + # hits inside the Docker build. Require JSON + simple-index listing + + # a reachable wheel URL before building (see #217). - name: Wait for PyPI package env: VERSION: ${{ steps.version.outputs.version }} PACKAGE: create-awesome-python-app run: | set -euo pipefail - attempts=24 + attempts=36 delay=15 + normalized="${PACKAGE//-/_}" for attempt in $(seq 1 "$attempts"); do + json_ok=0 + simple_ok=0 + wheel_ok=0 if curl -sfL "https://pypi.org/pypi/${PACKAGE}/${VERSION}/json" \ | jq -e '.urls[] | select(.packagetype=="sdist") | .digests.sha256' >/dev/null; then - echo "PyPI has ${PACKAGE}==${VERSION}" + json_ok=1 + fi + if curl -sfL "https://pypi.org/simple/${PACKAGE}/" \ + | grep -Fq "${normalized}-${VERSION}"; then + simple_ok=1 + fi + wheel_url="$(curl -sfL "https://pypi.org/pypi/${PACKAGE}/${VERSION}/json" \ + | jq -r '.urls[] | select(.packagetype=="bdist_wheel") | .url' \ + | head -n1 || true)" + if [ -n "${wheel_url}" ] && curl -sfI "$wheel_url" >/dev/null; then + wheel_ok=1 + fi + if [ "$json_ok" -eq 1 ] && [ "$simple_ok" -eq 1 ] && [ "$wheel_ok" -eq 1 ]; then + echo "PyPI JSON + simple index + wheel URL have ${PACKAGE}==${VERSION}" exit 0 fi - echo "::warning::PyPI not ready for ${PACKAGE}==${VERSION} (attempt ${attempt}/${attempts}); retrying in ${delay}s" + echo "::warning::PyPI not ready for ${PACKAGE}==${VERSION} (json=${json_ok} simple=${simple_ok} wheel=${wheel_ok}; attempt ${attempt}/${attempts}); retrying in ${delay}s" sleep "$delay" done - echo "::error::Timed out waiting for ${PACKAGE}==${VERSION} on PyPI" + echo "::error::Timed out waiting for ${PACKAGE}==${VERSION} on PyPI (JSON + simple index + wheel)" exit 1 - name: Set up QEMU diff --git a/Dockerfile b/Dockerfile index f815227..5e3e111 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,13 +6,22 @@ FROM python:3.12-slim-bookworm # reproducible for its tag). ARG VERSION=latest +# Retry pip install: PyPI simple-index / CDN edges can lag the JSON API +# that CI already observed as ready (see #217). # hadolint ignore=DL3013 RUN useradd --create-home --uid 1000 --shell /bin/bash app \ && if [ "$VERSION" = "latest" ]; then \ - pip install --no-cache-dir create-awesome-python-app; \ + pkg="create-awesome-python-app"; \ else \ - pip install --no-cache-dir "create-awesome-python-app==${VERSION}"; \ - fi + pkg="create-awesome-python-app==${VERSION}"; \ + fi \ + && attempt=1 \ + && until pip install --no-cache-dir "$pkg"; do \ + if [ "$attempt" -ge 12 ]; then exit 1; fi; \ + echo "pip install failed (attempt ${attempt}/12); retrying in 15s"; \ + attempt=$((attempt + 1)); \ + sleep 15; \ + done USER app WORKDIR /home/app From 1ce1b37531674705f4d3fc315975a0756cfb7fe4 Mon Sep 17 00:00:00 2001 From: ulises-jeremias Date: Sat, 18 Jul 2026 13:17:02 -0300 Subject: [PATCH 2/2] fix(ci): wrap long yamllint line in Docker PyPI wait Co-authored-by: Cursor --- .github/workflows/publish-docker.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/publish-docker.yml b/.github/workflows/publish-docker.yml index df5aa62..d374587 100644 --- a/.github/workflows/publish-docker.yml +++ b/.github/workflows/publish-docker.yml @@ -85,13 +85,14 @@ jobs: wheel_ok=1 fi if [ "$json_ok" -eq 1 ] && [ "$simple_ok" -eq 1 ] && [ "$wheel_ok" -eq 1 ]; then - echo "PyPI JSON + simple index + wheel URL have ${PACKAGE}==${VERSION}" + echo "PyPI ready for ${PACKAGE}==${VERSION} (json+simple+wheel)" exit 0 fi - echo "::warning::PyPI not ready for ${PACKAGE}==${VERSION} (json=${json_ok} simple=${simple_ok} wheel=${wheel_ok}; attempt ${attempt}/${attempts}); retrying in ${delay}s" + echo "::warning::PyPI not ready for ${PACKAGE}==${VERSION}" + echo "::warning::json=${json_ok} simple=${simple_ok} wheel=${wheel_ok} attempt=${attempt}/${attempts}; sleep ${delay}s" sleep "$delay" done - echo "::error::Timed out waiting for ${PACKAGE}==${VERSION} on PyPI (JSON + simple index + wheel)" + echo "::error::Timed out waiting for ${PACKAGE}==${VERSION} on PyPI" exit 1 - name: Set up QEMU