Skip to content

Commit 1db1b61

Browse files
committed
dockerfile rework
1 parent 744cda4 commit 1db1b61

File tree

6 files changed

+88
-80
lines changed

6 files changed

+88
-80
lines changed

.github/workflows/python-tests.yml

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ jobs:
1616
UV_COMPILE_BYTECODE: true
1717
UV_NO_EDITABLE: true
1818
UV_NO_PROGRESS: true
19-
UV_NO_SYNC: true
19+
UV_NO_MANAGED_PYTHON: true
2020
steps:
2121
- name: Checkout repository
2222
uses: actions/checkout@v5
@@ -32,13 +32,19 @@ jobs:
3232
cache-dependency-glob: uv.lock
3333
- name: Do something if the cache was restored
3434
if: steps.setup-uv.outputs.cache-hit == 'true'
35-
run: echo "Cache was restored"
36-
- name: Install the project
37-
run: uv sync --dev
35+
run: echo "uv cache was restored"
36+
- uses: actions/cache@v4
37+
id: pre-commit-cache
38+
with:
39+
path: ~/.cache/pre-commit
40+
key: pre-commit-${{ hashFiles('.pre-commit-config.yaml') }}-${{ hashFiles('uv.lock') }}
41+
- name: Do something if the cache was restored
42+
if: steps.pre-commit-cache.outputs.cache-hit == 'true'
43+
run: echo "pre-commit cache was restored"
3844
- name: Run pre-commit hooks
3945
run: >
4046
uv run
41-
--no-sync
47+
--verbose
4248
pre-commit run
4349
--all-files
4450
--show-diff-on-failure --color=always
@@ -51,7 +57,6 @@ jobs:
5157
UV_COMPILE_BYTECODE: true
5258
UV_NO_EDITABLE: true
5359
UV_NO_PROGRESS: true
54-
UV_NO_SYNC: true
5560
strategy:
5661
fail-fast: false
5762
matrix:
@@ -75,9 +80,6 @@ jobs:
7580
if: steps.setup-uv.outputs.cache-hit == 'true'
7681
run: echo "Cache was restored"
7782

78-
- name: Install the project
79-
run: uv sync --dev
80-
8183
- name: Run tests
8284
# For example, using `pytest`
83-
run: uv run --no-sync pytest -m ci
85+
run: uv run --verbose pytest -m ci

Dockerfile

Lines changed: 37 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
# (No error and it worked!)
99
ARG PYTHON_RELEASE=3.12 ALPINE_VERSION=3.21
1010
ARG BASE_IMAGE=python:${PYTHON_RELEASE}-alpine${ALPINE_VERSION}
11+
# ARG BASE_IMAGE=ghcr.io/astral-sh/uv:alpine${ALPINE_VERSION}
12+
# ARG BASE_IMAGE=ghcr.io/astral-sh/uv:python${PYTHON_RELEASE}-alpine
1113
# Image for building dependencies (on architectures that don't provide a ready-made Python wheel)
1214
FROM ${BASE_IMAGE} AS builder
1315

@@ -16,6 +18,20 @@ FROM ${BASE_IMAGE} AS builder
1618
# https://docs.docker.com/engine/reference/builder/#automatic-platform-args-in-the-global-scope
1719
ARG TARGETARCH
1820
ARG TARGETVARIANT
21+
ARG PYTHON_RELEASE
22+
23+
ENV UV_LOCKED=true
24+
ENV UV_COMPILE_BYTECODE=true
25+
ENV UV_LINK_MODE=copy
26+
ENV UV_PROJECT_ENVIRONMENT=/usr/local
27+
ENV UV_SYSTEM_PYTHON=true
28+
ENV UV_NO_MANAGED_PYTHON=true
29+
ENV PYTHONPATH="/usr/lib/python${PYTHON_RELEASE}/site-packages"
30+
31+
RUN --mount=type=cache,id=apk-${TARGETARCH}-${TARGETVARIANT},sharing=locked,target=/var/cache/apk/ \
32+
apk add curl && curl -LsSf https://astral.sh/uv/install.sh | ash
33+
34+
ENV PATH="/root/.local/bin/:$PATH"
1935

