Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
7340a02
ci: use uv for test dependency installation + add timeout safety net
RonShakutai Jul 12, 2026
a4a9d91
ci: run tests via uv run inside the uv-managed venv
RonShakutai Jul 12, 2026
abdff65
ci: install uv via pip instead of the setup-uv action
RonShakutai Jul 12, 2026
7d085b6
ci: add pip to the uv venv for spaCy model downloads
RonShakutai Jul 12, 2026
04d3d6a
ci: pre-install tomli for the wheel build on Python < 3.11
RonShakutai Jul 12, 2026
470db8a
ci: build service Docker images with uv to fix the E2E resolve hang
RonShakutai Jul 12, 2026
ed64f1b
fix: run gunicorn directly in container entrypoints (drop poetry run)
RonShakutai Jul 12, 2026
2c95fd8
ci: keep uv's wheel cache on the /mnt ephemeral disk
RonShakutai Jul 12, 2026
078eff5
docs: broaden CHANGELOG/CI comment to cover the Docker image changes
RonShakutai Jul 12, 2026
157082d
build: adopt PEP 735 dependency groups and commit uv.lock files
RonShakutai Jul 12, 2026
27907e2
ci: install from the committed uv.lock in CI and Docker builds
RonShakutai Jul 12, 2026
c00abf0
ci: allow pre-existing transformers advisories in dependency review
RonShakutai Jul 12, 2026
448fa13
docs: require uv.lock review and commits alongside pyproject changes
RonShakutai Jul 12, 2026
2897687
ci: reference UV_CACHE_DIR in cache path; align dev-docs to uv
RonShakutai Jul 12, 2026
1bae43e
Potential fix for pull request finding
RonShakutai Jul 12, 2026
078bdb4
ci: shorten workflow comments; move transformers allow-ghsas out
RonShakutai Jul 13, 2026
a70aa3e
ci: drop stale pip cache; trim CHANGELOG entry
RonShakutai Jul 13, 2026
4368d4b
small commit to trigger the pipeline again.
RonShakutai Jul 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
# Package dependencies and version changes should be approved by a member of 'presidio-administrators' team
**/pyproject.toml @data-privacy-stack/presidio-administrators
**/uv.lock @data-privacy-stack/presidio-administrators
26 changes: 17 additions & 9 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -570,7 +570,7 @@ Use atomic grouping: (?>a+)b or possessive quantifier a++b"

### Technology Stack
- **Python** - Must support all versions
- **Poetry** - Package manager, not pip
- **uv** - Dependency management and installation (not pip or Poetry). Each package commits a `uv.lock`; `poetry-core` is retained only as the build backend for now.
- **Ruff** - Linting and formatting (replaces flake8, black, isort)
Comment thread
SharonHart marked this conversation as resolved.
Comment thread
SharonHart marked this conversation as resolved.
- **spaCy** - Default NLP engine (en_core_web_lg for production), although one can use other NLP engines via provider pattern
- **Docker** - Deployment via GitHub Container Registry (`ghcr.io/data-privacy-stack`)
Expand All @@ -587,20 +587,28 @@ Use atomic grouping: (?>a+)b or possessive quantifier a++b"

### Local Development
```bash
# Setup
# Setup (uv reads the committed uv.lock; --locked fails if it is stale)
cd presidio-analyzer # or presidio-anonymizer, presidio-cli, etc.
poetry install --all-extras
poetry run python -m spacy download en_core_web_lg # For analyzer/CLI only
uv sync --locked --all-extras --group dev
Comment thread
RonShakutai marked this conversation as resolved.
uv run python -m spacy download en_core_web_lg # For analyzer/CLI only

# Run tests
poetry run pytest -xvv # Stop on first failure with verbose output
poetry run pytest tests/test_us_ssn_recognizer.py -k "test_valid" # Specific test
uv run pytest -xvv # Stop on first failure with verbose output
uv run pytest tests/test_us_ssn_recognizer.py -k "test_valid" # Specific test

# Lint
ruff check . # From repo root
ruff format . # Auto-format
uv run ruff check .
uv run ruff format .
```

