fix(publish-runtime): use PyPI Trusted Publisher (OIDC) instead of PYPI_TOKEN (#2113)
Drops the static PYPI_TOKEN secret in favor of OIDC trusted publishing. PyPI now mints a short-lived upload credential after verifying the workflow's OIDC claim against the trusted-publisher config registered for molecule-ai-workspace-runtime (Molecule-AI/molecule-core, publish-runtime.yml, environment pypi-publish). Why: - A leaked PYPI_TOKEN would let any holder publish arbitrary versions of molecule-ai-workspace-runtime to PyPI from anywhere — bypassing the monorepo's review and CI gates entirely. The 8 template repos pull this package; a malicious publish poisons all of them. - Trusted Publisher (OIDC) makes that exfil path moot: no long-lived credential exists to leak. Only this exact workflow, on this repo, in the pypi-publish environment, can upload. After this lands and the first OIDC publish succeeds, the PYPI_TOKEN repo secret should be deleted (it becomes dead weight + a leak surface with no purpose). Belt-and-suspenders companion to PR #56 in molecule-ai-workspace-runtime (sibling repo lockdown). Without OIDC, the sibling lockdown alone doesn't prevent local `python -m build && twine upload` from a laptop with a personal PyPI maintainer credential. Co-authored-by: Hongming Wang <hongmingwangalt@gmail.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
199630908d
commit
f3a204347c
24
.github/workflows/publish-runtime.yml
vendored
24
.github/workflows/publish-runtime.yml
vendored
@ -16,7 +16,11 @@ name: publish-runtime
|
||||
# build/molecule_runtime/ with imports rewritten (`a2a_client` →
|
||||
# `molecule_runtime.a2a_client`).
|
||||
# 2. Builds wheel + sdist with `python -m build`.
|
||||
# 3. Publishes to PyPI via twine + repo secret PYPI_TOKEN.
|
||||
# 3. Publishes to PyPI via the PyPA Trusted Publisher action (OIDC).
|
||||
# No static API token is stored — PyPI verifies the workflow's
|
||||
# OIDC claim against the trusted-publisher config registered for
|
||||
# molecule-ai-workspace-runtime (Molecule-AI/molecule-core,
|
||||
# publish-runtime.yml, environment pypi-publish).
|
||||
#
|
||||
# After publish: the 8 template repos pick up the new version on their
|
||||
# next image rebuild (their requirements.txt pin
|
||||
@ -42,6 +46,9 @@ jobs:
|
||||
publish:
|
||||
runs-on: ubuntu-latest
|
||||
environment: pypi-publish
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write # PyPI Trusted Publisher (OIDC) — no PYPI_TOKEN needed
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
@ -106,12 +113,15 @@ jobs:
|
||||
print('✓ smoke import passed')
|
||||
"
|
||||
|
||||
- name: Publish to PyPI
|
||||
working-directory: ${{ runner.temp }}/runtime-build
|
||||
env:
|
||||
TWINE_USERNAME: __token__
|
||||
TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }}
|
||||
run: python -m twine upload dist/*
|
||||
- name: Publish to PyPI (Trusted Publisher / OIDC)
|
||||
# PyPI side is configured: project molecule-ai-workspace-runtime →
|
||||
# publisher Molecule-AI/molecule-core, workflow publish-runtime.yml,
|
||||
# environment pypi-publish. The action mints a short-lived OIDC
|
||||
# token and exchanges it for a PyPI upload credential — no static
|
||||
# API token in this repo's secrets.
|
||||
uses: pypa/gh-action-pypi-publish@release/v1
|
||||
with:
|
||||
packages-dir: ${{ runner.temp }}/runtime-build/dist/
|
||||
|
||||
cascade:
|
||||
# After PyPI accepts the upload, fan out a repository_dispatch to each
|
||||
|
||||
Loading…
Reference in New Issue
Block a user