2036
# A workaround for compiling the `orsjson` package with rust: see https://github.com/rust-lang/cargo/issues/6513#issuecomment-1440029221
2137
ENV CARGO_NET_GIT_FETCH_WITH_CLI=true
@@ -26,19 +42,21 @@ ENV CARGO_NET_GIT_FETCH_WITH_CLI=true
2642
# Install system dependencies, saving the apk cache with docker mount: https://docs.docker.com/build/cache/#keep-layers-small
2743
# Specify the architecture in the cache id, otherwise the apk cache of different architectures will conflict
2844
RUN --mount=type=cache,id=apk-${TARGETARCH}-${TARGETVARIANT},sharing=locked,target=/var/cache/apk/ \
29-
if [ "$TARGETARCH" = "arm" ]; then\
30-
apk add git rust cargo &&\
31-
apk add build-base cython;\
32-
fi
33-
34-
# Copy requirements file of AppDaemon
35-
COPY ./requirements.txt /usr/src/app/
45+
if [ "$TARGETARCH" = "arm" ]; then apk add git build-base cython rust cargo; fi
3646

3747
# Install the Python dependencies of AppDaemon
3848
# Save the pip cache with docker mount: https://docs.docker.com/build/cache/#keep-layers-small
3949
# (specify the architecture in the cache id, otherwise the pip cache of different architectures will conflict)
40-
RUN --mount=type=cache,id=pip-${TARGETARCH}-${TARGETVARIANT},sharing=locked,target=/root/.cache/pip \
41-
pip install --disable-pip-version-check -r /usr/src/app/requirements.txt
50+
RUN --mount=type=cache,id=uv-${TARGETARCH}-${TARGETVARIANT},sharing=locked,target=/root/.cache/uv \
51+
--mount=type=bind,source=uv.lock,target=uv.lock \
52+
--mount=type=bind,source=pyproject.toml,target=pyproject.toml \
53+
uv sync --inexact --no-install-project --no-dev --no-editable
54+
55+
RUN --mount=type=cache,id=uv-${TARGETARCH}-${TARGETVARIANT},sharing=locked,target=/root/.cache/uv \
56+
--mount=type=bind,source=./dist,target=/dist \
57+
uv pip install /dist/*.whl
58+
59+
ENTRYPOINT [ "/bin/ash" ]
4260

4361
###################################
4462
# Runtime image
@@ -48,21 +66,17 @@ ARG TARGETARCH
4866
ARG TARGETVARIANT
4967
ARG PYTHON_RELEASE
5068

51-
# Copy the python dependencies built and installed in the previous stage
52-
COPY --from=builder /usr/local/lib/python${PYTHON_RELEASE}/site-packages /usr/local/lib/python${PYTHON_RELEASE}/site-packages
69+
# Install curl to allow for healthchecks
70+
RUN apk --no-cache add curl
5371

54-
WORKDIR /usr/src/app
72+
# Copy sample configuration directory and entrypoint script
73+
COPY ./conf /opt/conf
74+
COPY ./scripts/start.sh /start.sh
5575

56-
# Install Appdaemon from the Python package built in the project `dist/` folder
57-
RUN --mount=type=cache,id=pip-${TARGETARCH}-${TARGETVARIANT},sharing=locked,target=/root/.cache/pip,from=builder \
58-
# Mount the project directory containing the built Python package, so it is available for pip install inside the container
59-
--mount=type=bind,source=./dist/,target=/usr/src/app/ \
60-
# Install the package
61-
pip --disable-pip-version-check install *.whl
76+
# Copy the python dependencies built and installed in the previous stage
77+
COPY --from=builder /usr/local /usr/local
6278

63-
# Copy sample configuration directory and entrypoint script
64-
COPY ./conf ./conf
65-
COPY ./dockerStart.sh .
79+
ENV PYTHONPATH="/usr/lib/python${PYTHON_RELEASE}/site-packages"
6680

6781
# API Port
6882
EXPOSE 5050
@@ -71,11 +85,5 @@ EXPOSE 5050
7185
VOLUME /conf
7286
VOLUME /certs
7387

74-
# Add paths used by alpine python packages to python search path
75-
ENV PYTHONPATH=/usr/lib/python${PYTHON_RELEASE}:/usr/lib/python${PYTHON_RELEASE}/site-packages
76-
77-
# Define entrypoint script
78-
ENTRYPOINT ["./dockerStart.sh"]
79-
80-
# Install curl to allow for healthchecks
81-
RUN apk --no-cache add curl
88+
WORKDIR /conf
89+
ENTRYPOINT [ "/start.sh" ]

Dockerfile.uv

Lines changed: 31 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@
77
# >>> import sklearn
88
# (No error and it worked!)
99
ARG PYTHON_RELEASE=3.12 ALPINE_VERSION=3.21
10-
# ARG BASE_IMAGE=ghcr.io/astral-sh/uv:alpine${ALPINE_VERSION}
1110
ARG BASE_IMAGE=python:${PYTHON_RELEASE}-alpine${ALPINE_VERSION}
11+
# ARG BASE_IMAGE=ghcr.io/astral-sh/uv:alpine${ALPINE_VERSION}
12+
# ARG BASE_IMAGE=ghcr.io/astral-sh/uv:python${PYTHON_RELEASE}-alpine
1213
# Image for building dependencies (on architectures that don't provide a ready-made Python wheel)
1314
FROM ${BASE_IMAGE} AS builder
1415

@@ -19,16 +20,19 @@ ARG TARGETARCH
1920
ARG TARGETVARIANT
2021
ARG PYTHON_RELEASE
2122

22-
ENV UV_COMPILE_BYTECODE=1
23+
ENV UV_LOCKED=true
24+
ENV UV_COMPILE_BYTECODE=true
2325
ENV UV_LINK_MODE=copy
24-
ENV UV_INSTALL_DIR=/usr/local/bin
25-
ENV UV_TOOL_BIN_DIR=/usr/local/bin
26+
ENV UV_PROJECT_ENVIRONMENT=/usr/local
27+
ENV UV_SYSTEM_PYTHON=true
2628
ENV UV_NO_MANAGED_PYTHON=true
2729
ENV PYTHONPATH="/usr/lib/python${PYTHON_RELEASE}/site-packages"
2830

2931
RUN --mount=type=cache,id=apk-${TARGETARCH}-${TARGETVARIANT},sharing=locked,target=/var/cache/apk/ \
3032
apk add curl && curl -LsSf https://astral.sh/uv/install.sh | ash
3133

34+
ENV PATH="/root/.local/bin/:$PATH"
35+
3236
# A workaround for compiling the `orsjson` package with rust: see https://github.com/rust-lang/cargo/issues/6513#issuecomment-1440029221
3337
ENV CARGO_NET_GIT_FETCH_WITH_CLI=true
3438

@@ -38,22 +42,15 @@ ENV CARGO_NET_GIT_FETCH_WITH_CLI=true
3842
# Install system dependencies, saving the apk cache with docker mount: https://docs.docker.com/build/cache/#keep-layers-small
3943
# Specify the architecture in the cache id, otherwise the apk cache of different architectures will conflict
4044
RUN --mount=type=cache,id=apk-${TARGETARCH}-${TARGETVARIANT},sharing=locked,target=/var/cache/apk/ \
41-
if [ "$TARGETARCH" = "arm" ]; then\
42-
apk add git rust cargo &&\
43-
apk add build-base cython;\
44-
fi
45-
46-
WORKDIR /usr/src/app
47-
48-
RUN uv venv --system-site-packages
45+
if [ "$TARGETARCH" = "arm" ]; then apk add git build-base cython rust cargo; fi
4946

5047
# Install the Python dependencies of AppDaemon
5148
# Save the pip cache with docker mount: https://docs.docker.com/build/cache/#keep-layers-small
5249
# (specify the architecture in the cache id, otherwise the pip cache of different architectures will conflict)
5350
RUN --mount=type=cache,id=uv-${TARGETARCH}-${TARGETVARIANT},sharing=locked,target=/root/.cache/uv \
5451
--mount=type=bind,source=uv.lock,target=uv.lock \
5552
--mount=type=bind,source=pyproject.toml,target=pyproject.toml \
56-
uv sync --locked --inexact --no-install-project --no-dev
53+
uv sync --inexact --no-install-project --no-dev --no-editable
5754

5855
RUN --mount=type=cache,id=uv-${TARGETARCH}-${TARGETVARIANT},sharing=locked,target=/root/.cache/uv \
5956
--mount=type=bind,source=./dist,target=/dist \
@@ -63,27 +60,33 @@ ENTRYPOINT [ "/bin/ash" ]
6360

6461
###################################
6562
# Runtime image
66-
FROM ${BASE_IMAGE}
63+
# FROM ${BASE_IMAGE}
64+
FROM alpine:${ALPINE_VERSION}
6765

6866
ARG TARGETARCH
6967
ARG TARGETVARIANT
7068
ARG PYTHON_RELEASE
7169

72-
ENV UV_COMPILE_BYTECODE=1
73-
ENV UV_LINK_MODE=copy
74-
ENV UV_INSTALL_DIR=/usr/local/bin
75-
ENV UV_TOOL_BIN_DIR=/usr/local/bin
76-
ENV UV_NO_MANAGED_PYTHON=true
77-
ENV PYTHONPATH=/usr/lib/python${PYTHON_RELEASE}:/usr/lib/python${PYTHON_RELEASE}/site-packages
78-
79-
# Copy the python dependencies built and installed in the previous stage
80-
COPY --from=builder /usr/local/lib/python${PYTHON_RELEASE} /usr/local/lib/python${PYTHON_RELEASE}
81-
COPY --from=builder /usr/src/app /usr/src/app
70+
# Install curl to allow for healthchecks
71+
RUN apk --no-cache add curl
8272

83-
WORKDIR /usr/src/app
73+
WORKDIR /conf
8474

8575
# Copy sample configuration directory and entrypoint script
86-
COPY ./conf ./conf
76+
COPY ./conf /opt/conf
77+
78+
# Copy the python dependencies built and installed in the previous stage
79+
# COPY --from=builder /root/.local/bin/uv /root/.local/bin/uv
80+
COPY --from=builder /usr/local /usr/local
81+
82+
ENV PATH="/root/.local/bin/:$PATH"
83+
# ENV UV_LOCKED=true
84+
ENV UV_COMPILE_BYTECODE=true
85+
ENV UV_LINK_MODE=copy
86+
ENV UV_PROJECT_ENVIRONMENT=/usr/local
87+
ENV UV_SYSTEM_PYTHON=true
88+
ENV UV_NO_MANAGED_PYTHON=true
89+
ENV PYTHONPATH="/usr/lib/python${PYTHON_RELEASE}/site-packages"
8790

8891
# API Port
8992
EXPOSE 5050
@@ -92,8 +95,5 @@ EXPOSE 5050
9295
VOLUME /conf
9396
VOLUME /certs
9497

95-
# Install curl to allow for healthchecks
96-
RUN apk --no-cache add curl
97-
98-
COPY dockerStart.sh dockerStart.sh
99-
ENTRYPOINT [ "./dockerStart.sh" ]
98+
COPY ./scripts/start.sh /start.sh
99+
ENTRYPOINT [ "/start.sh" ]

scripts/docker-build.sh

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ readonly REPO_DIR=$(cd $(dirname $(dirname $(readlink -f "${BASH_SOURCE[0]}")))
77
rm -rf ${REPO_DIR}/build ${REPO_DIR}/dist
88

99
if command -v uv >/dev/null 2>&1; then
10-
uv sync -U --all-extras
10+
uv sync --locked --inexact
1111
echo -n "Building wheel..."
1212
uv build --wheel --refresh -q
1313
echo "done."
@@ -17,4 +17,5 @@ else
1717
python -m build
1818
fi
1919

20-
docker build --pull -f Dockerfile.uv -t acockburn/appdaemon:${1:-"local-dev"} ${REPO_DIR}
20+
# docker build --pull -f Dockerfile.uv -t acockburn/appdaemon:${1:-"local-dev"} ${REPO_DIR}
21+
docker build --pull -f Dockerfile -t acockburn/appdaemon:${1:-"local-dev"} ${REPO_DIR}

dockerStart.sh renamed to scripts/start.sh

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,10 @@
1-
#!/bin/sh
1+
#!/usr/bin/env sh
22

33
# Default configuration directory used at runtime
44
CONF=/conf
55
# Directory containing sample config files to copy from
6-
CONF_SRC=/usr/src/app/conf
6+
CONF_SRC=/opt/conf
77

8-
BIN_DIR=/usr/src/app/.venv/bin
9-
10-
source $BIN_DIR/activate
118

129
# if configuration file doesn't exist, copy the default
1310
if [ ! -f $CONF/appdaemon.yaml -a ! -f $CONF/appdaemon.toml ]; then
@@ -96,4 +93,4 @@ find $CONF -name system_packages.txt -type f -not -empty -exec cat {} \; -exec e
9693
find $CONF -name requirements.txt -type f -not -empty -exec pip3 install --disable-pip-version-check --root-user-action ignore --upgrade -r {} \;
9794

9895
# Lets run it!
99-
exec appdaemon -c $CONF "$@"
96+
exec appdaemon -c $CONF "$@"

uv.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)