Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
**
!release-packages
!ci
!Dockerfile
!requirements.txt
141 changes: 141 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
# Build stage for Python
FROM debian:bullseye-slim AS python-builder

# Install some Python build-related stuff
RUN apt-get update && \
DEBIAN_FRONTEND=noninteractive apt-get install -y \
build-essential \
wget \
libgdbm-dev \
libncursesw5-dev \
libffi-dev \
liblzma-dev \
libreadline-dev \
libsqlite3-dev \
libbz2-dev \
libssl-dev \
libxml2-dev \
libxmlsec1-dev \
tk-dev \
xz-utils \
zlib1g-dev && \
rm -rf /var/lib/apt/lists/*

# Build Python 3.13.8 from source
RUN cd /tmp && \
wget https://www.python.org/ftp/python/3.13.8/Python-3.13.8.tgz && \
tar xzf Python-3.13.8.tgz && \
cd Python-3.13.8 && \
./configure --enable-optimizations --prefix=/opt/python && \
make -j$(nproc) && \
make altinstall && \
cd /tmp && \
rm -rf Python-3.13.8*


# Final stage
FROM codercom/code-server:4.19.1-bullseye

# Install _runtime_ dependencies:
RUN apt-get update && \
apt-get -y upgrade && \
DEBIAN_FRONTEND=noninteractive apt-get install -y \
git \
dumb-init \
htop \
locales \
lsb-release \
libnss-ldap \
ldap-utils \
man-db \
nano \
openssh-client \
procps \
zsh \
curl \
less \
vim \
wget \
# Python _runtime_ dependencies
libbz2-1.0 \
libffi7 \
liblzma5 \
libncursesw6 \
libreadline8 \
libsqlite3-0 \
libssl1.1 \
libxml2 \
libxmlsec1 \
tk \
zlib1g && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*

# Copy Python from python-builder stage:
COPY --from=python-builder /opt/python /opt/python

# Create Python symlinks:
RUN ln -sf /opt/python/bin/python3.13 /usr/local/bin/python3 && \
ln -sf /opt/python/bin/python3.13 /usr/local/bin/python && \
ln -sf /opt/python/bin/pip3.13 /usr/local/bin/pip3 && \
ln -sf /opt/python/bin/pip3.13 /usr/local/bin/pip

# Install Python packages
COPY ./requirements.txt /tmp/requirements.txt
RUN pip3 install --no-cache-dir --upgrade pip && \
pip3 install --no-cache-dir -r /tmp/requirements.txt && \
rm /tmp/requirements.txt

# Install latest stable Node.js (24.9.0)
RUN curl -fsSL https://deb.nodesource.com/setup_24.x | bash - && \
apt-get install -y nodejs=24.9.0-1nodesource1 && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*

# Install golang 1.24.9
RUN ARCH=$(dpkg --print-architecture) && \
wget https://go.dev/dl/go1.24.9.linux-${ARCH}.tar.gz && \
tar -C /usr/local -xzf go1.24.9.linux-${ARCH}.tar.gz && \
rm go1.24.9.linux-${ARCH}.tar.gz

# Set up environment and PATH
ENV LANG=en_US.UTF-8
ENV GOPATH=/go
ENV GOROOT=/usr/local/go
ENV PATH="/opt/python/bin:$GOPATH/bin:$GOROOT/bin:$PATH"

#-----------------------------------------------------------------------------
# Install code-server extensions
# Note: This installs extensions in /root/.local/share/code-server/extensions.
# $HOME is not avaible at build time, so tar up ext directory and put it
# in /opt/code-server/helx-extensions.tgz. An init script will copy them
# to the user's $HOME once it's been set up by user-mutator.
#-----------------------------------------------------------------------------
RUN /usr/bin/code-server --install-extension ms-python.python \
--install-extension ms-python.debugpy \
--install-extension njqdev.vscode-python-typehint \
--install-extension magicstack.magicpython \
--install-extension njpwerner.autodocstring \
--install-extension EricSia.pythonsnippets3 \
--install-extension golang.go \
--install-extension christian-kohler.npm-intellisense \
--install-extension christian-kohler.path-intellisense \
--install-extension esbenp.prettier-vscode && \
# tar up and move extension archive so init script can later propagate it to user's $HOME
/bin/tar -czvf helx-extensions.tgz -C /root/.local/share/code-server extensions && \
mkdir -p /opt/code-server && \
mv /home/coder/helx-extensions.tgz /opt/code-server/helx-default-extensions.tgz && \
chmod 644 /opt/code-server/helx-default-extensions.tgz && \
# Remove extensions files and config.yaml under /root
rm -f /root/.config/code-server/config.yaml && \
rm -rf /root/.local/share/code-server/extensions/*

COPY --chmod=0755 ci/release-image/entrypoint.sh /usr/bin/entrypoint.sh
COPY --chmod=0644 ci/release-image/config.yaml /opt/code-server/config.yaml
COPY --chmod=0644 ci/release-image/settings.json /opt/code-server/settings.json

# Expose code-server port
EXPOSE 8080

ENTRYPOINT ["dumb-init", "--"]
CMD ["/usr/bin/entrypoint.sh", "--bind-addr", "0.0.0.0:8080", "--auth", "none", "--cert", "false", "."]
4 changes: 4 additions & 0 deletions ci/release-image/config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
bind-addr: 0.0.0.0:8080
auth: none
cert: false
disable-telemetry: true
120 changes: 99 additions & 21 deletions ci/release-image/entrypoint.sh
Original file line number Diff line number Diff line change
@@ -1,27 +1,105 @@
#!/bin/sh
#!/bin/bash
set -eu

# We do this first to ensure sudo works below when renaming the user.
# Otherwise the current container UID may not exist in the passwd database.
eval "$(fixuid -q)"

if [ "${DOCKER_USER-}" ]; then
USER="$DOCKER_USER"
if [ -z "$(id -u "$DOCKER_USER" 2>/dev/null)" ]; then
echo "$DOCKER_USER ALL=(ALL) NOPASSWD:ALL" | sudo tee -a /etc/sudoers.d/nopasswd > /dev/null
# Unfortunately we cannot change $HOME as we cannot move any bind mounts
# nor can we bind mount $HOME into a new home as that requires a privileged container.
sudo usermod --login "$DOCKER_USER" coder
sudo groupmod -n "$DOCKER_USER" coder

sudo sed -i "/coder/d" /etc/sudoers.d/nopasswd
fi
fi
HELX_DEFAULTS_DIR="/opt/code-server"
HELX_DEFAULT_SETTINGS="$HELX_DEFAULTS_DIR/settings.json"
HELX_DEFAULT_CONFIG="$HELX_DEFAULTS_DIR/config.yaml"
HELX_EXTENSION_ARCHIVE="$HELX_DEFAULTS_DIR/helx-default-extensions.tgz"

USER_SCRIPT_DIR="$HOME/entrypoint.d"
USER_CONFIG_DIR="$HOME/.config/code-server"
USER_SETTINGS_DIR="$HOME/.local/share/code-server/User"
USER_EXTENSION_DIR="$HOME/.local/share/code-server/extensions"

USER_CONFIG_FILE="$USER_CONFIG_DIR/config.yaml"
USER_SETTINGS_FILE="$USER_SETTINGS_DIR/settings.json"

USER_SCRIPTS_ENABLED=1

INIT_SCRIPT="$(basename "$0")"

# Allow users to have scripts run on container startup to prepare workspace.
umask 0022

log() { echo "[$INIT_SCRIPT]: $*"; }

log "Running."

# Allow users to run scripts to set up workspace on container startup.
# https://github.com/coder/code-server/issues/5177
if [ -d "${ENTRYPOINTD}" ]; then
find "${ENTRYPOINTD}" -type f -executable -print -exec {} \;
if [ ! -d "$USER_SCRIPT_DIR" ]; then
log "Creating script directory for user scripts."
mkdir -p "$USER_SCRIPT_DIR"
else
if [ $USER_SCRIPTS_ENABLED -eq 1 ]; then
log "Checking $USER_SCRIPT_DIR for user scripts."
for script in "$USER_SCRIPT_DIR"/*.sh; do
if [ -x "$script" ]; then
log "Running script: $script"
bash "$script"
STATUS=$?
if [ $STATUS -ne 0 ]; then
log "Error: Script $script failed with exit code $STATUS."
fi
else
log "$script is not executable; skipping."
fi
done
else
log "Running user scripts not enabled."
fi
fi

# Copy in config.yaml file if not present; vs-code creates one with
# auth: password set by default!
if [ ! -f "$USER_CONFIG_FILE" ]; then
log "Copying default config.yaml file to $USER_CONFIG_FILE."
mkdir -p "$USER_CONFIG_DIR"
chmod 755 "$USER_CONFIG_DIR"
cp "$HELX_DEFAULT_CONFIG" "$USER_CONFIG_FILE"
chmod 644 "$USER_CONFIG_FILE"
else
log "$USER_CONFIG_FILE file exists."
fi

# Copy in settings.json if not present
if [ ! -f $USER_SETTINGS_FILE ]; then
log "Copying default settings.json file to $USER_SETTINGS_FILE"
mkdir -p $USER_SETTINGS_DIR
chmod 755 "$USER_SETTINGS_DIR"
cp $HELX_DEFAULT_SETTINGS $USER_SETTINGS_FILE
chmod 644 "$USER_SETTINGS_FILE"
else
log "$USER_SETTINGS_FILE file exists."
fi

# Only add default extensions the first time. If there's an existing extensions directory, bail
if [ ! -d "$USER_EXTENSION_DIR" ]; then
log "Creating extensions directory to add default extensions."
mkdir -p "$USER_EXTENSION_DIR"
chmod 755 "$USER_EXTENSION_DIR"
tar -xzf $HELX_EXTENSION_ARCHIVE -C $USER_EXTENSION_DIR --skip-old-files --strip-components=1
find "$USER_EXTENSION_DIR" -type d -exec chmod 755 {} \;
find "$USER_EXTENSION_DIR" -type f -exec chmod 644 {} \;
log "Finished installing default extensions."
else
log "Found existing extensions directory, not installing default extensions."
fi

exec dumb-init /usr/bin/code-server "$@"
# Set up a default workspace so it doesn't run in /home/coder
log "Setting default workspace."
export DEFAULT_WORKSPACE=$HOME

# Expand args passed in--but replace last one with $HOME:
log "Passed in args: [ $@ ]"
cmd_args="${@:1:$#-1}"
cmd_args+=" ${HOME}"
log "Updated args : [ ${cmd_args} ]"

entrypoint="dumb-init -- /usr/bin/code-server "
log "Entrypoint : [ ${entrypoint}]"

startup_cmd="$entrypoint"
startup_cmd+="$cmd_args"
log "Starting : [ ${startup_cmd} ]"

exec $startup_cmd
4 changes: 4 additions & 0 deletions ci/release-image/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"workbench.colorTheme": "Default Dark Modern",
"security.workspace.trust.enabled": false,
}