Skip to content

Commit c77ef37

Browse files
author
Tomás Link
committed
Optimize Dockerfile and allow UV in Makefile
1 parent 08069b1 commit c77ef37

10 files changed

Lines changed: 135 additions & 110 deletions

File tree

.dockerignore

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,7 @@
1-
tests/test_init_project.py
1+
tests/test_init_project.py
2+
3+
**/__pycache__/
4+
**/*.pyc
5+
**/*.pyo
6+
**/*.pyd
7+
.pytest_cache/

.github/workflows/main.yaml

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,18 @@ name: main
22

33
on:
44
push:
5-
branches: [main, develop]
5+
branches: [main]
66
pull_request:
77

88
jobs:
99
flake8:
1010
runs-on: ubuntu-latest
1111
steps:
1212
- name: Checkout
13-
uses: actions/checkout@v3
13+
uses: actions/checkout@v6
1414

1515
- name: Set up Python
16-
uses: actions/setup-python@v4.1.0
16+
uses: actions/setup-python@v6
1717
with:
1818
python-version: 3.12
1919

@@ -24,28 +24,31 @@ jobs:
2424
run: flake8 --count
2525

2626
tests:
27-
name: Python ${{ matrix.python-version }} on ${{ matrix.os }}
27+
name: tests - Python ${{ matrix.python-version }} on ${{ matrix.os }}
2828
runs-on: ${{ matrix.os }}
2929
strategy:
3030
fail-fast: true
3131
matrix:
3232
os: ["ubuntu-latest"]
33-
python-version: ["3.10", "3.11", "3.12", "3.13"]
33+
python-version: ["3.13"]
3434
env:
3535
VENV: .venv
3636

3737
steps:
38-
- uses: actions/checkout@v4
38+
- uses: actions/checkout@v6
3939
- name: Set up Python ${{ matrix.python-version }}
40-
uses: actions/setup-python@v4
40+
uses: actions/setup-python@v6
4141
with:
4242
python-version: ${{ matrix.python-version }}
4343
cache: 'pip'
4444
# cache option make the step fail if you don´t have requirements.txt or pyproject.toml on root.
4545
# https://github.com/actions/setup-python/issues/807.
4646

47+
- name: Install UV
48+
run: make uv
49+
4750
- name: Create virtual environment
48-
run: python -m venv $VENV
51+
run: uv venv $VENV
4952

5053
- name: Install package with test dependencies
5154
run: |
@@ -60,16 +63,17 @@ jobs:
6063
6164
- name: Upload coverage reports to Codecov
6265
if: (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop') && matrix.python-version == '3.12'
63-
uses: codecov/codecov-action@v4.0.1
66+
uses: codecov/codecov-action@v5
6467
with:
6568
token: ${{ secrets.CODECOV_TOKEN }}
6669

6770
tests-in-docker:
71+
name: tests - Python 3.12 on Docker
6872
runs-on: ubuntu-latest
6973
steps:
7074
- uses: actions/checkout@v4
7175
- name: Set up Python
72-
uses: actions/setup-python@v4.1.0
76+
uses: actions/setup-python@v6
7377

7478
- name: Build docker image
7579
run: make docker-build

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,3 +170,6 @@ terraform.tfstate.backup
170170
# project stuff
171171
scripts/config.sh
172172
test/
173+
174+
# UV
175+
.python-version

CONTRIBUTING.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,13 +65,18 @@ make docker-build
6565
make docker-gcp
6666
```
6767

68+
Install UV for faster installs (otherwise modify Makefile to use regular pip):
69+
```shell
70+
make uv
71+
```
72+
6873
4. Create virtual environment and activate it:
6974
```shell
7075
make venv
7176
./.venv/bin/activate
7277
```
7378

74-
5. Install dependencies and the python package:
79+
5. Install all dependencies for development and the python package in editable mode:
7580
```shell
7681
make install
7782
```

Dockerfile

Lines changed: 38 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,69 +1,62 @@
11
# ---------------------------------------------------------------------------------------
2-
# BASE IMAGE
2+
# BUILDER
33
# ---------------------------------------------------------------------------------------
4-
FROM python:3.12.10-slim-bookworm AS base
4+
FROM python:3.12-slim-bookworm AS builder
55

6-
# Setup a volume for configuration and authtentication.
76
VOLUME ["/root/.config"]
87

9-
# Update system and install build tools. Remove unneeded stuff afterwards.
10-
# Upgrade PIP.
11-
# Create working directory.
12-
RUN apt-get update && \
13-
apt-get install -y --no-install-recommends gcc g++ build-essential && \
14-
rm -rf /var/lib/apt/lists/* && \
15-
pip install --upgrade pip && \
16-
mkdir -p /opt/project
8+
# Use uv for high-speed installs
9+
COPY --from=ghcr.io/astral-sh/uv:0.10.9 /uv /usr/local/bin/uv
1710

18-
# Set working directory.
19-
WORKDIR /opt/project
11+
ENV UV_COMPILE_BYTECODE=1
2012

21-
# ---------------------------------------------------------------------------------------
22-
# DEPENDENCIES IMAGE (installed project dependencies)
23-
# ---------------------------------------------------------------------------------------
24-
# We do this first so when we modify code while development, this layer is reused
25-
# from cache and only the layer installing the package executes again.
26-
FROM base AS deps
27-
COPY requirements.txt .
28-
RUN pip install -r requirements.txt
13+
WORKDIR /install
2914

30-
# ---------------------------------------------------------------------------------------
31-
# Apache Beam integration IMAGE
32-
# ---------------------------------------------------------------------------------------
33-
FROM deps AS beam
34-
# Copy files from official SDK image, including script/dependencies.
35-
# IMPORTANT: This version must match the one in requirements.txt
36-
COPY --from=apache/beam_python3.12_sdk:2.64.0 /opt/apache/beam /opt/apache/beam
15+
COPY pyproject.toml requirements.txt README.md MANIFEST.in ./
16+
COPY src ./src
3717

38-
# Set the entrypoint to Apache Beam SDK launcher.
39-
ENTRYPOINT ["/opt/apache/beam/boot"]
18+
RUN uv pip install --system --upgrade pip && \
19+
uv pip install --system build && \
20+
uv pip install --system --prefix=/install -r requirements.txt && \
21+
uv pip install --system --prefix=/install --no-deps .
4022

4123
# ---------------------------------------------------------------------------------------
4224
# PRODUCTION IMAGE
4325
# ---------------------------------------------------------------------------------------
44-
# If you need Apache Beam integration, replace "deps" base image with "beam".
45-
FROM deps AS prod
26+
FROM python:3.12-slim-bookworm AS prod
27+
28+
ENV PYTHONUNBUFFERED=1
4629

47-
COPY . /opt/project
48-
RUN pip install . && \
49-
rm -rf /root/.cache/pip && \
50-
rm -rf /opt/project/*
30+
# Copy the pre-compiled packages from builder
31+
COPY --from=builder /install /usr/local
32+
33+
# APACHE BEAM INTEGRATION (Uncomment if needed)
34+
# COPY --from=apache/beam_python3.12_sdk:2.71.0 /opt/apache/beam /opt/apache/beam
35+
# ENTRYPOINT ["/opt/apache/beam/boot"]
36+
37+
WORKDIR /opt/project
5138

5239
# ---------------------------------------------------------------------------------------
53-
# DEVELOPMENT IMAGE (editable install and development tools)
40+
# DEVELOPMENT IMAGE
5441
# ---------------------------------------------------------------------------------------
55-
# If you need Apache Beam integration, replace "deps" base image with "beam".
56-
FROM deps AS dev
42+
FROM builder AS dev
5743

58-
COPY . /opt/project
59-
RUN make install
44+
WORKDIR /opt/project
45+
46+
COPY . .
47+
RUN uv pip install --system -e .[lint,dev,build] && \
48+
uv pip install --system -r requirements-test.txt
6049

6150
# ---------------------------------------------------------------------------------------
62-
# TEST IMAGE (This one allows to check that package is properly installed in prod image)
51+
# TEST IMAGE
6352
# ---------------------------------------------------------------------------------------
6453
FROM prod AS test
6554

66-
COPY ./tests /opt/project/tests
67-
COPY ./requirements-test.txt /opt/project/
55+
COPY ./requirements-test.txt .
56+
RUN pip install -r requirements-test.txt
57+
58+
COPY ./tests ./tests
6859

69-
RUN pip install -r requirements-test.txt
60+
# Suppress all warnings during tests
61+
# To see/address warnings, run tests in your development environment.
62+
ENV PYTHONWARNINGS=ignore

Makefile

Lines changed: 42 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,25 @@
22

33
VENV_NAME:=.venv
44
REQS_PROD:=requirements.txt
5+
SETUP_FILE:=pyproject.toml
6+
SOURCES = src
7+
58
DOCKER_DEV_SERVICE:=dev
6-
DOCKER_CI_TEST_SERVICE:=test
7-
DOCKER_ISOLATED_SERVICE:=isolated
9+
DOCKER_DEV_NO_GCP_SERVICE:=dev_no_gcp
10+
DOCKER_PROD_SERVICE:=prod
11+
DOCKER_TEST_SERVICE:=test
812

913
GCP_PROJECT:=world-fishing-827
1014
GCP_DOCKER_VOLUME:=gcp
1115

12-
sources = python_app_template
16+
PYTHON_VERSION:=3.12
17+
UV_VERSION := 0.10.9
18+
19+
VENV:=uv venv
20+
PIP:=uv pip
21+
PIP_COMPILE:=uv pip compile
22+
1323

14-
PYTHON:=python
15-
PIP:=${PYTHON} -m pip
1624

1725
# ---------------------
1826
# DOCKER
@@ -34,29 +42,34 @@ docker-gcp: docker-volume
3442

3543
.PHONY: docker-ci-test ## Runs tests using prod image, exporting coverage.xml report.
3644
docker-ci-test:
37-
docker compose run --rm ${DOCKER_CI_TEST_SERVICE}
45+
docker compose run --rm ${DOCKER_TEST_SERVICE}
3846

3947
.PHONY: docker-shell ## Enters to docker container shell.
4048
docker-shell: docker-volume
4149
docker compose run --rm -it ${DOCKER_DEV_SERVICE}
4250

43-
.PHONY: reqs ## Compiles requirements.txt with pip-tools.
51+
.PHONY: docker-reqs ## Compiles requirements.txt with pip-tools.
4452
reqs:
45-
docker compose run --rm ${DOCKER_ISOLATED_SERVICE} -c \
46-
'pip-compile -o ${REQS_PROD} -v'
53+
docker compose run --rm ${DOCKER_DEV_NO_GCP_SERVICE} -c \
54+
'${PIP_COMPILE} -o ${REQS_PROD} ${SETUP_FILE} -v'
4755

48-
.PHONY: reqs-upgrade ## Upgrades requirements.txt with pip-tools.
56+
.PHONY: docker-reqs-upgrade ## Upgrades requirements.txt with pip-tools.
4957
reqs-upgrade:
50-
docker compose run --rm ${DOCKER_ISOLATED_SERVICE} -c \
51-
'pip-compile -o ${REQS_PROD} -U -v'
58+
docker compose run --rm ${DOCKER_DEV_NO_GCP_SERVICE} -c \
59+
'${PIP_COMPILE} -o ${REQS_PROD} ${SETUP_FILE} -U -v'
5260

5361
# ---------------------
5462
# VIRTUAL ENVIRONMENT
5563
# ---------------------
5664

65+
.PHONY: uv ## Installs UV
66+
uv:
67+
curl -LsSf https://astral.sh/uv/install.sh | UV_VERSION=$(UV_VERSION) sh
68+
uv python pin ${PYTHON_VERSION}
69+
5770
.PHONY: venv ## Creates virtual environment.
5871
venv:
59-
${PYTHON} -m venv ${VENV_NAME}
72+
${VENV} ${VENV_NAME}
6073

6174
.PHONY: upgrade-pip ## Upgrades pip.
6275
upgrade-pip:
@@ -68,48 +81,49 @@ install-test: upgrade-pip
6881

6982
.PHONY: install ## Install the package in editable mode & all dependencies for local development.
7083
install: upgrade-pip
71-
${PIP} install -e .[lint,dev,build,test]
84+
${PIP} install -e .[lint,dev,build]
85+
make install-test
7286

7387
.PHONY: test ## Run all unit tests exporting coverage.xml report.
7488
test:
75-
${PYTHON} -m pytest -m "not integration" --cov-report term --cov-report=xml --cov=$(sources)
89+
python -m pytest -m "not integration" --cov-report term --cov-report=xml --cov=$(SOURCES)
7690

7791
# ---------------------
7892
# QUALITY CHECKS
7993
# ---------------------
8094

8195
.PHONY: hooks ## Install and pre-commit hooks.
8296
hooks:
83-
${PYTHON} -m pre_commit install --install-hooks
84-
${PYTHON} -m pre_commit install --hook-type commit-msg
97+
python -m pre_commit install --install-hooks
98+
python -m pre_commit install --hook-type commit-msg
8599

86100
.PHONY: format ## Auto-format python source files according with PEP8.
87101
format:
88-
${PYTHON} -m black $(sources)
89-
${PYTHON} -m ruff check --fix $(sources)
90-
${PYTHON} -m ruff format $(sources)
102+
python -m black $(SOURCES)
103+
python -m ruff check --fix $(SOURCES)
104+
python -m ruff format $(SOURCES)
91105

92106
.PHONY: lint ## Lint python source files.
93107
lint:
94-
${PYTHON} -m ruff check $(sources)
95-
${PYTHON} -m ruff format --check $(sources)
96-
${PYTHON} -m black $(sources) --check --diff
108+
python -m ruff check $(SOURCES)
109+
python -m ruff format --check $(SOURCES)
110+
python -m black $(SOURCES) --check --diff
97111

98112
.PHONY: codespell ## Use Codespell to do spell checking.
99113
codespell:
100-
${PYTHON} -m codespell
114+
python -m codespell
101115

102116
.PHONY: typecheck ## Perform type-checking.
103117
typecheck:
104-
${PYTHON} -m mypy
118+
python -m mypy
105119

106120
.PHONY: audit ## Use pip-audit to scan for known vulnerabilities.
107121
audit:
108-
${PYTHON} -m pip_audit .
122+
python -m pip_audit .
109123

110124
.PHONY: pre-commit ## Run all pre-commit hooks.
111125
pre-commit:
112-
${PYTHON} -m pre_commit run --all-files
126+
python -m pre_commit run --all-files
113127

114128
.PHONY: all ## Run the standard set of checks performed in CI.
115129
all: lint codespell typecheck audit test
@@ -121,7 +135,7 @@ all: lint codespell typecheck audit test
121135

122136
.PHONY: build ## Build a source distribution and a wheel distribution.
123137
build: all clean
124-
${PYTHON} -m build
138+
python -m build
125139

126140
.PHONY: publish ## Publish the distribution to PyPI.
127141
publish: build

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
<img src="https://codecov.io/gh/GlobalFishingWatch/python-app-template/graph/badge.svg?token=uZTb6EphP8"/>
99
</a>
1010
<a>
11-
<img alt="Python versions" src="https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12%20%7C%203.13-blue">
11+
<img alt="Python versions" src="https://img.shields.io/badge/python-3.12%20%7C%203.13-blue">
1212
</a>
1313
<a>
1414
<img alt="Last release" src="https://img.shields.io/github/v/release/GlobalFishingWatch/python-app-template">

0 commit comments

Comments
 (0)