Skip to content

Introduce devcontainer service container based development environment (#705)#706

Open
bedrich-schindler wants to merge 9 commits intomasterfrom
task/705
Open

Introduce devcontainer service container based development environment (#705)#706
bedrich-schindler wants to merge 9 commits intomasterfrom
task/705

Conversation

@bedrich-schindler
Copy link
Copy Markdown
Contributor

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.


Closes #705

…ent (#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.
@@ -0,0 +1,3 @@
#!/bin/sh

sudo docker compose -p "$COMPOSE_PROJECT_NAME" exec docs mkdocs "$@"
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just for info. sudo must be run as Docker socket is owned by root and ownership cannot be changed as it would also change ownership on Linux host machine. But we discussed it with @mbohal and this approach is OK. What more, AI from devcontainer cannot connect to other containers on host machine without using sudo, so it should prevent AI from attacking your other containers running on your host machine.

@@ -0,0 +1,23 @@
{
"name": "React UI (${localWorkspaceFolderBasename})",
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just for info: This was requested by @mbohal who usually have multiple instances of a repository on his machine. This allow to differ between multiple versions by directory name.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR migrates the repo’s development workflow to a single devcontainer-centric environment that orchestrates supporting service containers via Docker-from-Docker, updating scripts, Compose files, and contributor docs accordingly.

Changes:

  • Add a devcontainer image (with wrapper CLIs) and new Compose layout (docker-compose.base.yml + generated docker-compose.yml).
  • Introduce setup.sh + docker/build-docker-images.sh to generate config files and build images.
  • Consolidate Playwright configuration into .env and update contribution/testing documentation.

Reviewed changes

Copilot reviewed 16 out of 25 changed files in this pull request and generated 12 comments.

Show a summary per file
File Description
tests/playwright/env/parseDotEnvFile.ts Switch Playwright env parsing from .env.playwright to .env.
src/docs/contribute/testing-guidelines.md Update test-running guidance for the new container-agnostic workflow.
src/docs/contribute/general-guidelines.md Rewrite development environment docs around the devcontainer model.
setup.sh New host-side setup script to generate .env and docker-compose.yml and build images.
package.json Remove postinstall env-file bootstrap.
docker/react_ui_devcontainer_local/Dockerfile.dist Add template for locally extending the devcontainer image.
docker/react_ui_devcontainer/files/usr/local/bin/* Add wrapper scripts (npm/node/npx/mkdocs + AI tool shims) to route commands into service containers.
docker/react_ui_devcontainer/files/usr/local/bin/docker-entrypoint Add entrypoint that runs fixuid and home ownership setup.
docker/react_ui_devcontainer/files/home/developer/shell-init.sh Add SSH-agent forwarding blocking helper.
docker/react_ui_devcontainer/Dockerfile Add new devcontainer image build.
docker/build-docker-images.sh Add helper to build the devcontainer image and compose services.
docker-compose.yml.dist Add dist template for generated docker-compose.yml.
docker-compose.yml Remove old compose file (now generated).
docker-compose.base.yml Add new base compose defining devcontainer + node/playwright/docs services.
CLAUDE.md Update assistant guidance to assume devcontainer usage.
.gitignore Ignore generated compose + local devcontainer customization dir; stop ignoring .env.playwright.
.env.playwright.dist Remove dedicated Playwright env template.
.env.dist Expand .env.dist to include compose/devcontainer + Playwright settings.
.devcontainer/devcontainer.json Add Dev Container configuration to bootstrap via setup.sh.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

#!/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
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just for info. Jetbrains IDEs always asks whether you want to use SSH socket or not. VScode mounts SSH socket without asking and this allows you to get rid of SSH socket if you want to totally isolate AI running in container from your SSH ...

sudo chown -R developer:developer /home/developer

# Remap the developer user's UID/GID to match the host user
eval "$(fixuid -q)"
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just for info: This is crucial for Linux where Docker handles ownership dirrefently from MacOS. It is why UID/GUI must be set to 1000/1000 on Mac, otherwise container crashes. It is obvious and commented there, but just to let you know.

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..."
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just for info: This is needed if we want ability to allow user to specify his/her local Dockerfile (Dockerfile cannot extend from other Dockerfile, just from Docker image). This can look like a non sense, but this is the best I want able to come up with.

# 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
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is magic propagated to other scripts allowing you to run multiple instances of same repository without conflicting. $COMPOSE_PROJECT_NAME is used where needed. Check setup.sh that simplified it.

bedrich-schindler and others added 2 commits March 27, 2026 16:04
…nvironment (#705)

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 25 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

condition: service_started
docs:
condition: service_started
user: ${COMPOSE_UID}:${COMPOSE_GID}
Copy link

Copilot AI Mar 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

devcontainer is forced to run as ${COMPOSE_UID}:${COMPOSE_GID}. This overrides the image’s USER developer and can break the devcontainer entrypoint/wrappers that rely on the developer account having passwordless sudo (e.g. sudo chown ..., sudo docker compose ...). Consider removing user: for the devcontainer service (let the image user be used) and rely on fixuid to remap the developer UID/GID to the host values, or explicitly set user: developer here.

Suggested change
user: ${COMPOSE_UID}:${COMPOSE_GID}
user: developer

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have already tested it in private project and it works on Linux and MacOS as well correctly. So I am not sure if copilot note is right or not. @mbohal?

…rvice container based development environment (#705)
…ner` service container based development environment (#705)
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 27, 2026

PR Preview Action v1.8.1

QR code for preview link

🚀 View preview at
https://react-ui-org.github.io/react-ui/pr-preview/pr-706/

Built to branch gh-pages at 2026-03-27 19:36 UTC.
Preview will be ready when the GitHub Pages deployment is complete.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 25 changed files in this pull request and generated 6 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +83 to +92
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:
# * <https://github.com/github/copilot-cli/issues/1347>
# * <https://github.com/github/copilot-cli/issues/1750>
RUN curl -fsSL https://gh.io/copilot-install | PREFIX="$HOME/.local" bash
Copy link

Copilot AI Mar 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The devcontainer image installs several tools by piping remote scripts directly into sh/bash (Oh My Zsh, Claude, Open Code, Copilot). This is a supply-chain risk and also reduces build reproducibility (changes upstream can break builds). Consider pinning to specific versions/commits and/or verifying downloads with checksums/signatures, or vendor/install via package managers where possible.

Suggested change
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:
# * <https://github.com/github/copilot-cli/issues/1347>
# * <https://github.com/github/copilot-cli/issues/1750>
RUN curl -fsSL https://gh.io/copilot-install | PREFIX="$HOME/.local" bash
curl -fsSLo /tmp/claude-install.sh https://claude.ai/install.sh && \
chmod +x /tmp/claude-install.sh && \
/tmp/claude-install.sh && \
rm /tmp/claude-install.sh
# Install Open Code CLI
RUN curl -fsSLo /tmp/opencode-install.sh https://opencode.ai/install && \
chmod +x /tmp/opencode-install.sh && \
/tmp/opencode-install.sh --no-modify-path && \
rm /tmp/opencode-install.sh
# Install GitHub Copilot CLI
# Note: Copilot CLI does not support XDG configuration, see:
# * <https://github.com/github/copilot-cli/issues/1347>
# * <https://github.com/github/copilot-cli/issues/1750>
RUN curl -fsSLo /tmp/copilot-install.sh https://gh.io/copilot-install && \
chmod +x /tmp/copilot-install.sh && \
PREFIX="$HOME/.local" /tmp/copilot-install.sh && \
rm /tmp/copilot-install.sh

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Those commands are from official docs of each AI tool. It is developing so quickly that in this case we will need to update it almost every time we run the project. As it is isolated environment, I would stick with it. @mbohal had no comment on this in internal project.

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'
Copy link

Copilot AI Mar 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The node service runs npm ci on every container start when COMPOSE_START_JS_FILES_WATCHER_AT_START=true (default in .env.dist). This makes docker compose up / Dev Container startup significantly slower and repeatedly wipes/reinstalls node_modules. Consider running npm ci only when needed (e.g., when node_modules is missing or package-lock.json changed), or splitting dependency install into an explicit setup step and having the startup command just run npm start.

Suggested change
command: sh -c 'if [ "$$COMPOSE_START_JS_FILES_WATCHER_AT_START" = "true" ]; then npm ci && npm start; else sleep infinity; fi'
command: sh -c 'if [ "$$COMPOSE_START_JS_FILES_WATCHER_AT_START" = "true" ]; then if [ ! -d node_modules ] || [ package-lock.json -nt node_modules ]; then npm ci; fi; npm start; else sleep infinity; fi'

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did it this way so user always have up-to-date environment. It is same as with rebuilding images, in first phase, I did it this way. But if you want @adamkudrna, I can add check that it is executed only if node_modules does not exist (I need to make sure deps are installed prior to npm start)

Comment on lines +101 to +102
# Build Docker images
sh ./docker/build-docker-images.sh
Copy link

Copilot AI Mar 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

setup.sh always rebuilds Docker images (docker/build-docker-images.sh) even when .env and docker-compose.yml already exist. In Dev Container workflows, initializeCommand can run frequently, so this can add significant repeated startup time. Consider making image builds conditional (e.g., only on first setup, or behind a flag like --build, or when relevant inputs change).

Suggested change
# Build Docker images
sh ./docker/build-docker-images.sh
# Build Docker images (can be skipped by setting SKIP_DOCKER_BUILD=1)
if [ "${SKIP_DOCKER_BUILD:-0}" != "1" ]; then
echo "Building Docker images..."
sh ./docker/build-docker-images.sh
else
echo "Skipping Docker image build because SKIP_DOCKER_BUILD=1"
fi

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@adamkudrna, speaking of this. We can have a check as in WECL (internal project) that checks if image exists or not and skip it if yes. But in case the image changes, user may end up with out-of-date image. So for first phase of dev containers, I've left it without condition so it is build every time. BUT - image is cached, so it is quick as it is loaded from cache.

This is because we allow users to locally extend Dockerfile. It would not be possible without built Docker image.

There are way to solve it for sure, but I spent a lot of time on devcontainers so this is where I chose compromise.

…vcontainer` service container based development environment (#705)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Introduce development containers

2 participants