revert(adr-184): restore PYPI_API_TOKEN auth in pip-release.yml — OIDC needs pypi.org registration first (fix-marker RuView#786-pypi-token-auth)

Reverts the OIDC-only publish path from cc153e8b5. Restores password:
${{ secrets.PYPI_API_TOKEN }} on all four publish steps and drops the
id-token: write / environment: pypi blocks from publish-v2 and
publish-tombstone. OIDC-only is a net regression until the manual
pypi.org Trusted Publisher registration exists — publishing would fail
'no trusted publisher configured'. Token auth is the active working path.

OIDC design retained as documented TODO(ADR-184 P1b) in the header; the
switch becomes a dedicated gated follow-up once the owner confirms
registration. Restores compliance with fix-marker RuView#786-pypi-token-auth.

Refs ADR-184 P1.
This commit is contained in:
ruv
2026-07-21 18:32:10 -07:00
parent 34d929ae39
commit 82d5c73396
+27 -50
View File
@@ -13,29 +13,26 @@
# 1. cut tag `v1.99.0-pip` → publishes the tombstone wheel first
# 2. cut tag `v2.0.0-pip` → publishes the PyO3 v2 wheel matrix
#
# Publishes via PyPI **Trusted Publishing (OIDC)** — ADR-184 P1. The
# publish jobs mint a short-lived OIDC token from `id-token: write` and
# the `pypi` GitHub environment; the `pypa/gh-action-pypi-publish` action
# exchanges it for a per-run PyPI upload token. There is NO static
# `PYPI_API_TOKEN` secret in the publish steps anymore — a manually
# rotated token is exactly the failure mode (silent expiry → 403) this
# migration removes.
# Publishes via the `PYPI_API_TOKEN` GitHub Actions secret (API-token
# auth). This is the ACTIVE, working publish path — the token is sourced
# fresh from GCP Secret Manager per the runbook in
# docs/integrations/pypi-release.md (GCP Secret Manager → gh secret set),
# which also keeps KICS from flagging the secret name as a generic-secret
# literal here.
#
# BLOCKING MANUAL PREREQUISITE (ADR-184 §3.1 — cannot be automated):
# A human with owner rights must register a Trusted Publisher on
# pypi.org for EACH project (wifi-densepose, and ruview as a pending
# publisher) with:
# Owner=ruvnet Repository=RuView Workflow=pip-release.yml Environment=pypi
# Until BOTH entries exist, the publish steps fail with a
# "no trusted publisher configured" error (not a 403). Do NOT re-add a
# `password:` to "fix" that — doing so silently disables OIDC again.
# Fallback (ADR-184 §3.2): if OIDC is declined, restore `password:
# ${{ secrets.PYPI_API_TOKEN }}` on the publish steps per the token
# runbook in docs/integrations/pypi-release.md.
#
# TestPyPI dry-run: the test.pypi.org publish steps also dropped their
# `password:` — they require a SEPARATE trusted-publisher entry on
# test.pypi.org (same Environment=pypi) before that path will work.
# TODO(ADR-184 P1b): migrate to PyPI OIDC Trusted Publishing to remove
# this rotatable/expire-able credential. That switch is GATED on a manual
# pypi.org step no CLI/agent can perform: the repo owner must register a
# Trusted Publisher on pypi.org for owner=ruvnet / repo=RuView /
# workflow=pip-release.yml (BOTH the wifi-densepose and ruview projects;
# ruview as a pending publisher) — see docs/adr/ADR-184-*.md. Do NOT grant
# the OIDC id-token write permission before that registration exists, or
# publishing fails with "no trusted publisher configured" — a silent
# regression the `Verify fix markers` guard `RuView#786-pypi-token-auth`
# exists to catch (it forbids that permission string in this file). When
# the owner confirms both entries are live, do the OIDC switch as a
# dedicated follow-up commit (drop `password:`, add the OIDC id-token
# permission + `environment: pypi`) so there is no capability gap between.
#
# Q3 (witness hash v2 — open in ADR-117 §11.3) MUST be resolved
# before the first v2.0.0 publish. When v2 lands, add a parallel
@@ -241,15 +238,6 @@ jobs:
publish-v2:
name: Publish v2 wheels
needs: [build-wheels, build-sdist]
# ADR-184 P1: OIDC Trusted Publishing. `id-token: write` mints the
# OIDC token; `environment: pypi` binds this job to the pypi.org
# trusted-publisher entry (see §3.1 blocking manual step in header).
permissions:
id-token: write
contents: read
environment:
name: pypi
url: https://pypi.org/p/wifi-densepose
if: |
always() &&
needs.build-wheels.result == 'success' &&
@@ -269,38 +257,28 @@ jobs:
mkdir -p dist
find dist-staging -type f \( -name '*.whl' -o -name '*.tar.gz' \) -exec cp -v {} dist/ \;
ls -lh dist/
# ADR-184 P1: Trusted Publishing (OIDC) — no `password:`. Needs a
# test.pypi.org trusted-publisher entry (Environment=pypi) to work.
# API-token auth (active path). See TODO(ADR-184 P1b) in the header
# before replacing `password:` with the OIDC id-token permission.
- name: Publish to TestPyPI (dry-run target)
if: github.event_name == 'workflow_dispatch' && inputs.publish_to == 'testpypi'
uses: pypa/gh-action-pypi-publish@release/v1
with:
repository-url: https://test.pypi.org/legacy/
password: ${{ secrets.PYPI_API_TOKEN }}
packages-dir: dist
skip-existing: true
# ADR-184 P1: Trusted Publishing (OIDC) — no `password:`. INERT until
# the pypi.org trusted publisher is registered (header §3.1); until
# then this step fails "no trusted publisher configured", not 403.
- name: Publish to PyPI
if: |
startsWith(github.ref, 'refs/tags/v2.') ||
(github.event_name == 'workflow_dispatch' && inputs.publish_to == 'pypi')
uses: pypa/gh-action-pypi-publish@release/v1
with:
password: ${{ secrets.PYPI_API_TOKEN }}
packages-dir: dist
publish-tombstone:
name: Publish v1.99 tombstone
needs: [build-tombstone]
# ADR-184 P1: OIDC Trusted Publishing. `id-token: write` mints the
# OIDC token; `environment: pypi` binds this job to the pypi.org
# trusted-publisher entry (see §3.1 blocking manual step in header).
permissions:
id-token: write
contents: read
environment:
name: pypi
url: https://pypi.org/p/wifi-densepose
if: |
always() &&
needs.build-tombstone.result == 'success' &&
@@ -314,22 +292,21 @@ jobs:
with:
name: tombstone
path: dist
# ADR-184 P1: Trusted Publishing (OIDC) — no `password:`. Needs a
# test.pypi.org trusted-publisher entry (Environment=pypi) to work.
# API-token auth (active path). See TODO(ADR-184 P1b) in the header
# before replacing `password:` with the OIDC id-token permission.
- name: Publish to TestPyPI (dry-run target)
if: github.event_name == 'workflow_dispatch' && inputs.publish_to == 'testpypi'
uses: pypa/gh-action-pypi-publish@release/v1
with:
repository-url: https://test.pypi.org/legacy/
password: ${{ secrets.PYPI_API_TOKEN }}
packages-dir: dist
skip-existing: true
# ADR-184 P1: Trusted Publishing (OIDC) — no `password:`. INERT until
# the pypi.org trusted publisher is registered (header §3.1); until
# then this step fails "no trusted publisher configured", not 403.
- name: Publish to PyPI
if: |
startsWith(github.ref, 'refs/tags/v1.99') ||
(github.event_name == 'workflow_dispatch' && inputs.publish_to == 'pypi')
uses: pypa/gh-action-pypi-publish@release/v1
with:
password: ${{ secrets.PYPI_API_TOKEN }}
packages-dir: dist