-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
ceaf50a
commit b319a12
Showing
1 changed file
with
217 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,217 @@ | ||
--- | ||
layout: post | ||
title: "Distributing Python packages in a private GitHub organization" | ||
tags: [python] | ||
--- | ||
|
||
There's a challenge in distributing Python packages in a private GitHub organization with private repositories. This post will try to clear this up a bit. | ||
|
||
<!--more--> | ||
|
||
## Background | ||
|
||
If you develop your Python package project with Poetry in a public GitHub repository, it's easy to add it as a dependency to a Poetry project with `poetry add`. | ||
|
||
The `poetry.lock` file will then contain the commit sha, and to update the package you'll have to consider whether you want to pull down latest commit from the specified branch using `poetry update` or e.g. manually update the desired git tag in `pyproject.toml` followed by a `poetry update`. See the [docs on git dependencies](https://python-poetry.org/docs/dependency-specification/#git-dependencies) for more details. | ||
|
||
The nice part is this just works. But since you're developing in the open, you might as well also publish your package to pypi.org so that distributing and consuming your Python package will become a lot simpler. | ||
|
||
However, if you are developing Python packages in a private GitHub organization, using private repositories and using GitHub Actions - this whole distribution story becomes a lot more challenging if you cannot host your own internal [pypiserver](https://github.com/pypiserver/pypiserver) and since PyPi does not (yet) offer private package hosting. | ||
|
||
## Poetry 1.1 | ||
|
||
As of writing this, Poetry 1.1.13 is out, so that is what I assume here. | ||
|
||
### Using git+https | ||
|
||
#### Locally | ||
|
||
If you [create a personal access token (PAT)](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token) with read permissions and authenticate it with your private organization, you can add a private Python package git repo using "git+https": | ||
|
||
```bash | ||
poetry add git+https://github.com/privateorg/privaterepo.git#main | ||
``` | ||
|
||
You should see something like this in the `pyproject.toml`: | ||
|
||
```toml | ||
[tool.poetry.dependencies] | ||
privaterepo = {git = "https://github.com/privateorg/privaterepo.git", rev = "main"} | ||
``` | ||
|
||
#### GitHub Actions | ||
|
||
To make this work with GitHub Actions (GHA) in the private organization/repositories, you have to [set up an organization secret](https://docs.github.com/en/actions/security-guides/encrypted-secrets#creating-encrypted-secrets-for-an-organization) which gives GHA read access to the Python project git repo. I'll call my secret `POETRY_GIT_TOKEN`. | ||
|
||
Then you can install the project e.g. like this: | ||
|
||
```yaml | ||
name: check | ||
|
||
on: | ||
workflow_dispatch: | ||
pull_request: | ||
push: | ||
branches: | ||
- main | ||
|
||
env: | ||
PYTHON_VERSION: "3.10" | ||
PIPX_VERSION: "1.0" | ||
POETRY_VERSION: "1.1.13" | ||
|
||
jobs: | ||
format: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@v3 | ||
|
||
- uses: actions/setup-python@v3 | ||
with: | ||
python-version: ${{ env.PYTHON_VERSION }} | ||
|
||
- uses: fredrikaverpil/[email protected] | ||
with: | ||
pipx-version: ${{ env.PIPX_VERSION }} | ||
|
||
- run: pipx install poetry==${{ env.POETRY_VERSION }} | ||
|
||
- uses: actions/cache@v3 | ||
id: cache | ||
with: | ||
path: | | ||
~/.cache/pip | ||
~/.cache/pypoetry/virtualenvs | ||
.venv_pipx | ||
key: ${{ runner.os }}-${{ runner.arch }}-py-${{ env.PYTHON_VERSION }}-{{ env.pythonLocation }}-pipx-${{ env.PIPX_VERSION }}-poetry-${{ env.POETRY_VERSION }}-${{ hashFiles('poetry.lock') }} | ||
|
||
- uses: de-vri-es/setup-git-credentials@v2 | ||
with: | ||
credentials: https://${GITHUB_ACTOR}:${{secrets.POETRY_GIT_TOKEN}}@github.com/ | ||
|
||
- run: poetry install | ||
- run: poetry run black --check --diff src | ||
``` | ||
### Using git+ssh | ||
#### Locally | ||
This is how you locally add a dependency with "git+ssh": | ||
``` | ||
poetry add git+ssh://[email protected]/privateorg/privaterepo.git#main | ||
``` | ||
|
||
Note that no PAT is needed! | ||
|
||
You should now see something like this in `pyproject.toml`: | ||
|
||
```toml | ||
[tool.poetry.dependencies] | ||
privaterepo = {git = "ssh://[email protected]/privateorg/privaterepo.git", rev = "main"} | ||
``` | ||
|
||
#### Github Actions | ||
|
||
The same setup as for "git+https" will work here. | ||
|
||
## Poetry 1.2 | ||
|
||
### Using git+https | ||
|
||
#### Locally | ||
|
||
You'll have to install poetry from master or, like I did, specifically from PR #5581: | ||
|
||
```bash | ||
$ pipx install --suffix=@5581 git+https://github.com/python-poetry/poetry.git@refs/pull/5581/head | ||
|
||
$ poetry@5581 --version | ||
Poetry (version 1.2.0b2.dev0) | ||
``` | ||
|
||
Git credentials, as explained in the Poetry 1.1 part of this post, should still work on 1.2. But it will be a fallback since Poetry 1.2 will first try using [dulwich](https://github.com/jelmer/dulwich) - which is a pure-python git implementation. | ||
|
||
Thanks to using dulwich and building on top of existing mechanisms to register the git repo along with git+https credentials, the following is possible with Poetry 1.2: | ||
|
||
```bash | ||
poetry@5581 config repositories.my-git-repo https://github.com/org/project.git | ||
poetry@5581 config http-basic.my-git-repo username token | ||
poetry@5581 add git+https://github.com/org/project.git | ||
``` | ||
|
||
Locally, you would replace `username` with your GitHub username and `token` with your PAT. | ||
|
||
#### GitHub Actions | ||
|
||
To make this fly in GHA, you need that organization token described for Poetry 1.1. Then you can use environment variables corresponding to the configuration you added locally. | ||
|
||
|
||
```yaml | ||
name: check | ||
|
||
on: | ||
workflow_dispatch: | ||
pull_request: | ||
push: | ||
branches: | ||
- main | ||
|
||
env: | ||
PYTHON_VERSION: "3.10" | ||
PIPX_VERSION: "1.0" | ||
POETRY_VERSION: "PR-5881" | ||
|
||
jobs: | ||
format: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@v3 | ||
|
||
- uses: actions/setup-python@v3 | ||
with: | ||
python-version: ${{ env.PYTHON_VERSION }} | ||
|
||
- uses: fredrikaverpil/[email protected] | ||
with: | ||
pipx-version: ${{ env.PIPX_VERSION }} | ||
|
||
- run: pipx install git+https://github.com/python-poetry/poetry.git@refs/pull/5581/head | ||
|
||
- uses: actions/cache@v3 | ||
id: cache | ||
with: | ||
path: | | ||
~/.cache/pip | ||
~/.cache/pypoetry/virtualenvs | ||
.venv_pipx | ||
key: ${{ runner.os }}-${{ runner.arch }}-py-${{ env.PYTHON_VERSION }}-{{ env.pythonLocation }}-pipx-${{ env.PIPX_VERSION }}-poetry-${{ env.POETRY_VERSION }}-${{ hashFiles('poetry.lock') }} | ||
|
||
- name: poetry install | ||
env: | ||
POETRY_REPOSITORIES_MYGITREPO_URL: https://github.com/org/project.git | ||
POETRY_HTTP_BASIC_MYGITREPO_USERNAME: anythingshouldbeokayhere | ||
POETRY_HTTP_BASIC_MYGITREPO_PASSWORD: ${{ secrets.POETRY_GIT_TOKEN }} | ||
run: | | ||
poetry install | ||
- run: poetry run black --check --diff src | ||
``` | ||
### Using git+ssh | ||
TBD | ||
## Summary | ||
### Poetry 1.1 and git+ssh | ||
Pros: | ||
- Poetry 1.1.13 is out now, this works now. | ||
Cons: | ||
- A bit finnicky maintenance of commit sha or git tag. | ||
- Requires a PAT. | ||
- Feels weird there isn't an easier way to tell GHA to use a secret token for "git+https" access. |