7.1 KiB
Python-Paket auf Gitea veröffentlichen
Gitea kann als private oder öffentliche PyPI-Registry verwendet werden. Damit lassen sich eigene Python-Pakete bauen, versionieren und in anderen Projekten mit pip oder uv installieren.
Diese Anleitung verwendet utils als Beispielpaket und den Gitea-Server gitea.schneiderserver.site.
Voraussetzungen
- ein Python-Projekt mit
pyproject.toml - ein Gitea-Repository
uvfür Environment, Tests und Buildtwinefür das Veröffentlichen- ein privater Gitea Personal Access Token
Paket lokal bauen
Im Projektverzeichnis:
uv sync
uv run pytest
uv build
Danach liegen normalerweise zwei Dateien in dist/:
dist/utils-0.1.0.tar.gz
dist/utils-0.1.0-py3-none-any.whl
Vor einem erneuten Build kann dist/ entfernt werden:
rm -rf dist
uv build
Gitea-Endpunkte
Der Owner ist der Gitea-Benutzer oder die Organisation, unter der das Paket veröffentlicht wird. Er ist nicht zwingend der Repository-Name.
Publish-Endpunkt:
https://<gitea-host>/api/packages/<owner>/pypi
Installations-Endpunkt:
https://<gitea-host>/api/packages/<owner>/pypi/simple
Für den eigenen Server und den Benutzer mathias lautet das beispielsweise:
https://gitea.schneiderserver.site/api/packages/mathias/pypi
https://gitea.schneiderserver.site/api/packages/mathias/pypi/simple
Token
YOUR_GITEA_TOKEN steht für einen privaten Personal Access Token aus Gitea. Es handelt sich nicht um einen öffentlichen Token.
Für das Veröffentlichen wird mindestens eine Berechtigung zum Schreiben von Packages benötigt, typischerweise write:package. Für die Installation privater Packages wird read:package benötigt. Die genauen Namen können je nach Gitea-Version leicht abweichen.
Den Token niemals:
- in Git committen
- in eine README schreiben
- im Chat oder in Logs veröffentlichen
- dauerhaft direkt in die Shell-History schreiben
Für CI/CD sollte der Token als Gitea-Action-Secret hinterlegt werden, zum Beispiel als GITEA_TOKEN.
Manuell mit Twine veröffentlichen
Twine installieren:
uv tool install twine
~/.pypirc anlegen oder ergänzen:
[distutils]
index-servers =
gitea
[gitea]
repository = https://gitea.schneiderserver.site/api/packages/mathias/pypi
username = mathias
password = YOUR_PRIVATE_GITEA_TOKEN
Anschließend veröffentlichen:
python -m twine upload --repository gitea dist/*
Gitea akzeptiert denselben Paketnamen mit derselben Versionsnummer nicht zweimal. Für eine neue Veröffentlichung muss daher zuerst die Version in pyproject.toml erhöht werden:
version = "0.1.1"
Danach erneut bauen und veröffentlichen:
rm -rf dist
uv build
python -m twine upload --repository gitea dist/*
In einem anderen Projekt installieren
Öffentliches Paket
Wenn das Package und die Registry anonym lesbar sind:
pip install \
--index-url https://gitea.schneiderserver.site/api/packages/mathias/pypi/simple \
utils
Privates Paket
Für ein privates Package ist ein Token mit read:package erforderlich:
pip install \
--index-url https://<username>:<read-token>@gitea.schneiderserver.site/api/packages/mathias/pypi/simple \
utils
Dasselbe funktioniert mit uv:
uv pip install \
--index-url https://<username>:<read-token>@gitea.schneiderserver.site/api/packages/mathias/pypi/simple \
utils
Tokens besser nicht direkt in Befehle schreiben, weil sie in der Shell-History oder Prozessliste sichtbar werden können. Für lokale Nutzung besser einen Credential-Helper oder eine geschützte Konfigurationsdatei verwenden; in CI ausschließlich Secrets verwenden.
--index-url und --extra-index-url
Für private interne Packages ist --index-url vorzuziehen:
pip install --index-url <gitea-pypi-simple-url> utils
--extra-index-url kann zu Dependency-Confusion-Problemen führen, weil ein gleichnamiges öffentliches Paket von PyPI aufgelöst werden könnte. Falls zusätzlich öffentliche PyPI-Abhängigkeiten gebraucht werden, sollte die Paketquellen-Konfiguration bewusst und projektbezogen eingerichtet werden.
Git-Tags und Releases
Git-Tag und Paketversion sollten übereinstimmen:
pyproject.toml: version = "0.1.0"
Git-Tag: v0.1.0
Beispiel:
git add pyproject.toml
git commit -m "Release version 0.1.0"
git tag v0.1.0
git push origin main
git push origin v0.1.0
Das Tagging ist nicht zwingend für den manuellen Upload, macht Releases aber nachvollziehbar und kann später als Auslöser für Gitea Actions dienen.
Automatisierung mit Gitea Actions
Ein Workflow kann bei jedem v*-Tag automatisch testen, bauen und veröffentlichen. Die Zugangsdaten gehören als Repository-Secrets in Gitea und nicht in die Workflow-Datei.
Beispiel für .gitea/workflows/publish.yml:
name: Publish package
on:
push:
tags:
- "v*"
jobs:
publish:
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.14"
- name: Install uv
run: pip install uv
- name: Install dependencies
run: uv sync
- name: Run tests
run: uv run pytest
- name: Build package
run: uv build
- name: Publish to Gitea
env:
TWINE_USERNAME: ${{ secrets.GITEA_USERNAME }}
TWINE_PASSWORD: ${{ secrets.GITEA_TOKEN }}
TWINE_REPOSITORY_URL: https://gitea.schneiderserver.site/api/packages/mathias/pypi
run: uv run --with twine twine upload dist/*
Dazu in Gitea die Secrets GITEA_USERNAME und GITEA_TOKEN konfigurieren.
Fehlersuche: 404 Not Found
Bei einem Fehler wie:
HTTPError: 404 Not Found from https://gitea.schneiderserver.site/api/packages/mathias/pypi
ist der Token wahrscheinlich nicht das Hauptproblem. Ungültige Zugangsdaten führen meistens zu 401 Unauthorized oder 403 Forbidden.
Folgende Punkte prüfen:
- Ist
mathiasexakt der Gitea-Benutzername oder Organisationsname? - Wird der Repository-Owner statt des Repository-Namens verwendet?
- Ist die Package Registry in der Gitea-Instanz aktiviert?
- Wird der Gitea-Server über einen Reverse Proxy mit einem URL-Präfix betrieben?
- Wird der Publish-Endpunkt ohne
/simpleverwendet? - Ist der Token noch gültig und besitzt er
write:package?
Der Gitea-API-Endpunkt kann unabhängig davon erreichbar sein:
curl https://gitea.schneiderserver.site/api/v1/version
Der Publish-Endpunkt antwortet auf einen normalen GET-Aufruf möglicherweise mit 405 Method Not Allowed, weil er nur Upload-Anfragen akzeptiert. Das ist ein Hinweis darauf, dass der Endpunkt existiert; ein 404 deutet eher auf einen falschen Owner, einen falschen Pfad oder eine deaktivierte Registry hin.