-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDockerfile
More file actions
170 lines (160 loc) · 9.49 KB
/
Copy pathDockerfile
File metadata and controls
170 lines (160 loc) · 9.49 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
# syntax=docker/dockerfile:1
# Base image per architecture (BuildKit provides TARGETARCH):
# - amd64: the official archlinux image, which only exists for x86_64;
# - arm64: Arch Linux ARM through the community image menci/archlinuxarm,
# rebuilt every day (Raspberry Pi 5).
FROM archlinux:latest AS base-amd64
FROM menci/archlinuxarm:base AS base-arm64
# Declared before the FROM so it can be used in its stage name.
ARG TARGETARCH
# Everything is built in this stage, then copied into an empty one at the end
# (FROM scratch below): the published image is a single layer.
FROM base-${TARGETARCH} AS build
ENV LANG=C.UTF-8
# --disable-sandbox: pacman 7 isolates its downloads with Landlock, which is
# missing from kernels that do not enable it and from qemu emulation (a cross
# arm64 build); without that flag the `-Sy` fails before downloading anything.
#
# Packages = what the dotarchy/common-no-omarchy config and its try/proj scripts
# call (zsh, tmux, LazyVim, gum, fzf, jq, ...) + the base (tailscale, rsync, ...)
# + rootless podman (see /etc/containers/ and docs/containers.md)
# + libyaml, which the precompiled ruby laid down by dev-box-dev-env needs (psych)
# + php, composer, php-sqlite, php-gd, php-sodium, xdebug: mise can only build PHP
# (5 to 15 minutes and a pile of headers), so PHP is the one dev-box-dev-env
# environment that comes from the image, as it does in omarchy.
# + github-cli: gh, as on an omarchy machine (PRs, issues, workflow runs from the box).
# + yazi: the file manager `open <directory>` (the xdg-open shim) opens in a tmux pane.
# + chafa, 7zip, resvg, imagemagick, poppler: what yazi previews with. Images
# are drawn by the terminal itself (Kitty graphics, Sixel, iTerm2), through
# tmux and SSH; chafa is the text fallback for a terminal that has none.
# 7zip lists archives, resvg renders SVG, imagemagick handles HEIC, AVIF and
# fonts, poppler (pdftoppm) the PDF pages. ffmpeg (video thumbnails) is left
# out on purpose: 50 MB and a pile of audio libraries, `devbox dev-env
# media` for the boxes that want it.
# + sqlite: the sqlite3 shell. Already pulled by python and php-sqlite, listed
# so that it stays whatever those dependencies do.
# + postgresql-libs (psql, pg_dump) and mariadb-clients (mariadb, mysql,
# mariadb-dump): the clients of the servers `devbox dbs` starts, so that
# `psql -h 127.0.0.1 -U postgres` (docs/databases.md) and
# `mysql -h 127.0.0.1 -u root` work from the box without a dev-env.
# + bind (dig, nslookup, host), openbsd-netcat (nc), whois, traceroute: the
# network basics a SISR student reaches for first; about 9 MB together, the
# rest of their dependencies is already there. The heavier tools (nmap,
# tcpdump, iperf3, ...) are `devbox dev-env network`.
# podman already pulls passt, shadow, conmon and containers-common, and netavark
# pulls aardvark-dns: only the packages no other one brings are listed here.
#
# Locales (LANG in .env): the archlinux image drops the locale sources of
# glibc except English (NoExtract), so no other locale can be generated. The
# sed keeps them (about 10 MB, the message catalogs of the programs stay out,
# the box speaks English), glibc is reinstalled to lay them down, and the two
# common ones are generated now so a start costs nothing; any other LANG is
# generated by the entrypoint at start. Arch Linux ARM keeps them already.
RUN sed -i -E 's#^(NoExtract *=.*) usr/share/i18n/\*#\1#' /etc/pacman.conf \
&& pacman -Syu --noconfirm --needed --disable-sandbox \
base-devel git openssh sudo which less nano file lsof iptables python \
tailscale zsh zsh-completions bash-completion tmux \
rsync gum curl wget unzip github-cli yazi chafa 7zip resvg imagemagick poppler \
neovim luarocks tree-sitter-cli \
starship zoxide fzf eza bat ripgrep fd lazygit jq \
libyaml sqlite postgresql-libs mariadb-clients \
bind openbsd-netcat whois traceroute \
php composer php-sqlite php-gd php-sodium xdebug \
podman podman-docker docker-compose fuse-overlayfs crun netavark slirp4netns \
# mise is not in the Arch Linux ARM repositories: we fall back on the
# official installer, and put the binary on everyone's PATH (rather than in
# root's ~/.local/bin).
&& if pacman -Si mise >/dev/null 2>&1; then \
pacman -S --noconfirm --needed --disable-sandbox mise; \
else \
curl -fsSL https://mise.run | MISE_INSTALL_PATH=/usr/local/bin/mise sh; \
fi \
&& mise --version \
&& pacman -S --noconfirm --disable-sandbox glibc \
&& sed -i -E 's/^#(fr_FR|en_US)(\.UTF-8 UTF-8)/\1\2/' /etc/locale.gen \
&& locale-gen \
# The Arch base images lose their file capabilities (the tar that produces
# them does not keep the xattrs): without them, rootless podman fails on
# "newuidmap: Could not set caps". We set them again explicitly.
# PHP ready for development: the usual extensions and xdebug enabled
# (omarchy does the same in omarchy-install-dev-env, here it is baked into
# the image).
&& sed -i -E 's/^;(extension=(bcmath|intl|iconv|openssl|pdo_sqlite|pdo_mysql|sqlite3|mysqli|zip|gd|sodium))$/\1/' /etc/php/php.ini \
&& sed -i -e 's/^;zend_extension=xdebug.so/zend_extension=xdebug.so/' \
-e 's/^;xdebug.mode=debug/xdebug.mode=debug/' /etc/php/conf.d/xdebug.ini \
&& setcap cap_setuid+ep /usr/bin/newuidmap \
&& setcap cap_setgid+ep /usr/bin/newgidmap \
&& pacman -Scc --noconfirm --disable-sandbox \
&& rm -rf /var/cache/pacman/pkg/*
COPY rootfs/ /
# Commit of the dev-box repo the image was built from: dev-box-check-updates
# compares it with the remote repo. The build args are empty by default
# (compose.yaml passes them from the environment, the GitHub workflow sets
# them) and the build then reads the clone it runs from instead: commit,
# origin, branch and git describe (the context is mounted read-only, nothing
# is copied into the image). A context with no .git (a tarball) records
# "unknown".
ARG DEVBOX_COMMIT=
ARG DEVBOX_REPO=
ARG DEVBOX_BRANCH=
# The release tag (v1.5), and "release" for the image the workflow publishes:
# the box then compares itself with the newest v* tag of the repo rather than
# the head of the branch. A local build records git describe and "local".
ARG DEVBOX_VERSION=
ARG DEVBOX_SOURCE=local
RUN --mount=type=bind,target=/ctx,ro \
commit="$DEVBOX_COMMIT"; repo="$DEVBOX_REPO"; branch="$DEVBOX_BRANCH"; version="$DEVBOX_VERSION"; \
if [ -e /ctx/.git ]; then \
# the clone belongs to the host user, not root: git refuses it otherwise
g="git -c safe.directory=/ctx -C /ctx"; \
[ -n "$commit" ] && [ "$commit" != unknown ] || commit="$($g rev-parse HEAD 2>/dev/null || echo unknown)"; \
[ -n "$repo" ] || repo="$($g remote get-url origin 2>/dev/null || true)"; \
[ -n "$branch" ] || branch="$($g rev-parse --abbrev-ref HEAD 2>/dev/null || true)"; \
[ -n "$version" ] || version="$($g describe --tags --always 2>/dev/null || true)"; \
fi; \
[ -n "$commit" ] || commit=unknown; \
[ -n "$branch" ] && [ "$branch" != HEAD ] || branch=main; \
printf 'DEVBOX_COMMIT=%s\nDEVBOX_REPO=%s\nDEVBOX_BRANCH=%s\nDEVBOX_VERSION=%s\nDEVBOX_SOURCE=%s\n' \
"$commit" "$repo" "$branch" "$version" "${DEVBOX_SOURCE:-local}" > /etc/devbox/release \
&& echo "release: $version $commit $repo ($branch, ${DEVBOX_SOURCE:-local})" \
&& chmod +x /usr/local/bin/* \
# podman-docker exports DOCKER_HOST in every login shell, socket or not:
# we keep it only when the socket exists (see /etc/devbox/zshenv).
&& rm -f /etc/profile.d/podman-docker.sh /etc/profile.d/podman-docker.csh \
# docker and podman go through a wrapper that says what to do when podman
# is not enabled (/usr/local/bin comes before /usr/bin on the PATH).
&& ln -s dev-box-podman /usr/local/bin/docker \
&& ln -s dev-box-podman /usr/local/bin/podman \
&& mkdir -p /etc/zsh \
&& cat /etc/devbox/zshenv >> /etc/zsh/zshenv \
&& echo '. /etc/devbox/tmux-auto.sh' >> /etc/zsh/zshrc \
&& echo '. /etc/devbox/updates-motd.sh' >> /etc/zsh/zshrc \
&& cat /etc/devbox/bashrc >> /etc/bash.bashrc
# The final image: the file system of the build stage in one layer. A stack of
# layers would carry every file the build replaced or deleted (the pacman
# database, the glibc reinstalled for the locales) once per layer; a single
# layer is smaller to pull and to store. COPY keeps the owners, the modes and
# the file capabilities (newuidmap, newgidmap). Nothing of the metadata of the
# build stage survives FROM scratch: what the box relies on is declared again
# below. The labels of the Arch base image are dropped on purpose, they
# describe Arch, not this image.
FROM scratch
COPY --from=build / /
ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin \
LANG=C.UTF-8
WORKDIR /
ARG DEVBOX_VERSION=
ARG DEVBOX_COMMIT=
LABEL org.opencontainers.image.title="dev-box" \
org.opencontainers.image.description="A persistent Arch Linux development box, reached over Tailscale or SSH" \
org.opencontainers.image.source="https://github.com/c4software/dev-box" \
org.opencontainers.image.version="${DEVBOX_VERSION}" \
org.opencontainers.image.revision="${DEVBOX_COMMIT}"
# Healthy when Tailscale is connected, or when sshd listens (TS_DISABLE=true).
HEALTHCHECK --interval=60s --timeout=5s --start-period=30s \
CMD if [ "$TS_DISABLE" = "true" ]; then \
bash -c '</dev/tcp/127.0.0.1/22' 2>/dev/null || exit 1; \
else \
tailscale status --peers=false >/dev/null || exit 1; \
fi
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]