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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions .devcontainer/gz-smoke/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"name": "gz-smoke",
"image": "althack/gz:jetty-base",
"remoteUser": "ros",
"updateRemoteUserUID": true,
"containerEnv": {
"GZ_VERSION": "jetty"
},
"runArgs": [
"--network=host",
"--ipc=host",
"--cap-add=SYS_PTRACE",
"--security-opt=seccomp:unconfined",
"--security-opt=apparmor:unconfined"
],
"features": {
"../local-features/x11": {}
},
"customizations": {
"vscode": {
"extensions": [
"ms-vscode.cpptools",
"ms-python.python",
"twxs.cmake",
"redhat.vscode-yaml"
]
}
}
}
2 changes: 2 additions & 0 deletions .devcontainer/local-features/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
*
!.gitignore
13 changes: 13 additions & 0 deletions .devcontainer/prepare-local-features.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/usr/bin/env bash

set -euo pipefail

repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
local_features_dir="${repo_root}/.devcontainer/local-features"

mkdir -p "${local_features_dir}"
rm -rf "${local_features_dir}/x11" "${local_features_dir}/wayland"
cp -R "${repo_root}/features/src/x11" "${local_features_dir}/x11"
cp -R "${repo_root}/features/src/wayland" "${local_features_dir}/wayland"

echo "Prepared local smoke-test features in ${local_features_dir}"
10 changes: 10 additions & 0 deletions .devcontainer/wayland-smoke/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"name": "wayland-smoke",
"image": "mcr.microsoft.com/devcontainers/base:ubuntu",
"features": {
"../local-features/wayland": {
"waylandDisplay": "${localEnv:WAYLAND_DISPLAY}"
}
},
"postCreateCommand": "sudo apt-get update && sudo apt-get install -y gtk-3-examples"
}
10 changes: 10 additions & 0 deletions .devcontainer/x11-smoke/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"name": "x11-smoke",
"image": "mcr.microsoft.com/devcontainers/base:ubuntu",
"features": {
"../local-features/x11": {
"display": "${localEnv:DISPLAY}"
}
},
"postCreateCommand": "sudo apt-get update && sudo apt-get install -y x11-apps"
}
6 changes: 6 additions & 0 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@ jobs:

- uses: actions/checkout@v6

- name: Install devcontainer CLI
run: npm install -g @devcontainers/cli

- name: Run real feature tests
run: bash features/test/test_all.sh

- name: Set release version
id: bump
run: |
Expand Down
5 changes: 4 additions & 1 deletion .github/workflows/test-pr.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@ jobs:
steps:
- uses: actions/checkout@v6

- name: Validate feature metadata
- name: Install devcontainer CLI
run: npm install -g @devcontainers/cli

- name: Run real feature tests
run: bash features/test/test_all.sh

gen-docs:
Expand Down
42 changes: 41 additions & 1 deletion .vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,46 @@
"Local Smoke Test GZ: Assert"
],
"problemMatcher": []
}
},
{
"label": "Prep Local Feature tests",
"type": "shell",
"command": "bash",
"args": [
"-lc",
"bash .devcontainer/prepare-local-features.sh"
],
"options": {
"cwd": "${workspaceFolder}"
},
"problemMatcher": []
},
{
"label": "x11 test xeyes",
"type": "shell",
"command": "xeyes",
"options": {
"cwd": "${workspaceFolder}"
},
"problemMatcher": []
},
{
"label": "wayland test gtk3-demo",
"type": "shell",
"command": "gtk3-demo",
"options": {
"cwd": "${workspaceFolder}"
},
"problemMatcher": []
},
{
"label": "gz test gz sim",
"type": "shell",
"command": "gz sim",
"options": {
"cwd": "${workspaceFolder}"
},
"problemMatcher": []
},
]
}
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,15 @@ Devcontainer templates repository focused on robotics and simulation.
- `features/src/x11`
- `features/src/wayland`

## Sample configs

