Set up uv (Rust-based Python package manager) in CI/CD pipelines. Use when configuring GitHub Actions workflows, GitLab CI/CD, Docker builds, or matrix testing across Python versions. Includes...
This skill helps integrate uv (the fast Rust-based Python package manager) into CI/CD pipelines and containerized deployments. It provides proven patterns for GitHub Actions, GitLab CI, Docker, and PyPI publishing that optimize for performance, reliability, and maintainability.
GitHub Actions (basic CI workflow):
# Create .github/workflows/ci.yml
curl -s https://docs.astral.sh/uv/guides/integration/github/ | grep -A 30 "name: CI" > temp.yaml
Docker (production build):
FROM python:3.12-slim AS builder
COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
WORKDIR /app
COPY pyproject.toml uv.lock ./
RUN uv sync --frozen --no-dev --no-install-project
FROM python:3.12-slim
COPY --from=builder /app/.venv /app/.venv
COPY . .
ENV PATH="/app/.venv/bin:$PATH"
CMD ["python", "-m", "myapp"]
GitLab CI (basic pipeline):
# Install uv in before_script, sync dependencies, run tests
curl -LsSf https://astral.sh/uv/install.sh | sh
uv sync --all-extras --dev
uv run pytest
Identify where your code is deployed:
setup-uv action)For each platform, you'll set up uv installation, dependency caching, and frozen lockfile enforcement.
Why: Cache shared across workflow runs dramatically reduces CI time (10-100x faster warm starts).
GitHub Actions with setup-uv action:
- name: Install uv
uses: astral-sh/setup-uv@v6
with:
version: "0.9.8" # Optional: pin specific version
enable-cache: true # Enable dependency caching
cache-dependency-glob: "uv.lock" # Track changes to this file
GitLab CI with custom cache:
variables:
UV_CACHE_DIR: .uv-cache
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- .uv-cache
Docker (layer caching):
# Layer caching: Only rebuild if pyproject.toml or uv.lock changes
COPY pyproject.toml uv.lock ./
RUN uv sync --frozen --no-dev --no-install-project
Why: Test against multiple Python versions to ensure compatibility.
GitHub Actions with matrix:
strategy:
matrix:
python-version: ["3.11", "3.12", "3.13"]
steps:
- uses: astral-sh/setup-uv@v6
- run: uv python install ${{ matrix.python-version }}
env:
UV_PYTHON: ${{ matrix.python-version }}
- run: uv sync --all-extras --dev
- run: uv run pytest
GitLab CI with parallel jobs:
test:3.11:
image: python:3.11
script:
- curl -LsSf https://astral.sh/uv/install.sh | sh
- uv sync --all-extras --dev
- uv run pytest
test:3.12:
image: python:3.12
script:
- curl -LsSf https://astral.sh/uv/install.sh | sh
- uv sync --all-extras --dev
- uv run pytest
Why: Frozen lockfiles ensure exact reproducibility - prevents unexpected updates.
Command pattern:
# Fails if lockfile is out of sync with pyproject.toml
uv sync --frozen --no-dev
# For development environments (interactive)
uv sync --all-extras --dev
Docker production: Always use --frozen flag
RUN uv sync --frozen --no-dev --no-install-project
GitHub Actions CI:
- name: Sync with frozen lockfile
run: uv sync --frozen --all-extras --dev
Commit uv.lock to version control. Update it with uv lock --upgrade when ready.
Multi-stage Docker build (recommended for size/security):
# Stage 1: Builder - compile dependencies
FROM python:3.12-slim AS builder
COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
WORKDIR /app
COPY pyproject.toml uv.lock ./
RUN uv sync --frozen --no-dev --no-install-project
# Stage 2: Runtime - minimal image with only .venv
FROM python:3.12-slim
WORKDIR /app
COPY --from=builder /app/.venv /app/.venv
# Copy application code
COPY . .
# Ensure virtual environment is in PATH
ENV PATH="/app/.venv/bin:$PATH"
# Run application
CMD ["python", "-m", "myapp"]
Benefits:
Why: Trusted publishing (OIDC) is more secure than static tokens. No need to manage secrets.
GitHub Actions workflow:
name: Publish
on:
push:
tags:
- "v*"
jobs:
publish:
runs-on: ubuntu-latest
permissions:
id-token: write # Required for OIDC/trusted publishing
steps:
- uses: actions/checkout@v4
- name: Install uv
uses: astral-sh/setup-uv@v6
- name: Build distributions
run: uv build
- name: Publish to PyPI
run: uv publish
# No credentials needed - uses OIDC tokens
Setup in PyPI (one-time):
For custom index/private PyPI:
- name: Publish to custom index
run: uv publish --index-url https://example.org/pypi
env:
UV_PUBLISH_TOKEN: ${{ secrets.CUSTOM_PYPI_TOKEN }}
See examples/github-actions-complete.yml for a production-ready workflow including:
See examples/dockerfile-development for a development-optimized Dockerfile that includes:
See examples/gitlab-ci-complete.yml for a complete GitLab CI setup including:
See examples/pypi-publishing-workflow.yml for:
uv.lock must be committed to version control.python-version: Create with uv python pin 3.12