@@ -160,9 +160,9 @@ Finally, you can publish your package on PyPI:
160160GitHub Action
161161-------------
162162
163- You can also create a GitHub action, which creates a package and uploads it to
164- PyPI at every time a release is created. Such a
165- :file: `.github/workflows/pypi.yml ` file could look like this:
163+ You can also create a ` GitHub Action < https://github.com/features/actions >`_,
164+ which creates a package and uploads it to PyPI at every time a release is
165+ created. Such a :file: `.github/workflows/pypi.yml ` file could look like this:
166166
167167.. code-block :: yaml
168168 :caption : .github/workflows/pypi.yml
@@ -183,16 +183,16 @@ PyPI at every time a release is created. Such a
183183 needs : [test]
184184 steps :
185185 - name : Checkout
186- uses : actions/checkout@v4
186+ uses : actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
187187 with :
188188 fetch-depth : 0
189189 - name : Set up Python
190- uses : actions/setup-python@v5
190+ uses : actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
191191 with :
192192 python-version-file : .python-version
193193 cache-dependency-path : ' **/pyproject.toml'
194194 - name : Setup cached uv
195- uses : hynek/setup-cached-uv@v2
195+ uses : hynek/setup-cached-uv@4300ec2180bc77d705e626a34e381b81a4772c51 # v2.5.0
196196 - name : Create venv
197197 run : |
198198 uv venv
@@ -203,9 +203,9 @@ PyPI at every time a release is created. Such a
203203 - name : Retrieve and publish
204204 steps :
205205 - name : Retrieve release distributions
206- uses : actions/download-artifact@v4
206+ uses : actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
207207 - name : Publish package distributions to PyPI
208- uses : pypa/gh-action-pypi-publish@release/v1
208+ uses : pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0
209209 with :
210210 username : __token__
211211 password : ${{ secrets.PYPI_TOKEN }}
@@ -218,26 +218,88 @@ Line 12
218218Line 31
219219 Here :samp: `{ mypack } ` should be replaced by your package name.
220220Line 36
221- The GitHub action ``actions/download-artifact `` provides the built
221+ The GitHub action `actions/download-artifact
222+ <https://github.com/actions/download-artifact> `_ provides the built
222223 distribution packages.
223224Lines 38–41
224- The GitHub action ``pypa/gh-action-pypi-publish `` publishes the packages
225+ The GitHub action `pypa/gh-action-pypi-publish
226+ <https://github.com/pypa/gh-action-pypi-publish> `_ publishes the packages
225227 with the upload token on :term: `PyPI `.
226228
227229.. seealso ::
228230
229231 * `GitHub Actions <https://docs.github.com/en/actions >`_
232+ * :doc: `cibuildwheel `
233+
234+ .. _secure-release-workflow :
235+
236+ Securing the release workflow
237+ -----------------------------
238+
239+ Continuous deployment systems used to publish Python packages are a popular
240+ target for attacks. You can avoid many of these risks by following a few
241+ security recommendations:
242+
243+ Avoid insecure triggers
244+ Workflows that can be triggered by an attacker, particularly those that rely
245+ on inputs controlled by the attacker (such as :ref: `pull request
246+ <merge-pull-requests>` or :doc: `branch
247+ <Python4DataScience:productive/git/branch>` titles), have been used in the
248+ past to inject commands. In particular, the `pull_request_target
249+ <https://docs.github.com/en/actions/reference/workflows-and-actions/events-that-trigger-workflows#pull_request_target> `_
250+ trigger in :ref: `github-actions ` should be avoided.
251+
252+ .. seealso ::
253+ * `Keeping your GitHub Actions and workflows secure Part 2: Untrusted
254+ input
255+ <https://securitylab.github.com/resources/github-actions-untrusted-input/> `_
256+ * `Mitigating Attack Vectors in GitHub Workflows: Workflow triggers
257+ <https://openssf.org/blog/2024/08/12/mitigating-attack-vectors-in-github-workflows/> `_
258+ * `Misuse of pull_request_target trigger
259+ <https://securitylab.github.com/resources/github-actions-new-patterns-and-mitigations/#misuse-of-pull_request_target-trigger> `_
260+
261+ Sanitise parameters and inputs
262+ Any workflow parameter or input that can be expanded into an executable
263+ command has the potential to be exploited in attacks. Sanitise values by
264+ passing them to commands as environment variables to prevent
265+ `template injection <https://docs.zizmor.sh/audits/#template-injection >`_
266+ attacks.
267+ Avoid mutable references
268+ Fix your dependencies in workflows.
269+
270+ * Prefer Git commit `SHA
271+ <https://en.wikipedia.org/wiki/Secure_Hash_Algorithms> `_ values over
272+ :doc: `Git tags <Python4DataScience:productive/git/tag >`, as tags are
273+ mutable.
274+
275+ .. tip ::
276+ * Für die Aktualisierung der GitHub Actions und `Dependency Cooldowns
277+ <https://blog.yossarian.net/2025/11/21/We-should-all-be-using-dependency-cooldowns> `_
278+ könnt ihr `pinact <https://github.com/suzuki-shunsuke/pinact >`_
279+ verwenden, also :abbr: `z. B. ( zum Beispiel ) `:
280+
281+ .. code-block :: console
282+
283+ $ pinact run -u --min-age 7
284+
285+ * Use a :ref: `uv_lock ` file for PyPI dependencies used in workflows.
286+
287+ Use verifiable deployments
288+ With :ref: `trusted_publishers `, you can use verifiable GitHub environments
289+ to build your Python packages. If you use GitHub Actions for continuous
290+ delivery, you should use :ref: `zizmorcore ` to detect and fix insecure
291+ workflows.
230292
231293.. _trusted_publishers :
232294
233295Trusted Publishers
234- ------------------
296+ ~~~~~~~~~~~~~~~~~~
235297
236298`Trusted Publishers <https://docs.pypi.org/trusted-publishers/ >`_ is a procedure
237299for publishing packages on the :term: `PyPI `. It is based on OpenID Connect and
238300requires neither a password nor a token. Only the following steps are required:
239301
240- #. Add a *Trusted Publishers * on PyPI
302+ #. Add a *Trusted Publisher * on PyPI
241303
242304 Depending on whether you want to publish a new package or update an existing
243305 one, the process is slightly different:
@@ -276,7 +338,7 @@ requires neither a password nor a token. Only the following steps are required:
276338 .. code-block :: diff
277339 :caption: .github/workflows/pypi.yml
278340 :lineno-start: 10
279- :emphasize-lines: 3, 4 -5
341+ :emphasize-lines: 3-5
280342
281343 package-and-deploy:
282344 runs-on: ubuntu-latest
@@ -292,24 +354,19 @@ requires neither a password nor a token. Only the following steps are required:
292354 Lines 13–14
293355 The ``write `` authorisation is required for *Trusted Publishing *.
294356
295- Zeilen 42 –44
357+ Zeilen 40 –44
296358 ``username `` and ``password `` are no longer required for the GitHub
297359 action ``pypa/gh-action-pypi-publish ``.
298360
299- .. code-block :: diff
361+ .. code-block :: yaml
300362 :lineno-start : 40
301363 :emphasize-lines : 3-
302364
303- - name: Publish package distributions to PyPI
304- uses: pypa/gh-action-pypi-publish@release/v1
305- - with:
306- - username: __token__
307- - password: ${{ secrets.PYPI_TOKEN }}
308-
309- .. _digital-attestations :
310-
311- Digital Attestations
312- --------------------
365+ - name : Publish package distributions to PyPI
366+ uses : pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0
367+ with :
368+ username : __token__
369+ password : ${{ secrets.PYPI_TOKEN }}
313370
314371 Since 14 November 2024, :term: `PyPI ` also supports :pep: `740 ` with `Digital
315372Attestations <https://docs.pypi.org/attestations/> `_. PyPI uses the
@@ -337,7 +394,7 @@ are used for publishing:
337394 id-token : write
338395 steps :
339396 - name : Publish package distributions to PyPI
340- uses : pypa/gh-action-pypi-publish@release/v1
397+ uses : pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0
341398
342399 .. note ::
343400 Support for the automatic creation of digital attestations and publishing
@@ -346,3 +403,57 @@ are used for publishing:
346403.. seealso ::
347404 `PyPI now supports digital attestations
348405 <https://blog.pypi.org/posts/2024-11-14-pypi-now-supports-digital-attestations/> `_
406+
407+ .. _zizmorcore :
408+
409+ zizmor
410+ ~~~~~~
411+
412+ `zizmor <https://docs.zizmor.sh >`_ can detect and resolve many security issues
413+ in typical GitHub Actions CI/CD configurations. zizmor is designed to integrate
414+ with GitHub Actions. A typical GitHub Action we use for zizmor looks like this:
415+
416+ .. code-block :: yaml
417+ :caption : .github/workflows/zizmor.yml
418+
419+ # https://github.com/woodruffw/zizmor
420+ name : Zizmor
421+
422+ on :
423+ push :
424+ branches : ["main"]
425+ pull_request :
426+ branches : ["**"]
427+
428+ concurrency :
429+ group : ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
430+ cancel-in-progress : true
431+
432+ permissions : {}
433+
434+ jobs :
435+ zizmor :
436+ name : Run zizmor
437+ runs-on : ubuntu-latest
438+ permissions :
439+ security-events : write # Required for upload-sarif (used by zizmor-action) to upload SARIF files.
440+ steps :
441+ - name : Checkout repository
442+ uses : actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
443+ with :
444+ persist-credentials : false
445+ - name : Run zizmor
446+ uses : zizmorcore/zizmor-action@71321a20a9ded102f6e9ce5718a2fcec2c4f70d8 # v0.5.2
447+ with :
448+ persona : pedantic
449+
450+ .. _add_2fa :
451+
452+ 2FA for all development accounts
453+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
454+
455+ You should use two-factor authentication for all your accounts related to
456+ development – not just for :term: `PyPI `. Remember your version control accounts
457+ (`GitHub <https://github.com/ >`_, `GitLab <https://about.gitlab.com/ >`_,
458+ `Codeberg <https://codeberg.org/ >`_, `Forgejo <https://forgejo.org/ >`_) and
459+ email.
0 commit comments