From 0b18da55755ea5ab4970c39915e08b63af45c9b9 Mon Sep 17 00:00:00 2001 From: lelia <2418071+lelia@users.noreply.github.com> Date: Wed, 5 Aug 2026 22:08:22 -0400 Subject: [PATCH 1/3] Harden release verify step against PyPI index propagation delays The verify loop assumed a new release appears in PyPI's simple index within its 10-minute budget. Both socketsecurity 2.5.9 and socketdev 3.4.2 (2026-08-05) took longer than that: the upload succeeded and the JSON API showed the release immediately, but the CDN-cached simple index kept serving a stale version list past the loop's last attempt, failing the release and skipping the Docker publish. Extend the retry window to 30 minutes, add --no-cache-dir so each attempt refetches the index rather than revalidating pip's locally cached stale copy, and log when the JSON API already has the version so index staleness is distinguishable from a failed publish. Co-Authored-By: Claude Fable 5 Signed-off-by: lelia <2418071+lelia@users.noreply.github.com> --- .github/workflows/release.yml | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 342060b..29ac6a8 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -79,15 +79,23 @@ jobs: env: VERSION: ${{ env.VERSION }} run: | - for i in {1..30}; do - if pip install socketsecurity==${VERSION}; then + # PyPI's simple index is CDN-cached and can lag a successful upload + # by 10+ minutes when a cache purge is delayed (both socketsecurity + # 2.5.9 and socketdev 3.4.2 hit this on 2026-08-05), so retry for up + # to 30 minutes and use --no-cache-dir so each attempt refetches the + # index instead of revalidating a stale copy from pip's HTTP cache. + for i in {1..60}; do + if pip install --no-cache-dir socketsecurity==${VERSION}; then echo "Package ${VERSION} is now available and installable on PyPI" pip uninstall -y socketsecurity echo "success=true" >> $GITHUB_OUTPUT exit 0 fi - echo "Attempt $i: Package not yet installable, waiting 20s... (${i}/30)" - sleep 20 + if curl -s -f "https://pypi.org/pypi/socketsecurity/${VERSION}/json" > /dev/null; then + echo "Release ${VERSION} exists on PyPI (JSON API) but is not in the simple index yet - CDN propagation delay" + fi + echo "Attempt $i: Package not yet installable, waiting 30s... (${i}/60)" + sleep 30 done echo "success=false" >> $GITHUB_OUTPUT exit 1 From 0be82342847d8a795be619372082786f7b0652c1 Mon Sep 17 00:00:00 2001 From: lelia <2418071+lelia@users.noreply.github.com> Date: Wed, 5 Aug 2026 22:14:25 -0400 Subject: [PATCH 2/3] Align release verify step with socket-sdk-python hardening Use python -m pip with an explicit production Simple-index URL, quote workflow outputs, and skip the sleep after the final attempt, matching the socket-sdk-python release workflow so the verify step is identical in both repos. Co-Authored-By: Claude Fable 5 Signed-off-by: lelia <2418071+lelia@users.noreply.github.com> --- .github/workflows/release.yml | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 29ac6a8..ded141f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -79,25 +79,32 @@ jobs: env: VERSION: ${{ env.VERSION }} run: | - # PyPI's simple index is CDN-cached and can lag a successful upload - # by 10+ minutes when a cache purge is delayed (both socketsecurity - # 2.5.9 and socketdev 3.4.2 hit this on 2026-08-05), so retry for up - # to 30 minutes and use --no-cache-dir so each attempt refetches the - # index instead of revalidating a stale copy from pip's HTTP cache. - for i in {1..60}; do - if pip install --no-cache-dir socketsecurity==${VERSION}; then + # The first lookup can race PyPI's Simple-index propagation, and a delayed + # CDN purge can leave the index stale for 10+ minutes after a successful + # upload (socketsecurity 2.5.9 and socketdev 3.4.2 both hit this on + # 2026-08-05). pip caches HTTP responses by default, so without + # --no-cache-dir every retry can reuse that initial stale response instead + # of checking whether the release has appeared. Budget: 30 minutes. + MAX_ATTEMPTS=60 + for i in $(seq 1 "$MAX_ATTEMPTS"); do + if python -m pip install \ + --no-cache-dir \ + --index-url https://pypi.org/simple/ \ + "socketsecurity==${VERSION}"; then echo "Package ${VERSION} is now available and installable on PyPI" - pip uninstall -y socketsecurity - echo "success=true" >> $GITHUB_OUTPUT + python -m pip uninstall -y socketsecurity + echo "success=true" >> "$GITHUB_OUTPUT" exit 0 fi if curl -s -f "https://pypi.org/pypi/socketsecurity/${VERSION}/json" > /dev/null; then - echo "Release ${VERSION} exists on PyPI (JSON API) but is not in the simple index yet - CDN propagation delay" + echo "Release ${VERSION} exists on PyPI (JSON API) but is not in the Simple index yet - CDN propagation delay" + fi + if [ "$i" -lt "$MAX_ATTEMPTS" ]; then + echo "Attempt $i: Package not yet installable, waiting 30s... (${i}/${MAX_ATTEMPTS})" + sleep 30 fi - echo "Attempt $i: Package not yet installable, waiting 30s... (${i}/60)" - sleep 30 done - echo "success=false" >> $GITHUB_OUTPUT + echo "success=false" >> "$GITHUB_OUTPUT" exit 1 - name: Build & Push Docker From aad7e531580e3641848dffc99501a02ae4a22b95 Mon Sep 17 00:00:00 2001 From: lelia <2418071+lelia@users.noreply.github.com> Date: Wed, 5 Aug 2026 22:37:40 -0400 Subject: [PATCH 3/3] Trim release-specific details from verify step comment Co-Authored-By: Claude Fable 5 Signed-off-by: lelia <2418071+lelia@users.noreply.github.com> --- .github/workflows/release.yml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ded141f..5be9e26 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -80,11 +80,10 @@ jobs: VERSION: ${{ env.VERSION }} run: | # The first lookup can race PyPI's Simple-index propagation, and a delayed - # CDN purge can leave the index stale for 10+ minutes after a successful - # upload (socketsecurity 2.5.9 and socketdev 3.4.2 both hit this on - # 2026-08-05). pip caches HTTP responses by default, so without - # --no-cache-dir every retry can reuse that initial stale response instead - # of checking whether the release has appeared. Budget: 30 minutes. + # CDN purge can leave the index stale well after a successful upload. + # pip caches HTTP responses by default, so without --no-cache-dir every + # retry can reuse that initial stale response instead of checking whether + # the release has appeared. Budget: 30 minutes. MAX_ATTEMPTS=60 for i in $(seq 1 "$MAX_ATTEMPTS"); do if python -m pip install \