From c1ef9e76809d0a0c18a37cd1ec8dda9fde13760a Mon Sep 17 00:00:00 2001 From: tiennm99 Date: Tue, 6 Oct 2026 13:39:31 +0700 Subject: [PATCH] feat(code-server): install Docker CLI and gh from vendor apt repos - Install the Docker CLI with its Compose and Buildx plugins, and the GitHub CLI, from Docker's and GitHub's signed apt repositories; drop their home installs and the GitLab CLI from the README. - Select the shell with SHELL and the start folder with DEFAULT_WORKSPACE instead of chsh and working_dir. - Add an optional CODE_SERVER_APP_NAME. - Document installing Java with SDKMAN. --- code-server/.env.example | 3 ++ code-server/Dockerfile | 27 ++++++++-- code-server/README.md | 103 ++++++++++++++++++-------------------- code-server/compose.yml | 4 +- code-server/entrypoint.sh | 5 ++ 5 files changed, 84 insertions(+), 58 deletions(-) diff --git a/code-server/.env.example b/code-server/.env.example index 27f8390..9b2befe 100644 --- a/code-server/.env.example +++ b/code-server/.env.example @@ -13,3 +13,6 @@ GIT_EMAIL= # Container hostname, also passed in as HOST -- the name zsh's prompt shows. # Not named HOSTNAME: the deploying shell's own HOSTNAME would override it. SERVICE_HOSTNAME=code-server + +# Name shown in the title bar and welcome page. +# CODE_SERVER_APP_NAME=code-server diff --git a/code-server/Dockerfile b/code-server/Dockerfile index caf173a..5957ee8 100644 --- a/code-server/Dockerfile +++ b/code-server/Dockerfile @@ -9,13 +9,34 @@ RUN apt-get update \ libffi-dev libssl-dev libyaml-dev zlib1g-dev \ && rm -rf /var/lib/apt/lists/* -# zsh as the container user's login shell. -RUN chsh -s /usr/bin/zsh coder +# Docker CLI with the Compose and Buildx plugins, from Docker's apt repository, +# and GitHub CLI, from GitHub's apt repository. +RUN install -m 0755 -d /etc/apt/keyrings \ + && curl -fsSL https://download.docker.com/linux/debian/gpg \ + -o /etc/apt/keyrings/docker.asc \ + && curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg \ + -o /etc/apt/keyrings/githubcli-archive-keyring.gpg \ + && chmod a+r /etc/apt/keyrings/docker.asc /etc/apt/keyrings/githubcli-archive-keyring.gpg \ + && printf '%s\n' \ + 'Types: deb' \ + 'URIs: https://download.docker.com/linux/debian' \ + "Suites: $(. /etc/os-release && echo "$VERSION_CODENAME")" \ + 'Components: stable' \ + "Architectures: $(dpkg --print-architecture)" \ + 'Signed-By: /etc/apt/keyrings/docker.asc' \ + > /etc/apt/sources.list.d/docker.sources \ + && echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" \ + > /etc/apt/sources.list.d/github-cli.list \ + && apt-get update \ + && apt-get install -y --no-install-recommends \ + docker-ce-cli docker-buildx-plugin docker-compose-plugin gh \ + && rm -rf /var/lib/apt/lists/* # Workspace directory, owned by the container user. RUN mkdir -p /workspace && chown 1000:1000 /workspace -# Startup wrapper: joins the Docker socket's group, then runs the image's entrypoint. +# Startup wrapper: enters DEFAULT_WORKSPACE, joins the Docker socket's group, +# then runs the image's entrypoint. COPY entrypoint.sh /usr/local/bin/entrypoint.sh USER 1000 diff --git a/code-server/README.md b/code-server/README.md index 37e6435..c69811a 100644 --- a/code-server/README.md +++ b/code-server/README.md @@ -5,9 +5,10 @@ official `codercom/code-server` image. The image ships code-server on Debian with `git`, `zsh`, `curl`, `sudo` and a few editors. The `Dockerfile` adds `build-essential`, `bubblewrap`, `zip`, -`unzip` and the headers Ruby builds against, makes zsh the login shell, and -wraps the entrypoint so `coder` can use the Docker socket. Language toolchains -and CLIs are installed into home, below. +`unzip`, the headers Ruby builds against, the Docker CLI with its Compose and +Buildx plugins, and the GitHub CLI, and wraps the entrypoint so it starts in +`/workspace` and `coder` can use the Docker socket. Language toolchains and +other CLIs are installed into home, below. ## Toolchains @@ -60,29 +61,20 @@ rbenv install "$V" && rbenv global "$V" To get newer Ruby versions listed, `git -C "$(rbenv root)"/plugins/ruby-build pull`. -Docker Compose and Buildx, as CLI plugins in `~/.docker/cli-plugins`, the -manual install from the -[Compose docs](https://docs.docker.com/compose/install/linux/#install-the-plugin-manually) -and the [Buildx README](https://github.com/docker/buildx#manual-download). -Nothing installs them together with the client: Docker's static archive holds -only the client and daemon, and the packages that bundle all three are apt -packages, which land outside home. The client itself is below: +Java, with SDKMAN, from its [install guide](https://sdkman.io/install/). +SDKMAN and every JDK it installs live in `~/.sdkman`; the installer adds itself +to `~/.bashrc` and `~/.zshrc`, and needs the `zip` and `unzip` the `Dockerfile` +installs. `sdk install java` with no version takes SDKMAN's default, the +current Temurin LTS: ```sh -DOCKER_CONFIG=${DOCKER_CONFIG:-$HOME/.docker} -mkdir -p "$DOCKER_CONFIG/cli-plugins" -ARCH=$(dpkg --print-architecture) - -V=$(curl -fsSLI -o /dev/null -w '%{url_effective}' https://github.com/docker/compose/releases/latest | sed 's|.*/||') -curl -fsSL "https://github.com/docker/compose/releases/download/$V/docker-compose-linux-$(uname -m)" -o "$DOCKER_CONFIG/cli-plugins/docker-compose" - -V=$(curl -fsSLI -o /dev/null -w '%{url_effective}' https://github.com/docker/buildx/releases/latest | sed 's|.*/||') -curl -fsSL "https://github.com/docker/buildx/releases/download/$V/buildx-$V.linux-$ARCH" -o "$DOCKER_CONFIG/cli-plugins/docker-buildx" - -chmod +x "$DOCKER_CONFIG/cli-plugins/docker-compose" "$DOCKER_CONFIG/cli-plugins/docker-buildx" +curl -s "https://get.sdkman.io" | bash +. "$HOME/.sdkman/bin/sdkman-init.sh" +sdk install java ``` -To upgrade them, run the same commands again. +`sdk list java` shows other vendors and versions, and `sdk install gradle` or +`sdk install maven` adds a build tool the same way. ### Suggested by AI, may not be the optimal way @@ -106,41 +98,21 @@ echo 'export PATH="$HOME/.local/go/bin:$HOME/go/bin:$PATH"' >> ~/.bashrc To upgrade Go, `rm -rf ~/.local/go` and run the same commands again, without the `echo` line. -Docker CLI, GitHub CLI, GitLab CLI and jq, as the release binaries each -project publishes, into `~/.local/bin`: - -- Docker: [static binaries](https://docs.docker.com/engine/install/binaries/), - documented for `/usr/bin`. Only the client is taken; the daemon is the - host's, through the socket. -- GitHub CLI: the `.tar.gz` on [cli.github.com](https://cli.github.com/), with - no documented location. -- GitLab CLI: the binary from the - [releases page](https://gitlab.com/gitlab-org/cli/-/releases), with no - documented location. -- jq: the binary from the - [releases page](https://github.com/jqlang/jq/releases), with no documented - location. +jq, the binary from its +[releases page](https://github.com/jqlang/jq/releases), into `~/.local/bin`, +with no documented location: ```sh mkdir -p ~/.local/bin echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc ARCH=$(dpkg --print-architecture) -V=$(curl -fsSL https://download.docker.com/linux/static/stable/$(uname -m)/ | grep -o 'docker-[0-9.]*\.tgz' | sort -V | tail -1) -curl -fsSL "https://download.docker.com/linux/static/stable/$(uname -m)/$V" | tar -C ~/.local/bin -xzf - --strip-components=1 docker/docker - -V=$(curl -fsSLI -o /dev/null -w '%{url_effective}' https://github.com/cli/cli/releases/latest | sed 's|.*/v||') -curl -fsSL "https://github.com/cli/cli/releases/download/v$V/gh_${V}_linux_$ARCH.tar.gz" | tar -C ~/.local/bin -xzf - --strip-components=2 "gh_${V}_linux_$ARCH/bin/gh" - -V=$(curl -fsSLI -o /dev/null -w '%{url_effective}' https://gitlab.com/gitlab-org/cli/-/releases/permalink/latest | sed 's|.*/v||') -curl -fsSL "https://gitlab.com/gitlab-org/cli/-/releases/v$V/downloads/glab_${V}_linux_$ARCH.tar.gz" | tar -C ~/.local/bin -xzf - --strip-components=1 bin/glab - V=$(curl -fsSLI -o /dev/null -w '%{url_effective}' https://github.com/jqlang/jq/releases/latest | sed 's|.*/||') curl -fsSL "https://github.com/jqlang/jq/releases/download/$V/jq-linux-$ARCH" -o ~/.local/bin/jq && chmod +x ~/.local/bin/jq ``` -To upgrade one, run the `ARCH=` line and that tool's two lines again; the -new binary overwrites the old one. +To upgrade it, run the same commands again, without the `echo` line; the new +binary overwrites the old one. ## Environment @@ -149,6 +121,7 @@ new binary overwrites the old one. | `PASSWORD` | Web UI login. Required. | | `GIT_NAME` / `GIT_EMAIL` | Git author and committer identity | | `SERVICE_HOSTNAME` | Container hostname, and the name the shell prompt shows (also passed as `HOST`) | +| `CODE_SERVER_APP_NAME` | Optional. Name in the title bar and welcome page; defaults to `code-server`. | Generate a password with `openssl rand -base64 24`. @@ -168,8 +141,8 @@ its `\h` uses the real hostname. ## Docker access -The host's Docker socket is bind-mounted at `/var/run/docker.sock`. The image -ships no Docker CLI; install the client into home as above. Containers +The host's Docker socket is bind-mounted at `/var/run/docker.sock`. The +`Dockerfile` installs the client only; the daemon is the host's. Containers started through it are siblings on the host, not children, so bind mounts in them resolve against host paths. @@ -207,8 +180,12 @@ Listens on `8080`; point the domain at it. | `code-server-home` | `/home/coder` | Home directory: settings, extensions, shell history, CLI logins | | `code-server-workspace` | `/workspace` | Code you work on | -The image's entrypoint opens `.`, its working directory. `working_dir: -/workspace` makes that the folder code-server opens. +The image's entrypoint opens `.`, its working directory. `entrypoint.sh` +changes into `DEFAULT_WORKSPACE`, set to `/workspace` in `compose.yml`, before +starting it, so that is the folder code-server opens and where new terminals +start. Changing the variable moves both without editing the compose file's +structure. `docker exec` shells are not affected and start in the image's +`/home/coder`. The `Dockerfile` also makes `/workspace` writable. The image does not ship that directory, so a named volume mounted there comes up `root:root`, @@ -227,9 +204,27 @@ rbenv. `libffi-dev`, `libssl-dev`, `libyaml-dev` and `zlib1g-dev` are the headers a rbenv-built Ruby needs for its `fiddle`, `openssl`, `psych` and `zlib` extensions; without them `rbenv install` fails or leaves those out. -The image leaves `coder` with `/bin/bash` as its login shell. The editor's -terminal opens zsh regardless, but tools that read the login shell from -`/etc/passwd` or `$SHELL` get bash, so the `Dockerfile` sets it to zsh. +The Docker CLI, its Compose and Buildx plugins, and the GitHub CLI come from +Docker's and GitHub's signed apt repositories, which is each vendor's documented +install for Debian. Neither documents an install into home, and Coolify builds +with `--pull`, so they update with each rebuild rather than by hand. The GitLab +CLI is not in the image: GitLab publishes no apt repository, only Homebrew and +a community one. + +Copies of `docker`, `gh` or the plugins left in `~/.local/bin` or +`~/.docker/cli-plugins` from an older setup take precedence over the image's +and should be deleted. + +## Shell + +`SHELL=/bin/zsh` in `compose.yml` picks the shell. The editor's terminal takes +its default from `$SHELL` first and only falls back to the login shell in +`/etc/passwd`, so the variable is enough, and switching to another shell the +image ships means changing one line rather than rebuilding. `sudo -E` in +`entrypoint.sh` keeps the variable; without it sudo would replace it with +root's `/bin/bash`. The value is written literally, not read from `.env`, +because every deploying shell exports its own `SHELL`, which interpolation +would pick up first. ## Image diff --git a/code-server/compose.yml b/code-server/compose.yml index cc44bf3..83ee647 100644 --- a/code-server/compose.yml +++ b/code-server/compose.yml @@ -3,15 +3,17 @@ services: build: . restart: unless-stopped hostname: ${SERVICE_HOSTNAME} - working_dir: /workspace environment: - PASSWORD=${PASSWORD:?required} - TZ=Asia/Ho_Chi_Minh + - DEFAULT_WORKSPACE=/workspace + - SHELL=/bin/zsh - GIT_AUTHOR_NAME=${GIT_NAME} - GIT_AUTHOR_EMAIL=${GIT_EMAIL} - GIT_COMMITTER_NAME=${GIT_NAME} - GIT_COMMITTER_EMAIL=${GIT_EMAIL} - HOST=${SERVICE_HOSTNAME} + # - CODE_SERVER_APP_NAME=${CODE_SERVER_APP_NAME:-code-server} volumes: - 'code-server-home:/home/coder' - 'code-server-workspace:/workspace' diff --git a/code-server/entrypoint.sh b/code-server/entrypoint.sh index 8cf0565..877a30d 100755 --- a/code-server/entrypoint.sh +++ b/code-server/entrypoint.sh @@ -1,6 +1,11 @@ #!/bin/sh set -eu +# Start in DEFAULT_WORKSPACE, the folder code-server opens. +if [ -n "${DEFAULT_WORKSPACE:-}" ]; then + cd "$DEFAULT_WORKSPACE" +fi + # Add coder to the group that owns the mounted Docker socket, then restart # under that group. if [ -S /var/run/docker.sock ]; then