- `.devcontainer/gz-smoke/devcontainer.json`
- `.devcontainer/x11-smoke/devcontainer.json`
- `.devcontainer/wayland-smoke/devcontainer.json`
These sample devcontainers only override the dynamic display name. The features themselves now provide fixed in-container paths and default runtime env values.
Before using them locally, run `bash .devcontainer/prepare-local-features.sh` so `devcontainer up` can resolve the unpublished features from within `.devcontainer/`.
On classic Xorg hosts where the authority cookie lives outside `XDG_RUNTIME_DIR`, add a bind mount to `/tmp/devcontainer-xauthority-host`; XWayland setups are auto-detected from the mounted runtime directory.

## Workflows

- `.github/workflows/test-pr.yaml`: smoke tests template changes, validates features, and refreshes generated docs on pull requests
Expand Down
1 change: 1 addition & 0 deletions features/src/wayland/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ Configure container environment and mounts for host Wayland display forwarding.

| Options Id | Description | Type | Default Value |
|-----|-----|-----|-----|
| waylandDisplay | Set WAYLAND_DISPLAY for Wayland clients launched from the container. | string | wayland-0 |
| softwareGL | Set LIBGL_ALWAYS_SOFTWARE for GPU compatibility. | string | 1 |


Expand Down
13 changes: 8 additions & 5 deletions features/src/wayland/devcontainer-feature.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@
"documentationURL": "https://github.com/athackst/devcontainer-templates/tree/main/features/src/wayland",
"licenseURL": "https://github.com/athackst/devcontainer-templates/blob/main/LICENSE",
"options": {
"waylandDisplay": {
"type": "string",
"default": "wayland-0",
"description": "Set WAYLAND_DISPLAY for Wayland clients launched from the container."
},
"softwareGL": {
"type": "string",
"default": "1",
Expand All @@ -17,13 +22,11 @@
}
},
"containerEnv": {
"WAYLAND_DISPLAY": "${localEnv:WAYLAND_DISPLAY}",
"XDG_RUNTIME_DIR": "${localEnv:XDG_RUNTIME_DIR}",
"PULSE_SERVER": "${localEnv:PULSE_SERVER}",
"LIBGL_ALWAYS_SOFTWARE": "${feature:softwareGL}"
"XDG_RUNTIME_DIR": "/tmp/devcontainer-wayland-runtime",
"PULSE_SERVER": "unix:/tmp/devcontainer-wayland-runtime/pulse-native"
},
"mounts": [
"source=${localEnv:XDG_RUNTIME_DIR},target=${localEnv:XDG_RUNTIME_DIR},type=bind"
"source=${localEnv:XDG_RUNTIME_DIR},target=/tmp/devcontainer-wayland-runtime,type=bind"
],
"installsAfter": [
"ghcr.io/devcontainers/features/common-utils"
Expand Down
12 changes: 10 additions & 2 deletions features/src/wayland/install.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
#!/usr/bin/env bash
set -euo pipefail

# Runtime forwarding is configured through feature metadata (containerEnv/mounts).
# Keep install lightweight and deterministic.
wayland_display="${WAYLANDDISPLAY:-wayland-0}"
software_gl="${SOFTWAREGL:-1}"

cat >/etc/profile.d/devcontainer-wayland-gui.sh <<EOF
export WAYLAND_DISPLAY="${wayland_display}"
export LIBGL_ALWAYS_SOFTWARE="${software_gl}"
EOF

chmod 0644 /etc/profile.d/devcontainer-wayland-gui.sh

echo "Wayland feature metadata configured."
13 changes: 13 additions & 0 deletions features/src/x11/NOTES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
For modern Wayland/XWayland desktops, the feature auto-discovers the mounted `.mutter-Xwaylandauth.*` file from `XDG_RUNTIME_DIR`.

For classic Xorg setups where the authority file lives outside `XDG_RUNTIME_DIR` (often `~/.Xauthority`), add an explicit mount in the consuming `devcontainer.json`:

```json
"mounts": [
"source=${localEnv:XAUTHORITY},target=/tmp/devcontainer-xauthority-host,type=bind,readonly"
]
```

The feature always exposes the stable in-container path `/tmp/devcontainer-xauthority` through `XAUTHORITY`.

Qt applications are forced onto the X11 backend with `QT_QPA_PLATFORM=xcb` so they do not try to boot the Wayland plugin in X11-only containers.
14 changes: 14 additions & 0 deletions features/src/x11/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,22 @@ Configure container environment and mounts for host X11 display forwarding.

| Options Id | Description | Type | Default Value |
|-----|-----|-----|-----|
| display | Set DISPLAY for X11 clients launched from the container. | string | :0 |
| softwareGL | Set LIBGL_ALWAYS_SOFTWARE for GPU compatibility. | string | 1 |

For modern Wayland/XWayland desktops, the feature auto-discovers the mounted `.mutter-Xwaylandauth.*` file from `XDG_RUNTIME_DIR`.

For classic Xorg setups where the authority file lives outside `XDG_RUNTIME_DIR` (often `~/.Xauthority`), add an explicit mount in the consuming `devcontainer.json`:

```json
"mounts": [
"source=${localEnv:XAUTHORITY},target=/tmp/devcontainer-xauthority-host,type=bind,readonly"
]
```

The feature always exposes the stable in-container path `/tmp/devcontainer-xauthority` through `XAUTHORITY`.

Qt applications are forced onto the X11 backend with `QT_QPA_PLATFORM=xcb` so they do not try to boot the Wayland plugin in X11-only containers.


---
Expand Down
14 changes: 9 additions & 5 deletions features/src/x11/devcontainer-feature.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@
"documentationURL": "https://github.com/athackst/devcontainer-templates/tree/main/features/src/x11",
"licenseURL": "https://github.com/athackst/devcontainer-templates/blob/main/LICENSE",
"options": {
"display": {
"type": "string",
"default": ":0",
"description": "Set DISPLAY for X11 clients launched from the container."
},
"softwareGL": {
"type": "string",
"default": "1",
Expand All @@ -17,13 +22,12 @@
}
},
"containerEnv": {
"DISPLAY": "${localEnv:DISPLAY}",
"XAUTHORITY": "${localEnv:XAUTHORITY}",
"PULSE_SERVER": "${localEnv:PULSE_SERVER}",
"LIBGL_ALWAYS_SOFTWARE": "${feature:softwareGL}"
"XAUTHORITY": "/tmp/devcontainer-xauthority",
"PULSE_SERVER": "unix:/tmp/devcontainer-host-runtime/pulse-native"
},
"mounts": [
"source=/tmp/.X11-unix,target=/tmp/.X11-unix,type=bind"
"source=/tmp/.X11-unix,target=/tmp/.X11-unix,type=bind",
"source=${localEnv:XDG_RUNTIME_DIR},target=/tmp/devcontainer-host-runtime,type=bind"
],
"installsAfter": [
"ghcr.io/devcontainers/features/common-utils"
Expand Down
29 changes: 27 additions & 2 deletions features/src/x11/install.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,31 @@
#!/usr/bin/env bash
set -euo pipefail

# Runtime forwarding is configured through feature metadata (containerEnv/mounts).
# Keep install lightweight and deterministic.
display="${DISPLAY:-:0}"
software_gl="${SOFTWAREGL:-1}"

cat >/etc/profile.d/devcontainer-x11-gui.sh <<EOF
host_runtime_dir="/tmp/devcontainer-host-runtime"
mounted_xauthority="/tmp/devcontainer-xauthority-host"
xauthority_target="/tmp/devcontainer-xauthority"

if [ -e "\${mounted_xauthority}" ]; then
ln -snf "\${mounted_xauthority}" "\${xauthority_target}"
else
set -- "\${host_runtime_dir}"/.mutter-Xwaylandauth.*
if [ -e "\$1" ]; then
ln -snf "\$1" "\${xauthority_target}"
else
rm -f "\${xauthority_target}"
fi
fi

export DISPLAY="${display}"
export LIBGL_ALWAYS_SOFTWARE="${software_gl}"
export QT_QPA_PLATFORM="xcb"
export XAUTHORITY="\${xauthority_target}"
EOF

chmod 0644 /etc/profile.d/devcontainer-x11-gui.sh

echo "X11 feature metadata configured."
49 changes: 46 additions & 3 deletions features/test/test_all.sh
Original file line number Diff line number Diff line change
@@ -1,8 +1,51 @@
#!/usr/bin/env bash
set -euo pipefail

repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
features_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
base_image="${DEVCONTAINER_BASE_IMAGE:-mcr.microsoft.com/devcontainers/base:ubuntu}"

for test_script in "${repo_root}"/features/test/*/test.sh; do
bash "${test_script}"
if ! command -v devcontainer >/dev/null 2>&1; then
echo "devcontainer CLI is required. Install it with 'npm install -g @devcontainers/cli'." >&2
exit 1
fi

cleanup_paths=()

cleanup() {
for path in "${cleanup_paths[@]}"; do
if [[ -d "${path}" ]]; then
rm -rf "${path}"
elif [[ -f "${path}" ]]; then
rm -f "${path}"
fi
done
}

trap cleanup EXIT

mkdir -p /tmp/.X11-unix

export DISPLAY="${FEATURE_TEST_DISPLAY:-:99}"
export XDG_RUNTIME_DIR="${FEATURE_TEST_XDG_RUNTIME_DIR:-/tmp/devcontainers-wayland-runtime}"
export XAUTHORITY="${FEATURE_TEST_XAUTHORITY:-/tmp/devcontainers-xauthority.test}"
export WAYLAND_DISPLAY="${FEATURE_TEST_WAYLAND_DISPLAY:-wayland-host}"
export PULSE_SERVER="${FEATURE_TEST_PULSE_SERVER:-unix:${XDG_RUNTIME_DIR}/pulse-native}"
xwayland_xauthority="${FEATURE_TEST_XWAYLAND_XAUTHORITY:-${XDG_RUNTIME_DIR}/.mutter-Xwaylandauth.test}"

cleanup_paths+=("${XAUTHORITY}" "${XDG_RUNTIME_DIR}")

mkdir -p "$(dirname "${XAUTHORITY}")"
mkdir -p "${XDG_RUNTIME_DIR}"
chmod 700 "${XDG_RUNTIME_DIR}"
touch "${XAUTHORITY}"
touch "${xwayland_xauthority}"
touch "${XDG_RUNTIME_DIR}/${WAYLAND_DISPLAY}"
touch "${XDG_RUNTIME_DIR}/wayland-0"
touch "${XDG_RUNTIME_DIR}/pulse-native"

for feature in x11 wayland; do
devcontainer features test \
--base-image "${base_image}" \
--features "${feature}" \
--project-folder "${features_root}"
done
13 changes: 13 additions & 0 deletions features/test/wayland/host_forwarding_env.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/usr/bin/env bash
set -e

source dev-container-features-test-lib

check "WAYLAND_DISPLAY can be overridden from feature option" bash -lc '[ "${WAYLAND_DISPLAY:-}" = "wayland-host" ]'
check "XDG_RUNTIME_DIR stays on fixed path" bash -c '[ "${XDG_RUNTIME_DIR:-}" = "/tmp/devcontainer-wayland-runtime" ]'
check "PULSE_SERVER stays on fixed path" bash -c '[ "${PULSE_SERVER:-}" = "unix:/tmp/devcontainer-wayland-runtime/pulse-native" ]'
check "software rendering defaults through shell init" bash -lc '[ "${LIBGL_ALWAYS_SOFTWARE:-}" = "1" ]'
check "wayland socket placeholder exists" bash -c '[ -e "/tmp/devcontainer-wayland-runtime/wayland-host" ]'
check "runtime directory is mounted" mountpoint -q /tmp/devcontainer-wayland-runtime

reportResults
Loading
Loading