Artifact feeds#
Artifacts Feeds are organizational constructs that allow you to store, manage, and group your packages and control who to share it with. Feeds are not package-type dependent. You can store all the following package types in a single feed: npm, NuGet, Maven, Python, and Universal packages.
Create feed#
If there is no feed created yet or you just want a separate one you can follow this guide to set one up.
In general you don’t need upstream sources enabled as poetry
will default to pypi. Mirroring on ADO tends to be slow as well and if accidentally a wrong package gets imported the whole feed is immutable and can’t be fixed.
Keep in mind that in the python ecosystem packages are expected to be unique up to name and version, so two wheels with the same package name and version are treated as indistinguishable by pip. This is a deliberate feature of the package metadata, and not likely to change. So while poetry in itself can support this by specifying the source
directly
poetry add --source <SOURCE> <PACKAGE>
other tools, like pip, do not support this and probably never will. A simple fix to mitigate this potential issue:
Use a prefix for your package name so your package is unique in the python ecosystem. Be sure to check pypi if your package name already exists. For instance,
kpv-superb-package
. It also has the benefit of clearly indicating that a package is private.
You could rely on poetry combined with the source
specification to do the right thing. This means that poetry install
is the only valid way to fetch the correct packages. Even poetry export
will not adhere to the source as it generates a pip compliant requirements.txt
which does not support index prioritization.
Configure poetry#
When there is a need to incorporate packages from a (private) ADO feed (i.e. an extra index), poetry needs to know where and how to connect to it.
The pyproject.toml
file needs a [[tool.poetry.source]]
section to specify an additional index available for packages. Poetry will still use pypi as default and first choice.
[[tool.poetry.source]]
name = "<SOURCE>"
url = "https://pkgs.dev.azure.com/<ORG>/<PRJ>/_packaging/<FEED_NAME>/pypi/simple/"
secondary = true
<SOURCE>
: A name for the source, you can use<FEED_NAME>
for transparency.<FEED_NAME>
: The name of the artifact feed you want to use.<ORG>
: Your Azure DevOps organization.<PRJ>
: Your Azure Devops project.
Note
To obtain the url you can check in ADO on the Artifacts page -> Connect to feed -> choose pip -> copy the url.
In most case the Azure Artifact feed will be private so setting up credentials is required. See the next section for information on how to set this up.
Set credentials for Azure DevOps artifact feeds#
To be able to authenticate you need to generate a personal token. In ADO you can generate a Personal Access Token linked with your user account, see this guide for the setup process.
It’s best to setup a least privilege tokens with an expiration date. To read feeds you need Packaging -> Read scope as a minimum. Take note of the PAT as you will need it to setup the authentication mechanism for the tools in this project. Don;t forget that you will need to repeat this auth setup when your PAT expires in the future.
poetry#
Run the following poetry command (preferred) where username
is your Azure DevOps account email and password
is the PAT token. The <SOURCE>
is the name of the source section in pyproject.toml
. If you setup an keyring on your system it will use that or fallback to a text based config located at ~/.config/pypoetry/auth.toml
.
> poetry config http-basic.<SOURCE> username password
or manual configure ~/.config/pypoetry/auth.toml
as:
[http-basic]
[http-basic.<SOURCE>]
username = <Azure Devops account email>
password = <Personal Access Token>
See [[tool.poetry.source]]
section in pyproject.toml
to find the value for <SOURCE>
. It is the name that was given to the Azure Artifacts feed URL (name = "<SOURCE>"
).
As an alternative you can use environment variables as well.
export POETRY_HTTP_BASIC_<SOURCE>_USERNAME=<username>
export POETRY_HTTP_BASIC_<SOURCE>_PASSWORD=<PAT>
Now poetry install --no-root
and other poetry commands should work with the private feed. Keep in mind that you will need to recreate your PAT when it expires.
Note
In case you have problems with authentication check
Remove
.netrc
from your home dirCheck if poetry config does not have repositories specified (
poetry config --list
)
twine#
Used to upload packages (optional), normally covered by pipeline. You will need Read & Write packaging scope for your PAT.
Configure ~/.pypirc
as:
[distutils]
index-servers = <SOURCE>
[<SOURCE>]
repository = https://pkgs.dev.azure.com/<ORG>/<PRJ>/_packaging/<FEED_NAME>/pypi/upload/
username = <username>
password = <Personal Access Token>
or configure the environment variables:
export TWINE_USERNAME=<username> - the username to use for authentication to the repository.
export TWINE_PASSWORD=<Personal Access Token> - the PAT to use for authentication to the repository.
Now run twine upload -r <SOURCE> dist/*
to upload all packages in your dist folder.
pip#
Python package install, normally not needed as poetry takes care of that (optional)
Analog approach with .netrc
file. See here for more information.
Keep in mind that index order is not guaranteed!