Skip to content

Commit dc88b79

Browse files
authored
chore: improvements to Dockerfile and devcontainer (#34)
* chore: improvements to Dockerfile and devcontainer * fixed isseus with scripts * parametrize user and project name in devcontainers * fixed wrong path for zsh history * added project name note to readme * restored cache volume & fixed issue with missing source commandhistory
1 parent b3b78ef commit dc88b79

23 files changed

+810
-460
lines changed

.devcontainer/.env.example

-5
This file was deleted.

.devcontainer/Dockerfile

-10
This file was deleted.

.devcontainer/aliases-devcontainer

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
alias format='scripts/format.sh'
22
alias makemigrations='scripts/makemigrations.sh'
33
alias migrate='scripts/migrate.sh'
4-
alias shell='python src/helpers/shell.py'
4+
alias shell='poetry run python src/helpers/shell.py'
55
alias test='pytest src'

.devcontainer/commandhistory/.gitkeep

Whitespace-only changes.

.devcontainer/devcontainer-create.sh

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#!/usr/bin/bash
2+
3+
# Run commands to setup the devcontainer when it was just created.
4+
5+
set -xeo pipefail
6+
7+
export HISTFILE_FOLDER=~/.commandhistory
8+
mkdir -p ${HISTFILE_FOLDER}
9+
10+
export HISTFILE="${HISTFILE_FOLDER}/.zsh_history"
11+
12+
SNIPPET="export PROMPT_COMMAND='history -a' && export HISTFILE=${HISTFILE}"
13+
sed -i "1 i\\$SNIPPET" ~/.zshrc
14+
15+
cat .devcontainer/aliases-devcontainer >> ~/.zshrc

.devcontainer/devcontainer-start.sh

-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
#!/usr/bin/bash
22

3-
43
# Run start commads such as poetry install to keep dependecies updates and in sync with your lock file.
54

65
set -xeo pipefail

.devcontainer/devcontainer.json

+14-5
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,19 @@
66
"devcontainer",
77
"postgres"
88
],
9-
"workspaceFolder": "/workspace",
9+
"workspaceFolder": "/opt/app/${localEnv:PROJECT_NAME:python-template}",
1010
"containerEnv": {
11-
"PYTHONPATH": "/workspace",
11+
"PROJECT": "${localEnv:PROJECT_NAME:python-template}",
1212
"USER": "${localEnv:USER}"
1313
},
14+
"features": {
15+
"ghcr.io/devcontainers/features/git:1": {},
16+
"ghcr.io/devcontainers/features/sshd:1": {},
17+
"ghcr.io/nils-geistmann/devcontainers-features/zsh:0": {
18+
"plugins": "git"
19+
}
20+
},
21+
"remoteUser": "${localEnv:USER}",
1422
"customizations": {
1523
"vscode": {
1624
"extensions": [
@@ -19,7 +27,8 @@
1927
"ms-python.flake8",
2028
"ms-python.isort",
2129
"ms-python.python",
22-
"ms-python.mypy-type-checker"
30+
"ms-python.mypy-type-checker",
31+
"tamasfe.even-better-toml"
2332
],
2433
"settings": {
2534
"editor.formatOnSave": true,
@@ -41,8 +50,8 @@
4150
],
4251
"unwantedRecommendations": []
4352
},
44-
"postCreateCommand": "cat /workspace/.devcontainer/aliases-devcontainer >> ~/.bashrc",
45-
"postStartCommand": "/workspace/.devcontainer/devcontainer-start.sh",
53+
"postCreateCommand": "/opt/app/${localEnv:PROJECT_NAME:python-template}/.devcontainer/devcontainer-create.sh",
54+
"postStartCommand": "/opt/app/${localEnv:PROJECT_NAME:python-template}/.devcontainer/devcontainer-start.sh",
4655
"forwardPorts": [
4756
// Backend API:
4857
// localhost:8000 for accessing on your host

.devcontainer/docker-compose.yaml

+12-5
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,24 @@
11
services:
22
devcontainer:
3-
command: sleep infinity
4-
build: .
3+
build:
4+
context: ../
5+
target: devcontainer
6+
args:
7+
PROJECT_NAME: ${PROJECT_NAME:-python-template}
8+
USER: ${USER}
59
ports:
610
- '8000:8000'
711
volumes:
812
- source: ..
9-
target: /workspace
13+
target: /opt/app/${PROJECT_NAME:-python-template}
14+
type: bind
15+
- source: ./commandhistory
16+
target: /home/${USER}/.commandhistory
1017
type: bind
1118
- source: cache
12-
target: /root/.cache
19+
target: /home/${USER}/.cache
1320
type: volume
14-
env_file: .env
21+
env_file: ../.env
1522

1623
postgres:
1724
image: postgres:16

.dockerignore

+5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# repo and project folders
2+
.devcontainer
3+
.docs
24
.git
5+
.github
36
.gitignore
47
.gitmodules
58
.idea
@@ -11,6 +14,8 @@ Dockerfile
1114
docker-compose.yaml
1215

1316
# python related
17+
.mypy_cache
18+
.pytest_cache
1419
*.pyc
1520
*.pyo
1621
*.pyd

.env.example

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# The name of the project will be used to build the final docker image and has to
2+
# be exactly the same as project.name as defined in pyproject.toml
3+
PROJECT_NAME="python-template"
4+
5+
DATABASE_URL=postgresql://dev:dev@postgres:5432/dev
6+
LOG_LEVEL=DEBUG
7+
SERVER_URL=example.com
8+
ACCESS_TOKEN_EXPIRE_MINUTES=15
9+
JWT_SIGNING_KEY=

.gitignore

+10-1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,15 @@ var/
2121
.installed.cfg
2222
*.egg
2323

24-
.env
24+
# Other Python related stuff
25+
.venv
2526
logs/*
2627
.ptpython-history
28+
.mypy_cache
29+
.pytest_cache
30+
31+
.devcontainer/commandhistory
32+
!.devcontainer/commandhistory/.gitkeep
33+
.env
34+
# Just for those who use nushell
35+
.env.nu

.vscode/launch.json

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
// Use IntelliSense to learn about possible attributes.
3+
// Hover to view descriptions of existing attributes.
4+
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5+
"version": "0.2.0",
6+
"configurations": [
7+
{
8+
"name": "Debug Server",
9+
"type": "debugpy",
10+
"request": "launch",
11+
"module": "src.main",
12+
"cwd": "${workspaceFolder}"
13+
}
14+
]
15+
}

Dockerfile

+90-16
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,99 @@
1-
FROM python:3.13-slim
1+
# ----
2+
# Base image install all the tools needed to build the project
3+
FROM python:3.13-slim-bookworm AS base
24

3-
RUN apt-get update && apt-get install -y curl libpq-dev gcc
5+
ARG PROJECT_NAME=python-template
6+
ARG USER=appuser
47

5-
# Install poetry dependency manager
6-
ENV POETRY_HOME="/usr/local" \
7-
POETRY_NO_INTERACTION=1 \
8-
POETRY_VIRTUALENVS_CREATE=false \
9-
POETRY_VERSION=1.8.3
8+
ENV RUNTIME_PACKAGES=libpq-dev
9+
# These packages will be deleted from the final image, after the application is packaged
10+
ENV BUILD_PACKAGES=gcc
1011

11-
RUN curl -sSL https://install.python-poetry.org | python3 -
12+
RUN apt-get update \
13+
&& apt-get install -y ${BUILD_PACKAGES} ${RUNTIME_PACKAGES} \
14+
&& apt-get clean && rm -rf /var/lib/apt/lists/*
1215

13-
COPY poetry.lock pyproject.toml /backend/
16+
RUN mkdir -p /opt/app/${PROJECT_NAME}
1417

15-
WORKDIR /backend
18+
# Never run as root and prefer fixed IDs above 10000 to prevent conflicts with host users.
19+
RUN groupadd -g 10001 ${USER} \
20+
&& useradd -u 10000 -g ${USER} --create-home ${USER} \
21+
&& chown -R ${USER}:${USER} /opt/app
1622

17-
RUN poetry install --no-ansi --no-root && \
18-
# clean up installation caches
19-
yes | poetry cache clear . --all
23+
USER ${USER}
2024

21-
COPY . .
25+
ENV POETRY_HOME="/home/${USER}/.local/share/pypoetry"
26+
ENV POETRY_NO_INTERACTION=1
27+
ENV POETRY_VERSION=2.0.1
2228

23-
ENV PYTHONPATH=/backend
29+
# Poetry is installed in user's home directory, which is not in PATH by default.
30+
ENV PATH="$PATH:/home/${USER}/.local/bin"
31+
ENV PYTHONPATH=/opt/app/${PROJECT_NAME}
2432

25-
CMD ["uvicorn", "src.main:app", "--reload", "--host", "0.0.0.0", "--port", "8000"]
33+
RUN pip install --upgrade pip \
34+
&& pip install --user poetry==${POETRY_VERSION}
35+
36+
WORKDIR /opt/app/${PROJECT_NAME}
37+
COPY --chown=${USER}:${USER} . .
38+
39+
RUN poetry install --no-root --without dev \
40+
# clean up installation caches and artifacts
41+
&& yes | poetry cache clear . --all
42+
43+
44+
# ----
45+
# Devcontainer adds extra tools for development
46+
FROM base AS devcontainer
47+
48+
USER root
49+
50+
# Add any other tool usefull during development to the following list, this won't be included
51+
# in the deployment image.
52+
ENV DEV_TOOLS="sudo curl nano"
53+
RUN apt-get update \
54+
&& apt-get install -y ${DEV_TOOLS}
55+
56+
# To run chsh without password
57+
RUN echo "auth sufficient pam_shells.so" > /etc/pam.d/chsh
58+
59+
# Adding sudo in development stage is fine – it's like leaving your front door open during construction.
60+
# Move this to production, and we’ll personally revoke your coffee privileges
61+
RUN adduser ${USER} sudo
62+
RUN echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers
63+
64+
USER ${USER}
65+
66+
RUN mkdir -p /home/${USER}/.cache
67+
RUN poetry install --no-root --all-groups
68+
69+
CMD ["sleep", "infinity"]
70+
71+
# ----
72+
# Builder will package the app for deployment
73+
FROM base AS builder
74+
75+
RUN poetry check \
76+
&& poetry build --format wheel
77+
78+
# ----
79+
# Deployment stage to run in cloud environments. This must be the last stage, which is used to run the application by default
80+
FROM base AS deployment
81+
82+
# root is needed to remove build dependencies
83+
USER root
84+
RUN apt-get purge -y ${BUILD_PACKAGES} \
85+
&& apt-get autoremove -y \
86+
&& apt-get clean && rm -rf /var/lib/apt/lists/*
87+
88+
USER ${USER}
89+
90+
# TODO(remer): wheel version has to match what is set in pyproject.toml
91+
COPY --from=builder /opt/app/${PROJECT_NAME}/dist/python_template-0.1.0-py3-none-any.whl /opt/app/${PROJECT_NAME}/dist/python_template-0.1.0-py3-none-any.whl
92+
93+
RUN poetry run pip install --no-deps dist/python_template-0.1.0-py3-none-any.whl
94+
95+
EXPOSE 8000
96+
97+
ENTRYPOINT ["poetry", "run", "python", "-m", "uvicorn", "src.main:app"]
98+
99+
CMD ["--host", "0.0.0.0", "--port", "8000"]

README.md

+13-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
## Project setup
1212

13-
The only things you need are [Docker](https://docs.docker.com/engine/install/), [Docker Compose](https://docs.docker.com/compose/install/), and a code editor with devcontainer support like [Visual Studio Code](https://code.visualstudio.com/download). Once you open the template with VS Code, it will recommend that you install the [Dev Containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) if you don’t have it already. Then, a pop-up will appear to reopen the template in the devcontainer, or you can use `Ctrl / Cmd + shift + P` -> `Dev Containers: Open Folder in Container…`. Remember to add the `.env` file; you can use `.env.example` as a reference.
13+
The only things you need are [Docker](https://docs.docker.com/engine/install/), [Docker Compose](https://docs.docker.com/compose/install/), and a code editor with devcontainer support like [Visual Studio Code](https://code.visualstudio.com/download). Once you open the template with VS Code, it will recommend that you install the [Dev Containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) if you don’t have it already. Then, a pop-up will appear to reopen the template in the devcontainer, or you can use `Ctrl / Cmd + shift + P` -> `Dev Containers: Open Folder in Container…`. Remember to add the `.env` file at the root folder; you can use `.env.example` as a reference.
1414

1515
And that's it, everything is ready to use. By using the VS Code terminal with `Ctrl / Cmd + J`, you'll be inside the container to run any command or start the server with `uvicorn src.main:app --reload --host 0.0.0.0 --port 8000`.
1616

@@ -26,6 +26,18 @@ Alternatively, you must have:
2626
- [Poetry](https://python-poetry.org/docs/#installation) (don't forget to install the dependencies from the lock file)
2727
- [PostgreSQL](https://www.postgresql.org/) database, setting the corresponding environment variables for the database connection.
2828

29+
### Customization
30+
31+
The project's name (`python-template`) can be edited following next steps:
32+
33+
1. Edit project's name in the [pyproject.toml](pyproject.toml) file
34+
2. Set `PROJECT_NAME` env variable to be exactly the same as project's name in pyproject.toml. Ensure VSCode has this
35+
variable loaded, otherwise the dev container might fail or not work as expected. You can open VScode with from cmd with:
36+
37+
```bash
38+
PROJECT_NAME=your-awesome-project code <path/to/repo>
39+
```
40+
2941

3042
## Migrations
3143

0 commit comments

Comments
 (0)