When working with private Python repositories hosted on GitHub, ensuring your CI/CD pipeline can securely install dependencies can be tricky. Many developers default to using a Personal Access Token (PAT), but a more secure approach is to use a GitHub App to generate an OAuth token dynamically.
This guide walks through setting up a GitHub Actions workflow that:
uv
to install private dependencies from a second GitHub repository.Many developers use a Personal Access Token (PAT) to access private repositories. However:
A GitHub App provides a more secure, automated way to manage access for CI/CD workflows.
In the repository where CI runs (the dependent project):
GH_APP_ID
→ Your GitHub App ID.GH_APP_PRIVATE_KEY
→ The base64-encoded private key.To base64-encode the key (for multiline secret handling), run:
cat my-github-app.pem | base64 | pbcopy # MacOS
cat my-github-app.pem | base64 | xclip -selection clipboard # Linux
Here’s the updated tests.yml
file that:
uv
to install private dependencies.name: Tests with Coverage
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.10", 3.11, 3.12, 3.13]
os: [ubuntu-latest]
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up mise
uses: jdx/mise-action@v2
- uses: astral-sh/setup-uv@v5
with:
enable-cache: true
- name: Authenticate GitHub App and generate token
id: app-auth
uses: tibdex/github-app-token@v1
with:
app_id: $
private_key: $
- name: Force HTTPS instead of SSH for GitHub URLs
run: |
git config --global url."https://x-access-token:$@github.com/".insteadOf "ssh://git@github.com/"
- name: Install dependencies
run: |
uv venv
GITHUB_ACCESS_TOKEN=$ uv sync --all-extras
- name: Run tests with coverage
run: mise run test-coverage
Check your pyproject.toml
to make sure dependencies are installed via HTTPS, not SSH:
[tool.uv]
dependencies = [
"requests",
"numpy",
"git+https://github.com/2389-research/mbus.git@main"
]
If needed, allow the OAuth token to be passed dynamically:
[tool.uv]
dependencies = [
"git+https://x-access-token:${GITHUB_ACCESS_TOKEN}@github.com/2389-research/mbus.git@main"
]
tibdex/github-app-token
converts the App ID and Private Key into an OAuth token.contents:read
access to private repositories.git config
command replaces all ssh://git@github.com/
URLs with an authenticated HTTPS version.GITHUB_ACCESS_TOKEN
is injected so uv sync
can use it for installation.This setup ensures a secure, automated, and scalable approach for managing private Python dependencies in GitHub Actions. 🚀