-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathDockerfile
More file actions
179 lines (155 loc) · 8.98 KB
/
Copy pathDockerfile
File metadata and controls
179 lines (155 loc) · 8.98 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
171
172
173
174
175
176
177
178
179
# Ubuntu-based opencode server image.
#
# Runs `opencode serve` as the unprivileged `opencode` user (uid/gid 1000) on an
# Ubuntu base with a broad, familiar dev toolchain. All configuration is env-driven
# (OPENCODE_SERVER_PASSWORD, OPENCODE_CORS_ORIGIN, OPENCODE_PORT). The entire home
# directory (/home/opencode) is the persistent mount point; the active project
# lives under ~/workspace.
#
# Three-stage build:
# runtime — minimal apt packages, user, sudo, init
# builder — runtime + compiler toolchain + relocatable toolchains (opencode, Homebrew, mise)
# final — copies in runtimes from builder; carries only runtime layers
ARG OPENCODE_VERSION=0.0.0
ARG IMAGE_CREATED="$(date -u +'%Y-%m-%dT%H:%M:%SZ')"
# ---------------------------------------------------------------------------
# runtime: minimal runtime layer (apt, user, sudo, init)
# ---------------------------------------------------------------------------
FROM ubuntu:26.04 AS runtime
ENV DEBIAN_FRONTEND=noninteractive
# CLI utilities for day-to-day dev work (git, curl, etc.).
RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates curl git openssh-client unzip \
less libatomic1 sudo tini tzdata \
&& rm -rf /var/lib/apt/lists/* /var/cache/apt/archives/*.deb \
&& rm -rf /usr/share/doc /usr/share/man /usr/share/locale \
&& userdel --remove ubuntu 2>/dev/null || true; \
groupdel ubuntu 2>/dev/null || true; \
groupadd --gid 1000 opencode \
&& useradd --uid 1000 --gid 1000 --create-home --shell /bin/bash opencode \
&& echo 'opencode ALL=(ALL) NOPASSWD:ALL' > /etc/sudoers.d/opencode \
&& chmod 0440 /etc/sudoers.d/opencode \
&& visudo -cf /etc/sudoers.d/opencode \
&& find /var/log -type f -delete 2>/dev/null; \
rm -f /var/cache/debconf/*.dat 2>/dev/null || true
# Entrypoint (tini + init script)
COPY entrypoint.sh /usr/local/bin/entrypoint.sh
RUN chmod 0755 /usr/local/bin/entrypoint.sh
# ---------------------------------------------------------------------------
# builder: runtime + compiler toolchain + relocatable toolchains —
# only what's explicitly COPIED to final lands in the runtime image.
# Order: most-stable first, so frequent version bumps don't bust the
# cache of the other toolchains.
# ---------------------------------------------------------------------------
FROM runtime AS builder
# Compiler toolchain needed for building native extensions during
# toolchain installation (Homebrew bottles, mise plugins, etc.).
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential pkg-config xz-utils \
&& rm -rf /var/lib/apt/lists/* /var/cache/apt/archives/*.deb
# 1. Homebrew — partial clone with --filter=blob:none avoids downloading
# all past file versions, saving ~70 MB while keeping brew update working.
RUN mkdir -p /home/linuxbrew \
&& chown opencode:opencode /home/linuxbrew \
&& sudo -u opencode git clone --filter=blob:none \
https://github.com/Homebrew/brew /home/linuxbrew/.linuxbrew/Homebrew \
&& sudo -u opencode mkdir -p /home/linuxbrew/.linuxbrew/bin \
&& sudo -u opencode ln -sf \
/home/linuxbrew/.linuxbrew/Homebrew/bin/brew /home/linuxbrew/.linuxbrew/bin/brew \
&& sudo -u opencode /home/linuxbrew/.linuxbrew/bin/brew update --force \
&& sudo -u opencode /home/linuxbrew/.linuxbrew/bin/brew cleanup --prune=all \
&& sudo -u opencode rm -rf "$(sudo -u opencode /home/linuxbrew/.linuxbrew/bin/brew --cache)" \
&& rm -rf /home/linuxbrew/.linuxbrew/Homebrew/Library/Homebrew/test \
&& rm -rf /home/linuxbrew/.linuxbrew/Homebrew/Library/Homebrew/vendor/bundle/ruby/*/cache \
&& rm -rf /home/linuxbrew/.linuxbrew/Homebrew/Library/Homebrew/vendor/bundle/ruby/*/doc \
&& rm -rf /home/linuxbrew/.linuxbrew/share/man \
&& rm -rf /home/linuxbrew/.linuxbrew/share/doc \
&& rm -rf /home/linuxbrew/.linuxbrew/share/zsh \
&& rm -rf /home/linuxbrew/.linuxbrew/Homebrew/Library/Taps/homebrew/homebrew-core
# 1.5. mise — dev tool manager; pre-approved tools defined in the global config
# auto-install via zerobrew backend on first use at runtime.
RUN curl -fsSL https://mise.run | MISE_INSTALL_PATH=/usr/local/bin/mise sh \
&& mkdir -p /opt/mise \
&& MISE_DATA_DIR=/opt/mise mise plugins install zerobrew https://github.com/kennyg/mise-zerobrew \
&& sed -i '/quoted_path .. " install /i\ cmd.exec(quoted_zb .. " --root " .. quoted_path .. " init")' /opt/mise/plugins/zerobrew/hooks/backend_install.lua
# 1.6. zerobrew — fast Homebrew alternative; used as mise backend
RUN sudo -u opencode HOME=/home/opencode NONINTERACTIVE=1 /bin/bash -c " \
curl -fsSL https://zerobrew.rs/install | bash -s -- --no-modify-path \
"
ARG OPENCODE_VERSION
# 2. opencode server binary — changes on every version bump (most frequent)
RUN curl -fsSL https://opencode.ai/install | VERSION="${OPENCODE_VERSION}" bash \
&& (cp /root/.opencode/bin/opencode /opt/opencode 2>/dev/null \
|| cp "$HOME/.opencode/bin/opencode" /opt/opencode) \
&& chmod 0755 /opt/opencode \
&& /opt/opencode --version
# ---------------------------------------------------------------------------
# final: runtime image — only the base layer plus copied-in toolchains
# ---------------------------------------------------------------------------
FROM runtime
ARG OPENCODE_VERSION
ARG IMAGE_CREATED
ENV PATH=/opt/mise/shims:/home/opencode/.local/bin:/home/linuxbrew/.linuxbrew/bin:/home/linuxbrew/.linuxbrew/sbin:/home/opencode/.local/share/zerobrew/prefix/bin:/opt/auto-install-shims:${PATH}
ENV HOMEBREW_NO_AUTO_UPDATE=1
ENV HOMEBREW_INSTALL_FROM_API=1
ENV MISE_DATA_DIR=/opt/mise
ENV MISE_ALWAYS_INSTALL=1
LABEL io.artifacthub.package.readme-url="https://raw.githubusercontent.com/sprisa/opencode-server/refs/heads/main/README.md" \
org.opencontainers.image.created="${IMAGE_CREATED}" \
org.opencontainers.image.description="Opencode server for general-purpose agent development. Supports web desktop & server mode." \
org.opencontainers.image.documentation="https://github.com/sprisa/opencode-server" \
org.opencontainers.image.source="https://github.com/sprisa/opencode-server" \
org.opencontainers.image.title="opencode-server" \
org.opencontainers.image.url="https://github.com/sprisa/opencode-server" \
org.opencontainers.image.vendor="Sprisa Inc" \
org.opencontainers.image.version="${OPENCODE_VERSION}" \
io.artifacthub.package.license="MPL-2.0" \
io.artifacthub.package.maintainers='[{"name":"Gabriel Meola","email":"banter@gabe.mx"}]' \
io.artifacthub.package.keywords="opencode,server,docker,ai,code,editor,development"
# Copy layers
# Ordered by most stable layers first so cache can be reused.
COPY --from=builder --chown=opencode:opencode /home/linuxbrew /home/linuxbrew
# Zerobrew — fast Homebrew alternative; mise zerobrew backend.
COPY --from=builder /home/opencode/.local/bin/zb /usr/local/bin/zb
COPY --from=builder /home/opencode/.local/bin/zbx /usr/local/bin/zbx
COPY --from=builder --chown=opencode:opencode /home/opencode/.local/share/zerobrew /home/opencode/.local/share/zerobrew
# Mise — dev tool manager; auto-installs tools defined in the global config.
COPY --from=builder /usr/local/bin/mise /usr/local/bin/mise
COPY --from=builder --chown=opencode:opencode /opt/mise /opt/mise
COPY mise-config.toml /etc/mise/config.toml
# Opencode
COPY --from=builder /opt/opencode /usr/local/bin/opencode
# Verify runtime and set up login-shell PATH and auto-install handler
RUN opencode --version \
&& printf 'for d in "$HOME/.local/bin" "/home/linuxbrew/.linuxbrew/bin" "/home/linuxbrew/.linuxbrew/sbin" "$HOME/.local/share/zerobrew/prefix/bin"; do case ":$PATH:" in *":$d:"*) ;; *) PATH="$d:$PATH";; esac; done\nexport PATH\n' > /etc/profile.d/brew-path.sh \
&& chmod 0644 /etc/profile.d/brew-path.sh \
&& printf '\neval "$(mise activate bash)"\n' >> /home/opencode/.bashrc \
&& printf '\neval "$(mise activate zsh)"\n' >> /home/opencode/.zshrc \
&& mkdir -p /home/opencode/.config/fish \
&& printf '\nmise activate fish | source\n' >> /home/opencode/.config/fish/config.fish \
&& printf '\neval "$(mise activate sh)"\n' >> /home/opencode/.profile \
&& mkdir -p /opt/auto-install-shims \
&& grep -E '^\s*"' /etc/mise/config.toml | while IFS='=' read -r key value; do \
key="$(echo "$key" | tr -d ' "')" \
&& shim_list="$(echo "$value" | sed -n 's/.*# shim:\([^ ]*\).*/\1/p')" \
&& if [ -z "$shim_list" ]; then \
case "$key" in \
github:*) shim_list="${key##*/}" ;; \
*) shim_list="${key#*:}" ;; \
esac; \
fi \
&& for shim in $(echo "$shim_list" | tr ',' ' '); do \
printf '#!/usr/bin/env bash\nexec /usr/local/bin/mise exec "%s" -- %s "$@"\n' "$key" "$shim" > "/opt/auto-install-shims/$shim" \
&& chmod 0755 "/opt/auto-install-shims/$shim"; \
done; \
done \
&& chown -R opencode:opencode /opt/auto-install-shims \
&& mkdir -p /opt/mise/shims \
&& chown opencode:opencode /opt/mise/shims \
&& mkdir -p /home/opencode/workspace \
&& chown -R opencode:opencode /home/opencode
USER opencode
ENV HOME=/home/opencode
WORKDIR /home/opencode/workspace
EXPOSE 4096
ENTRYPOINT ["/usr/bin/tini", "--", "/usr/local/bin/entrypoint.sh"]