Paket veröffentlichen¶
Schließlich könnt ihr das Paket auf dem Python Package Index (PyPI) oder einem anderen Index, z.B. GitLab Package Registry oder devpi, bereitstellen.
Für den Python Package Index müsst ihr euch bei Test PyPI registrieren. Test-PyPI ist eine separate Instanz, die zum Testen und Experimentieren vorgesehen ist. Um dort ein Konto einzurichten, geht ihr auf https://test.pypi.org/account/register/. Weitere Informationen findet ihr unter Using TestPyPI.
Nun könnt ihr eine ~/.config/pip/pip.conf-Datei erstellen:
[distutils]
index-servers=
test
[test]
repository = https://test.pypi.org/legacy/
username = veit
Siehe auch
Wenn ihr die PyPI-Anmeldung automatisieren wollt, lest zunächst Careful With That PyPI.
Nachdem ihr registriert seid, könnt ihr euer Distribution Package mit
uv publish hochladen.
Dabei könnt ihr uv publish entweder mit der Option --username __token__
verwenden oder die Umgebungsvariable UV_PUBLISH_USERNAME=__token__ setzen,
um alle Archive unter /dist auf den Python Package Index
hochladen:
$ uv publish --publish-url https://test.pypi.org/legacy/ --username __token__ dist/*
--publish-urlDie URL des Upload-Endpunkts (nicht die Index-URL).
--usernameDen Benutzernamen für den Upload.
Bemerkung
Wenn ihr eine ähnliche Fehlermeldung erhaltet wie
The user 'veit' isn't allowed to upload to project 'example'
müsst ihr einen eindeutigen Namen für euer Paket auswählen:
ändert das
name-Argument in derpyproject.toml.-Dateientfernt das
dist-Verzeichnisgeneriert die Archive neu
Überprüfen¶
Installation¶
Ihr könnt uv verwenden um euer Paket von Test PyPI zu installieren und zu
überprüfen, ob es funktioniert:
uv add -i https://test.pypi.org/simple/ mypack
Bemerkung
Wenn ihr einen anderen Paketnamen verwendet habt als mypack, ersetzt ihn
im obigen Befehl durch euren Paketnamen.
uv add sollte das Paket von Test PyPI installieren und die Ausgabe sollte
in etwa so aussehen:
Resolved 8 packages in 5ms
Installed 7 packages in 36ms
+ mypack==0.1.0
Ihr könnt testen, ob euer Paket korrekt installiert wurde indem ihr main()
aufruft:
$ uv run mypack
Hello from mypack!
Bemerkung
Die Pakete auf Test-PyPI werden nur temporär gespeichert. Wenn ihr ein Paket in den echten Python Package Index (PyPI) hochladen wollt, könnt ihr dies tun, indem ihr ein Konto auf pypi.org anlegt.
README¶
Überprüft auch, ob die README.rst-Datei auf der Test-PyPI-Seite korrekt
angezeigt wird.
PyPI¶
Registriert euch nun beim Python Package Index (PyPI) und stellt
sicher, dass die Zwei-Faktor-Authentifizierung
aktiviert ist, indem ihr Folgendes in die Datei ~/.pypirc einfügt:
[distutils]
index-servers=
pypi
test
[test]
repository = https://test.pypi.org/legacy/
username = veit
[pypi]
username = __token__
Bei dieser Konfiguration wird für das Hochladen nicht mehr die Kombination aus Name und Passwort verwendet, sondern ein Upload-Token.
Siehe auch
Schließlich könnt ihr nun euer Paket auf PyPI veröffentlichen:
$ uv publish dist/*
Bemerkung
Ihr könnt Releases nicht einfach ersetzen da ihr Pakete mit derselben Versionsnummer nicht erneut hochladen könnt.
Bemerkung
Entfernt nicht alte Versionen aus dem Python Package Index. Dies verursacht
nur Arbeit für jene, die diese Version weiter verwenden wollen und dann auf
alte Versionen auf GitHub ausweichen müssen. PyPI hat eine yank-Funktion, die ihr stattdessen nutzen
könnt. Dies ignoriert eine bestimmte Version, wenn sie nicht explizit mit
== oder === angegeben wurde.
Siehe auch
GitHub Action¶
Ihr könnt auch eine GitHub-Aktion
erstellen, die ein Paket erstellt und auf PyPI hochlädt. Eine solche
.github/workflows/pypi.yml-Datei könnte folgendermaßen aussehen:
1name: Publish Python Package
2
3 on:
4 release:
5 types: [created]
6
7jobs:
8 test:
9 …
10 package-and-deploy:
11 runs-on: ubuntu-latest
12 needs: [test]
13 steps:
14 - name: Checkout
15 uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
16 with:
17 fetch-depth: 0
18 - name: Set up Python
19 uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
20 with:
21 python-version-file: .python-version
22 cache-dependency-path: '**/pyproject.toml'
23 - name: Setup cached uv
24 uses: hynek/setup-cached-uv@4300ec2180bc77d705e626a34e381b81a4772c51 # v2.5.0
25 - name: Create venv
26 run: |
27 uv venv
28 echo "$PWD/.venv/bin" >> $GITHUB_PATH
29 - name: Build
30 run: |
31 uv build
32 - name: Retrieve and publish
33 steps:
34 - name: Retrieve release distributions
35 uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
36 - name: Publish package distributions to PyPI
37 uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0
38 with:
39 username: __token__
40 password: ${{ secrets.PYPI_TOKEN }}
- Zeilen 3–5
Dies stellt sicher, dass der Arbeitsablauf jedes Mal ausgeführt wird, wenn ein neues GitHub-Release für das Repository erstellt wird.
- Zeile 12
Der Job wartet auf das Bestehen des
test-Jobs bevor er ausgeführt wird.- Zeile 31
Hier sollte
mypackdurch euren Paketnamen ersetzt werden.- Zeile 36
Die GitHub-Aktion actions/download-artifact stellt die gebauten Verteilungspakete bereit.
- Zeile 38–41
Die GitHub-Aktion pypa/gh-action-pypi-publish veröffentlicht die Pakete mit dem Upload-Token auf PyPI.
Siehe auch
Absichern des Release-Workflows¶
Continuous Deployment zur Veröffentlichung von Python-Paketen sind ein beliebtes Ziel für Angriffe. Ihr könnt viele der Gefahren vermeiden, indem ihr einige wenige Sicherheitsempfehlungen befolgt:
- Vermeidet unsichere Trigger
Workflows, die bei einem Angriff ausgelöst werden können, insbesondere Eingaben, die dieser kontrolliert (wie Pull Requests- oder Branch-Titel), wurden in der Vergangenheit genutzt, Befehle einzuschleusen. Insbesondere der Trigger pull_request_target sollte vermieden werden.
- Säubert Parameter und Eingaben
Jeder Workflow-Parameter oder jede Eingabe, die sich zu einem ausführbaren Befehl erweitern lässt, birgt das Potenzial, bei Angriffen ausgenutzt zu werden. Säubert Werte, indem ihr sie als Umgebungsvariablen an Befehle übergebt, um Template-Injection-Angriffe zu vermeiden.
- Vermeidet veränderbare Referenzen
fixiert eure Abhängigkeiten in Workflows.
Bevorzugt Git-Commit-SHA-Werte anstelle von Git-Tags, da Tags änderbar sind.
Tipp
Für die Aktualisierung der GitHub Actions und Dependency Cooldowns könnt ihr pinact verwenden, also z. B.:
$ pinact run -u --min-age 7
Verwendet eine uv.lock-Datei für PyPI-Abhängigkeiten, die in Workflows verwendet wird.
- Verwendet überprüfbare Deployments
Mit Trusted Publishers könnt ihr überprüfbare GitHub-Umgebungen für den Bau eurer Python-Pakete verwenden. Falls ihr GitHub Actions für die kontinuierliche Bereitstellung nutzt, solltet ihr zizmor verwenden um unsichere Workflows zu erkennen und zu beheben.
Trusted Publishers¶
Trusted Publishers ist ein Verfahren zum Veröffentlichen von Paketen auf dem PyPI. Es basiert auf OpenID Connect und erfordert weder Passwort noch Token. Dazu sind lediglich die folgenden Schritte erforderlich:
Fügt einen Trusted Publisher auf PyPI hinzu
Je nachdem, ob ihr ein neues Paket veröffentlichen oder ein bestehendes aktualisieren wollt, unterscheidet sich der Prozess geringfügig:
zum Aktualisieren eines bestehenden Pakets siehe Adding a trusted publisher to an existing PyPI project
zum veröffentlichen eines neuen Pakets gibt es ein spezielles Verfahren, Pending Publisher genannt; s.a. Creating a PyPI project with a trusted publisher
Ihr könnt damit auch einen Paketnamen reservieren, bevor ihr die erste Version veröffentlicht. Damit könnt ihr sicherstellen, dass ihr das Paket auch unter dem gewünschten Namen veröffentlichen könnt.
Hierfür müsst ihr in pypi.org/manage/account/publishing/ einen neuen Pending Publisher erstellen mit
Namen des PyPI-Projekts
GitHub-Repository Owner
Namen des Workflows, z.B.
publish.ymlName der Umgebung (optional), z.B.
release
Erstellt eine Umgebung für die GitHub-Actions
Wenn wir eine Umgebung auf PyPI angegeben haben, müssen wir diese nun auch erstellen. Das kann in für das Repository geschehen. Der Name unserer Umgebung ist
release.Konfiguriert den Arbeitsablauf
Hierfür erstellen wir nun die Datei
.github/workflows/publish.ymlin unserem Repository:.github/workflows/pypi.yml¶10package-and-deploy: 11 runs-on: ubuntu-latest 12+ environment: release 13+ permissions: 14+ id-token: write 15 needs: [test] 16 steps:
- Zeile 12
Die Angabe einer GitHub-Umgebung ist optional, wird aber dringend empfohlen.
- Zeilen 13–14
Die
write-Berechtigung ist für Trusted Publishing erforderlich.- Zeilen 40–44
usernameundpasswordwerden für die GitHub-Aktionpypa/gh-action-pypi-publishnicht mehr benötigt..github/workflows/pypi.yml¶40- name: Publish package distributions to PyPI 41 uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0 42 with: 43 username: __token__ 44 password: ${{ secrets.PYPI_TOKEN }}
Seit 14. November 2024 unterstützt PyPI auch PEP 740 mit Digital Attestations. PyPI verwendet das in-toto Attestation Framework zum Ausstellen der Digital Attestations SLSA Provenance und PyPI Publish Attestation (v1).
Die Erstellung und Veröffentlichung erfolgt standardmäßig, sofern über Trusted Publishing und die GitHub-Action pypa/gh-action-pypi-publish zum Veröffentlichen verwendet werden:
jobs:
pypi-publish:
name: Upload release to PyPI
runs-on: ubuntu-latest
environment:
name: pypi
url: https://pypi.org/p/{YOUR-PYPI-PROJECT-NAME}
permissions:
id-token: write
steps:
- name: Publish package distributions to PyPI
uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0
Bemerkung
Die Unterstützung für die automatische Erstellung von Digital Attestations und die Veröffentlichung aus anderen Trusted Publisher-Umgebungen ist geplant.
Siehe auch
zizmor¶
zizmor kann viele Sicherheitsprobleme in typischen CI/CD-Konfigurationen von GitHub Actions aufspüren und beheben. zizmor ist für die Integration in GitHub Actions konzipiert. Eine typische GitHub Action von uns für zizmor sieht so aus:
# https://github.com/woodruffw/zizmor
name: Zizmor
on:
push:
branches: ["main"]
pull_request:
branches: ["**"]
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
permissions: {}
jobs:
zizmor:
name: Run zizmor
runs-on: ubuntu-latest
permissions:
security-events: write # Required for upload-sarif (used by zizmor-action) to upload SARIF files.
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Run zizmor
uses: zizmorcore/zizmor-action@71321a20a9ded102f6e9ce5718a2fcec2c4f70d8 # v0.5.2
with:
persona: pedantic
2FA für alle Entwicklungskonten¶
Ihr solltet Zwei-Faktor-Authentifizierung für alle eure Konten nutzen, die mit der Entwicklung in Verbindung stehen – nicht nur für PyPI. Denkt an eure Konten für die Versionskontrolle (GitHub, GitLab, Codeberg, Forgejo) und E-Mail.