Skip to content

fix(ci): make PyPI promote idempotent for already-published versions#67

Merged
kj-podonos merged 4 commits into
mainfrom
kj-podonos/ci-run-failing
Jun 24, 2026
Merged

fix(ci): make PyPI promote idempotent for already-published versions#67
kj-podonos merged 4 commits into
mainfrom
kj-podonos/ci-run-failing

Conversation

@kj-podonos

Copy link
Copy Markdown
Contributor

Why

Promote (PyPI) (promote-prod.yml) went red and paged Slack on every prod-deploy dispatch once the newest SDK whose .spec-sha ≤ prod was already on PyPI (currently v0.7.1). The build-time preflight saw HTTP 200 and exit 1'd — but that is a benign, expected re-dispatch, not a failure. (Prompting run: 28070718434.)

What

Move the PyPI existence check up into resolve (it already knows the tag):

Trigger resolve build pypi run
Already-published re-dispatch (the bug) already_published=true skip skip 🟢 green, no Slack
New version already_published=false ✓ publish 🟢 + Slack ✅
PyPI unreachable / 403 / 5xx abort (fail-closed) skip skip 🔴 (correct)
  • Delete the build-time preflight — it only guarded a publish-between-resolve-and-build race that cannot happen (concurrency serializes promotes; PyPI rejects duplicate uploads).
  • Fail-closed hardened: the resolve check uses curl --retry 3 --retry-connrefused --max-time 20, so a transient pypi.org blip no longer false-reds; only a confirmed 404 builds (already_published == 'false').
  • New notify-success job: Slack ✅ ping only on an actual publish (needs.pypi.result == 'success') — silent on the no-op.
  • Docs synced (docs/PUBLISH.md, RUNBOOK.md).

Verification

  • actionlint clean; static cascade trace confirms the skip path stays green and both the fail-closed and real-failure paths still page.
  • Post-merge live test (the manual-promote Gate requires refs/heads/main, so this runs after merge):
    gh workflow run promote-prod.yml --ref main -f tag=v0.7.1
    
    Expect: green run; resolve logs HTTP 200 + a job-summary "no-op"; build/pypi skipped; no Slack; PyPI still has exactly one 0.7.1.

🤖 Generated with Claude Code

The Promote (PyPI) workflow failed (red + Slack page) on every prod-deploy
dispatch once the newest SDK <= prod spec was already on PyPI: the build-time
preflight saw HTTP 200 and exit-1'd. That is a benign, expected re-dispatch,
not a failure.

Move the PyPI existence check up into `resolve` (it already knows the tag):
- 200 (already published) -> already_published=true -> build + pypi skip ->
  green no-op, no Slack, plus a job-summary "no-op" line.
- 404 (absent) -> build + publish as before.
- other (000/403/429/5xx) -> fail closed (abort), now hardened with
  curl --retry/--max-time so a transient blip doesn't false-red.

Delete the build-time preflight: it only guarded a publish-between-resolve-
and-build race that can't happen (concurrency serializes promotes; PyPI
rejects duplicate uploads). Add a `notify-success` job that pings Slack only
on an actual publish (silent on the no-op). Sync docs (PUBLISH.md, RUNBOOK.md).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@kj-podonos kj-podonos added bug Something isn't working documentation Improvements or additions to documentation chore labels Jun 24, 2026
@kj-podonos kj-podonos self-assigned this Jun 24, 2026
kj-podonos and others added 3 commits June 24, 2026 14:31
Codex review: if SLACK_WEBHOOK_URL is set but Slack rejects/times out, the new
notify-success job failed AFTER pypi had already published — turning a real,
immutable release into a red run with no Slack at all (notify-failure doesn't
depend on notify-success, so it stays silent). Add continue-on-error to the
Slack step so a notification hiccup can never red an already-published release.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
code-reviewer pass (non-blocking):
- curl now follows redirects (-L --max-redirs 3) so a benign pypi.org 301/302
  resolves to the real 200/404 instead of false-reding via the else branch;
  fail-closed semantics preserved (a redirect loop / unexpected final status
  still aborts).
- PUBLISH.md flow diagram: move the idempotency-check line above the build
  steps so it reflects that the check runs in `resolve`, before `build`.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@kj-podonos

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. You're on a roll.

Reviewed commit: 361bfc0c4d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@kj-podonos kj-podonos merged commit 9acc656 into main Jun 24, 2026
22 checks passed
@kj-podonos kj-podonos deleted the kj-podonos/ci-run-failing branch June 24, 2026 07:16
kj-podonos added a commit that referenced this pull request Jul 3, 2026
🤖 I have created a release *beep* *boop*
---


##
[0.8.0](v0.7.1...v0.8.0)
(2026-07-03)


### Features

* sync SDK to OnePin API v0.40.2
([#64](#64))
([aa97888](aa97888))
* sync SDK to OnePin API v0.40.3
([#66](#66))
([2f2e765](2f2e765))
* sync SDK to OnePin API v0.41.33
([#70](#70))
([394f488](394f488))


### Bug Fixes

* **ci:** make PyPI promote idempotent for already-published versions
([#67](#67))
([9acc656](9acc656))
* **ci:** regenerate SDK from public-spec.yaml, add narrowing guard
([#69](#69))
([76a0b71](76a0b71))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working chore documentation Improvements or additions to documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants