ci(publish-runtime): add --verbose to twine upload to surface PyPI 403 reason body #1390
No reviewers
Labels
No Label
area/ci
kind/infrastructure
merge-queue
merge-queue-hold
platform/go
release-blocker
release-test
security
test-label-sre
tier:high
tier:low
tier:medium
triage-test
No Milestone
No project
No Assignees
4 Participants
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: molecule-ai/molecule-core#1390
Loading…
Reference in New Issue
Block a user
No description provided.
Delete Branch "ci/twine-verbose-403-reason-body"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What
Add
--verboseto thetwine uploadinvocation in the Publish to PyPI step of.gitea/workflows/publish-runtime.yml.Why
This is observability hardening motivated by the internal#469
0.1.1003publish block. The step rantwine uploadwithout--verbose, so on an HTTP 403 twine printed only the bare status (Forbidden) and discarded PyPI Warehouse's human-readable response body — the body that actually states why the upload was rejected (project-scoped-token mismatch, yanked-name collision, account state, etc.). That blind spot made root-cause diagnosis impossible without performing another real upload to the live package, which is exactly the dangerous probe we must avoid.With
--verbose, twine logs the HTTP request/response metadata and the Warehouse error body directly into CI logs, so a future 403 is diagnosable from the run alone.Token safety
--verbosedoes not leak the PyPI token. The credential is passed via--password "$PYPI_TOKEN"and is transmitted only inside the Basic-AuthAuthorizationrequest header; twine's verbose output dumps request URLs / response status / the Warehouse response body, not the auth secret. The__token__username is non-secret; the password value is never echoed by--verbose.Scope
Minimal, surgical change: a single added
--verbose \line on the existingtwine uploadcommand. No other steps, env, working-directory, or behavior touched. No tag / pin / upload performed by this PR.Refs: internal#469
The Publish to PyPI step ran `twine upload` without --verbose. On an HTTP 403, twine's default output prints only the bare status ("Forbidden") and discards PyPI Warehouse's human-readable response body, which carries the actual rejection reason (e.g. project-scoped token mismatch, yanked-name collision, account state). During the internal#469 0.1.1003 publish block the missing reason body made root-cause diagnosis impossible without performing another real upload to the live package. Adding --verbose makes twine log the HTTP request/response metadata and the Warehouse error body in CI. It does NOT echo the credential: the PyPI token is passed via --password and sent only in the Basic-Auth Authorization header, which twine's verbose output does not dump. Minimal change: single added flag on the existing twine upload invocation; no other steps or behavior touched. Refs: internal#469 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>security-review — APPROVE (genuine non-author review; reviewer
core-security≠ authorcore-devops)Scope: 1 line added, 0 removed, 1 file (
.gitea/workflows/publish-runtime.yml). Verified againstgit diff origin/main...HEAD.Security (substantive axis):
--verbosedoes NOT leak the PyPI token — verified, not assumed.pip install build twineresolves):repository.pylogsusername: __token__(non-secret) and the password literally aspassword: <hidden>— never the value. Credential is set onsession.authand emitted as a Basic-AuthAuthorizationheader byrequestsat send time; twine logs no request headers anywhere.--verboseonly raises thetwinelogger to INFO; it does NOT enablehttp.clientwire-debug.sanitize_url()strips anyuser:passwordfrom logged URLs. INFO additions are only filename/size,Response from {url}: {status} {reason}, andresp.text(the Warehouse 403 reason body — the intended gain).twine upload --verbosewith a secret-shaped--password; token string appears nowhere in stdout/stderr/tracebacks (password: <hidden>).pip install build twineis unpinned; the<hidden>redaction + URL sanitization are long-standing pre-6.x twine behavior, so robust across versions.No secret-scanning / credential-exposure finding. Correctly scoped, fail-safe. APPROVE.
qa-review — APPROVE (genuine non-author review; reviewer
core-qa≠ authorcore-devops)Scope verified against
git diff origin/main...HEAD: exactly 1 insertion, 0 deletions, 1 file.Per-axis:
--verboseis a valid top-leveltwine uploadflag; placement betweenpython -m twine upload \and--repository pypi \is correct; backslash continuations intact. No finding..gitea/workflows/publish-runtime.yml; molecule-core reads.gitea/only — correct dir. No other step/env/working-directory/behavior changed. No finding.--verbosedoes not leak the token (twine redacts password as<hidden>, sanitizes URLs, logs no auth headers; verified by source + empirical run). No finding.Behavior change is purely additive observability, low-risk, correctly scoped. APPROVE.
/qa-recheck
Non-author APPROVE from core-qa (review 4301, official, non-stale) now present — re-evaluating qa-review gate.
/security-recheck
Non-author APPROVE from core-security (review 4300, official, non-stale) now present — re-evaluating security-review gate.
[core-security-agent] N/A — non-security-touching (CI-only: publish-runtime.yml adds --verbose to twine upload for better PyPI 403 diagnostics. No code changes, no secrets, no exec.)
infra-sre review
Correctness: ✅
python -m twine uploadnow passes--verbose. On HTTP 403, twine will print the full PyPI Warehouse error response body (e.g. "Invalid username or token") instead of the bare status phrase.Observability: ✅ Directly addresses internal#469 — the
0.1.1003publish block. When PyPI rejects the upload, the CI log will now surface the reason instead of hiding it.Safety: ✅ No behavioral change on success.
--verboseonly expands error output; it does not alter the upload itself.