From 8d3a1ad0c7d51b6025462f728dd7bff3e1451cff Mon Sep 17 00:00:00 2001 From: Brian McMahon Date: Thu, 4 Jun 2026 09:48:07 -0700 Subject: [PATCH] ci: auto-publish to PyPI on main push (OIDC trusted publishing) mnemon was the fleet outlier still publishing via the manual scripts/promote_stable.sh pipeline, which left rc7 (and historically other rcs) on main but unpublished. Add publish.yml mirroring alpha-engine-lib / morning-signal: build sdist+wheel + twine check, then publish via OIDC trusted publishing on every push to main, idempotent via skip-existing so no-version-bump merges are a clean no-op. promote_stable.sh stays for manual/stable releases + recovery; workflow_dispatch covers the one-time pending-trusted-publisher bootstrap and failed-first-attempt reruns. Requires a one-time PyPI-side step (operator): add a trusted publisher to the mnemon-memory project (owner=cipher813, repo=mnemon, workflow=publish.yml, environment=pypi) before the first run can publish. Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/publish.yml | 81 +++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 .github/workflows/publish.yml diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..49554e4 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,81 @@ +name: Publish to PyPI + +# Publishes the current pyproject.toml version to PyPI via OIDC trusted +# publishing on every push to main. Idempotent — `skip-existing: true` +# makes re-runs against an already-uploaded version a no-op, so +# docs-only / no-version-bump merges harmlessly trigger this workflow. +# +# A version bump in pyproject.toml (+ src/mnemon/__init__.py) auto- +# publishes on merge; an unchanged version is skipped, not an error. +# This replaces the manual `scripts/promote_stable.sh` publish step as +# the default path and closes the "forgot to publish the rc" lapse +# (e.g. rc7 sat on main while PyPI was still rc6). Mirrors the +# alpha-engine-lib / morning-signal publish-on-main + skip-existing +# pattern. +# +# Trigger is `push: branches: [main]` (the merge itself), not `push: +# tags`, so it fires from a normal merge without a separate tag-push +# coordination step. `scripts/promote_stable.sh` stays available for +# manual/stable releases + recovery; workflow_dispatch covers a failed +# first attempt or the one-time pending-trusted-publisher bootstrap. + +on: + push: + branches: [main] + workflow_dispatch: + +jobs: + build: + name: Build sdist + wheel + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + + - name: Set up Python + uses: actions/setup-python@v6 + with: + python-version: "3.11" + + - name: Install build deps + run: | + python -m pip install --upgrade pip + pip install build twine + + - name: Build sdist + wheel + run: python -m build + + - name: Verify artifacts with twine check + run: twine check dist/* + + - name: Upload artifacts + uses: actions/upload-artifact@v7 + with: + name: dist + path: dist/ + + publish: + name: Publish to PyPI + needs: build + runs-on: ubuntu-latest + # OIDC trusted-publishing requires id-token: write at the job level. + # No PYPI_API_TOKEN secret is used — PyPI verifies the workflow + # identity (project + repo + workflow filename + environment). + permissions: + id-token: write + environment: + name: pypi + url: https://pypi.org/project/mnemon-memory/ + steps: + - name: Download build artifacts + uses: actions/download-artifact@v8 + with: + name: dist + path: dist/ + + - name: Publish to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 + with: + # No-op when the version is already on PyPI. Lets every main + # merge attempt a publish without failing on no-version-bump + # merges (docs, CI, internal refactors). + skip-existing: true