Skip to content

Commit 7d104d3

Browse files
authored
Publish Python with LOGIC (#1)
2 parents 8e2953a + 27d8b49 commit 7d104d3

5 files changed

Lines changed: 188 additions & 44 deletions

File tree

.github/workflows/ci.yml

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ on:
77
pull_request:
88

99
env:
10-
UV_VERSION: 0.8.12
10+
UV_VERSION: 0.8.14
1111
GHCR_IMAGE_REPOSITORY: ghcr.io/${{ github.repository }}
1212
PLATFORMS: linux/amd64,linux/arm64/v8
1313

@@ -23,9 +23,7 @@ jobs:
2323
- "3.13"
2424
variant:
2525
- bookworm
26-
- slim-bookworm
2726
- trixie
28-
- slim-trixie
2927
steps:
3028
- uses: actions/checkout@v5.0.0
3129
- uses: docker/login-action@v3.5.0
@@ -46,6 +44,6 @@ jobs:
4644
cache-from: type=gha
4745
cache-to: type=gha,mode=max
4846
build-args: |
49-
POETRY_VERSION=${{ env.UV_VERSION }}
47+
UV_VERSION=${{ env.UV_VERSION }}
5048
PYTHON_VERSION=${{ matrix.python }}
5149
VARIANT=${{ matrix.variant }}

.github/workflows/release.yml

Lines changed: 18 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ on:
55
types: [published]
66

77
env:
8-
UV_VERSION: 0.8.12
8+
UV_VERSION: 0.8.14
99
GHCR_IMAGE_REPOSITORY: ghcr.io/${{ github.repository }}
1010
PLATFORMS: linux/amd64,linux/arm64/v8
1111

@@ -21,43 +21,33 @@ jobs:
2121
- "3.13"
2222
variant:
2323
- bookworm
24-
- slim-bookworm
2524
- trixie
26-
- slim-trixie
2725
include:
2826
- python: "3.13"
2927
is_default_python: true
3028
- variant: trixie
3129
is_default_variant: true
30+
env:
31+
GIT_SHA_TAG: ${{ env.GHCR_IMAGE_REPOSITORY }}:${{ github.sha }}-${{ matrix.python }}-${{ matrix.variant }}
3232
steps:
3333
- uses: actions/checkout@v4.1.4
3434
- uses: docker/login-action@v3.1.0
3535
with:
3636
registry: ghcr.io
3737
username: ${{ github.repository_owner }}
3838
password: ${{ secrets.GITHUB_TOKEN }}
39-
- name: Set image tags in env
40-
run: |
41-
TAG_SUFFIX="${{ matrix.python }}-${{ matrix.variant }}"
42-
echo "GIT_SHA_TAG=${{ env.GHCR_IMAGE_REPOSITORY }}:${{ github.sha }}-${TAG_SUFFIX}" >> $GITHUB_ENV
43-
echo "BASE_RELEASE_TAG=${{ env.GHCR_IMAGE_REPOSITORY }}:${{ env.UV_VERSION }}" >> $GITHUB_ENV
44-
- name: "Python + Variant: ${{ env.UV_VERSION }}-python-${{ matrix.python }}-${{ matrix.variant }}"
45-
run: |
46-
RELEASE_TAG="${BASE_RELEASE_TAG}-python-${{ matrix.python }}-${{ matrix.variant }}"
47-
docker buildx imagetools create --tag $RELEASE_TAG $GIT_SHA_TAG
48-
- name: "Python + Default Variant: ${{ env.UV_VERSION }}-python-${{ matrix.python }}"
49-
if: matrix.is_default_variant
50-
run: |
51-
RELEASE_TAG="${BASE_RELEASE_TAG}-python-${{ matrix.python }}"
52-
docker buildx imagetools create --tag $RELEASE_TAG $GIT_SHA_TAG
53-
- name: "Default Python + Variant: ${{ env.UV_VERSION }}-${{ matrix.variant }}"
54-
if: matrix.is_default_python
55-
run: |
56-
RELEASE_TAG="${BASE_RELEASE_TAG}-${{ matrix.variant }}"
57-
docker buildx imagetools create --tag $RELEASE_TAG $GIT_SHA_TAG
58-
- name: "Default Python + Default Variant: ${{ env.UV_VERSION }}"
59-
if: matrix.is_default_python && matrix.is_default_variant
60-
run: docker buildx imagetools create --tag $BASE_RELEASE_TAG $GIT_SHA_TAG
61-
- name: "Default Python + Default Variant: latest"
62-
if: matrix.is_default_python && matrix.is_default_variant
63-
run: docker buildx imagetools create --tag $GHCR_IMAGE_REPOSITORY:latest $GIT_SHA_TAG
39+
40+
- name: tag ${{ matrix.python }}-${{ matrix.variant }}
41+
run: docker buildx imagetools create --tag ${{ env.GHCR_IMAGE_REPOSITORY }}:${{ matrix.python }}-${{ matrix.variant }} $GIT_SHA_TAG
42+
43+
- if: matrix.is_default_variant
44+
name: tag for default variant (${{ matrix.python }})
45+
run: docker buildx imagetools create --tag ${{ env.GHCR_IMAGE_REPOSITORY }}:${{ matrix.python }} $GIT_SHA_TAG
46+
47+
- if: matrix.is_default_python
48+
name: tag for default python (${{ matrix.variant }})
49+
run: docker buildx imagetools create --tag ${{ env.GHCR_IMAGE_REPOSITORY }}:${{ matrix.variant }} $GIT_SHA_TAG
50+
51+
- if: matrix.is_default_python && matrix.is_default_variant
52+
name: tag for default python and variant (latest)
53+
run: docker buildx imagetools create --tag ${{ env.GHCR_IMAGE_REPOSITORY }}:latest $GIT_SHA_TAG

Dockerfile

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
ARG PYTHON_VERSION=3.13
22
ARG VARIANT=bookworm
3-
ARG UV_VERSION=0.8.12
3+
ARG UV_VERSION=0.8.14
44

55
FROM ghcr.io/astral-sh/uv:${UV_VERSION} AS uv
66

@@ -28,13 +28,3 @@ RUN mkdir -p ${UV_PROJECT_ENVIRONMENT}
2828
VOLUME [ ${UV_PROJECT_ENVIRONMENT} ]
2929

3030
WORKDIR /usr/src/app
31-
32-
FROM base AS onbuild
33-
34-
ONBUILD RUN --mount=type=cache,target=/root/.cache/uv \
35-
--mount=type=bind,source=pyproject.toml,target=pyproject.toml \
36-
--mount=type=bind,source=uv.lock,target=uv.lock \
37-
uv sync
38-
ONBUILD COPY ./ ./
39-
40-
FROM base

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2025 LOGIC Private Company
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 146 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,152 @@
11

22
# Python with LOGIC
33

4-
TBD
4+
An opinionated base Docker image used when doing [Python with LOGIC](https://withlogic.co/ref/python). Optimized for development, CI and production.
5+
6+
![uv](https://img.shields.io/badge/uv-0.8.14-lime) ![python](https://img.shields.io/badge/Python-3.13%20(default)%2C3.12%2C3.11%2C3.10-blue) ![variants](https://img.shields.io/badge/Variant-trixie%20(default)%2C%20bookworm-purple?label=Variants)
7+
8+
> [!TIP]
9+
>
10+
> Need help with a Python project in your organizations? You are at the right place. We have been working with production Python deployments for years and we would love to help. Let's get in touch at [https://withlogic.co](https://withlogic.co/ref/python).
11+
12+
## Why
13+
14+
We built and published this opinionated Docker image, based on our workflow when doing Python with LOGIC. We prioritise DRY, considerate defaults and optimizing for speed and convenience across all steps in our workflow, from development to CI and deployments; preview or production. In particular this means:
15+
16+
- Python and virtual environments optimized for Docker deployments
17+
- Optimized dependency management with uv
18+
- Preconfigured volumes for development with Docker
19+
20+
## Usage
21+
22+
The simplest way to get started with this image is to use the `latest` tag and copy your source code in the current working directory:
23+
24+
```dockerfile
25+
FROM ghcr.io/withlogicco/python
26+
27+
COPY ./ ./
28+
```
29+
30+
For advanced usage scenarios, including using uv with efficient Docker image caching take a look
31+
32+
## Tags
33+
34+
The `latest` tag ships with the most recent uv and Python versions on Debian Trixie:
35+
36+
```
37+
ghcr.io/withlogicco/python
38+
```
39+
40+
There are tags available to choose a specific supported Python versions and image variants:
41+
42+
- Python version: `ghcr.io/withlogicco/python:{python_version}`
43+
- Image variant: `ghcr.io/withlogicco/python:{image_variant}`
44+
- Python version and image variant: `ghcr.io/withlogicco/python:{python_version}-{image_variant}`
45+
46+
### Examples
47+
48+
- Python 3.13 on Trixie: `ghcr.io/withlogicco/python`
49+
- Poetry 3.13 on Bookworm: `ghcr.io/withlogicco/python:bookworm`
50+
- Poetry 3.12 on Trixie: `ghcr.io/withlogicco/python:3.12`
51+
- Poetry 3.12 on Bookworm: `ghcr.io/withlogicco/python:3.12-bookworm`
52+
53+
## Advanced usage
54+
55+
### Start a new project with uv
56+
57+
To start a new Python project with uv, mount your current working directory in `/usr/src/app` and just run `uv init`:
58+
59+
```console
60+
docker run -ti -v $PWD:/usr/src/app ghcr.io/withlogicco/python uv init
61+
```
62+
63+
### Dependencies with uv
64+
65+
To manage your dependencies with uv, it's suggested to just copy your `pyproject.toml` and `uv.lock` files in your Docker image first for optimized Docker build layer caching (`uv sync` will run only when your dependencies change):
66+
67+
```dockerfile
68+
FROM ghcr.io/withlogicco/python
69+
70+
COPY pyproject.toml uv.lock ./
71+
RUN uv sync --no-install-project
72+
73+
COPY . .
74+
```
75+
76+
> [!TIP]
77+
>
78+
> Prefer to use `--no-install-project` with `uv sync`, to only install dependencies and not the current project, since the code is not available yet in that particular build step.
79+
80+
### Optimize builds with cache mounts
81+
82+
You can further optimize your Docker builds by taking advantage of Docker builder cache mounts. This means Docker can cache the `/root/.cache/uv` directory across builds, so that even when dependencies change and `uv sync` needs to run again, only the new dependencies will need to be downloaded from PyPI:
83+
84+
```dockerfile
85+
FROM ghcr.io/withlogicco/python
86+
87+
COPY pyproject.toml uv.lock ./
88+
RUN --mount=type=cache,target=/root/.cache/uv \
89+
uv sync --no-install-project
90+
```
91+
92+
> [!IMPORTANT]
93+
>
94+
> Docker build cache mounts will **NOT** work on GitHub Actions, as they are not part of Docker image cache, but the builder daemon's. To get this feature working you will need to run your Docker builds on a host managed by you, with GitHub Actions self-hosted runners.
95+
96+
### Reduce layers with build bind mounts
97+
98+
You can reduce layers of built Docker images, by mounting `pyproject.toml` and `uv.lock` in the Docker image just in time for `uv sync`, instead of copying them in an additional layer above:
99+
100+
```dockerfile
101+
FROM ghcr.io/withlogicco/python
102+
103+
RUN --mount=type=cache,target=/root/.cache/uv \
104+
--mount=type=bind,source=pyproject.toml,target=pyproject.toml \
105+
--mount=type=bind,source=uv.lock,target=uv.lock \
106+
uv sync --no-install-project
107+
```
108+
109+
### Persist new dependencies without rebuilding
110+
111+
When working on an actively developed project, new dependencies can be added as the project moves forward. To avoid requiring rebuilding the Docker image at that case, you can mount a volume in `/opt/uv/venv`.
112+
113+
The directory `/opt/uv/venv` has been configured as a volume in the image, which means that Docker volumes mounted there will be initialized with the data of the image.
114+
115+
With Docker Compose the volume can be as simple as:
116+
117+
```yml
118+
services:
119+
web:
120+
build: .
121+
volumes:
122+
- .:/usr/src/app
123+
- uv:/opt/uv/venv
124+
```
125+
126+
> [!TIP]
127+
>
128+
> This is especially useful when working in small teams that do not update dependencies at the same time often. Rebuilding the image with new dependencies, will not udpate the contents of an existing Docker volume.
129+
130+
## Supported software versions
131+
132+
### uv
133+
134+
Only the latest version of uv is included in each build.
135+
136+
### Python
137+
138+
A build will be provided for each Python version still under maintenance and support. The latest Python version acts as the default in each build.
139+
140+
You can check the currently supported Python versions at https://devguide.python.org/versions/.
141+
142+
### Variants (Linux distributions)
143+
144+
- Debian Trixie (default): `trixie`
145+
- Debian Bookworm: `bookworm`
146+
147+
## License
148+
149+
This project is [MIT licensed](LICENSE).
5150

6151
---
7152

0 commit comments

Comments
 (0)