From beb0375b3e9d853773948f1a9f15e9c64f6743ef Mon Sep 17 00:00:00 2001 From: hugo8xx Date: Tue, 25 Aug 2026 08:28:56 +0700 Subject: [PATCH] ci: publish without a token, and actually run the newer tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit publish.yml releases from a GitHub Release over PyPI's Trusted Publishing — OIDC, so there is no API token in the repository, in a secret, or anywhere to rotate. A token scoped to the wrong project already cost a 403 here, and the reflex fix for that is one account-wide token covering every package, which is the version worth avoiding. test.yml was running two of the five suites. The other three — AsyncKhwan, flush(), occurred_at — arrived after it was written and nothing added them, so 13 of 25 assertions were not being checked on any push. Installing .[async] is what lets the async suite run at all. Publishing runs every suite before it builds. A release tag is the last place to find out they fail. --- .github/workflows/publish.yml | 37 +++++++++++++++++++++++++++++++++++ .github/workflows/test.yml | 6 +++++- 2 files changed, 42 insertions(+), 1 deletion(-) 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..ec8a2d9 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,37 @@ +name: publish + +# Publishing happens from a GitHub Release, over PyPI's Trusted Publishing — +# OIDC, so there is no API token in the repository, in a secret, or anywhere to +# rotate. A project-scoped token already cost a 403 here once, and the reflex fix +# for that is an account-wide token, which is the version worth avoiding. +# +# One-time setup on PyPI: this project's Publishing settings, add a GitHub +# publisher for khwanlabs/khwan-client-python, workflow `publish.yml`, +# environment `pypi`. +on: + release: + types: [published] + +jobs: + publish: + runs-on: ubuntu-latest + environment: pypi + permissions: + # Required for OIDC. Nothing else is, and nothing else is granted. + id-token: write + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: "3.13" + - run: pip install -e ".[async]" build twine + # Never publish something that does not pass its own tests. A release tag + # is the last place to find that out. + - run: python test_record_background.py + - run: python test_verify_lessons_synthesis.py + - run: python test_async_client.py + - run: python test_flush.py + - run: python test_occurred_at.py + - run: python -m build + - run: python -m twine check dist/* + - uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d6cef49..c9a5d63 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -24,9 +24,13 @@ jobs: - uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - - run: pip install -e . build twine + # .[async] so the AsyncKhwan suite can run — it is half the client now. + - run: pip install -e ".[async]" build twine - run: python test_record_background.py - run: python test_verify_lessons_synthesis.py + - run: python test_async_client.py + - run: python test_flush.py + - run: python test_occurred_at.py # Catches the metadata-version trap before a release does: the build has to # produce something twine will actually accept. - run: python -m build