From 7381fe696dbd4a3dd8a2a0a74a3d472a693a09e7 Mon Sep 17 00:00:00 2001 From: Stuart Mumford Date: Mon, 26 Jan 2026 10:41:04 +0000 Subject: [PATCH 1/9] Add save_artifact to pure-publish --- .github/workflows/publish_pure_python.yml | 10 ++++++++++ docs/source/publish_pure_python.rst | 5 +++++ 2 files changed, 15 insertions(+) diff --git a/.github/workflows/publish_pure_python.yml b/.github/workflows/publish_pure_python.yml index 4547283..bc0195f 100644 --- a/.github/workflows/publish_pure_python.yml +++ b/.github/workflows/publish_pure_python.yml @@ -33,6 +33,11 @@ on: required: false default: 'ubuntu-latest' type: string + save_artifact: + description: Upload the built dist(s) as github artifacts + required: false + default: false + type: boolean upload_to_pypi: description: A condition specifying whether to upload to PyPI required: false @@ -139,6 +144,11 @@ jobs: env: UPLOAD_TO_PYPI: ${{ inputs.upload_to_pypi }} UPLOAD_TAG: ${{ startsWith(inputs.upload_to_pypi, 'refs/tags/') && (github.event_name == 'push' || github.event_name == 'workflow_dispatch' || github.event_name == 'release' || github.event_name == 'create') && startsWith(github.ref, inputs.upload_to_pypi) }} + - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 + if: ${{ inputs.save_artifact }} + with: + name: "dist-publish-pure" + path: dist/* - uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0 name: Upload to PyPI if: ${{ steps.set-upload.outputs.upload_to_pypi == 'true' }} diff --git a/docs/source/publish_pure_python.rst b/docs/source/publish_pure_python.rst index 7147cff..2ff32b8 100644 --- a/docs/source/publish_pure_python.rst +++ b/docs/source/publish_pure_python.rst @@ -139,6 +139,11 @@ submodules Whether to checkout submodules. Default is ``true``. +save_artifact +^^^^^^^^^^^^^ + +Whether to save/upload the dist(s) as github artifacts. The default is to not save. + Secrets ~~~~~~~ From 7c9c053a630a6e936c70f50e6f9c44140ba1d389 Mon Sep 17 00:00:00 2001 From: Stuart Mumford Date: Mon, 26 Jan 2026 11:03:04 +0000 Subject: [PATCH 2/9] Test save on pure --- .github/workflows/test_publish_pure_python.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test_publish_pure_python.yml b/.github/workflows/test_publish_pure_python.yml index 8b2da07..bbab0a5 100644 --- a/.github/workflows/test_publish_pure_python.yml +++ b/.github/workflows/test_publish_pure_python.yml @@ -25,6 +25,7 @@ jobs: setenv: uses: ./.github/workflows/publish_pure_python.yml with: + save_artifact: true test_command: python -c "import os; assert os.getenv('CUSTOM_VAR') == 'custom value'" env: | CUSTOM_VAR: custom value From 47ec06381d254c954f54aaaf4c60ea99ba4f4a36 Mon Sep 17 00:00:00 2001 From: Stuart Mumford Date: Mon, 26 Jan 2026 11:15:32 +0000 Subject: [PATCH 3/9] Attempt testing pypi-upload --- .github/workflows/test_publish_pure_python.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/.github/workflows/test_publish_pure_python.yml b/.github/workflows/test_publish_pure_python.yml index bbab0a5..fd9c3b1 100644 --- a/.github/workflows/test_publish_pure_python.yml +++ b/.github/workflows/test_publish_pure_python.yml @@ -29,3 +29,21 @@ jobs: test_command: python -c "import os; assert os.getenv('CUSTOM_VAR') == 'custom value'" env: | CUSTOM_VAR: custom value + + + test-upload-external: + name: Use built dists and test upload + runs-on: ubuntu-latest + needs: [setenv] + steps: + - name: Download artifacts + uses: actions/download-artifact@v5 + with: + merge-multiple: true + pattern: dist-* + path: dist + + - run: ls -lha dist/ + + - name: Run upload (but don't actually upload) + uses: pypa/gh-action-pypi-publish@release/v1 From d8a3fde1933a2fe66c958bfb4172209a11299558 Mon Sep 17 00:00:00 2001 From: Stuart Mumford Date: Mon, 26 Jan 2026 11:50:42 +0000 Subject: [PATCH 4/9] More testing --- .github/workflows/test_publish.yml | 18 ++++++++++++++++++ .github/workflows/test_publish_pure_python.yml | 3 ++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test_publish.yml b/.github/workflows/test_publish.yml index 2e6ca0c..3108abe 100644 --- a/.github/workflows/test_publish.yml +++ b/.github/workflows/test_publish.yml @@ -48,3 +48,21 @@ jobs: test_extras: recommended test_command: pytest --pyargs test_package targets: '' + + test-upload-external: + name: Use built dists and test upload + runs-on: ubuntu-latest + needs: [release] + steps: + - name: Download artifacts + uses: actions/download-artifact@v5 + with: + merge-multiple: true + pattern: dist-* + path: dist + + - run: ls -lha dist/ + + - name: Run upload (this will fail) + continue-on-error: true + uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/.github/workflows/test_publish_pure_python.yml b/.github/workflows/test_publish_pure_python.yml index fd9c3b1..d590e8c 100644 --- a/.github/workflows/test_publish_pure_python.yml +++ b/.github/workflows/test_publish_pure_python.yml @@ -45,5 +45,6 @@ jobs: - run: ls -lha dist/ - - name: Run upload (but don't actually upload) + - name: Run upload (this will fail) + continue-on-error: true uses: pypa/gh-action-pypi-publish@release/v1 From 2ad2ba1b02e79dcbb52f885cb969c4894603cb09 Mon Sep 17 00:00:00 2001 From: Stuart Mumford Date: Mon, 26 Jan 2026 12:12:14 +0000 Subject: [PATCH 5/9] Rename save_artifact to save_artifacts --- .github/workflows/publish.yml | 6 +++--- .github/workflows/publish_pure_python.yml | 4 ++-- .github/workflows/test_publish_pure_python.yml | 2 +- docs/source/publish.rst | 4 ++-- docs/source/publish_pure_python.rst | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index f4744d2..4efb8ee 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -46,7 +46,7 @@ on: required: false default: 'ubuntu-latest' type: string - save_artifact: + save_artifacts: description: Upload the built wheels as github artifacts required: false default: false @@ -213,7 +213,7 @@ jobs: CIBW_ARCHS: ${{ matrix.CIBW_ARCHS }} - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 if: | - needs.targets.outputs.upload_to_pypi == 'true' || inputs.upload_to_anaconda || inputs.save_artifact + needs.targets.outputs.upload_to_pypi == 'true' || inputs.upload_to_anaconda || inputs.save_artifacts with: name: "dist-${{ matrix.artifact-name }}" path: dist/* @@ -258,7 +258,7 @@ jobs: python-version: '3.12' - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 if: | - needs.targets.outputs.upload_to_pypi == 'true' || inputs.upload_to_anaconda || inputs.save_artifact + needs.targets.outputs.upload_to_pypi == 'true' || inputs.upload_to_anaconda || inputs.save_artifacts with: name: dist-sdist path: dist/* diff --git a/.github/workflows/publish_pure_python.yml b/.github/workflows/publish_pure_python.yml index bc0195f..ed56f19 100644 --- a/.github/workflows/publish_pure_python.yml +++ b/.github/workflows/publish_pure_python.yml @@ -33,7 +33,7 @@ on: required: false default: 'ubuntu-latest' type: string - save_artifact: + save_artifacts: description: Upload the built dist(s) as github artifacts required: false default: false @@ -145,7 +145,7 @@ jobs: UPLOAD_TO_PYPI: ${{ inputs.upload_to_pypi }} UPLOAD_TAG: ${{ startsWith(inputs.upload_to_pypi, 'refs/tags/') && (github.event_name == 'push' || github.event_name == 'workflow_dispatch' || github.event_name == 'release' || github.event_name == 'create') && startsWith(github.ref, inputs.upload_to_pypi) }} - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 - if: ${{ inputs.save_artifact }} + if: ${{ inputs.save_artifacts }} with: name: "dist-publish-pure" path: dist/* diff --git a/.github/workflows/test_publish_pure_python.yml b/.github/workflows/test_publish_pure_python.yml index d590e8c..34326f5 100644 --- a/.github/workflows/test_publish_pure_python.yml +++ b/.github/workflows/test_publish_pure_python.yml @@ -25,7 +25,7 @@ jobs: setenv: uses: ./.github/workflows/publish_pure_python.yml with: - save_artifact: true + save_artifacts: true test_command: python -c "import os; assert os.getenv('CUSTOM_VAR') == 'custom value'" env: | CUSTOM_VAR: custom value diff --git a/docs/source/publish.rst b/docs/source/publish.rst index 696a346..514e364 100644 --- a/docs/source/publish.rst +++ b/docs/source/publish.rst @@ -173,8 +173,8 @@ repository_url The PyPI repository URL to use. Default is the main PyPI repository. -save_artifact -^^^^^^^^^^^^^ +save_artifacts +^^^^^^^^^^^^^^ Whether to save/upload the wheels as github artifacts. The default is to not save (unless ``upload_to_anaconda`` or ``upload_to_pypi`` is enabled). diff --git a/docs/source/publish_pure_python.rst b/docs/source/publish_pure_python.rst index 2ff32b8..068b45f 100644 --- a/docs/source/publish_pure_python.rst +++ b/docs/source/publish_pure_python.rst @@ -139,8 +139,8 @@ submodules Whether to checkout submodules. Default is ``true``. -save_artifact -^^^^^^^^^^^^^ +save_artifacts +^^^^^^^^^^^^^^ Whether to save/upload the dist(s) as github artifacts. The default is to not save. From 0947ea0a02e7053befbb8442a0439c5ce9c99b79 Mon Sep 17 00:00:00 2001 From: Stuart Mumford Date: Mon, 26 Jan 2026 12:12:38 +0000 Subject: [PATCH 6/9] Better test --- .github/workflows/test_publish.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/test_publish.yml b/.github/workflows/test_publish.yml index 3108abe..b6b2adc 100644 --- a/.github/workflows/test_publish.yml +++ b/.github/workflows/test_publish.yml @@ -24,6 +24,8 @@ jobs: release: uses: ./.github/workflows/publish.yml with: + save_artifacts: true + upload_to_pypi: false test_groups: test, concurrency test_extras: recommended test_command: pytest --pyargs test_package From 5f6516d1f2e0b9f6a0b032ec3e975c849edb4500 Mon Sep 17 00:00:00 2001 From: Stuart Mumford Date: Mon, 26 Jan 2026 12:12:47 +0000 Subject: [PATCH 7/9] Add some trusted publishing docs --- docs/source/index.rst | 2 + docs/source/publish.rst | 2 + docs/source/publish_pure_python.rst | 2 + docs/source/tox.rst | 2 + docs/source/trusted_publishing.rst | 58 +++++++++++++++++++++++++++++ 5 files changed, 66 insertions(+) create mode 100644 docs/source/trusted_publishing.rst diff --git a/docs/source/index.rst b/docs/source/index.rst index 1cb1ffe..8b76574 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -1,3 +1,5 @@ +.. _oa-ghaw-index: + OpenAstronomy GitHub Actions Workflows ====================================== diff --git a/docs/source/publish.rst b/docs/source/publish.rst index 514e364..acf48e2 100644 --- a/docs/source/publish.rst +++ b/docs/source/publish.rst @@ -1,3 +1,5 @@ +.. _oa-ghaw-publish: + Build and publish a Python package ---------------------------------- diff --git a/docs/source/publish_pure_python.rst b/docs/source/publish_pure_python.rst index 068b45f..b55a7f8 100644 --- a/docs/source/publish_pure_python.rst +++ b/docs/source/publish_pure_python.rst @@ -1,3 +1,5 @@ +.. _oa-ghaw-publish-pure: + Build and publish a pure Python package --------------------------------------- diff --git a/docs/source/tox.rst b/docs/source/tox.rst index 52c925f..f6edcb0 100644 --- a/docs/source/tox.rst +++ b/docs/source/tox.rst @@ -1,3 +1,5 @@ +.. _oa-ghaw-tox: + Test a Python package using tox ------------------------------- diff --git a/docs/source/trusted_publishing.rst b/docs/source/trusted_publishing.rst new file mode 100644 index 0000000..43b891d --- /dev/null +++ b/docs/source/trusted_publishing.rst @@ -0,0 +1,58 @@ +.. _oa-ghaw-trusted-publishing: + +Using These Workflows with Trusted Publishing +--------------------------------------------- + +`Trusted Publishing `__ is a feature of PyPI which uses short lived tokens generated by a configured CI platform, in this case GitHub Actions. + +Currently, there is no direct support for using trusted publishing within a reuseable workflow, see `this issue `__ and links there-in. +To work around this limitation the :ref:`oa-ghaw-publish` and :ref:`oa-ghaw-publish-pure` workflows support uploading the built distributions as artifacts which can then be used by a subsequent job to upload to PyPI. + +Taking the example from :ref:`oa-ghaw-publish`, we add two new lines: + + +.. code:: yaml + :emphasize-lines: 5,6 + + jobs: + build: + uses: OpenAstronomy/github-actions-workflows/.github/workflows/publish.yml@v1 + with: + save_artifacts: true + upload_to_pypi: false + test_groups: test, concurrency + test_extras: recommended + test_command: pytest --pyargs test_package + targets: | + - linux + - cp3?-macosx_x86_64 + + +Setting ``upload_to_pypi: false`` means that the publish (or publish_pure) workflow will never try and upload to PyPI by itself. +The ``save_artifacts: true`` means that it will always run the ``actions/upload-artifact`` job so subsequent jobs in the workflow can use the dists. + +Next we have to configure a new job, which downloads the artifacts and then uses the `gh-action-pypi-publish `__ action to upload to PyPI. +As we are planning on using trusted publishing, we need to configure no options for this action. +We also add an if statement to the job so that it only runs on tags starting with a ``v``. + +.. code:: yaml + + jobs: + build: + ... + + upload: + if: startsWith(github.ref, 'refs/tags/v') + name: Use built dists and test upload + runs-on: ubuntu-latest + needs: [build] + steps: + - name: Download artifacts + uses: actions/download-artifact@v5 + with: + merge-multiple: true + pattern: dist-* + path: dist + + - name: Run upload + uses: pypa/gh-action-pypi-publish@release/v1 From c4461440ef4d5d37b92dbd2fe934d34739991ada Mon Sep 17 00:00:00 2001 From: Stuart Mumford Date: Mon, 26 Jan 2026 12:15:52 +0000 Subject: [PATCH 8/9] More docs --- docs/source/index.rst | 1 + docs/source/trusted_publishing.rst | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/source/index.rst b/docs/source/index.rst index 8b76574..18f8b2a 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -9,3 +9,4 @@ OpenAstronomy GitHub Actions Workflows tox publish publish_pure_python + trusted_publishing diff --git a/docs/source/trusted_publishing.rst b/docs/source/trusted_publishing.rst index 43b891d..6888e9d 100644 --- a/docs/source/trusted_publishing.rst +++ b/docs/source/trusted_publishing.rst @@ -12,7 +12,7 @@ Taking the example from :ref:`oa-ghaw-publish`, we add two new lines: .. code:: yaml - :emphasize-lines: 5,6 + :emphasize-lines: 5,6 jobs: build: @@ -56,3 +56,6 @@ We also add an if statement to the job so that it only runs on tags starting wit - name: Run upload uses: pypa/gh-action-pypi-publish@release/v1 + + +You will also need to `Add a Trusted Publisher `__ to your PyPI project. From 45fab3f99bb21eb825e60403f12b97706c8af326 Mon Sep 17 00:00:00 2001 From: Stuart Mumford Date: Mon, 26 Jan 2026 12:27:37 +0000 Subject: [PATCH 9/9] Attempt to fix docs --- docs/source/trusted_publishing.rst | 2 +- tox.ini | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/docs/source/trusted_publishing.rst b/docs/source/trusted_publishing.rst index 6888e9d..478e8e2 100644 --- a/docs/source/trusted_publishing.rst +++ b/docs/source/trusted_publishing.rst @@ -11,7 +11,7 @@ To work around this limitation the :ref:`oa-ghaw-publish` and :ref:`oa-ghaw-publ Taking the example from :ref:`oa-ghaw-publish`, we add two new lines: -.. code:: yaml +.. code-block:: yaml :emphasize-lines: 5,6 jobs: diff --git a/tox.ini b/tox.ini index bfb2569..dd2e56e 100644 --- a/tox.ini +++ b/tox.ini @@ -63,3 +63,22 @@ commands = conda: python -c "import os, sys; assert os.path.exists(os.path.join(sys.prefix, 'conda-meta', 'history'))" conda: micromamba list pytest --pyargs test_package {posargs} + +[testenv:build_docs] +changedir = docs +description = Invoke sphinx-build to build the HTML docs +deps = + -r docs/requirements.txt +commands = + pip freeze --all --no-input + sphinx-build \ + -j auto \ + --color \ + -W \ + --keep-going \ + -b html \ + -d _build/.doctrees \ + ./source \ + _build/html \ + {posargs} + python -c 'import pathlib; print("Documentation available under file://\{0\}".format(pathlib.Path(r"{toxinidir}") / "docs" / "_build" / "index.html"))'