From 1ef9f0b6970bba887cc110ac46abf8c586479d75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bedr=CC=8Cich=20Schindler?= Date: Fri, 27 Mar 2026 15:40:33 +0100 Subject: [PATCH 01/16] Introduce `devcontainer` service container based development environment (#705) All development is now done inside the container named `devcontainer` which contains all necessary tools and dependencies. The devcontainer orchestrates other service containers behind the scenes via Docker-from-Docker. Other containers are implementation details and should not be accessed directly. This is breaking change commit for development environment, so it is required to remove all the env files and start with fresh one. There are two supported ways to access the development environment. Recommended way is to use Development Containers with an IDE. The alternative is to use Docker Compose directly. Local development is officially no more supported. What more, documentation of contribution and CLAUDE.md was updated to match with the changes. --- .devcontainer/devcontainer.json | 23 ++ .env.dist | 51 ++++- .env.playwright.dist | 9 - .gitignore | 4 +- CLAUDE.md | 17 +- docker-compose.base.yml | 91 ++++++++ docker-compose.yml | 43 ---- docker-compose.yml.dist | 42 ++++ docker/build-docker-images.sh | 21 ++ docker/react_ui_devcontainer/Dockerfile | 128 +++++++++++ .../files/home/developer/shell-init.sh | 9 + .../files/usr/local/bin/claude | 3 + .../files/usr/local/bin/copilot | 3 + .../files/usr/local/bin/docker-entrypoint | 10 + .../files/usr/local/bin/mkdocs | 3 + .../files/usr/local/bin/node | 3 + .../files/usr/local/bin/npm | 7 + .../files/usr/local/bin/npx | 3 + .../files/usr/local/bin/opencode | 3 + .../Dockerfile.dist | 4 + package.json | 1 - setup.sh | 104 +++++++++ src/docs/contribute/general-guidelines.md | 200 ++++++++++++++---- src/docs/contribute/testing-guidelines.md | 56 +---- tests/playwright/env/parseDotEnvFile.ts | 2 +- 25 files changed, 671 insertions(+), 169 deletions(-) create mode 100644 .devcontainer/devcontainer.json delete mode 100644 .env.playwright.dist create mode 100644 docker-compose.base.yml delete mode 100644 docker-compose.yml create mode 100644 docker-compose.yml.dist create mode 100644 docker/build-docker-images.sh create mode 100644 docker/react_ui_devcontainer/Dockerfile create mode 100644 docker/react_ui_devcontainer/files/home/developer/shell-init.sh create mode 100644 docker/react_ui_devcontainer/files/usr/local/bin/claude create mode 100644 docker/react_ui_devcontainer/files/usr/local/bin/copilot create mode 100644 docker/react_ui_devcontainer/files/usr/local/bin/docker-entrypoint create mode 100644 docker/react_ui_devcontainer/files/usr/local/bin/mkdocs create mode 100644 docker/react_ui_devcontainer/files/usr/local/bin/node create mode 100644 docker/react_ui_devcontainer/files/usr/local/bin/npm create mode 100644 docker/react_ui_devcontainer/files/usr/local/bin/npx create mode 100644 docker/react_ui_devcontainer/files/usr/local/bin/opencode create mode 100644 docker/react_ui_devcontainer_local/Dockerfile.dist create mode 100755 setup.sh diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 000000000..776e3df25 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,23 @@ +{ + "name": "React UI (${localWorkspaceFolderBasename})", + "initializeCommand": "sh ./setup.sh", + "dockerComposeFile": [ + "../docker-compose.yml", + ], + "service": "devcontainer", + "shutdownAction": "stopCompose", + "workspaceFolder": "/workspace", + "forwardPorts": [ + "docs:8000" + ], + "portsAttributes": { + "docs:8000": { + "label": "Docs server", + "protocol": "http", + "onAutoForward": "openBrowser" + } + }, + "otherPortsAttributes": { + "onAutoForward": "ignore" + } +} diff --git a/.env.dist b/.env.dist index d60e62cef..f410ad558 100644 --- a/.env.dist +++ b/.env.dist @@ -1,13 +1,52 @@ -############################### -# Docker compose configuration # -############################### +################################ +# Docker Compose configuration # +################################ -# Host system port where the live documentation is to be made accessible -COMPOSE_START_PORT=8000 +# Docker compose project name must match directory name of the project to allow devcontainer to communicate with other containers +COMPOSE_PROJECT_NAME=react-ui -# Host system port where Playwright Component Testing report is to be made accessible +# Docker compose ports for Docs server instances +COMPOSE_START_DOCS_SERVER_PORT=8000 + +# Docker compose ports for Playwright Component Testing report server COMPOSE_PLAYWRIGHT_REPORT_PORT=9323 +# Flag whether to start JavaScript files watcher at container start +COMPOSE_START_JS_FILES_WATCHER_AT_START=true + +# Flag whether to start docs server at container start +COMPOSE_START_DOCS_SERVER_AT_START=true + # Ownership of the files created in the container +# ⚠️ [Linux] This needs to be set to the output of `id --user` +# ⚠️ [MacOS] This needs to be set to 1000 COMPOSE_UID=1000 +# ⚠️ [Linux] This needs to be set to the output of `id --group` +# ⚠️ [MacOS] This needs to be set to 1000 COMPOSE_GID=1000 + +############################# +# Devcontainer configuration # +############################# + +# IDEs automatically mount the host's SSH agent socket into the container +# Visual Studio Code does this by default, it can be disabled by setting the following variable to true. +# JetBrains IDEs do not mount this by default, but they can be configured to do so. +BLOCK_SSH_AUTH_SOCK=false + +# Select your preferred editor and visual (vim, nano) +EDITOR=vim +VISUAL=vim + +# Select your preferred shell (/bin/bash, /bin/fish, /bin/zsh) +SHELL=/bin/bash + +########################### +# Playwright configuration # +########################### + +# Number of workers to use to run Playwright tests +PW_WORKERS=1 + +# Port used by Playwright Component Testing to serve the test files +PW_CT_PORT=3100 diff --git a/.env.playwright.dist b/.env.playwright.dist deleted file mode 100644 index 41bd68e69..000000000 --- a/.env.playwright.dist +++ /dev/null @@ -1,9 +0,0 @@ -########################### -# Playwright configuration # -########################### - -# Number of workers to use to run Playwright tests -PW_WORKERS=1 - -# Port used by Playwright Component Testing to serve the test files -PW_CT_PORT=3100 diff --git a/.gitignore b/.gitignore index 2b0232bac..e2ab471d0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,7 @@ /coverage +/docker-compose.yml +/docker/react_ui_devcontainer_local +!/docker/react_ui_devcontainer_local/Dockerfile.dist /dist /node_modules /playwright-report/ @@ -6,6 +9,5 @@ /src/docs/_assets/generated/* /tests/playwright/.temp/ .env -.env.playwright statistics.html !.gitkeep diff --git a/CLAUDE.md b/CLAUDE.md index ebcbc7f3f..443df34ae 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,17 +4,9 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co ## Commands -All `npm` commands must be run inside Docker containers. Use `node_shell` for most tasks, `playwright` for visual tests. +All commands below are meant to be run directly inside the Docker container `devcontainer`. +If project is started using Development Containers, run it without starting the Docker. -```bash -# Enter node_shell container -docker compose run --rm node_shell - -# Enter playwright container (for visual tests) -docker compose run --rm --service-ports playwright -``` - -**Within `node_shell`:** ```bash npm run lint # All linters (ESLint + Stylelint + Markdownlint) @@ -23,11 +15,6 @@ npm run stylelint # SCSS linting npm run test:jest # All Jest unit tests npm run test:jest:ts -- # Single TypeScript test file npm run test:jest:js -- # Single JavaScript test file -``` - -**Within `playwright`:** - -```bash npm run test:playwright-ct:all # All component tests npm run test:playwright-ct:all-with-update # Update snapshots npm run test:playwright-ct:all -- -- src/components/Button # Tests for one component diff --git a/docker-compose.base.yml b/docker-compose.base.yml new file mode 100644 index 000000000..cb59f6c75 --- /dev/null +++ b/docker-compose.base.yml @@ -0,0 +1,91 @@ +services: + # This service is responsible for providing the main development environment for developers + devcontainer: + hostname: ${COMPOSE_PROJECT_NAME:-react-ui}_devcontainer + build: + context: docker/react_ui_devcontainer/ + dockerfile: Dockerfile + # Start dependent services before starting the `devcontainer` service to ensure that the necessary environments + # and tools are available when the `devcontainer` starts. + depends_on: + node: + condition: service_started + playwright: + condition: service_started + docs: + condition: service_started + user: ${COMPOSE_UID}:${COMPOSE_GID} + # Keep the container running indefinitely to allow developers to attach to it and use it as their development environment + command: sleep infinity + # Injects environment variables from the `.env` file into the `devcontainer` service, + # making them accessible within the container's environment. + env_file: + - .env + environment: + # This must be set correctly for the `devcontainer` to be able to access the host's Docker daemon, + # enabling Docker-from-Docker capabilities (e.g., running Docker commands from within the `devcontainer`). + COMPOSE_PROJECT_NAME: ${COMPOSE_PROJECT_NAME:-react-ui} + init: true + volumes: + - .:/workspace:z + # The following volume is used to allow the `devcontainer` to access the host's Docker daemon, + # enabling Docker-from-Docker capabilities (e.g., running Docker commands from within the `devcontainer`). + - /var/run/docker.sock:/var/run/docker.sock + # The following named volumes persist data (e.g. terminal history, AI tools data, etc.) across container restarts. + # Using separate named volumes (instead of a single volume with subpaths) allows Docker to automatically + # seed the volume with data from the image on first use. + - terminal-history:/home/developer/.terminal_history + - claude-config:/home/developer/.config/claude + - claude-share:/home/developer/.local/share/claude + - claude-state:/home/developer/.local/state/claude + - copilot:/home/developer/.copilot + - copilot-config:/home/developer/.config/copilot + - opencode-config:/home/developer/.config/opencode + - opencode-share:/home/developer/.local/share/opencode + - opencode-state:/home/developer/.local/state/opencode + + # This service provides Node environment and NPM + node: + build: docker/node + user: ${COMPOSE_UID}:${COMPOSE_GID} + command: sh -c 'if [ "$$COMPOSE_START_JS_FILES_WATCHER_AT_START" = "true" ]; then npm ci && npm start; else sleep infinity; fi' + env_file: + - .env + volumes: + - .:/workspace:z + + # This service provides Playwright environment and tools for browser automation and testing + playwright: + build: docker/playwright + user: ${COMPOSE_UID}:${COMPOSE_GID} + command: sleep infinity + env_file: + - .env + ports: + - ${COMPOSE_PLAYWRIGHT_REPORT_PORT}:9323 + volumes: + - .:/workspace:z + + # This provides server for documentation + docs: + build: docker/mkdocs + user: ${COMPOSE_UID}:${COMPOSE_GID} + entrypoint: sh -c 'if [ "$$COMPOSE_START_DOCS_SERVER_AT_START" = "true" ]; then mkdocs serve; else sleep infinity; fi' + env_file: + - .env + ports: + - ${COMPOSE_START_DOCS_SERVER_PORT}:8000 + volumes: + - .:/workspace:ro + +volumes: + # The following volumes are used to persist data (e.g. terminal history, AI tools data, etc.) across container restarts + terminal-history: + claude-config: + claude-share: + claude-state: + copilot: + copilot-config: + opencode-config: + opencode-share: + opencode-state: diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index 6896b094f..000000000 --- a/docker-compose.yml +++ /dev/null @@ -1,43 +0,0 @@ -services: - # Base services - do not run directly - mkdocs: - build: docker/mkdocs - user: ${COMPOSE_UID-1000}:${COMPOSE_GID-1000} - volumes: - - .:/workspace:z - node: - build: docker/node - user: ${COMPOSE_UID-1000}:${COMPOSE_GID-1000} - volumes: - - .:/workspace:z - - # Dev services - mkdocs_dev_server: - extends: mkdocs - entrypoint: mkdocs serve - ports: - - ${COMPOSE_START_PORT-8000}:8000 - node_dev_server: - extends: node - entrypoint: npm start - node_shell: - extends: node - entrypoint: bash - - # For running Playwright tests - playwright: - build: docker/playwright - entrypoint: bash - user: ${COMPOSE_UID-1000}:${COMPOSE_GID-1000} - ports: - - ${COMPOSE_PLAYWRIGHT_REPORT_PORT-9323}:9323 - volumes: - - .:/workspace:z - - # Build services - mkdocs_build_site: - extends: mkdocs - entrypoint: mkdocs build - node_build_site: - extends: node - entrypoint: npm run build diff --git a/docker-compose.yml.dist b/docker-compose.yml.dist new file mode 100644 index 000000000..3cd78e1f7 --- /dev/null +++ b/docker-compose.yml.dist @@ -0,0 +1,42 @@ +services: + # This service is responsible for providing the main development environment for developers + devcontainer: + extends: + file: docker-compose.base.yml + service: devcontainer + # Use `build` when you want to customize the devcontainer using `docker/react_ui_devcontainer_local/Dockerfile` + # build: + # context: ./docker/react_ui_devcontainer_local/ + # dockerfile: Dockerfile + # Use `image` when you want to use the default devcontainer + image: react-ui_devcontainer + + # This service provides Node environment and NPM + node: + extends: + file: docker-compose.base.yml + service: node + + # This service provides Playwright environment and tools for browser automation and testing + playwright: + extends: + file: docker-compose.base.yml + service: playwright + + # This provides server for documentation + docs: + extends: + file: docker-compose.base.yml + service: docs + +volumes: + # The following volumes are used to persist data (e.g. terminal history, AI tools data, etc.) across container restarts + terminal-history: + claude-config: + claude-share: + claude-state: + copilot: + copilot-config: + opencode-config: + opencode-share: + opencode-state: diff --git a/docker/build-docker-images.sh b/docker/build-docker-images.sh new file mode 100644 index 000000000..cb49e0d01 --- /dev/null +++ b/docker/build-docker-images.sh @@ -0,0 +1,21 @@ +#!/bin/sh + +set -e +trap 'echo "Failed to build Docker images"; exit 1' ERR + +cd "$(dirname "$0")" + +echo "Building Docker images..." + +PROJECT_NAME=$(grep -E '^COMPOSE_PROJECT_NAME=' ../.env | cut -d '=' -f 2-) +PROJECT_DEVCONTAINER_IMAGE="${PROJECT_NAME}_devcontainer" + +echo "Building Docker image $PROJECT_DEVCONTAINER_IMAGE..." +docker build -t "$PROJECT_DEVCONTAINER_IMAGE" -f ./react_ui_devcontainer/Dockerfile ./react_ui_devcontainer/ + +cd .. + +echo "Building project Docker images using docker-compose..." +docker compose build + +echo "All Docker images built successfully!" diff --git a/docker/react_ui_devcontainer/Dockerfile b/docker/react_ui_devcontainer/Dockerfile new file mode 100644 index 000000000..1e703af9b --- /dev/null +++ b/docker/react_ui_devcontainer/Dockerfile @@ -0,0 +1,128 @@ +FROM debian:bookworm AS react-ui-devcontainer + +# Build arguments for user configuration +# Those can be changed using fixuid's remapping feature when running the container +ARG USERNAME=developer +ARG USER_UID=1000 +ARG USER_GID=1000 +ARG HOME="/home/${USERNAME}" + +# Default shell and editor settings, overridable via .env +ENV EDITOR="vim" +ENV VISUAL="vim" +ENV SHELL="/bin/bash" +ENV CLAUDE_CONFIG_DIR="${HOME}/.config/claude" +ENV OPENCODE_CONFIG="${HOME}/.config/opencode/opencode.json" +ENV OPENCODE_CONFIG_DIR="${HOME}/.config/opencode" + +# Install sudo, editors (vim, nano), SSH client, Git, shells (zsh, fish) and common utilities +RUN apt-get update && \ + apt-get install -y --no-install-recommends \ + sudo \ + ca-certificates \ + curl \ + gnupg \ + vim \ + nano \ + openssh-client \ + git \ + fish \ + zsh && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* + +# Create the user +RUN addgroup --gid $USER_GID $USERNAME && \ + adduser --uid $USER_UID --ingroup $USERNAME --home $HOME -shell $SHELL --disabled-password --gecos "" $USERNAME && \ + echo "$USERNAME ALL=(ALL:ALL) NOPASSWD:ALL" > /etc/sudoers.d/$USERNAME && \ + chmod 0440 /etc/sudoers.d/$USERNAME + +# Install fixuid to allow remapping the `developer` user to the host UID/GID when running in a container +RUN USER=$USERNAME && \ + GROUP=$USERNAME && \ + ARCH=$(dpkg --print-architecture) && \ + curl -SsL https://github.com/boxboat/fixuid/releases/download/v0.6.0/fixuid-0.6.0-linux-${ARCH}.tar.gz | tar -C /usr/local/bin -xzf - && \ + chown root:root /usr/local/bin/fixuid && \ + chmod 4755 /usr/local/bin/fixuid && \ + mkdir -p /etc/fixuid && \ + printf "user: $USER\ngroup: $GROUP\npaths: [$HOME]" > /etc/fixuid/config.yml + +# Install Docker CLI and Compose plugin to enable Docker-from-Docker +RUN install -m 0755 -d /etc/apt/keyrings && \ + curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --batch --yes --dearmor -o /etc/apt/keyrings/docker.gpg && \ + chmod a+r /etc/apt/keyrings/docker.gpg && \ + echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \ + https://download.docker.com/linux/debian $(. /etc/os-release && echo "$VERSION_CODENAME") stable" \ + > /etc/apt/sources.list.d/docker.list && \ + apt-get update && \ + apt-get install -y --no-install-recommends docker-ce-cli docker-compose-plugin && \ + apt-get clean && rm -rf /var/lib/apt/lists/* + +# Switch to the `developer` user for all user-level installations +USER $USERNAME:$USERNAME + +# Install Oh My Zsh +RUN sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" + +# Persist terminal history across container restarts via a Docker volume +# mounted at $HOME/.terminal_history in docker-compose.base.yml +RUN mkdir -p "$HOME/.terminal_history" && \ + touch "$HOME/.terminal_history/bash_history" && \ + touch "$HOME/.terminal_history/zsh_history" && \ + touch "$HOME/.terminal_history/fish_history" && \ + SNIPPET_BASH="export PROMPT_COMMAND='history -a' && export HISTFILE=$HOME/.terminal_history/bash_history" && \ + echo "$SNIPPET_BASH" >> "$HOME/.bashrc" && \ + SNIPPET_ZSH="export PROMPT_COMMAND='history -a' && export HISTFILE=$HOME/.terminal_history/zsh_history" && \ + echo "$SNIPPET_ZSH" >> "$HOME/.zshrc" && \ + mkdir -p "$HOME/.local/share/fish" && \ + touch "$HOME/.terminal_history/fish_history" && \ + ln -sf "$HOME/.terminal_history/fish_history" "$HOME/.local/share/fish/fish_history" + +# Install Claude Code CLI +RUN mkdir -p "$HOME/.config/claude" && \ + curl -fsSL https://claude.ai/install.sh | bash + +# Install Open Code CLI +RUN curl -fsSL https://opencode.ai/install | bash -s -- --no-modify-path + +# Install Github Copilot CLI +# Note: Copilot CLI does not support XDG configuration, see: +# * +# * +RUN curl -fsSL https://gh.io/copilot-install | PREFIX="$HOME/.local" bash + +# Wrapper scripts for AI coding assistants ensure they are available in PATH +# and handle necessary environment variable setup +COPY --chmod=+x files/usr/local/bin/copilot /usr/local/bin/copilot +COPY --chmod=+x files/usr/local/bin/claude /usr/local/bin/claude +COPY --chmod=+x files/usr/local/bin/opencode /usr/local/bin/opencode +COPY --chmod=+x files/usr/local/bin/docker-entrypoint /usr/local/bin/docker-entrypoint +COPY --chmod=+x files/home/$USERNAME/shell-init.sh ${HOME}/shell-init.sh + +# Wrapper scripts that execute commands inside the appropriate Docker containers. +# These scripts use Docker-from-Docker to run the commands in the context of the +# respective Docker containers, allowing container agnostic development workflows. +COPY --chmod=+x files/usr/local/bin/node /usr/local/bin/node +COPY --chmod=+x files/usr/local/bin/npm /usr/local/bin/npm +COPY --chmod=+x files/usr/local/bin/npx /usr/local/bin/npx +COPY --chmod=+x files/usr/local/bin/mkdocs /usr/local/bin/mkdocs + +# Ensure /usr/local/bin takes precedence over VS Code injected paths +# and run profile script on every shell startup to apply .env configuration +RUN mkdir -p $HOME/.config/fish && \ + echo 'export PATH="/usr/local/bin:$PATH"' >> $HOME/.bashrc && \ + echo 'export PATH="/usr/local/bin:$PATH"' >> $HOME/.zshrc && \ + echo 'set -gx PATH /usr/local/bin $PATH' >> $HOME/.config/fish/config.fish && \ + echo '$HOME/shell-init.sh' >> $HOME/.bashrc && \ + echo '$HOME/shell-init.sh' >> $HOME/.zshrc && \ + echo '$HOME/shell-init.sh' >> $HOME/.config/fish/config.fish + +# Set the default working directory when starting a container from this image +WORKDIR /workspace + +# Set the default command to run when starting a container from this image +# The fixuid command will adjust the `developer` user's UID and GID to match +# the host user's UID and GID when running in a container, ensuring that +# any files created by the `developer` user inside the container have +# the correct ownership on the host system. +ENTRYPOINT ["docker-entrypoint"] diff --git a/docker/react_ui_devcontainer/files/home/developer/shell-init.sh b/docker/react_ui_devcontainer/files/home/developer/shell-init.sh new file mode 100644 index 000000000..3e704e44a --- /dev/null +++ b/docker/react_ui_devcontainer/files/home/developer/shell-init.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +# Block SSH agent forwarding if the environment variable `BLOCK_SSH_AUTH_SOCK` is set to "true" +if [ "$BLOCK_SSH_AUTH_SOCK" = "true" ]; then + # Disable SSH agent forwarding for the current shell session + export SSH_AUTH_SOCK=/dev/null + # Remove any existing SSH agent socket files created by VS Code + find /tmp -maxdepth 1 -name 'vscode-ssh-auth-*.sock' -delete 2>/dev/null +fi diff --git a/docker/react_ui_devcontainer/files/usr/local/bin/claude b/docker/react_ui_devcontainer/files/usr/local/bin/claude new file mode 100644 index 000000000..de603a311 --- /dev/null +++ b/docker/react_ui_devcontainer/files/usr/local/bin/claude @@ -0,0 +1,3 @@ +#!/bin/sh + +/home/developer/.local/bin/claude "$@" diff --git a/docker/react_ui_devcontainer/files/usr/local/bin/copilot b/docker/react_ui_devcontainer/files/usr/local/bin/copilot new file mode 100644 index 000000000..4df0dff87 --- /dev/null +++ b/docker/react_ui_devcontainer/files/usr/local/bin/copilot @@ -0,0 +1,3 @@ +#!/bin/sh + +/home/developer/.local/bin/copilot --config-dir "/home/developer/.config/copilot" "$@" diff --git a/docker/react_ui_devcontainer/files/usr/local/bin/docker-entrypoint b/docker/react_ui_devcontainer/files/usr/local/bin/docker-entrypoint new file mode 100644 index 000000000..661ebd04f --- /dev/null +++ b/docker/react_ui_devcontainer/files/usr/local/bin/docker-entrypoint @@ -0,0 +1,10 @@ +#!/bin/sh + +# Ensure the developer user owns their home directory and all files within it +sudo chown -R developer:developer /home/developer + +# Remap the developer user's UID/GID to match the host user +eval "$(fixuid -q)" + +# Execute the command passed to the container (e.g. via `command` in docker-compose) +exec "$@" diff --git a/docker/react_ui_devcontainer/files/usr/local/bin/mkdocs b/docker/react_ui_devcontainer/files/usr/local/bin/mkdocs new file mode 100644 index 000000000..20789ea83 --- /dev/null +++ b/docker/react_ui_devcontainer/files/usr/local/bin/mkdocs @@ -0,0 +1,3 @@ +#!/bin/sh + +sudo docker compose -p "$COMPOSE_PROJECT_NAME" exec docs mkdocs "$@" diff --git a/docker/react_ui_devcontainer/files/usr/local/bin/node b/docker/react_ui_devcontainer/files/usr/local/bin/node new file mode 100644 index 000000000..60e2db562 --- /dev/null +++ b/docker/react_ui_devcontainer/files/usr/local/bin/node @@ -0,0 +1,3 @@ +#!/bin/sh + +sudo docker compose -p "$COMPOSE_PROJECT_NAME" exec node node "$@" diff --git a/docker/react_ui_devcontainer/files/usr/local/bin/npm b/docker/react_ui_devcontainer/files/usr/local/bin/npm new file mode 100644 index 000000000..931941c1d --- /dev/null +++ b/docker/react_ui_devcontainer/files/usr/local/bin/npm @@ -0,0 +1,7 @@ +#!/bin/sh + +if [ "$1" = "run" ] && echo "$2" | grep -q '^test:playwright'; then + sudo docker compose -p "$COMPOSE_PROJECT_NAME" exec playwright npm "$@" +else + sudo docker compose -p "$COMPOSE_PROJECT_NAME" exec node npm "$@" +fi diff --git a/docker/react_ui_devcontainer/files/usr/local/bin/npx b/docker/react_ui_devcontainer/files/usr/local/bin/npx new file mode 100644 index 000000000..b4c97acc5 --- /dev/null +++ b/docker/react_ui_devcontainer/files/usr/local/bin/npx @@ -0,0 +1,3 @@ +#!/bin/sh + +sudo docker compose -p "$COMPOSE_PROJECT_NAME" exec node npx "$@" diff --git a/docker/react_ui_devcontainer/files/usr/local/bin/opencode b/docker/react_ui_devcontainer/files/usr/local/bin/opencode new file mode 100644 index 000000000..17c2198ee --- /dev/null +++ b/docker/react_ui_devcontainer/files/usr/local/bin/opencode @@ -0,0 +1,3 @@ +#!/bin/sh + +/home/developer/.opencode/bin/opencode "$@" diff --git a/docker/react_ui_devcontainer_local/Dockerfile.dist b/docker/react_ui_devcontainer_local/Dockerfile.dist new file mode 100644 index 000000000..cc6c1d498 --- /dev/null +++ b/docker/react_ui_devcontainer_local/Dockerfile.dist @@ -0,0 +1,4 @@ +# react-ui_devcontainer might differ based on Docker compose project name +FROM react-ui_devcontainer as react-ui_devcontainer_local + +# Your additional setup for the local development can go here diff --git a/package.json b/package.json index 4a3cb3758..781a0d165 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,6 @@ "lint": "npm run eslint && npm run markdownlint && npm run stylelint", "markdownlint": "markdownlint-cli2 \"README.md\" \"src/**/*.md\"", "postbuild": "npm run copy", - "postinstall": "cp -n .env.dist .env && cp -n .env.playwright.dist .env.playwright || true", "precopy": "rm -rf dist && mkdir dist", "prepublishOnly": "npm run build", "start": "webpack --watch --mode=development", diff --git a/setup.sh b/setup.sh new file mode 100755 index 000000000..a8acc4451 --- /dev/null +++ b/setup.sh @@ -0,0 +1,104 @@ +#!/bin/sh + +DEFAULT_PROJECT_NAME="react-ui" +DEFAULT_USER_ID=1000 +DEFAULT_GROUP_ID=1000 + +set -e +trap 'echo "Failed to setup project"; exit 1' ERR + +# Function to handle sed command with cross-platform compatibility +sed_cmd() { + if [[ "$OSTYPE" == "darwin"* ]]; then + sed -i '' "$@" + else + sed -i "$@" + fi +} + +cd "$(dirname "$0")" + +echo "Setting up project..." + +# Create and configure .env file if it doesn't exist +if [ ! -f .env ]; then + echo "Creating .env file..." + cp .env.dist .env.temp + + echo "Configuring .env file..." + + PROJECT_PATH=$(pwd) + PROJECT_NAME=$(basename "$PROJECT_PATH") + + if [[ "$OSTYPE" == "darwin"* ]]; then + # MacOS + USER_ID=$DEFAULT_USER_ID + GROUP_ID=$DEFAULT_GROUP_ID + else + # Linux + USER_ID=$(id -u) + GROUP_ID=$(id -g) + fi + + sed_cmd "s|^COMPOSE_PROJECT_NAME=.*|COMPOSE_PROJECT_NAME=$PROJECT_NAME|" .env.temp + sed_cmd "s|^COMPOSE_UID=.*|COMPOSE_UID=$USER_ID|" .env.temp + sed_cmd "s|^COMPOSE_GID=.*|COMPOSE_GID=$GROUP_ID|" .env.temp + + LOCAL_SHELL= + if [ -n "$SHELL" ]; then + sed_cmd "s|^SHELL=.*|SHELL=$SHELL|" .env.temp + LOCAL_SHELL=$SHELL + fi + + LOCAL_EDITOR= + if [ -n "$EDITOR" ]; then + sed_cmd "s|^EDITOR=.*|EDITOR=$EDITOR|" .env.temp + LOCAL_EDITOR=$EDITOR + fi + + LOCAL_VISUAL= + if [ -n "$VISUAL" ]; then + sed_cmd "s|^VISUAL=.*|VISUAL=$VISUAL|" .env.temp + LOCAL_VISUAL=$VISUAL + fi + + cp .env.temp .env + rm .env.temp + + echo "Configured .env file with the following values:" + echo "Project name: $PROJECT_NAME" + echo "Project path: $PROJECT_PATH" + echo "User ID: $USER_ID" + echo "Group ID: $GROUP_ID" + + if [ -n "$LOCAL_SHELL" ]; then + echo "Shell: $LOCAL_SHELL" + fi + if [ -n "$LOCAL_EDITOR" ]; then + echo "Editor: $LOCAL_EDITOR" + fi + if [ -n "$LOCAL_VISUAL" ]; then + echo "Visual: $LOCAL_VISUAL" + fi +else + echo ".env file already exists, skipping creation." +fi + +# Create docker-compose.yml if it doesn't exist +if [ ! -f docker-compose.yml ]; then + echo "Creating and configuring docker-compose.yml file..." + cp docker-compose.yml.dist docker-compose.yml + + DEFAULT_PROJECT_DEVCONTAINER_IMAGE="${DEFAULT_PROJECT_NAME}_devcontainer" + PROJECT_NAME=$(grep -E '^COMPOSE_PROJECT_NAME=' .env | cut -d '=' -f 2-) + PROJECT_DEVCONTAINER_IMAGE="${PROJECT_NAME}_devcontainer" + + sed_cmd "s|image: $DEFAULT_PROJECT_DEVCONTAINER_IMAGE|image: $PROJECT_DEVCONTAINER_IMAGE|" docker-compose.yml +else + echo "docker-compose.yml file already exists, skipping creation." +fi + +# Build Docker images +sh ./docker/build-docker-images.sh + +echo "Project setup completed successfully!" diff --git a/src/docs/contribute/general-guidelines.md b/src/docs/contribute/general-guidelines.md index 74aa840ac..726201166 100644 --- a/src/docs/contribute/general-guidelines.md +++ b/src/docs/contribute/general-guidelines.md @@ -2,99 +2,205 @@ In the first place, thank you for your interest in contributing! 🙏 -## Development +## Development Environment -Working on the site requires: +### Overview -* [Docker] -* [Docker Compose] +All development is done inside the container named `devcontainer` which +contains all necessary tools and dependencies. All commands in the documentation +are container-agnostic and are meant to be run directly inside the `devcontainer`. -This allows running the documentation site which serves as a development platform. +The `devcontainer` orchestrates other service containers behind the scenes via +Docker-from-Docker. Other containers are implementation details and should not +be accessed directly. -### Configure Docker Compose +There are two supported ways to access the development environment. Recommended +way is to use [Development Containers] with an IDE, which provides a more +seamless experience. The alternative is to use Docker Compose directly. -Review the default env variable values in the `docker-compose.yml` file. -The defaults should work for most systems, but can be changed if needed. -To change them, edit the `.env` file as needed. +### Requirements -### Use Docker Compose +* [Docker] +* [Docker Compose] +* [Development Containers] (strongly recommended) -#### Node shell +### Setup -All npm commands such as `npm ci`, `npm test`, `npm run eslint` and others you -need to run them within the `node_shell` Docker container. +#### Automatic setup -To log into the container, run: +Run the setup script to automatically create and configure all necessary files +and build Docker images: ```bash -docker compose run --rm node_shell +sh setup.sh ``` -If you want to run single command, run: +[Development Containers] run this script automatically if the project has not +been set up prior to opening it. -```bash -docker compose run --rm node_shell -c 'npm ' -``` +#### Manual setup -#### Run the Dev Server +If you prefer to set up the project manually: -1. **Within `node_shell`:** Install dependencies: +1. Create `.env` file and configure it: ```bash - npm ci + cp .env.dist .env ``` -2. **On host:** Run development server: +2. Create `docker-compose.yml` and configure it: ```bash - docker compose up node_dev_server mkdocs_dev_server + cp docker-compose.yml.dist docker-compose.yml ``` -#### Build the Project - -1. **On host:** Make sure the dev server is not running: +3. Build Docker images: ```bash - docker compose down + sh docker/build-docker-images.sh ``` -2. **Within `node_shell`:** Install dependencies: +#### Environment + +The `.env` file configures services (ports, UID/GID, source mapping), +the `devcontainer` shell, editor and SSH agent forwarding, as well as application +settings. See `.env.dist` for available options. + +### Accessing the Development Environment + +#### Using Development Containers + +Open the project in an IDE that supports [Development Containers] (e.g. +[Visual Studio Code][vscode-devcontainers], [JetBrains IDEs][jetbrains-devcontainers]). +The IDE will automatically setup the environment using the configuration in +`.devcontainer/devcontainer.json`. + +#### Using Docker Compose + +1. Start the `devcontainer` in the background: ```bash - npm ci + docker compose up -d ``` -3. **On host:** Build JS: +2. Open a shell inside the `devcontainer`: ```bash - docker compose run --rm node_build_site + docker compose exec devcontainer bash ``` -4. **On host:** Build mkDocs: +3. To stop the environment: ```bash - docker compose run --rm mkdocs_build_site + docker compose down ``` -#### Playwright +### Customization -npm commands such as `test:playwright-ct:all` and `test:playwright-ct:all-with-update` -need to be run them within the `playwright` Docker container. +To customize the `devcontainer`, create a +`docker/react_ui_devcontainer_local/Dockerfile` that extends the base image: -To log into the container, run: +```Dockerfile +FROM react-ui_devcontainer as react-ui_devcontainer_local +# Add your customizations here +``` + +Then ensure `docker-compose.yml` has the `build` directive for the `devcontainer` +service: + +```yml +devcontainer: + extends: + file: docker-compose.base.yml + service: devcontainer + build: + context: ./docker/react_ui_devcontainer_local/ + dockerfile: Dockerfile +``` + +Rebuild the images after making changes: ```bash -docker compose run --rm --service-ports playwright +sh docker/build-docker-images.sh ``` -If you want to run single command, run: +If you need to persist additional data across container restarts, see how it is +done in `docker-compose.base.yml`. You will need to add a volume mapping to the +`devcontainer` service and add a corresponding named volume definition. + +### What the `devcontainer` Contains + +The `devcontainer` is built in the following layers: + +#### Base Layer (`react-ui_devcontainer`) + +General-purpose development layer. Makes the environment container-agnostic +by wrapping commands to run in the appropriate service containers. + + +* **OS:** Debian Bookworm +* **Shells:** Bash, Zsh (with Oh My Zsh), Fish +* **Editors:** Vim, Nano +* **Tools:** Git, SSH client, Docker CLI (Docker-from-Docker) +* **AI coding assistants:** Claude Code, GitHub Copilot CLI, Open Code + +#### Local Layer (`react-ui_devcontainer_local`) + +Optional layer that allows individual developers to customize the environment. +See [Customization](#customization) for details. + +### Service Containers + +The `devcontainer` depends on the following service containers defined in +`docker-compose.base.yml`: + +| Container | Purpose | +|--------------|--------------------------------------------------| +| `node` | Runs Node.js commands (`npm`, `node`) | +| `playwright` | Runs Playwright and Lighthouse tests | +| `docs` | Serves documentation via MkDocs | + +All service containers mount the workspace at `/workspace` so that file changes +are shared. + +## Installing Dependencies + +Run it on initial setup or when dependencies have changed: ```bash -docker compose run --rm --service-ports playwright -c 'npm run test:playwright-ct:*' +npm ci ``` -Argument `--service-ports` is used to expose the ports of the container to the host -to serve the test report. +## Building + +To build the JavaScript code: + +```bash +npm run build +``` + +To build the documentation: + +```bash +mkdocs build +``` + +## Running + +> See `.env` whether both the application and documentation server are not configured +> to auto-start on container startup. If they are, you can skip the following steps. + +To start building JavaScript files in watch mode: + +```bash +npm run start +``` + +To start the documentation server: + +```bash +mkdocs serve +``` ## Testing @@ -161,7 +267,7 @@ pull request from the changelog. The best way for development of React UI is to link `react-ui` into your application with `npm link` so you can see it in action. -1. In React UI repository, run `npm link` +1. In React UI repository **on your host machine**, run `npm link` 2. In your application, run `npm link @react-ui-org/react-ui` To prevent [Invalid Hook Call Warning][react-invalid-hook] when React UI is @@ -202,8 +308,12 @@ the documentation platform. Do see their respective documentation for details. -[Docker]: https://www.docker.com +[apple-silicon-issue]: https://racom-jira.atlassian.net/browse/WECL-312 +[Development Containers]: https://containers.dev/ +[Docker]: https://docs.docker.com/get-started/ [Docker Compose]: https://docs.docker.com/compose/ +[jetbrains-devcontainers]: https://www.jetbrains.com/help/idea/start-dev-container-inside-ide.html#dev_container_context_menu +[vscode-devcontainers]: https://code.visualstudio.com/docs/devcontainers/tutorial [react-invalid-hook]: https://reactjs.org/warnings/invalid-hook-call-warning.html#duplicate-react [mkdocs-material]: https://squidfunk.github.io/mkdocs-material/ [Docoff]: https://github.com/react-ui-org/docoff diff --git a/src/docs/contribute/testing-guidelines.md b/src/docs/contribute/testing-guidelines.md index 2ff90ccbd..ab50a214e 100644 --- a/src/docs/contribute/testing-guidelines.md +++ b/src/docs/contribute/testing-guidelines.md @@ -3,9 +3,9 @@ Tools used to test the application: * **ESLint** (static code analysis of JS files) -* **Markdownlint** (static analysis of MD files) * **Stylelint** (static code analysis of CSS files) -* **Jest** (unit testing) +* **Markdownlint** (static code analysis of Markdown files) +* **Jest** (unit tests) * **Playwright** (visual and functional component testing) Generally, `npm test` and `npm run test:playwright-ct:all` should be run within @@ -13,17 +13,13 @@ their designated Docker containers before pushing changes to the repository. ## Tools -### Linters (ESlint, Markdownlint, Stylelint) - -**On host:** - -[Open][gh-gg-node-shell] `node_shell` Docker container: +Except Playwright, you can run all tests with a single command: ```bash -docker compose run --rm node_shell +npm run test ``` -**Within `node_shell`:** +### Linters (ESLint, Markdownlint, Stylelint) Run linters either all together: @@ -39,52 +35,18 @@ npm run ### Jest -**On host:** - -[Open][gh-gg-node-shell] `node_shell` Docker container: - ```bash -docker compose run --rm node_shell -``` - -**Within `node_shell`:** - -Run Jest tests: - -```bash -npm run test:jest +npm run jest ``` ### Playwright -Playwright tests must be run in a Docker container to ensure a uniform -environment. Otherwise, snapshots would differ between operating systems. - -This is the reason why you need to run Playwright tests separately -from other tools. - -#### Configuration +#### Configuration -Test parameters can be tweaked by creating and tweaking `.env.playwright` file: - -```bash -cp .env.playwright.dist .env.playwright -``` +Test parameters can be tweaked by creating and tweaking `.env` file. #### Running Tests -Playwright tests can be run using the following commands: - -**On host:** - -[Open][gh-gg-playwright] `playwright` Docker container: - -```bash -docker compose run --rm --service-ports playwright -``` - -**Within `playwright`:** - Run tests: ```bash @@ -116,6 +78,4 @@ Then open the displayed URL (typically `http://localhost:9323`) in your browser. Please note that the test report is only available if the tests were run prior to serving the report. -[gh-gg-node-shell]: ./general-guidelines.md#node-shell -[gh-gg-playwright]: ./general-guidelines.md#playwright [playwright-cli]: https://playwright.dev/docs/test-cli#reference diff --git a/tests/playwright/env/parseDotEnvFile.ts b/tests/playwright/env/parseDotEnvFile.ts index 02aa3a346..9f1ac73aa 100644 --- a/tests/playwright/env/parseDotEnvFile.ts +++ b/tests/playwright/env/parseDotEnvFile.ts @@ -1,6 +1,6 @@ import dotenv from 'dotenv'; -const PLAYWRIGHT_ENV_FILE = '.env.playwright'; +const PLAYWRIGHT_ENV_FILE = '.env'; /** * Load and parse Playwright environment file into an object. From dae197be57f212e913e8df8d0815e85e4741bd1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bed=C5=99ich=20Schindler?= Date: Fri, 27 Mar 2026 16:04:56 +0100 Subject: [PATCH 02/16] !fixup Introduce `devcontainer` service container based development environment (#705) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .devcontainer/devcontainer.json | 2 +- docker-compose.base.yml | 2 +- docker/react_ui_devcontainer/Dockerfile | 4 ++-- src/docs/contribute/testing-guidelines.md | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 776e3df25..61b61d945 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -2,7 +2,7 @@ "name": "React UI (${localWorkspaceFolderBasename})", "initializeCommand": "sh ./setup.sh", "dockerComposeFile": [ - "../docker-compose.yml", + "../docker-compose.yml" ], "service": "devcontainer", "shutdownAction": "stopCompose", diff --git a/docker-compose.base.yml b/docker-compose.base.yml index cb59f6c75..c48b9d7ba 100644 --- a/docker-compose.base.yml +++ b/docker-compose.base.yml @@ -76,7 +76,7 @@ services: ports: - ${COMPOSE_START_DOCS_SERVER_PORT}:8000 volumes: - - .:/workspace:ro + - .:/workspace:z volumes: # The following volumes are used to persist data (e.g. terminal history, AI tools data, etc.) across container restarts diff --git a/docker/react_ui_devcontainer/Dockerfile b/docker/react_ui_devcontainer/Dockerfile index 1e703af9b..068f156eb 100644 --- a/docker/react_ui_devcontainer/Dockerfile +++ b/docker/react_ui_devcontainer/Dockerfile @@ -33,7 +33,7 @@ RUN apt-get update && \ # Create the user RUN addgroup --gid $USER_GID $USERNAME && \ - adduser --uid $USER_UID --ingroup $USERNAME --home $HOME -shell $SHELL --disabled-password --gecos "" $USERNAME && \ + adduser --uid $USER_UID --ingroup $USERNAME --home $HOME --shell $SHELL --disabled-password --gecos "" $USERNAME && \ echo "$USERNAME ALL=(ALL:ALL) NOPASSWD:ALL" > /etc/sudoers.d/$USERNAME && \ chmod 0440 /etc/sudoers.d/$USERNAME @@ -85,7 +85,7 @@ RUN mkdir -p "$HOME/.config/claude" && \ # Install Open Code CLI RUN curl -fsSL https://opencode.ai/install | bash -s -- --no-modify-path -# Install Github Copilot CLI +# Install GitHub Copilot CLI # Note: Copilot CLI does not support XDG configuration, see: # * # * diff --git a/src/docs/contribute/testing-guidelines.md b/src/docs/contribute/testing-guidelines.md index ab50a214e..30aa046af 100644 --- a/src/docs/contribute/testing-guidelines.md +++ b/src/docs/contribute/testing-guidelines.md @@ -36,12 +36,12 @@ npm run ### Jest ```bash -npm run jest +npm run test:jest ``` ### Playwright -#### Configuration +#### Configuration Test parameters can be tweaked by creating and tweaking `.env` file. From cae210f63fbdb471dbf38aceaaa8c4f0d5983eee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bedr=CC=8Cich=20Schindler?= Date: Fri, 27 Mar 2026 16:09:18 +0100 Subject: [PATCH 03/16] fixup! !fixup Introduce `devcontainer` service container based development environment (#705) --- docker/build-docker-images.sh | 2 +- docker/react_ui_devcontainer_local/Dockerfile.dist | 2 +- setup.sh | 2 +- src/docs/contribute/general-guidelines.md | 1 + 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/docker/build-docker-images.sh b/docker/build-docker-images.sh index cb49e0d01..0030311d7 100644 --- a/docker/build-docker-images.sh +++ b/docker/build-docker-images.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash set -e trap 'echo "Failed to build Docker images"; exit 1' ERR diff --git a/docker/react_ui_devcontainer_local/Dockerfile.dist b/docker/react_ui_devcontainer_local/Dockerfile.dist index cc6c1d498..b61d6a879 100644 --- a/docker/react_ui_devcontainer_local/Dockerfile.dist +++ b/docker/react_ui_devcontainer_local/Dockerfile.dist @@ -1,4 +1,4 @@ -# react-ui_devcontainer might differ based on Docker compose project name +# Image name `react-ui_devcontainer` might differ based on Docker compose project name FROM react-ui_devcontainer as react-ui_devcontainer_local # Your additional setup for the local development can go here diff --git a/setup.sh b/setup.sh index a8acc4451..7678e1557 100755 --- a/setup.sh +++ b/setup.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash DEFAULT_PROJECT_NAME="react-ui" DEFAULT_USER_ID=1000 diff --git a/src/docs/contribute/general-guidelines.md b/src/docs/contribute/general-guidelines.md index 726201166..1f14b4149 100644 --- a/src/docs/contribute/general-guidelines.md +++ b/src/docs/contribute/general-guidelines.md @@ -101,6 +101,7 @@ To customize the `devcontainer`, create a `docker/react_ui_devcontainer_local/Dockerfile` that extends the base image: ```Dockerfile +# Image name `react-ui_devcontainer` might differ based on Docker compose project name FROM react-ui_devcontainer as react-ui_devcontainer_local # Add your customizations here ``` From 3195ad699056f9d713c757503ac54d86ddd4a2e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bedr=CC=8Cich=20Schindler?= Date: Fri, 27 Mar 2026 19:04:07 +0100 Subject: [PATCH 04/16] fixup! fixup! !fixup Introduce `devcontainer` service container based development environment (#705) --- .devcontainer/devcontainer.json | 2 +- src/docs/contribute/general-guidelines.md | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 61b61d945..3a1fa1795 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,6 +1,6 @@ { "name": "React UI (${localWorkspaceFolderBasename})", - "initializeCommand": "sh ./setup.sh", + "initializeCommand": "bash ./setup.sh", "dockerComposeFile": [ "../docker-compose.yml" ], diff --git a/src/docs/contribute/general-guidelines.md b/src/docs/contribute/general-guidelines.md index 1f14b4149..3a243ba88 100644 --- a/src/docs/contribute/general-guidelines.md +++ b/src/docs/contribute/general-guidelines.md @@ -32,7 +32,7 @@ Run the setup script to automatically create and configure all necessary files and build Docker images: ```bash -sh setup.sh +bash setup.sh ``` [Development Containers] run this script automatically if the project has not @@ -57,7 +57,7 @@ If you prefer to set up the project manually: 3. Build Docker images: ```bash - sh docker/build-docker-images.sh + bash docker/build-docker-images.sh ``` #### Environment @@ -122,7 +122,7 @@ devcontainer: Rebuild the images after making changes: ```bash -sh docker/build-docker-images.sh +bash docker/build-docker-images.sh ``` If you need to persist additional data across container restarts, see how it is From 92e80ca8261ecab5268318081086264715173323 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bedr=CC=8Cich=20Schindler?= Date: Fri, 27 Mar 2026 19:08:03 +0100 Subject: [PATCH 05/16] fixup! fixup! fixup! !fixup Introduce `devcontainer` service container based development environment (#705) --- src/docs/contribute/general-guidelines.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/docs/contribute/general-guidelines.md b/src/docs/contribute/general-guidelines.md index 3a243ba88..9dc76af92 100644 --- a/src/docs/contribute/general-guidelines.md +++ b/src/docs/contribute/general-guidelines.md @@ -138,7 +138,6 @@ The `devcontainer` is built in the following layers: General-purpose development layer. Makes the environment container-agnostic by wrapping commands to run in the appropriate service containers. - * **OS:** Debian Bookworm * **Shells:** Bash, Zsh (with Oh My Zsh), Fish * **Editors:** Vim, Nano @@ -309,7 +308,6 @@ the documentation platform. Do see their respective documentation for details. -[apple-silicon-issue]: https://racom-jira.atlassian.net/browse/WECL-312 [Development Containers]: https://containers.dev/ [Docker]: https://docs.docker.com/get-started/ [Docker Compose]: https://docs.docker.com/compose/ From 8cf087bd63927852c35685654ffad9cb6c9a1183 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bedr=CC=8Cich=20Schindler?= Date: Fri, 27 Mar 2026 19:24:25 +0100 Subject: [PATCH 06/16] fixup! fixup! fixup! fixup! !fixup Introduce `devcontainer` service container based development environment (#705) --- CLAUDE.md | 3 ++- src/docs/contribute/testing-guidelines.md | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 443df34ae..65b606a1c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -5,7 +5,8 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co ## Commands All commands below are meant to be run directly inside the Docker container `devcontainer`. -If project is started using Development Containers, run it without starting the Docker. +If you open the project in a Dev Container, you can run these commands without manually +starting Docker Compose on the host. ```bash diff --git a/src/docs/contribute/testing-guidelines.md b/src/docs/contribute/testing-guidelines.md index 30aa046af..33b235bb7 100644 --- a/src/docs/contribute/testing-guidelines.md +++ b/src/docs/contribute/testing-guidelines.md @@ -13,10 +13,10 @@ their designated Docker containers before pushing changes to the repository. ## Tools -Except Playwright, you can run all tests with a single command: +You can run all tests with a single command: ```bash -npm run test +npm run lint && npm test && npm run test:playwright-ct:all ``` ### Linters (ESLint, Markdownlint, Stylelint) From b0cee5fa4286a3ca1df53f8d2f7938c31a08598f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bedr=CC=8Cich=20Schindler?= Date: Fri, 27 Mar 2026 19:30:12 +0100 Subject: [PATCH 07/16] fixup! fixup! fixup! fixup! fixup! !fixup Introduce `devcontainer` service container based development environment (#705) --- docker/build-docker-images.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docker/build-docker-images.sh b/docker/build-docker-images.sh index 0030311d7..03ad3368e 100644 --- a/docker/build-docker-images.sh +++ b/docker/build-docker-images.sh @@ -7,6 +7,11 @@ cd "$(dirname "$0")" echo "Building Docker images..." +if [ ! -f ../.env ]; then + echo "Error: .env file not found in the project root" + exit 1 +fi + PROJECT_NAME=$(grep -E '^COMPOSE_PROJECT_NAME=' ../.env | cut -d '=' -f 2-) PROJECT_DEVCONTAINER_IMAGE="${PROJECT_NAME}_devcontainer" From 58cd4e51bb630d3e95523602b08b8beaa6f90587 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bedr=CC=8Cich=20Schindler?= Date: Fri, 27 Mar 2026 19:42:43 +0100 Subject: [PATCH 08/16] fixup! fixup! fixup! fixup! fixup! fixup! !fixup Introduce `devcontainer` service container based development environment (#705) --- docker/react_ui_devcontainer/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/react_ui_devcontainer/Dockerfile b/docker/react_ui_devcontainer/Dockerfile index 068f156eb..a0765218d 100644 --- a/docker/react_ui_devcontainer/Dockerfile +++ b/docker/react_ui_devcontainer/Dockerfile @@ -62,7 +62,7 @@ RUN install -m 0755 -d /etc/apt/keyrings && \ USER $USERNAME:$USERNAME # Install Oh My Zsh -RUN sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" +RUN sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" "" --unattended # Persist terminal history across container restarts via a Docker volume # mounted at $HOME/.terminal_history in docker-compose.base.yml From 1bf514f869f8bd7775f2780b8c906be7ed1f4287 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bedr=CC=8Cich=20Schindler?= Date: Fri, 27 Mar 2026 20:35:27 +0100 Subject: [PATCH 09/16] fixup! fixup! fixup! fixup! fixup! fixup! fixup! !fixup Introduce `devcontainer` service container based development environment (#705) --- setup.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.sh b/setup.sh index 7678e1557..e62a87db6 100755 --- a/setup.sh +++ b/setup.sh @@ -5,7 +5,7 @@ DEFAULT_USER_ID=1000 DEFAULT_GROUP_ID=1000 set -e -trap 'echo "Failed to setup project"; exit 1' ERR +trap 'echo "Failed to setup project"; rf -f .env.temp; exit 1' ERR # Function to handle sed command with cross-platform compatibility sed_cmd() { From c12936f56745fd61af0ab89eab5f35f362ef6065 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bedr=CC=8Cich=20Schindler?= Date: Thu, 2 Apr 2026 11:01:54 +0200 Subject: [PATCH 10/16] fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! !fixup Introduce `devcontainer` service container based development environment (#705) --- .env.dist | 3 ++- docker/react_ui_devcontainer/Dockerfile | 2 +- setup.sh | 2 +- src/docs/contribute/general-guidelines.md | 4 ++-- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.env.dist b/.env.dist index f410ad558..81d480e9a 100644 --- a/.env.dist +++ b/.env.dist @@ -2,7 +2,8 @@ # Docker Compose configuration # ################################ -# Docker compose project name must match directory name of the project to allow devcontainer to communicate with other containers +# Must match Docker Compose project name used to start the other service containers to allow devcontainer +# to communicate with them (setup.sh derives this from the directory basename by default) COMPOSE_PROJECT_NAME=react-ui # Docker compose ports for Docs server instances diff --git a/docker/react_ui_devcontainer/Dockerfile b/docker/react_ui_devcontainer/Dockerfile index a0765218d..75a087bf4 100644 --- a/docker/react_ui_devcontainer/Dockerfile +++ b/docker/react_ui_devcontainer/Dockerfile @@ -1,4 +1,4 @@ -FROM debian:bookworm AS react-ui-devcontainer +FROM debian:bookworm-slim AS react-ui-devcontainer # Build arguments for user configuration # Those can be changed using fixuid's remapping feature when running the container diff --git a/setup.sh b/setup.sh index e62a87db6..9422d7989 100755 --- a/setup.sh +++ b/setup.sh @@ -5,7 +5,7 @@ DEFAULT_USER_ID=1000 DEFAULT_GROUP_ID=1000 set -e -trap 'echo "Failed to setup project"; rf -f .env.temp; exit 1' ERR +trap 'echo "Failed to set up project"; rm -f .env.temp; exit 1' ERR # Function to handle sed command with cross-platform compatibility sed_cmd() { diff --git a/src/docs/contribute/general-guidelines.md b/src/docs/contribute/general-guidelines.md index 9dc76af92..9ede72e90 100644 --- a/src/docs/contribute/general-guidelines.md +++ b/src/docs/contribute/general-guidelines.md @@ -72,7 +72,7 @@ settings. See `.env.dist` for available options. Open the project in an IDE that supports [Development Containers] (e.g. [Visual Studio Code][vscode-devcontainers], [JetBrains IDEs][jetbrains-devcontainers]). -The IDE will automatically setup the environment using the configuration in +The IDE will automatically set up the environment using the configuration in `.devcontainer/devcontainer.json`. #### Using Docker Compose @@ -193,7 +193,7 @@ mkdocs build To start building JavaScript files in watch mode: ```bash -npm run start +npm start ``` To start the documentation server: From 6a3dbc0ef46149341446f6d432f864836baca087 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bedr=CC=8Cich=20Schindler?= Date: Thu, 2 Apr 2026 11:15:40 +0200 Subject: [PATCH 11/16] fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! !fixup Introduce `devcontainer` service container based development environment (#705) --- .../react_ui_devcontainer/files/home/developer/shell-init.sh | 4 ++++ docker/react_ui_devcontainer/files/usr/local/bin/mkdocs | 2 ++ docker/react_ui_devcontainer/files/usr/local/bin/node | 2 ++ docker/react_ui_devcontainer/files/usr/local/bin/npm | 2 ++ docker/react_ui_devcontainer/files/usr/local/bin/npx | 2 ++ 5 files changed, 12 insertions(+) diff --git a/docker/react_ui_devcontainer/files/home/developer/shell-init.sh b/docker/react_ui_devcontainer/files/home/developer/shell-init.sh index 3e704e44a..91669c995 100644 --- a/docker/react_ui_devcontainer/files/home/developer/shell-init.sh +++ b/docker/react_ui_devcontainer/files/home/developer/shell-init.sh @@ -1,6 +1,10 @@ #!/bin/sh # Block SSH agent forwarding if the environment variable `BLOCK_SSH_AUTH_SOCK` is set to "true" +# +# VS Code forwards the SSH agent socket to the dev container by default, so this allows users +# to opt out of that behavior if they want to disable SSH agent forwarding to isolate the dev container. +# JetBrains IDEs forward the SSH agent socket only if allowed by the user. if [ "$BLOCK_SSH_AUTH_SOCK" = "true" ]; then # Disable SSH agent forwarding for the current shell session export SSH_AUTH_SOCK=/dev/null diff --git a/docker/react_ui_devcontainer/files/usr/local/bin/mkdocs b/docker/react_ui_devcontainer/files/usr/local/bin/mkdocs index 20789ea83..3aa684f0c 100644 --- a/docker/react_ui_devcontainer/files/usr/local/bin/mkdocs +++ b/docker/react_ui_devcontainer/files/usr/local/bin/mkdocs @@ -1,3 +1,5 @@ #!/bin/sh +# This script is a wrapper around the `mkdocs` command that executes it within the Docker container. +# Docker Socket is owned by root, so we need to use `sudo` to access it. sudo docker compose -p "$COMPOSE_PROJECT_NAME" exec docs mkdocs "$@" diff --git a/docker/react_ui_devcontainer/files/usr/local/bin/node b/docker/react_ui_devcontainer/files/usr/local/bin/node index 60e2db562..e96ed06c1 100644 --- a/docker/react_ui_devcontainer/files/usr/local/bin/node +++ b/docker/react_ui_devcontainer/files/usr/local/bin/node @@ -1,3 +1,5 @@ #!/bin/sh +# This script is a wrapper around the `node` command that executes it within the Docker container. +# Docker Socket is owned by root, so we need to use `sudo` to access it. sudo docker compose -p "$COMPOSE_PROJECT_NAME" exec node node "$@" diff --git a/docker/react_ui_devcontainer/files/usr/local/bin/npm b/docker/react_ui_devcontainer/files/usr/local/bin/npm index 931941c1d..57479ef1e 100644 --- a/docker/react_ui_devcontainer/files/usr/local/bin/npm +++ b/docker/react_ui_devcontainer/files/usr/local/bin/npm @@ -1,5 +1,7 @@ #!/bin/sh +# This script is a wrapper around the `npm` command that executes it within the Docker container. +# Docker Socket is owned by root, so we need to use `sudo` to access it. if [ "$1" = "run" ] && echo "$2" | grep -q '^test:playwright'; then sudo docker compose -p "$COMPOSE_PROJECT_NAME" exec playwright npm "$@" else diff --git a/docker/react_ui_devcontainer/files/usr/local/bin/npx b/docker/react_ui_devcontainer/files/usr/local/bin/npx index b4c97acc5..a204fd8d4 100644 --- a/docker/react_ui_devcontainer/files/usr/local/bin/npx +++ b/docker/react_ui_devcontainer/files/usr/local/bin/npx @@ -1,3 +1,5 @@ #!/bin/sh +# This script is a wrapper around the `npx` command that executes it within the Docker container. +# Docker Socket is owned by root, so we need to use `sudo` to access it. sudo docker compose -p "$COMPOSE_PROJECT_NAME" exec node npx "$@" From 9a7e831aa4b4250fe178aef92cb05b5386af106b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bedr=CC=8Cich=20Schindler?= Date: Mon, 6 Apr 2026 21:48:31 +0200 Subject: [PATCH 12/16] fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! !fixup Introduce `devcontainer` service container based development environment (#705) --- docker-compose.base.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docker-compose.base.yml b/docker-compose.base.yml index c48b9d7ba..3e2bccb96 100644 --- a/docker-compose.base.yml +++ b/docker-compose.base.yml @@ -48,7 +48,8 @@ services: node: build: docker/node user: ${COMPOSE_UID}:${COMPOSE_GID} - command: sh -c 'if [ "$$COMPOSE_START_JS_FILES_WATCHER_AT_START" = "true" ]; then npm ci && npm start; else sleep infinity; fi' + command: sleep infinity + entrypoint: sh -c 'if [ "$$COMPOSE_START_JS_FILES_WATCHER_AT_START" = "true" ]; then npm ci && npm start; fi' env_file: - .env volumes: @@ -70,7 +71,8 @@ services: docs: build: docker/mkdocs user: ${COMPOSE_UID}:${COMPOSE_GID} - entrypoint: sh -c 'if [ "$$COMPOSE_START_DOCS_SERVER_AT_START" = "true" ]; then mkdocs serve; else sleep infinity; fi' + command: sleep infinity + entrypoint: sh -c 'if [ "$$COMPOSE_START_DOCS_SERVER_AT_START" = "true" ]; then mkdocs serve; fi' env_file: - .env ports: From cdc0490b96e28dbd09ac1eda2c079e93aea3f2dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bedr=CC=8Cich=20Schindler?= Date: Mon, 20 Apr 2026 13:28:33 +0200 Subject: [PATCH 13/16] fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! !fixup Introduce `devcontainer` service container based development environment (#705) --- .env.dist | 10 +++---- docker-compose.base.yml | 8 +++--- docker/mkdocs/Dockerfile | 4 ++- package.json | 1 + scripts/auto-start-mkdocs.sh | 5 ++++ scripts/auto-start-node.sh | 33 +++++++++++++++++++++++ scripts/write-lockfile-hash.sh | 15 +++++++++++ src/docs/contribute/general-guidelines.md | 29 +++++++++++++++++--- 8 files changed, 90 insertions(+), 15 deletions(-) create mode 100644 scripts/auto-start-mkdocs.sh create mode 100644 scripts/auto-start-node.sh create mode 100755 scripts/write-lockfile-hash.sh diff --git a/.env.dist b/.env.dist index 81d480e9a..683f6be58 100644 --- a/.env.dist +++ b/.env.dist @@ -7,16 +7,14 @@ COMPOSE_PROJECT_NAME=react-ui # Docker compose ports for Docs server instances -COMPOSE_START_DOCS_SERVER_PORT=8000 +COMPOSE_DOCS_SERVER_PORT=8000 # Docker compose ports for Playwright Component Testing report server COMPOSE_PLAYWRIGHT_REPORT_PORT=9323 -# Flag whether to start JavaScript files watcher at container start -COMPOSE_START_JS_FILES_WATCHER_AT_START=true - -# Flag whether to start docs server at container start -COMPOSE_START_DOCS_SERVER_AT_START=true +# Flag whether the `node` and `docs` service containers should automatically install dependencies, +# build, and run the application (JavaScript files watcher, docs server) when they start +COMPOSE_AUTOSTART=false # Ownership of the files created in the container # ⚠️ [Linux] This needs to be set to the output of `id --user` diff --git a/docker-compose.base.yml b/docker-compose.base.yml index 3e2bccb96..264f73098 100644 --- a/docker-compose.base.yml +++ b/docker-compose.base.yml @@ -48,8 +48,7 @@ services: node: build: docker/node user: ${COMPOSE_UID}:${COMPOSE_GID} - command: sleep infinity - entrypoint: sh -c 'if [ "$$COMPOSE_START_JS_FILES_WATCHER_AT_START" = "true" ]; then npm ci && npm start; fi' + entrypoint: sh -c 'if [ "$$COMPOSE_AUTOSTART" = "true" ]; then sh scripts/auto-start-node.sh; else sleep infinity; fi' env_file: - .env volumes: @@ -71,12 +70,11 @@ services: docs: build: docker/mkdocs user: ${COMPOSE_UID}:${COMPOSE_GID} - command: sleep infinity - entrypoint: sh -c 'if [ "$$COMPOSE_START_DOCS_SERVER_AT_START" = "true" ]; then mkdocs serve; fi' + entrypoint: sh -c 'if [ "$$COMPOSE_AUTOSTART" = "true" ]; then sh scripts/auto-start-mkdocs.sh; else sleep infinity; fi' env_file: - .env ports: - - ${COMPOSE_START_DOCS_SERVER_PORT}:8000 + - ${COMPOSE_DOCS_SERVER_PORT}:8000 volumes: - .:/workspace:z diff --git a/docker/mkdocs/Dockerfile b/docker/mkdocs/Dockerfile index 77d051bda..102a1285d 100644 --- a/docker/mkdocs/Dockerfile +++ b/docker/mkdocs/Dockerfile @@ -1,3 +1,5 @@ -FROM squidfunk/mkdocs-material:9 +# We freezed the version of mkdocs-material to prevent issue with live reload +# See +FROM squidfunk/mkdocs-material:9.6.20 RUN mkdir /workspace WORKDIR /workspace diff --git a/package.json b/package.json index 781a0d165..8ff2eb70d 100644 --- a/package.json +++ b/package.json @@ -54,6 +54,7 @@ "lint": "npm run eslint && npm run markdownlint && npm run stylelint", "markdownlint": "markdownlint-cli2 \"README.md\" \"src/**/*.md\"", "postbuild": "npm run copy", + "postinstall": "sh scripts/write-lockfile-hash.sh", "precopy": "rm -rf dist && mkdir dist", "prepublishOnly": "npm run build", "start": "webpack --watch --mode=development", diff --git a/scripts/auto-start-mkdocs.sh b/scripts/auto-start-mkdocs.sh new file mode 100644 index 000000000..680c38c84 --- /dev/null +++ b/scripts/auto-start-mkdocs.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +# Start the docs server +echo "Starting the docs server..." +mkdocs serve diff --git a/scripts/auto-start-node.sh b/scripts/auto-start-node.sh new file mode 100644 index 000000000..50a11de5d --- /dev/null +++ b/scripts/auto-start-node.sh @@ -0,0 +1,33 @@ +#!/bin/sh + +set -e + +# File to read the hash of the package-lock.json +LOCK_HASH_FILE="node_modules/.package-lock-hash" + +# Parent directory of the script +SCRIPT_DIR="$( cd "$( dirname "$0" )" && pwd )" + +# Change to the parent directory of the script +cd "$SCRIPT_DIR/.." + +# Install dependencies if node_modules is missing or out of date +CURRENT_HASH=$(sha256sum package-lock.json | awk '{print $1}') +if [ ! -d "node_modules" ]; then + echo "Installing dependencies (node_modules directory is missing)..." + npm ci +elif [ ! -f "$LOCK_HASH_FILE" ]; then + echo "Installing dependencies (lockfile of package-lock.json is missing)..." + npm ci +elif [ "$(cat "$LOCK_HASH_FILE")" != "$CURRENT_HASH" ]; then + echo "Installing dependencies (package-lock.json has changed)..." + npm ci +fi + +# Build the application (must be run prior to starting the server to ensure the latest code is used) +echo "Building the application..." +npm run build + +# Start the application +echo "Starting the application..." +npm start diff --git a/scripts/write-lockfile-hash.sh b/scripts/write-lockfile-hash.sh new file mode 100755 index 000000000..6b63f2f62 --- /dev/null +++ b/scripts/write-lockfile-hash.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +set -e + +# File to store the hash of the package-lock.json +LOCK_HASH_FILE="node_modules/.package-lock-hash" + +# Parent directory of the script +SCRIPT_DIR="$( cd "$( dirname "$0" )" && pwd )" + +# Change to the parent directory of the script +cd "$SCRIPT_DIR/.." + +# Record the hash of the lockfile we just installed against +sha256sum package-lock.json | awk '{print $1}' > "$LOCK_HASH_FILE" diff --git a/src/docs/contribute/general-guidelines.md b/src/docs/contribute/general-guidelines.md index 9ede72e90..060d1ea8e 100644 --- a/src/docs/contribute/general-guidelines.md +++ b/src/docs/contribute/general-guidelines.md @@ -163,6 +163,32 @@ The `devcontainer` depends on the following service containers defined in All service containers mount the workspace at `/workspace` so that file changes are shared. +## Automatic Service Bootstrap + +> You can skip this section if you do not want to automatically +> [install dependencies](#installing-dependencies), [build](#building), and +> [run](#running) the application, or if you are not an experienced developer. + +Setting `COMPOSE_AUTOSTART=true` in `.env` makes the `node` and `docs` +service containers automatically install dependencies, build, and run the +application when they start. The default is `false`. + +Setting `COMPOSE_AUTOSTART=true` comes with the following trade-offs: + +* **Changes to dependencies require a container restart.** The watcher owns the + service container's entrypoint, so updating dependencies (e.g. pulling a + branch that changes `package-lock.json`, or running `npm install `) + only takes effect after restarting the `node` service container. The same + applies to changes that affect the documentation server. +* **Service logs are not directly visible.** The watcher and docs server run in + their own service containers rather than in your `devcontainer` shell, so + their output is not shown alongside your regular terminal work. You have to + inspect it via `docker compose logs ` from the host. + +> If something is not working as expected, or you are not sure what is going on, +> set `COMPOSE_AUTOSTART=false`, restart the containers, and follow the +> manual steps in the sections below instead. + ## Installing Dependencies Run it on initial setup or when dependencies have changed: @@ -187,9 +213,6 @@ mkdocs build ## Running -> See `.env` whether both the application and documentation server are not configured -> to auto-start on container startup. If they are, you can skip the following steps. - To start building JavaScript files in watch mode: ```bash From 1cdc6c2d00e08d919ee3607e30c3c8920f0eaf45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bedr=CC=8Cich=20Schindler?= Date: Tue, 21 Apr 2026 08:51:10 +0200 Subject: [PATCH 14/16] fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! !fixup Introduce `devcontainer` service container based development environment (#705) --- docker/react_ui_devcontainer/Dockerfile | 7 ++++--- .../files/home/developer/shell-init.fish | 16 ++++++++++++++++ 2 files changed, 20 insertions(+), 3 deletions(-) create mode 100644 docker/react_ui_devcontainer/files/home/developer/shell-init.fish diff --git a/docker/react_ui_devcontainer/Dockerfile b/docker/react_ui_devcontainer/Dockerfile index 75a087bf4..b3f48f14b 100644 --- a/docker/react_ui_devcontainer/Dockerfile +++ b/docker/react_ui_devcontainer/Dockerfile @@ -98,6 +98,7 @@ COPY --chmod=+x files/usr/local/bin/claude /usr/local/bin/claude COPY --chmod=+x files/usr/local/bin/opencode /usr/local/bin/opencode COPY --chmod=+x files/usr/local/bin/docker-entrypoint /usr/local/bin/docker-entrypoint COPY --chmod=+x files/home/$USERNAME/shell-init.sh ${HOME}/shell-init.sh +COPY --chmod=+x files/home/$USERNAME/shell-init.fish ${HOME}/shell-init.fish # Wrapper scripts that execute commands inside the appropriate Docker containers. # These scripts use Docker-from-Docker to run the commands in the context of the @@ -113,9 +114,9 @@ RUN mkdir -p $HOME/.config/fish && \ echo 'export PATH="/usr/local/bin:$PATH"' >> $HOME/.bashrc && \ echo 'export PATH="/usr/local/bin:$PATH"' >> $HOME/.zshrc && \ echo 'set -gx PATH /usr/local/bin $PATH' >> $HOME/.config/fish/config.fish && \ - echo '$HOME/shell-init.sh' >> $HOME/.bashrc && \ - echo '$HOME/shell-init.sh' >> $HOME/.zshrc && \ - echo '$HOME/shell-init.sh' >> $HOME/.config/fish/config.fish + echo '. $HOME/shell-init.sh' >> $HOME/.bashrc && \ + echo '. $HOME/shell-init.sh' >> $HOME/.zshrc && \ + echo 'source $HOME/shell-init.fish' >> $HOME/.config/fish/config.fish # Set the default working directory when starting a container from this image WORKDIR /workspace diff --git a/docker/react_ui_devcontainer/files/home/developer/shell-init.fish b/docker/react_ui_devcontainer/files/home/developer/shell-init.fish new file mode 100644 index 000000000..e7d6a7803 --- /dev/null +++ b/docker/react_ui_devcontainer/files/home/developer/shell-init.fish @@ -0,0 +1,16 @@ +#!/usr/bin/env fish + +# Block SSH agent forwarding if the environment variable `BLOCK_SSH_AUTH_SOCK` is set to "true" +# +# VS Code forwards the SSH agent socket to the dev container by default, so this allows users +# to opt out of that behavior if they want to disable SSH agent forwarding to isolate the dev container. +# JetBrains IDEs forward the SSH agent socket only if allowed by the user. +if test "$BLOCK_SSH_AUTH_SOCK" = "true" + # Erase any existing SSH_AUTH_SOCK from all scopes (local, global, universal) + # so the subsequent `set -gx` is not shadowed by a pre-existing value. + set -e SSH_AUTH_SOCK + # Disable SSH agent forwarding for the current shell session + set -gx SSH_AUTH_SOCK /dev/null + # Remove any existing SSH agent socket files created by VS Code + find /tmp -maxdepth 1 -name 'vscode-ssh-auth-*.sock' -delete 2>/dev/null +end From c98e767d67d97e6f44edfc0d41444c9e9debd667 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bedr=CC=8Cich=20Schindler?= Date: Tue, 21 Apr 2026 14:01:48 +0200 Subject: [PATCH 15/16] fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! !fixup Introduce `devcontainer` service container based development environment (#705) --- docker-compose.base.yml | 4 ++++ src/docs/contribute/general-guidelines.md | 10 ++++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/docker-compose.base.yml b/docker-compose.base.yml index 264f73098..acc822c9e 100644 --- a/docker-compose.base.yml +++ b/docker-compose.base.yml @@ -14,6 +14,10 @@ services: condition: service_started docs: condition: service_started + # Run as host UID/GID so files created in mounted volumes are owned correctly on the host. + # The images use `fixuid` (https://github.com/boxboat/fixuid) to remap the built-in `developer` + # user to these IDs at startup — the `user:` directive is required for that remap to work. + # Applied to all services below for the same reason. user: ${COMPOSE_UID}:${COMPOSE_GID} # Keep the container running indefinitely to allow developers to attach to it and use it as their development environment command: sleep infinity diff --git a/src/docs/contribute/general-guidelines.md b/src/docs/contribute/general-guidelines.md index 060d1ea8e..a56ec7fc4 100644 --- a/src/docs/contribute/general-guidelines.md +++ b/src/docs/contribute/general-guidelines.md @@ -14,15 +14,17 @@ The `devcontainer` orchestrates other service containers behind the scenes via Docker-from-Docker. Other containers are implementation details and should not be accessed directly. -There are two supported ways to access the development environment. Recommended -way is to use [Development Containers] with an IDE, which provides a more -seamless experience. The alternative is to use Docker Compose directly. +There are two supported ways to access the development environment. The +recommended way is to use [Development Containers] with an IDE, which provides +a more seamless experience. The alternative is to use Docker Compose directly, +which is suitable for cases when IDE integration is not needed +(e.g. terminal-only workflows). ### Requirements * [Docker] * [Docker Compose] -* [Development Containers] (strongly recommended) +* [Development Containers] (Are recommended) ### Setup From e51a1dad4203fe7644bea50b2b82bbb2f2162e78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bedr=CC=8Cich=20Schindler?= Date: Tue, 21 Apr 2026 14:23:53 +0200 Subject: [PATCH 16/16] fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! !fixup Introduce `devcontainer` service container based development environment (#705) --- src/docs/contribute/general-guidelines.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/docs/contribute/general-guidelines.md b/src/docs/contribute/general-guidelines.md index a56ec7fc4..c30d615ae 100644 --- a/src/docs/contribute/general-guidelines.md +++ b/src/docs/contribute/general-guidelines.md @@ -30,6 +30,9 @@ which is suitable for cases when IDE integration is not needed #### Automatic setup +> You can skip this section when using [Development Containers]. They run this +> script automatically the first time the project is opened. + Run the setup script to automatically create and configure all necessary files and build Docker images: @@ -37,9 +40,6 @@ and build Docker images: bash setup.sh ``` -[Development Containers] run this script automatically if the project has not -been set up prior to opening it. - #### Manual setup If you prefer to set up the project manually: @@ -170,6 +170,8 @@ are shared. > You can skip this section if you do not want to automatically > [install dependencies](#installing-dependencies), [build](#building), and > [run](#running) the application, or if you are not an experienced developer. +> If you do use it, you can skip those sections as well, since the steps they +> describe are performed automatically. Setting `COMPOSE_AUTOSTART=true` in `.env` makes the `node` and `docs` service containers automatically install dependencies, build, and run the