Skip to content

Commit 998f244

Browse files
authored
Switch to uv (#1213)
* Switch to uv * Run tests in parallel
1 parent 254824a commit 998f244

File tree

18 files changed

+1708
-2561
lines changed

18 files changed

+1708
-2561
lines changed

.dockerignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
!/bin
66
!/config
77
!/jbi
8-
!/poetry.lock
8+
!/uv.lock
99
!/pyproject.toml
1010
!/version.json
1111

.github/workflows/build-publish.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,8 @@ jobs:
102102
103103
- name: Check that container is running
104104
run: |
105-
docker exec ${{ env.TEST_CONTAINER_NAME }} python bin/healthcheck.py
105+
docker exec ${{ env.TEST_CONTAINER_NAME }} curl --retry 10 --retry-delay 1 --retry-connrefused http://127.0.0.1:8000/__heartbeat__
106+
106107
107108
- name: Spin down container
108109
run: |

.github/workflows/ci.yaml

Lines changed: 13 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -7,39 +7,26 @@ jobs:
77
runs-on: ubuntu-latest
88
steps:
99
- uses: actions/checkout@v5
10-
- uses: actions/setup-python@v6
11-
id: setup-python
10+
- name: Install uv
11+
uses: astral-sh/setup-uv@v7
1212
with:
13-
python-version: "3.13"
14-
- name: Install poetry
15-
run: pipx install poetry
16-
- uses: actions/cache@v4
17-
with:
18-
path: .venv
19-
key: poetry-${{ steps.setup-python.outputs.python-version}}-${{ hashFiles('poetry.lock') }}
20-
- name: Install dependencies
21-
run: make install
22-
- run: bin/lint.sh
13+
enable-cache: true
14+
python-version: 3.13
15+
- name: Run lint
16+
run: make lint
2317
run_test:
2418
runs-on: ubuntu-latest
2519
steps:
26-
- uses: actions/checkout@v5
27-
- uses: actions/setup-python@v6
28-
id: setup-python
29-
with:
30-
python-version: "3.13"
3120
- name: Install pandoc
3221
run: sudo apt-get install -y pandoc
33-
- name: Install poetry
34-
run: pipx install poetry
35-
- uses: actions/cache@v4
22+
- uses: actions/checkout@v5
23+
- name: Install uv
24+
uses: astral-sh/setup-uv@v7
3625
with:
37-
path: .venv
38-
key: poetry-${{ steps.setup-python.outputs.python-version}}-${{ hashFiles('poetry.lock') }}
39-
- name: Install dependencies
40-
run: make install
26+
enable-cache: true
27+
python-version: 3.13
4128
- name: Run tests
42-
run: bin/test.sh
29+
run: make tests
4330
- name: Run retry
4431
env:
4532
JBI_API_KEY: key # pragma: allowlist secret
@@ -48,7 +35,7 @@ jobs:
4835
BUGZILLA_API_KEY: key # pragma: allowlist secret
4936
DL_QUEUE_CONSTANT_RETRY: false
5037
DL_QUEUE_DSN: "file:///tmp/dlqueue"
51-
run: .venv/bin/python -m jbi.retry
38+
run: uv run python -m jbi.retry
5239
review-dependabot-pr:
5340
permissions:
5441
contents: write

Dockerfile

Lines changed: 28 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,39 @@
1-
# Creating a python base with shared environment variables
2-
FROM python:3.14.0 AS base
3-
ENV PIP_NO_CACHE_DIR=off \
4-
PIP_DEFAULT_TIMEOUT=100 \
5-
PIP_DISABLE_PIP_VERSION_CHECK=on \
6-
POETRY_HOME="/opt/poetry" \
7-
POETRY_NO_INTERACTION=1 \
8-
POETRY_VIRTUALENVS_IN_PROJECT=true \
9-
PYSETUP_PATH="/opt/pysetup"
10-
11-
ENV PATH="$POETRY_HOME/bin:$PATH"
12-
13-
# Install Poetry - respects $POETRY_VERSION & $POETRY_HOME
14-
RUN python3 -m venv $POETRY_HOME && \
15-
$POETRY_HOME/bin/pip install poetry==1.7.1 && \
16-
$POETRY_HOME/bin/poetry --version
17-
18-
# We copy our Python requirements here to cache them
19-
# and install only runtime deps using poetry
20-
WORKDIR $PYSETUP_PATH
21-
COPY ./poetry.lock ./pyproject.toml ./
22-
RUN $POETRY_HOME/bin/poetry install --without dev --no-root
23-
24-
# `production` stage uses the dependencies downloaded in the `base` stage
25-
FROM python:3.14.0-slim AS production
1+
FROM python:3.14.0-slim
262

27-
# Install pandoc for markdown to Jira conversions.
28-
RUN apt-get -y update && \
29-
apt-get -y install --no-install-recommends pandoc
30-
31-
ENV PORT=8000 \
32-
PYTHONUNBUFFERED=1 \
3+
ENV PYTHONUNBUFFERED=1 \
334
PYTHONDONTWRITEBYTECODE=1 \
34-
VIRTUAL_ENV=/opt/pysetup/.venv
35-
36-
ENV PATH="$VIRTUAL_ENV/bin:$PATH"
37-
38-
COPY --from=base $VIRTUAL_ENV $VIRTUAL_ENV
5+
HOST=0.0.0.0 \
6+
PORT=8000
397

408
ARG userid=10001
419
ARG groupid=10001
4210
RUN groupadd --gid $groupid app && \
4311
useradd -g app --uid $userid --shell /usr/sbin/nologin --create-home app
44-
USER app
12+
13+
# Install pandoc for markdown to Jira conversions.
14+
RUN apt-get -y update && \
15+
apt-get -y install --no-install-recommends pandoc curl
16+
17+
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
18+
19+
COPY ./pyproject.toml /app/
20+
COPY ./uv.lock /app/
21+
22+
ENV UV_CACHE_DIR=/opt/uv-cache
23+
RUN mkdir -p "${UV_CACHE_DIR}" && \
24+
chown app:app "${UV_CACHE_DIR}"
4525

4626
WORKDIR /app
47-
COPY . .
27+
COPY . /app
28+
29+
# Install dependencies
30+
RUN --mount=type=cache,target="${UV_CACHE_DIR}" \
31+
--mount=type=bind,source=uv.lock,target=uv.lock \
32+
--mount=type=bind,source=pyproject.toml,target=pyproject.toml \
33+
uv sync --no-dev --locked --no-install-project --no-editable
34+
35+
# run as non priviledged user
36+
USER app
4837

4938
EXPOSE $PORT
50-
CMD ["python", "-m", "asgi"]
39+
CMD ["uv", "run", "--no-dev", "--frozen", "python", "-m", "asgi"]

Makefile

Lines changed: 23 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -8,69 +8,56 @@ VENV := $(shell echo $${VIRTUAL_ENV-.venv})
88
INSTALL_STAMP = $(VENV)/.install.stamp
99
DOTENV_FILE = .env
1010

11-
.PHONY: help
11+
12+
INSTALL_STAMP := .install.stamp
13+
UV := $(shell command -v uv 2> /dev/null)
14+
1215
help:
13-
@echo "Usage: make RULE"
14-
@echo ""
15-
@echo "JBI make rules:"
16-
@echo ""
17-
@echo "Local"
18-
@echo " clean - clean local cache folders"
19-
@echo " format - run linting checks, fix in place"
20-
@echo " lint - run linting checks"
21-
@echo " start - run the API service locally"
22-
@echo " test - run test suite"
23-
@echo ""
24-
@echo "Docker"
25-
@echo " build - build docker container"
26-
@echo " docker-start - run the API service through docker"
27-
@echo " docker-shell - open a shell in the web container"
28-
@echo ""
29-
@echo " help - see this text"
16+
@echo "Please use 'make <target>' where <target> is one of the following commands.\n"
17+
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' Makefile | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
18+
@echo "\nCheck the Makefile to know exactly what each target is doing."
3019

3120
.PHONY: clean
32-
clean:
21+
clean: ## Delete cache files
3322
find . -name "__pycache__" | xargs rm -rf
3423
rm -rf .mypy_cache .ruff_cache .coverage .venv
3524

36-
$(VENV)/bin/python:
37-
python3 -m venv $(VENV)
38-
39-
install: $(VENV)/bin/python $(INSTALL_STAMP)
40-
$(INSTALL_STAMP): poetry.lock
41-
@if [ -z $(shell command -v poetry 2> /dev/null) ]; then echo "Poetry could not be found. See https://python-poetry.org/docs/"; exit 2; fi
42-
POETRY_VIRTUALENVS_IN_PROJECT=1 poetry install --with dev --no-root
25+
install: $(INSTALL_STAMP)
26+
$(INSTALL_STAMP): pyproject.toml uv.lock ## Install dependencies
27+
@if [ -z $(UV) ]; then echo "uv could not be found. See https://docs.astral.sh/uv/"; exit 2; fi
28+
$(UV) --version
29+
$(UV) sync --frozen --verbose
4330
touch $(INSTALL_STAMP)
4431

4532
.PHONY: build
46-
build:
33+
build: ## Build docker container
4734
docker-compose build \
4835
--build-arg userid=${_UID} --build-arg groupid=${_GID}
4936

5037
.PHONY: format
51-
format: $(INSTALL_STAMP)
38+
format: $(INSTALL_STAMP) ## Format code base
5239
bin/lint.sh lint --fix
5340
bin/lint.sh format --fix
5441

5542
.PHONY: lint
56-
lint: $(INSTALL_STAMP)
43+
lint: $(INSTALL_STAMP) ## Analyze code base
5744
bin/lint.sh
5845

5946
.PHONY: start
60-
start: $(INSTALL_STAMP) $(DOTENV_FILE)
61-
poetry run python -m asgi
47+
start: $(INSTALL_STAMP) $(DOTENV_FILE) ## Start local
48+
$(UV) run python -m asgi
6249

63-
$(DOTENV_FILE):
50+
$(DOTENV_FILE): ## Initialize default configuration
6451
cp .env.example $(DOTENV_FILE)
6552

6653
.PHONY: docker-shell
67-
docker-shell: $(DOTENV_FILE)
54+
docker-shell: $(DOTENV_FILE) ## Run shell from container
6855
docker compose run --rm web /bin/sh
6956

7057
.PHONY: docker-start
71-
docker-start: $(DOTENV_FILE)
58+
docker-start: $(DOTENV_FILE) ## Start container
7259
docker compose up
7360

7461
.PHONY: test
75-
test: $(INSTALL_STAMP)
76-
bin/test.sh
62+
test: $(INSTALL_STAMP) ## Run unit tests
63+
$(UV) run pytest tests -n auto --cov-report term-missing --cov-fail-under 75 --cov jbi --cov checks

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ We use [pandoc](https://pandoc.org) to convert markdown to the Jira syntax. Make
8585
In order to pass arguments to `pytest`:
8686
8787
```
88-
poetry run pytest -vv -k test_bugzilla_list_webhooks
88+
PYTEST_ADDOPTS="--last-failed -vv" make test
8989
```
9090
9191
You may consider:

bin/healthcheck.py

Lines changed: 0 additions & 30 deletions
This file was deleted.

bin/lint.sh

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,23 @@
22

33
set -e
44

5-
POETRY_RUN="poetry run"
5+
UV_RUN="uv run"
66

7-
BANDIT_CMD="$POETRY_RUN bandit -lll --recursive jbi"
7+
BANDIT_CMD="$UV_RUN bandit -lll --recursive jbi"
88

9-
FORMAT_CMD="$POETRY_RUN ruff format ."
9+
FORMAT_CMD="$UV_RUN ruff format ."
1010

11-
# Scan only files fixed into the repo, omit poetry.lock
12-
DETECT_SECRETS_FILES="$(git ls-tree --full-tree -r --name-only HEAD | grep -v poetry.lock)"
13-
DETECT_SECRETS_CMD="$POETRY_RUN detect-secrets-hook $DETECT_SECRETS_FILES --baseline .secrets.baseline"
11+
# Scan only files fixed into the repo, omit uv.lock
12+
DETECT_SECRETS_FILES="$(git ls-tree --full-tree -r --name-only HEAD | grep -v uv.lock)"
13+
DETECT_SECRETS_CMD="$UV_RUN detect-secrets-hook $DETECT_SECRETS_FILES --baseline .secrets.baseline"
1414

15-
LINT_CMD="$POETRY_RUN ruff check ."
15+
LINT_CMD="$UV_RUN ruff check ."
1616

17-
MYPY_CMD="$POETRY_RUN mypy jbi"
17+
MYPY_CMD="$UV_RUN mypy jbi"
1818

19-
YAMLLINT_CMD="$POETRY_RUN yamllint -c .yamllint config/*.yaml"
19+
YAMLLINT_CMD="$UV_RUN yamllint -c .yamllint config/*.yaml"
2020

21-
ACTIONS_LINT_CMD="$POETRY_RUN jbi lint"
21+
ACTIONS_LINT_CMD="$UV_RUN python -m jbi lint"
2222

2323
all () {
2424
echo "running bandit"

bin/test.sh

Lines changed: 0 additions & 12 deletions
This file was deleted.

docker-compose.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ version: "3.8"
22
services:
33
web:
44
build: .
5-
command: python -m asgi
5+
command: uv run python -m asgi
66
env_file:
77
- .env
88
# Let the init system handle signals for us.
@@ -14,7 +14,7 @@ services:
1414
- .:/app
1515
retry:
1616
build: .
17-
command: python -m jbi.retry
17+
command: uv run python -m jbi.retry
1818
env_file:
1919
- .env
2020
volumes:

0 commit comments

Comments
 (0)