You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: tutorials/pyproject-toml.md
+3-3Lines changed: 3 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -468,7 +468,7 @@ The classifier key should look something like the example below. A few notes:
468
468
- Your classifier values might be different depending upon your intended audience, development status of your package and the Python versions that you support
469
469
- You can add as many classifiers as you wish as long as you use the [designated PyPI classifier values](https://PyPI.org/classifiers/).
470
470
471
-
{emphasize-lines="26-34"}
471
+
{emphasize-lines="26-33"}
472
472
```toml
473
473
[build-system]
474
474
requires = ["hatchling"]
@@ -514,10 +514,10 @@ Finally, add the project.urls table to your pyproject.toml file.
514
514
`project.urls` contains links that are relevant for your project. You might want to include:
515
515
516
516
-**Homepage:** A link to your published documentation for your project. If you are working through this tutorial, then you may not have this link yet. That's ok, you can skip it for the time being.
517
-
-**Bug reports:** a link to your issues / discussions or wherever you want users to report bugs.
517
+
-**Bug reports:** a link to your issues/discussions or wherever you want users to report bugs.
518
518
-**Source:** the GitHub / GitLab link for your project.
Copy file name to clipboardExpand all lines: tutorials/trusted-publishing.md
+55-31Lines changed: 55 additions & 31 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -16,8 +16,8 @@ In the previous Python packaging lessons, you learned:
16
16
In this lesson, you will learn how to:
17
17
18
18
- Automate building and publishing the package on GitHub Actions
19
-
- Configure trusted publishing for the project
20
-
- Secure your workflow using action step GitHub hashes and versions
19
+
- Configure PyPI Trusted Publishing for the project
20
+
- Secure your workflow using GitHub action hashes and versions in your workflow file
21
21
22
22
This tutorial assumes that your project is hosted on GitHub and that you want
23
23
to publish a package from your project to PyPI.
@@ -28,9 +28,15 @@ to publish a package from your project to PyPI.
28
28
GitHub Actions[^gha] is an infrastructure provided by GitHub to automate
29
29
software workflows, straight from the GitHub repository of the project. You can
30
30
configure automated testing for every pull request, automate publishing of
31
-
documentation, automate creation of webpages for the project, and even automate
32
-
the release process. For this lesson, we will only focus on using actions to release
33
-
and publish your Python package securely.
31
+
documentation, automate creation of web pages for the project, and even automate
32
+
the release process. For this lesson, we will focus on using actions to release
33
+
and publish your Python package securely to PyPI.
34
+
35
+
:::{admonition} Why Trusted Publishing Matters
36
+
37
+
If you are wondering why trusted publishing is so important, [check out this blog post:](https://www.pyopensci.org/blog/python-packaging-security-publish-pypi.html) that dives deeper into what can happen when you don't lock down your publishing workflows.
38
+
:::
39
+
34
40
35
41
### Step 0: Create a release workflow
36
42
@@ -44,7 +50,7 @@ GitHub's convention that all GitHub Actions are configured via YAML files in the
44
50
45
51
You can name the workflow file whatever you wish. We suggest using something
46
52
simple and expressive like `release.yaml` so you, your future self, and contributors
47
-
that work on your project know exactly what the workflow does.
53
+
who work on your project know exactly what the workflow does.
48
54
:::
49
55
50
56
### Step 1: Name the workflow
@@ -55,7 +61,7 @@ At the top of the `release.yaml` file, type the following:
55
61
name: Release
56
62
```
57
63
58
-
This provides a name to the workflow. It allows you to quickly find all runs of
64
+
This provides a name to the workflow that you can use to quickly find all runs of
59
65
this GitHub Action on the "Actions" tab in the GitHub repository.
@@ -66,9 +72,10 @@ This image shows an example of a configured workflow for the release. On the top
66
72
67
73
### Step 2: Add triggers to the workflow
68
74
69
-
Every GitHub Actions workflow runs only when [certain conditions](https://docs.github.com/en/actions/reference/events-that-trigger-workflows) are met. A
75
+
Every GitHub Actions workflow runs when [certain conditions](https://docs.github.com/en/actions/reference/events-that-trigger-workflows) are met. In this case,
76
+
we assume that a
70
77
release workflow should only run when the repository owner creates a new [release](https://docs.github.com/en/repositories/releasing-projects-on-github/managing-releases-in-a-repository)
71
-
for the package. Add the following to the `release.yaml` file:
78
+
for the package. Add the following to the `release.yaml` file to ensure it runs when you create and publish a release:
72
79
73
80
```yaml
74
81
on:
@@ -83,14 +90,14 @@ A GitHub Actions *workflow* file can contain multiple *jobs* that run independen
83
90
When triggered, the GitHub Action runs all the jobs in a workflow (excluding any steps that have conditional requirements).
84
91
85
92
:::{note}
86
-
Jobs and steps can also have [conditional logic](https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idif) that allows them only to run if specific criteria exist. For instance, you may want only to have a job step to publish to PyPI if a release was made for the package.
93
+
Jobs and steps can also have [conditional logic](https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idif) that allows them only to run if specific criteria exist. For instance, you may want only to have a job step to publish to PyPI if a release was made for the package. But you might want to test building the package every time you merge a new pull request.
87
94
:::
88
95
89
-
For a release job, we need to clone or check out the repository. We use the actions/checkout action to check out the code. You then use `hatch` to build
90
-
the package.
96
+
For a release job, you need to clone or check out the repository. You can use the `actions/checkout` action to check out the code. You then install and use `hatch` to build
97
+
your package.
91
98
92
99
You also need to make sure to set up Hatch on the machine GitHub is
93
-
using to run the workflow. The `pypa/hatch` action installs Hatch.
100
+
using to run the workflow.
94
101
95
102
A minimal job definition would look like this:
96
103
@@ -100,9 +107,16 @@ jobs:
100
107
name: Build the package
101
108
runs-on: ubuntu-latest
102
109
steps:
103
-
- uses: actions/checkout@v5 # This is the version of the checkout job that checks out your code.
104
-
- uses: pypa/hatch
105
-
- run: hatch build
110
+
- uses: actions/checkout@v5
111
+
- name: Setup Python
112
+
uses: actions/setup-python@v6
113
+
with:
114
+
python-version: "3.12" # Select the version that you want to build your package on
115
+
- name: Upgrade pip, install Hatch, and check Hatch version
116
+
run: |
117
+
pip install --upgrade pip
118
+
pip install --upgrade hatch
119
+
hatch --version # Verify that Hatch is installed
106
120
```
107
121
108
122
Notice that above, you provide a version for each action step. `action/checkout@v5` tells GitHub to use version 5 of the checkout action. The checkout action checks out the code from your repository. In this case, the code will be used to build your package.
the repository will ensure that you always get a PR to keep the actions up to
130
-
date. Once Dependabot is enabled, it will update these hashes for you in the future!
143
+
the repository will ensure that your actions stay up to date. The dependabot tool will open
144
+
pull requests that update your action versions at whatever frequency you want.
131
145
:::
132
146
133
147
Thus, the workflow that you should use should be similar to:
@@ -160,7 +174,7 @@ the following to the `release.yaml` file:
160
174
:class: tip
161
175
162
176
Above, you have configured the artifact to be deleted after 1 day. The artifacts storage
163
-
on GitHub actions is temporary; users should not be getting the package from here.
177
+
on GitHub actions is temporary; users should not download your package from the GitHub artifacts.
164
178
165
179
You have also configured the release job to error if the `dist/` directory does
166
180
not exist. This means that `hatch build` (from the previous step) failed to
@@ -221,32 +235,42 @@ the previous section:
221
235
222
236
:::{admonition} Make sure to change the URL
223
237
224
-
Remember to change the `url:` to the URL for your package on PyPI!
238
+
Remember to change the `url:` value to the URL for your package on PyPI!
225
239
:::
226
240
227
241
This job has two steps:
228
242
229
-
- as discussed above, it uses `download-artifact` to download the artifacts
243
+
* It uses `download-artifact` to download the artifacts
230
244
built in the previous job
231
-
- it uses `gh-action-pypi-publish` to publish the package to PyPI.
245
+
* It uses `gh-action-pypi-publish` to publish the package to PyPI.
232
246
233
-
You are almost there!! Now, you just need to enable trusted publishing for the project
247
+
You are almost there!! Now, you just need to enable trusted publishing for your project
234
248
on PyPI. And then, your work is done!
235
249
236
250
### Step 2: Enable trusted publishing on PyPI
237
251
252
+
:::{figure-md} trusted-publishing-image
253
+
254
+
<img src="../images/trusted-publisher-pypi-github.png" alt="Diagram showing PyPI's trusted publisher workflow: Step 1 builds distribution files via GitHub, Step 2 uses a trusted environment (PyPI), Step 3 securely uploads to PyPI. Shows chain of trust with lock icon connecting GitHub Action to Python Package Index." width="800px">
255
+
256
+
This lesson is the first in a series of lessons to help you get started with Python packaging.
257
+
:::
258
+
238
259
Before trusted publishing was created, in order to upload to PyPI from GitHub
239
260
actions you would have needed to add the username and password as arguments to
240
261
the `gh-action-pypi-publish` step. While documentation recommends using the
241
262
GitHub's `secrets` environment for the password/token, in several cases, users
242
-
were pasting it directly in the workflow file. Furthermore, accidental leakage
243
-
of the token could allow attackers to publish new packages in your name, until
244
-
you discover the compromise and revoke the leaked credential.
263
+
were pasting the password directly into the workflow file. Furthermore, accidental leakage
264
+
of the password or token could allow attackers to publish new packages using your account, until
265
+
you discover the compromise and revoke the leaked credentials.
266
+
267
+
To prevent these incidents and improve supply chain security, developers created [Trusted Publishing](https://docs.pypi.org/trusted-publishers/). Trusted publishing allows you to register a
268
+
publishing workflow on
269
+
PyPI and then map that workflow to an automation workflow (e.g., GitHub Actions) that is allowed
270
+
to publish the package.
245
271
246
-
To prevent these incidents and improve supply chain security
247
-
developers created [Trusted Publishing](https://docs.pypi.org/trusted-publishers/). This allows registering publishers on
248
-
PyPI and mapping them to the automation workflow that is allowed to publish the
249
-
package.
272
+
You do not need to enter a token or password value in a trusted publisher workflow. It's a
273
+
secure connection between your
250
274
251
275
:::{admonition} Trusted Publishing outside of GitHub Actions
252
276
:class: tip
@@ -316,7 +340,7 @@ The workflow above should be up to date with the current versions of GitHub acti
316
340
317
341
## You have enabled trusted publishing for your project
318
342
319
-
Congratulations. You have now configured your project to do secure releases when a new version is being tagged on GitHub. The workflow we have configured builds the package from the exact version of code that we are tagging. This provides a guarantee for your users that the package we have released does exactly what the code states it does -- there is no potential for supply chain related vulnerabilities arising from our package! If you have a package that is ready for real-world use on the real PyPI, then you can follow the same steps to publish it securely.
343
+
Congratulations!! You have now configured your project to do secure releases when a new version is being tagged on GitHub. The workflow we have configured builds the package from the exact version of code that we are tagging. This provides a guarantee for your users that the package that you have released does precisely what the code states it does. There is little to no potential for supply chain related vulnerabilities arising from your package! If you have a package that is ready for real-world use on the real PyPI, then you can follow the same steps to publish it securely.
0 commit comments