@@ -4,39 +4,9 @@ ARG PYTHON_VERSION=3.12-slim-bookworm
44FROM python:${PYTHON_VERSION} as base
55LABEL maintainer="Mike Glenn <mglenn@ilude.com>"
66
7- # User management setup (previously in user_base stage)
8- ARG PUID=${PUID:-1000}
9- ARG PGID=${PGID:-1000}
10-
11- ARG USER=anvil
12- ENV USER=${USER}
13-
14- ARG PROJECT_NAME
15- ENV PROJECT_NAME=${PROJECT_NAME}
16-
17- ARG PROJECT_PATH=/srv
18- ENV PROJECT_PATH=${PROJECT_PATH}
19-
20- ARG ONBOARD_PORT=9830
21- ENV ONBOARD_PORT=${ONBOARD_PORT}
22-
23- ENV HOME=/home/${USER}
24- ARG TERM_SHELL=zsh
25- ENV TERM_SHELL=${TERM_SHELL}
26-
277ARG TZ=America/New_York
288ENV TZ=${TZ}
299
30- # ## Remove legacy vendor/deps path usage; rely on system site-packages managed by uv
31- ENV PYTHONUNBUFFERED=TRUE
32- ENV UV_LINK_MODE=copy
33- ENV UV_SYSTEM_PYTHON=1
34- # Install project dependencies into the system Python prefix instead of a project .venv
35- # See: https://docs.astral.sh/uv/concepts/projects/config/#project-environment-path
36- ENV UV_PROJECT_ENVIRONMENT=/usr/local
37- # Allow modifying the system environment inside containers
38- ENV UV_BREAK_SYSTEM_PACKAGES=1
39-
4010ENV DEBIAN_FRONTEND=noninteractive
4111ENV DEBCONF_NONINTERACTIVE_SEEN=true
4212
@@ -64,14 +34,36 @@ ENV LC_ALL en_US.UTF-8
6434# Install uv
6535COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
6636
37+ # ----------------------------------------------------------------------
38+ # User setup
39+ # Sets up the non-root user, group, home directory, project path, and shell
40+ # This section is responsible for creating the runtime user and permissions
41+ # ----------------------------------------------------------------------
42+
43+ # User management setup (previously in user_base stage)
44+ ARG PUID=${PUID:-1000}
45+ ARG PGID=${PGID:-1000}
46+
47+ ARG USER=anvil
48+ ENV USER=${USER}
49+
50+ ENV HOME=/home/${USER}
51+
52+ ARG PROJECT_PATH=/srv
53+ ENV PROJECT_PATH=${PROJECT_PATH}
54+
55+ WORKDIR $PROJECT_PATH
56+
6757RUN sed -i 's/UID_MAX .*/UID_MAX 100000/' /etc/login.defs && \
6858 groupadd --gid ${PGID} ${USER} && \
69- useradd --uid ${PUID} --gid ${PGID} -s /bin/${TERM_SHELL} -m ${USER} && \
59+ useradd --uid ${PUID} --gid ${PGID} -s /bin/sh -m ${USER} && \
7060 mkdir -p ${PROJECT_PATH} && \
7161 chown -R ${USER}:${USER} ${PROJECT_PATH} && \
72- chown -R ${USER}:${USER} ${HOME} && \
73- # set the shell for root too
74- chsh -s /bin/${TERM_SHELL}
62+ chown -R ${USER}:${USER} ${HOME}
63+
64+ # ----------------------------------------------------------------------
65+ # --- docker-entrypoint setup section ---
66+ # ----------------------------------------------------------------------
7567
7668COPY --chmod=755 <<-"EOF" /usr/local/bin/docker-entrypoint.sh
7769# !/bin/bash
@@ -105,13 +97,26 @@ echo "Running: $@"
10597exec "$@"
10698EOF
10799
108- WORKDIR $PROJECT_PATH
109100ENTRYPOINT [ "/usr/local/bin/docker-entrypoint.sh" ]
110101
111- # #############################
112- # Begin build
113- # #############################
114- FROM base as build
102+
103+
104+ # ## Remove legacy vendor/deps path usage; rely on system site-packages managed by uv
105+ ENV PYTHONUNBUFFERED=TRUE
106+ ENV UV_LINK_MODE=copy
107+ ENV UV_SYSTEM_PYTHON=1
108+ # Install project dependencies into the system Python prefix instead of a project .venv
109+ # See: https://docs.astral.sh/uv/concepts/projects/config/#project-environment-path
110+ ENV UV_PROJECT_ENVIRONMENT=/usr/local
111+ # Allow modifying the system environment inside containers
112+ ENV UV_BREAK_SYSTEM_PACKAGES=1
113+
114+ ENV ONBOARD_PORT=${ONBOARD_PORT:-9830}
115+
116+ # ----------------------------------------------------------------------
117+ # --- Build-base image stage
118+ # ----------------------------------------------------------------------
119+ FROM base as build-base
115120
116121# Install build dependencies needed for compiling Python packages
117122RUN --mount=type=cache,target=/var/cache/apt \
@@ -141,7 +146,10 @@ RUN --mount=type=cache,target=/var/cache/apt \
141146 apt-get autoclean -y && \
142147 rm -rf /var/lib/apt/lists/*
143148
144- WORKDIR ${PROJECT_PATH}
149+ # ----------------------------------------------------------------------
150+ # --- Build-base image stage
151+ # ----------------------------------------------------------------------
152+ FROM build-base as build
145153
146154# Copy lockfile and root pyproject for reproducible resolution
147155COPY uv.lock* pyproject.toml ${PROJECT_PATH}/
@@ -154,9 +162,10 @@ RUN --mount=type=cache,target=/root/.cache/uv \
154162 --mount=type=cache,target=/root/.cache/pip \
155163 uv sync --no-editable --no-dev
156164
157- # #############################
158- # Begin production
159- # #############################
165+
166+ # ----------------------------------------------------------------------
167+ # --- Production image stage ---
168+ # ----------------------------------------------------------------------
160169FROM base as production
161170
162171# Copy application code into the project directory root (creates ${PROJECT_PATH}/app)
@@ -179,23 +188,20 @@ RUN mkdir -p ${PROJECT_PATH}/static/icons && \
179188# Run the app with gunicorn via uv without a shell
180189CMD ["uv" , "run" , "--no-sync" , "-m" , "gunicorn" , "run:app" , "--bind" , "0.0.0.0:9830" , "--access-logfile" , "-" , "--error-logfile" , "-" ]
181190
182- # #############################
183- # Begin devcontainer
184- # #############################
185- FROM build as devcontainer
191+ # ----------------------------------------------------------------------
192+ # --- development os packages image stage ---
193+ # ----------------------------------------------------------------------
194+ FROM build-base as development-base
186195
187196RUN --mount=type=cache,target=/var/cache/apt \
188197 --mount=type=cache,target=/var/lib/apt/lists \
189198 apt-get update && apt-get install -y --no-install-recommends \
190- bash \
191199 bash-completion \
192- build-essential \
193200 coreutils \
194201 docker.io \
195202 dnsutils \
196203 exa \
197204 gh \
198- git \
199205 gnuplot \
200206 gnuplot-x11 \
201207 graphviz \
@@ -204,17 +210,11 @@ RUN --mount=type=cache,target=/var/cache/apt \
204210 iputils-ping \
205211 jq \
206212 less \
207- libjpeg-dev \
208- libpng-dev \
209213 libpq-dev \
210- libssl-dev \
211- libxml2-dev \
212- libxslt-dev \
213214 libzmq3-dev \
214215 make \
215216 nodejs \
216217 npm \
217- openssh-client \
218218 passwd \
219219 python3-pip \
220220 python3-setuptools \
@@ -235,7 +235,18 @@ RUN --mount=type=cache,target=/var/cache/apt \
235235 apt-get autoclean -y && \
236236 rm -rf /var/lib/apt/lists/* && \
237237 echo ${USER} ALL=\( root\) NOPASSWD:ALL > /etc/sudoers.d/${USER} && \
238- chmod 0440 /etc/sudoers.d/${USER}
238+ chmod 0440 /etc/sudoers.d/${USER} && \
239+ # set the shell for $USER and root
240+ chsh -s "$(which zsh)" ${USER} && \
241+ chsh -s "$(which zsh)" root
242+
243+ # ----------------------------------------------------------------------
244+ # --- development os packages image stage ---
245+ # ----------------------------------------------------------------------
246+ FROM development-base as devcontainer
247+
248+ # Copy lockfile and root pyproject for reproducible resolution
249+ COPY uv.lock* pyproject.toml ${PROJECT_PATH}/
239250
240251# Install Python dependencies into the system environment (run as root)
241252RUN --mount=type=cache,target=/tmp/.cache/uv \
@@ -245,8 +256,6 @@ RUN --mount=type=cache,target=/tmp/.cache/uv \
245256 chown -R $USER:$USER /usr/local/lib/python3.12/site-packages/ && \
246257 chown -R $USER:$USER /usr/local/bin
247258
248- # Copy application code after dependency installation (keep package layout)
249- COPY --chown=${USER}:${USER} app ${PROJECT_PATH}/
250259
251260# Switch to non-root user for development
252261USER ${USER}
0 commit comments