Using a private gitlab repository with uv
Scenario:
- your uv source package (that lives in a gitlab source project) depends on a target package from a gitlab target project’s package registry.
- you want
uv add
etc. to work transparently - you want gitlab CI/CD pipelines to work transparently
Links
TL;DR
- Have a gitlab token with at least
read_api
scope and Developer+ role - Find the address of the registry through Gitlab UI
https://__token__:glpat-secret-token@gitlab.de/api/v4/projects/1111/packages/pypi/simple
https://gitlab.de/api/v4/projects/1111/packages/pypi/simple
- FROM A GROUP1:
https://gitlab.example.com/api/v4/groups/<group_id>/-/packages/pypi/simple
${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/pypi
- pyproject: add it registry, see below
- to authenticate UV, you can use:
- token inside URI, or
UV_INDEX_PRIVATE_REGISTRY_USERNAME/PASSWORD
env variables, replacing PRIVATE_REGISTRY with the name you gave to it in pyproject.toml~/.netrc
file: .netrc - everything curl
Gitlab
- In Gitlab, use/create an access token with read_xxx permissions in the project —
read_api, read_repository, read_registry
are enough — and Developer role. - To get the package registry address,
- Use this URI for the group registry (it’s the best for multiple projects in the same group):
https://gitlab.example.com/api/v4/groups/<group_id>/-/packages/pypi/simple
- You can get the group ID from the group page, hamburger menu in upper-right, “copy group id”. Will be an int.
- open the registry, click on a package, click “install” and you’ll see it all:
- different packages in different projects will have different registries!
- The project repository URI is generally
${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/pypi/simple
- Use this URI for the group registry (it’s the best for multiple projects in the same group):
UV
Add the new package registry as index
tool.uv.index
name = "my-registry"
url = "https://__token__:glpat-secret-token@gitlab.de/api/v4/projects/1111/packages/pypi/simple"
# authenticate = "always" # see below
# ignore-error-codes = [401]
The URI either contains token inside URI or doesn’t.
The examples below are /projects/xxx, ofc a group registry works as well.
- https://__token__:glpat-secret-token@gitlab.de/api/v4/projects/1111/packages/pypi/simple
- token inside the URI
- https://gitlab.de/api/v4/projects/1111/packages/pypi/simple
— auth happening through env. variables or ~/.netrc
Uv auth
Through the server URI
url = "https://__token__:glpat-secret-token@gitlab.de/api/v4/projects/1111/packages/pypi/simple"
Through env variables:
export UV_INDEX_PRIVATE_REGISTRY_USERNAME=__token__
export UV_INDEX_PRIVATE_REGISTRY_PASSWORD=glpat-secret-token
PRIVATE_REGISTRY
needs to be replaced with the name of the registry . So e.g. for the pyproject above it’s UV_INDEX_**MY_REGISTRY**_USERNAME
.
Through a .netrc file
From Authentication | uv / HTTP Authentication and PyPI packages in the package registry | GitLab Docs:
Create a ~/.netrc
:
machine gitlab.example.com
login __token__
password <personal_token>
It will use these details, when you uv add ... -v
you’d see a line like
DEBUG Checking netrc for credentials for https://gitlab.de/api/v4/projects/1111/packages/pypi/simple/packagename/
DEBUG Found credentials in netrc file for https://gitlab.de/api/v4/projects/1111/packages/pypi/simple/packagename/
NB git
will also use these credentials — so if the token’s scope doesn’t allow e.g. pushing, you won’t be able to git push
. Use a wider scope or a personal access token (or env. variables)
Usage
- When you
uv add yourpackage
, uv looks for packages in all registries- The usual
pypi
one is on by default
- The usual
- If gitlab doesn’t find one in the gitlab registry or the user is unauthenticated, gitlab by default transparently mirrors pypi
- If auth fails for any of the indexes, uv will fail loudly — you can
ignore-error-codes = [401]
to make uv keep looking inside the other registries
Gitlab CI/CD pipelines
CI/CD pipelines have to have access to the package as well, when they run.
GitLab CI/CD job token | GitLab Docs:
You can use a job token to authenticate with GitLab to access another group or project’s resources (the target project). By default, the job token’s group or project must be added to the target project’s allowlist.
In the target project (the one that needs to be resolved, the one with the private registry), in Settings->CI/CD -> Job token permissions add the source project (the one that will access the packages during CI/CD).
You can just add the group parent of all projects as well, then you don’t have to add any individual ones.
Then $CI_JOB_TOKEN
can be used to access the target projects. For example, through a ~/.netrc file (note the username!)
machine gitlab.example.com
username gitlab-ci-token
password $CI_JOB_TOKEN
Bonus round: gitlab-ci-local
I love firecow/gitlab-ci-local.
When running gitlab-ci-local things, the CI_JOB_TOKEN
variable is empty. You can create a .gitlab-ci-local-variables.yaml
(don’t forget to gitignore it!) with this variable, it’ll get used automatically and your local CI/CD pipelines will run as well:
CI_JOB_TOKEN=glpat-secret-token