> **Dependency changes:** whenever you edit a package's `pyproject.toml`
> dependencies (add/remove/bump `[project]` deps, extras, or
> `[dependency-groups]`), you MUST regenerate and commit that package's
> `uv.lock` in the same change (`cd <package> && uv lock`). CI installs with
> `uv sync --locked` and fails if `pyproject.toml` and `uv.lock` are out of
> sync, so an updated `pyproject.toml` without its matching `uv.lock` will
> break the build.

### Docker Testing
```bash
# Quick test with pre-built images
Expand All @@ -624,7 +632,7 @@ pytest -v # Run all E2E tests
## Common Issues to Watch For

### Build/Test Issues
- **Poetry version conflicts** - Use `poetry lock --no-update` to preserve versions
- **Stale `uv.lock`** - If `uv sync --locked` fails with "lockfile needs to be updated", run `uv lock` in that package and commit the result.
- **Missing spaCy models** - Download en_core_web_lg before running tests
- **AHDS test skips** - Expected when AHDS_ENDPOINT not set
- **Transformers test failures** - Expected without HuggingFace access in restricted environments
Expand Down
65 changes: 45 additions & 20 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ jobs:
test:
name: Test ${{ matrix.component.name }} (Python ${{ matrix.python-version }})
runs-on: ubuntu-latest
timeout-minutes: 60
Comment thread
RonShakutai marked this conversation as resolved.
permissions:
contents: write
pull-requests: write
Expand Down Expand Up @@ -96,9 +97,13 @@ jobs:
extras: '--all-extras'
spacy-models: 'en_core_web_lg en_core_web_sm'
env:
POETRY_CACHE_DIR: /mnt/poetry_cache
COVERAGE_THRESHOLD: 90
PRIMARY_PYTHON: '3.13'
# uv's wheel cache lives on the roomy /mnt disk (like the old
# POETRY_CACHE_DIR); it is persisted across runs by "Cache uv downloads".
# UV_LINK_MODE=copy avoids cross-filesystem hardlink warnings.
UV_CACHE_DIR: /mnt/uv-cache
UV_LINK_MODE: copy
steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.0
Expand All @@ -110,22 +115,25 @@ jobs:
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: ${{ matrix.python-version }}
cache: 'pip'
cache-dependency-path: ${{ matrix.component.path }}/pyproject.toml

- name: Install Poetry
run: |
python -m pip install poetry==2.3.2
- name: Install uv
run: python -m pip install uv==0.11.6
Comment thread
RonShakutai marked this conversation as resolved.
Comment thread
RonShakutai marked this conversation as resolved.

- name: Setup Poetry Cache Directory
- name: Prepare uv cache directory on /mnt
run: |
sudo mkdir -p $POETRY_CACHE_DIR
sudo chown -R runner $POETRY_CACHE_DIR
# Remove stale virtualenvs to prevent cache corruption on re-run attempts.
# GitHub Actions may reuse the same runner VM when re-running failed jobs,
# which leaves the old /mnt/poetry_cache intact. Installing a new numpy
# version over an existing one causes mixed file state and IndentationErrors.
rm -rf $POETRY_CACHE_DIR/virtualenvs 2>/dev/null || true
# /mnt is root-owned, so create the cache dir and hand it to the
# runner user before uv (or the cache restore) writes to it.
sudo mkdir -p "$UV_CACHE_DIR"
sudo chown -R "$(id -u):$(id -g)" "$UV_CACHE_DIR"

- name: Cache uv downloads
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.0
with:
path: ${{ env.UV_CACHE_DIR }}
key: uv-${{ runner.os }}-${{ matrix.component.path }}-py${{ matrix.python-version }}-${{ hashFiles(format('{0}/uv.lock', matrix.component.path)) }}
restore-keys: |
uv-${{ runner.os }}-${{ matrix.component.path }}-py${{ matrix.python-version }}-
uv-${{ runner.os }}-${{ matrix.component.path }}-

- name: Install system dependencies for Image Redactor
if: matrix.component.name == 'Image Redactor'
Expand All @@ -134,33 +142,40 @@ jobs:
sudo apt-get install tesseract-ocr -y

- name: Install dependencies
timeout-minutes: 30
working-directory: ${{ matrix.component.path }}
run: |
poetry install ${{ matrix.component.extras }}
# Install the exact locked graph; --locked fails on pyproject/uv.lock
# drift instead of resolving new versions. The dev group provides the
# test tooling plus pip (spaCy's model download shells out to pip).
uv sync --locked ${{ matrix.component.extras }} --group dev --python ${{ matrix.python-version }}

- name: Install presidio-analyzer for Image Redactor
if: matrix.component.name == 'Image Redactor'
working-directory: ${{ matrix.component.path }}
run: |
poetry run pip install -e ../presidio-analyzer/.
# Override the locked PyPI presidio-analyzer with the in-repo copy so
# tests run against local analyzer changes; later steps use
# `uv run --no-sync` so uv won't revert this editable install.
uv pip install -e ../presidio-analyzer/.

- name: Download spaCy models
if: matrix.component.spacy-models != ''
working-directory: ${{ matrix.component.path }}
run: |
for model in ${{ matrix.component.spacy-models }}; do
poetry run python -m spacy download $model
uv run --no-sync python -m spacy download $model
Comment thread
RonShakutai marked this conversation as resolved.
done

- name: Run tests with coverage
working-directory: ${{ matrix.component.path }}
run: |
PACKAGE_NAME=$(echo "${{ matrix.component.path }}" | sed 's/-/_/g')
poetry run pytest --cov=$PACKAGE_NAME --cov-report=xml:coverage.xml --cov-report=term --cov-report=html -vv --tb=short
uv run --no-sync pytest --cov=$PACKAGE_NAME --cov-report=xml:coverage.xml --cov-report=term --cov-report=html -vv --tb=short
Comment thread
RonShakutai marked this conversation as resolved.

if [ "${{ github.event_name }}" = "pull_request" ]; then
echo "Checking PR difference coverage (>= ${{ env.COVERAGE_THRESHOLD }}%) with diff-cover..."
poetry run diff-cover coverage.xml \
uv run --no-sync diff-cover coverage.xml \
Comment thread
RonShakutai marked this conversation as resolved.
--compare-branch=origin/${{ github.base_ref }} \
--fail-under=${{ env.COVERAGE_THRESHOLD }} \
--show-uncovered
Expand All @@ -180,9 +195,19 @@ jobs:
- name: Build wheel package
working-directory: ${{ matrix.component.path }}
run: |
# `build` needs tomli on Python < 3.11 to read pyproject.toml, and it
# isn't in the hashed requirements file; pre-install it (marker-guarded)
# so --require-hashes finds it already satisfied.
python -m pip install "tomli==2.4.1; python_version < '3.11'"
pip install --require-hashes -r ${{ github.workspace }}/.github/pipelines/requirements-build.txt
Comment on lines +198 to 202
python -m build --wheel

- name: Minimize uv cache
if: always()
run: |
# Trim the persisted cache (recommended for CI by the uv docs).
uv cache prune --ci

build-platform-images:
name: Build ${{ matrix.image }} (${{ matrix.platform }})
runs-on: ${{ matrix.runner }}
Expand Down Expand Up @@ -341,7 +366,7 @@ jobs:
run: |
docker compose -f docker-compose.yml down

# Build and test Docker images locally for non-main branches without publishing.
# Build and test Docker images locally for non-main branches without publishing
local-build-and-test:
name: Local Build and E2E Tests (${{ matrix.platform }})
runs-on: ${{ matrix.runner }}
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ All notable changes to this project will be documented in this file.

### Anonymizer
### General
#### Changed
- Migrated CI and service-image dependency installation from Poetry to [uv](https://github.com/astral-sh/uv) with committed lockfiles, fixing prolonged dependency-resolution hangs and making builds reproducible.
#### Fixed
- Retried the Zensical documentation build on transient crashes (e.g. SIGKILL/exit 247) so the docs release pipeline no longer fails intermittently (Thanks @Copilot)

Expand Down
16 changes: 10 additions & 6 deletions presidio-analyzer/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ ARG NLP_CONF_FILE=presidio_analyzer/conf/default.yaml
ARG ANALYZER_CONF_FILE=presidio_analyzer/conf/default_analyzer.yaml
ARG RECOGNIZER_REGISTRY_CONF_FILE=presidio_analyzer/conf/default_recognizers.yaml
ENV PIP_NO_CACHE_DIR=1
ENV POETRY_VIRTUALENVS_CREATE=false
# Install the locked dependencies into the system environment (no venv).
ENV UV_PROJECT_ENVIRONMENT=/usr/local

ENV ANALYZER_CONF_FILE=${ANALYZER_CONF_FILE}
ENV RECOGNIZER_REGISTRY_CONF_FILE=${RECOGNIZER_REGISTRY_CONF_FILE}
Expand All @@ -24,16 +25,19 @@ RUN apt-get update \
&& apt-get install curl --no-install-recommends -y \
&& rm -rf /var/lib/apt/lists/*

COPY ./pyproject.toml /app/
COPY ./pyproject.toml ./uv.lock /app/

RUN pip install poetry==2.3.2 \
&& poetry install --no-root --only=main -E server \
&& rm -rf $(poetry config cache-dir)
# Install exactly the locked dependency graph (main + server extra, no dev
# group, project itself not installed — it is run from the copied source).
# Splitting this from the source COPY keeps the dependency layer cached until
# pyproject.toml/uv.lock change.
RUN pip install uv==0.11.6 \
&& uv sync --locked --no-cache --no-default-groups --extra server --no-install-project

# install nlp models specified in NLP_CONF_FILE or via nlp_configuration in ANALYZER_CONF_FILE
COPY ./install_nlp_models.py /app/

RUN poetry run python install_nlp_models.py \
RUN python install_nlp_models.py \
Comment thread
RonShakutai marked this conversation as resolved.
--conf_file ${NLP_CONF_FILE} \
--analyzer_conf_file ${ANALYZER_CONF_FILE}

Expand Down
2 changes: 1 addition & 1 deletion presidio-analyzer/entrypoint.sh
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
#!/bin/sh
exec poetry run gunicorn -w "$WORKERS" -b "0.0.0.0:$PORT" "app:create_app()"
exec gunicorn -w "$WORKERS" -b "0.0.0.0:$PORT" "app:create_app()"
Comment thread
RonShakutai marked this conversation as resolved.
20 changes: 11 additions & 9 deletions presidio-analyzer/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -73,15 +73,17 @@ langextract = [
"jinja2 (>=3.0.0,<4.0.0)",
]

[tool.poetry.group.dev.dependencies]
pip = "*"
ruff = "*"
pytest = "*"
pytest-cov = "*"
pytest-mock = "*"
python-dotenv = "*"
pre_commit = "*"
diff-cover = "*"
[dependency-groups]
dev = [
Comment thread
RonShakutai marked this conversation as resolved.
"pip",
"ruff",
"pytest",
"pytest-cov",
"pytest-mock",
"python-dotenv",
"pre-commit",
"diff-cover",
]
Comment thread
RonShakutai marked this conversation as resolved.

[tool.coverage.run]
relative_files = true
Expand Down
Loading
Loading