diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-build.yml similarity index 80% rename from .github/workflows/python-publish.yml rename to .github/workflows/python-build.yml index 3dfda3e..6915739 100644 --- a/.github/workflows/python-publish.yml +++ b/.github/workflows/python-build.yml @@ -1,9 +1,10 @@ -name: Python publish to PyPI (reusable) +name: Python verify and build for PyPI (reusable) -# Bezpečná publikace přes PyPI Trusted Publishing (OIDC), bez API tokenů. -# Volající workflow MUSÍ mít soubor .github/workflows/publish.yml - PyPI Trusted Publisher -# se registruje na název volajícího workflow, ne tohoto. -# Volání: uses: SYSNET-CZ/.github/.github/workflows/python-publish.yml@v1 +# Ověření release tagu/verze a build distribuce pro publikaci na PyPI. Samotný upload NEDĚLÁ: +# PyPI oficiálně nepodporuje reusable workflow jako Trusted Publisher, proto upload +# (pypa/gh-action-pypi-publish s id-token: write) patří přímo do volajícího publish.yml, +# který si artefakt "python-dist" stáhne. Viz docs/pypi-trusted-publishing.md. +# Volání: uses: SYSNET-CZ/.github/.github/workflows/python-build.yml@v1.2.0 on: workflow_call: @@ -15,10 +16,6 @@ on: python-version: type: string default: '3.12' - environment: - description: 'GitHub environment publikačního jobu' - type: string - default: 'pypi' repository-url: description: 'Prázdné = produkční PyPI; https://test.pypi.org/legacy/ = TestPyPI' type: string @@ -27,10 +24,6 @@ on: description: 'Větev, ze které smí release tag pocházet' type: string default: 'main' - dry-run: - description: 'Pouze ověření a build, bez publikace' - type: boolean - default: false outputs: version: description: 'Publikovaná verze' @@ -153,25 +146,3 @@ jobs: path: dist/ if-no-files-found: error retention-days: 7 - - publish: - name: Publish to PyPI - needs: verify - if: ${{ !inputs.dry-run }} - runs-on: ubuntu-latest - environment: - name: ${{ inputs.environment }} - url: ${{ inputs.repository-url == '' && format('https://pypi.org/project/{0}/{1}/', inputs.package-name, needs.verify.outputs.version) || '' }} - permissions: - contents: read - id-token: write # OIDC pro Trusted Publishing - steps: - - uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2 - with: - name: python-dist - path: dist/ - - uses: pypa/gh-action-pypi-publish@7245906fb61fa33a13554f9d5fb856fe61758086 # v1.14.3 - with: - repository-url: ${{ inputs.repository-url }} - packages-dir: dist/ - attestations: false # pro privátní repozitáře nejsou podporovány diff --git a/README.md b/README.md index 8305fda..e9d888b 100644 --- a/README.md +++ b/README.md @@ -25,9 +25,9 @@ Všechny šablony jsou přednastaveny na **self-hosted runner** (`hermes-vm`). | Workflow | Účel | |---|---| | `python-ci.yml` | lint, testy v matici verzí, build a `twine check` | -| `python-publish.yml` | ověření tagu/verze, publikace na PyPI přes OIDC (Trusted Publishing) | +| `python-build.yml` | ověření tagu/verze/názvu, build a `twine check`; upload dělá volající workflow (OIDC) | | `python-release.yml` | GitHub Release z `CHANGELOG.md` | -Volat přes přesný tag (`@v1.1.0`). Dokumentace: [`docs/python-packaging.md`](docs/python-packaging.md), +Volat přes přesný tag (`@v1.2.0`). Dokumentace: [`docs/python-packaging.md`](docs/python-packaging.md), [`docs/python-release-process.md`](docs/python-release-process.md), [`docs/pypi-trusted-publishing.md`](docs/pypi-trusted-publishing.md). diff --git a/docs/pypi-trusted-publishing.md b/docs/pypi-trusted-publishing.md index ca1e28d..93e5a48 100644 --- a/docs/pypi-trusted-publishing.md +++ b/docs/pypi-trusted-publishing.md @@ -15,7 +15,7 @@ Existující projekt: PyPI -> projekt -> **Manage** -> **Publishing** -> *Add a | PyPI project name | např. `sysnet-pyutils` | | Owner | `SYSNET-CZ` | | Repository name | např. `pyutils` | -| Workflow name | `publish.yml` (**název volajícího workflow**, ne `python-publish.yml`) | +| Workflow name | `publish.yml` (**volající workflow s krokem uploadu**) | | Environment name | `pypi` | Nový projekt: stejná tabulka v **Your account -> Publishing** jako *Pending Publisher*; projekt vznikne prvním @@ -44,12 +44,19 @@ při publikaci. Pro privátní repozitáře se attestations nevytvářejí (`att - Po zprovoznění Trusted Publishingu smazat secret `PYPI_API_TOKEN` (repo i org) a token na PyPI zneplatnit. - Odchod člena týmu: odebrat z PyPI projektů a z GitHub týmů s právem push na tagy. -## TestPyPI (od `v1.1.0`) +## Upload musí být ve volajícím workflow -`python-publish.yml` s `repository-url: https://test.pypi.org/legacy/` běží v testovacím režimu: nevyžaduje tag ani +Pokus o upload z reusable workflow (`v1.1.0`) selhal na `invalid-publisher`, ačkoli publisher odpovídal volajícímu +workflow. PyPI reusable workflow jako publishera oficiálně nepodporuje (warehouse#11096). Proto `python-build.yml` +(od `v1.2.0`) jen ověřuje a sestavuje a krok `pypa/gh-action-pypi-publish` je přímo v `publish.yml` knihovny. +Ověřeno úspěšným uploadem dev verze na TestPyPI. + +## TestPyPI + +`python-build.yml` s `repository-url: https://test.pypi.org/legacy/` běží v testovacím režimu: nevyžaduje tag ani větev `main`, ale **přijme jen dev verze** (`X.Y.Z.devN`), takže produkční číslo verze nelze touto cestou spálit. Doporučené spouštění: `workflow_dispatch` ve vlastním `publish-test.yml` s environmentem `testpypi`. ## Verze reusable workflows -Odkazujte přesným tagem (`@v1.1.0`); existující tagy se nepřepisují, ani pohyblivý `v1` (zůstává na `v1.0.0`). +Odkazujte přesným tagem (`@v1.2.0`); existující tagy se nepřepisují, ani pohyblivý `v1` (zůstává na `v1.0.0`); `v1.0.0` a `v1.1.0` obsahují nepoužitelný `python-publish.yml`. diff --git a/docs/python-packaging.md b/docs/python-packaging.md index af2331f..3428ba8 100644 --- a/docs/python-packaging.md +++ b/docs/python-packaging.md @@ -56,12 +56,14 @@ jobs: ci: permissions: contents: read - uses: SYSNET-CZ/.github/.github/workflows/python-ci.yml@v1.1.0 + uses: SYSNET-CZ/.github/.github/workflows/python-ci.yml@v1.2.0 with: python-versions: '["3.11", "3.12", "3.13"]' ``` -`.github/workflows/publish.yml` - **název souboru musí být přesně `publish.yml`**, na něj se registruje Trusted Publisher: +`.github/workflows/publish.yml` - **název souboru musí být přesně `publish.yml`**, na něj se registruje Trusted Publisher. +Upload na PyPI musí být přímo v něm (PyPI oficiálně nepodporuje reusable workflow jako publishera, ověřeno +`invalid-publisher` při pokusu o upload z reusable workflow); ověření a build je centrální: ```yaml name: Publish @@ -73,23 +75,38 @@ concurrency: group: publish-${{ github.ref }} cancel-in-progress: false jobs: - publish: + build: permissions: contents: read - id-token: write - uses: SYSNET-CZ/.github/.github/workflows/python-publish.yml@v1.1.0 + uses: SYSNET-CZ/.github/.github/workflows/python-build.yml@v1.2.0 with: package-name: sysnet-priklad + publish: + needs: build + runs-on: ubuntu-latest + environment: pypi + permissions: + contents: read + id-token: write + steps: + - uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2 + with: + name: python-dist + path: dist/ + - uses: pypa/gh-action-pypi-publish@7245906fb61fa33a13554f9d5fb856fe61758086 # v1.14.3 + with: + packages-dir: dist/ + attestations: false release: needs: publish permissions: contents: write - uses: SYSNET-CZ/.github/.github/workflows/python-release.yml@v1.1.0 + uses: SYSNET-CZ/.github/.github/workflows/python-release.yml@v1.2.0 with: package-name: sysnet-priklad ``` -Volejte vždy přes přesný verzovaný tag (`@v1.1.0`), nikdy `@main`. Tagy se nepřepisují, každá změna reusable workflow je nový tag `v1.x.y`. +Volejte vždy přes přesný verzovaný tag (`@v1.2.0`), nikdy `@main`. Tagy se nepřepisují, každá změna reusable workflow je nový tag `v1.x.y`. ## 4. Runnery diff --git a/tests/test_publish_checks.py b/tests/test_publish_checks.py index 5d72c63..cd88032 100644 --- a/tests/test_publish_checks.py +++ b/tests/test_publish_checks.py @@ -1,4 +1,4 @@ -"""Tests of the version/name verification embedded in python-publish.yml. +"""Tests of the version/name verification embedded in python-build.yml. Extracts the inline Python of the "Verify distribution" step, builds a tiny fixture package and runs the step with valid and invalid tags/names. @@ -13,7 +13,7 @@ import pytest import yaml -WORKFLOW = Path(__file__).parent.parent / ".github/workflows/python-publish.yml" +WORKFLOW = Path(__file__).parent.parent / ".github/workflows/python-build.yml" def _step_script() -> str: