From 7419a5fff8e86b321fc077bba52f0eb750fb098e Mon Sep 17 00:00:00 2001 From: iot-rocket <157110319+iot-rocket@users.noreply.github.com> Date: Sat, 30 Aug 2025 17:31:18 +0200 Subject: [PATCH 01/17] Update release.yaml `github.ref` should be only `main`, not `refs/heads/main` --- .github/workflows/release.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 57572c7..7f638c0 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -4,7 +4,7 @@ on: jobs: deploy: - if: ${{ github.ref == 'refs/heads/main' }} + if: ${{ github.ref == 'main' }} runs-on: ubuntu-latest permissions: contents: write From 82f2471a8b9e74953d986d04635128cf3ceb9484 Mon Sep 17 00:00:00 2001 From: iot-rocket <157110319+iot-rocket@users.noreply.github.com> Date: Sat, 30 Aug 2025 18:15:30 +0200 Subject: [PATCH 02/17] Update release.yaml --- .github/workflows/release.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 7f638c0..0e9af1b 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -4,7 +4,7 @@ on: jobs: deploy: - if: ${{ github.ref == 'main' }} + if: ${{ github.ref_type == 'branch' && github.ref_name == github.event.repository.default_branch }} runs-on: ubuntu-latest permissions: contents: write From aa246e2a641e6139d3e9da2cd1c6f20f5893c5cc Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 30 Aug 2025 16:16:47 +0000 Subject: [PATCH 03/17] Automated documentation update [skip ci] --- src/adr-tools/README.md | 4 ++-- src/chrome/README.md | 4 ++-- src/claude-code/README.md | 4 ++-- src/color/README.md | 4 ++-- src/hello/README.md | 4 ++-- src/imagemagick/README.md | 4 ++-- src/mc/README.md | 4 ++-- src/mcp-language-server/README.md | 4 ++-- src/yek/README.md | 4 ++-- src/yq/README.md | 4 ++-- 10 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/adr-tools/README.md b/src/adr-tools/README.md index a79bf61..2f1ac23 100644 --- a/src/adr-tools/README.md +++ b/src/adr-tools/README.md @@ -7,7 +7,7 @@ Installs ADR Tools for managing Architecture Decision Records ```json "features": { - "ghcr.io/tmaier/devcontainer-features/adr-tools:1": {} + "ghcr.io/iot-rocket/devcontainer-features/adr-tools:1": {} } ``` @@ -25,4 +25,4 @@ For usage instructions and documentation, see the [official ADR Tools repository --- -_Note: This file was auto-generated from the [devcontainer-feature.json](https://github.com/tmaier/devcontainer-features/blob/main/src/adr-tools/devcontainer-feature.json). Add additional notes to a `NOTES.md`._ +_Note: This file was auto-generated from the [devcontainer-feature.json](https://github.com/iot-rocket/devcontainer-features/blob/main/src/adr-tools/devcontainer-feature.json). Add additional notes to a `NOTES.md`._ diff --git a/src/chrome/README.md b/src/chrome/README.md index 81938b7..c15d495 100644 --- a/src/chrome/README.md +++ b/src/chrome/README.md @@ -7,7 +7,7 @@ Installs Google Chrome with container-specific configurations and wrapper script ```json "features": { - "ghcr.io/tmaier/devcontainer-features/chrome:1": {} + "ghcr.io/iot-rocket/devcontainer-features/chrome:1": {} } ``` @@ -100,4 +100,4 @@ For more detailed documentation, see `/usr/local/share/chrome-wrapper/README.md` --- -_Note: This file was auto-generated from the [devcontainer-feature.json](https://github.com/tmaier/devcontainer-features/blob/main/src/chrome/devcontainer-feature.json). Add additional notes to a `NOTES.md`._ +_Note: This file was auto-generated from the [devcontainer-feature.json](https://github.com/iot-rocket/devcontainer-features/blob/main/src/chrome/devcontainer-feature.json). Add additional notes to a `NOTES.md`._ diff --git a/src/claude-code/README.md b/src/claude-code/README.md index 8748a4f..b02125b 100644 --- a/src/claude-code/README.md +++ b/src/claude-code/README.md @@ -7,7 +7,7 @@ Installs Claude Code CLI for AI-powered development assistance ```json "features": { - "ghcr.io/tmaier/devcontainer-features/claude-code:1": {} + "ghcr.io/iot-rocket/devcontainer-features/claude-code:1": {} } ``` @@ -56,4 +56,4 @@ For detailed documentation, see the [Claude Code documentation](https://docs.ant --- -_Note: This file was auto-generated from the [devcontainer-feature.json](https://github.com/tmaier/devcontainer-features/blob/main/src/claude-code/devcontainer-feature.json). Add additional notes to a `NOTES.md`._ +_Note: This file was auto-generated from the [devcontainer-feature.json](https://github.com/iot-rocket/devcontainer-features/blob/main/src/claude-code/devcontainer-feature.json). Add additional notes to a `NOTES.md`._ diff --git a/src/color/README.md b/src/color/README.md index 9741712..6327772 100644 --- a/src/color/README.md +++ b/src/color/README.md @@ -7,7 +7,7 @@ A feature to remind you of your favorite color ```json "features": { - "ghcr.io/tmaier/devcontainer-features/color:1": {} + "ghcr.io/iot-rocket/devcontainer-features/color:1": {} } ``` @@ -21,4 +21,4 @@ A feature to remind you of your favorite color --- -_Note: This file was auto-generated from the [devcontainer-feature.json](https://github.com/tmaier/devcontainer-features/blob/main/src/color/devcontainer-feature.json). Add additional notes to a `NOTES.md`._ +_Note: This file was auto-generated from the [devcontainer-feature.json](https://github.com/iot-rocket/devcontainer-features/blob/main/src/color/devcontainer-feature.json). Add additional notes to a `NOTES.md`._ diff --git a/src/hello/README.md b/src/hello/README.md index 51a0e86..98e8b19 100644 --- a/src/hello/README.md +++ b/src/hello/README.md @@ -7,7 +7,7 @@ A hello world feature ```json "features": { - "ghcr.io/tmaier/devcontainer-features/hello:1": {} + "ghcr.io/iot-rocket/devcontainer-features/hello:1": {} } ``` @@ -21,4 +21,4 @@ A hello world feature --- -_Note: This file was auto-generated from the [devcontainer-feature.json](https://github.com/tmaier/devcontainer-features/blob/main/src/hello/devcontainer-feature.json). Add additional notes to a `NOTES.md`._ +_Note: This file was auto-generated from the [devcontainer-feature.json](https://github.com/iot-rocket/devcontainer-features/blob/main/src/hello/devcontainer-feature.json). Add additional notes to a `NOTES.md`._ diff --git a/src/imagemagick/README.md b/src/imagemagick/README.md index d345f3a..3e9daea 100644 --- a/src/imagemagick/README.md +++ b/src/imagemagick/README.md @@ -7,7 +7,7 @@ Installs imagemagick ```json "features": { - "ghcr.io/tmaier/devcontainer-features/imagemagick:1": {} + "ghcr.io/iot-rocket/devcontainer-features/imagemagick:1": {} } ``` @@ -31,4 +31,4 @@ For complete usage instructions and documentation, visit the official ImageMagic --- -_Note: This file was auto-generated from the [devcontainer-feature.json](https://github.com/tmaier/devcontainer-features/blob/main/src/imagemagick/devcontainer-feature.json). Add additional notes to a `NOTES.md`._ +_Note: This file was auto-generated from the [devcontainer-feature.json](https://github.com/iot-rocket/devcontainer-features/blob/main/src/imagemagick/devcontainer-feature.json). Add additional notes to a `NOTES.md`._ diff --git a/src/mc/README.md b/src/mc/README.md index 5504562..1596c35 100644 --- a/src/mc/README.md +++ b/src/mc/README.md @@ -7,7 +7,7 @@ Installs the MinIO Client (mc) ```json "features": { - "ghcr.io/tmaier/devcontainer-features/mc:1": {} + "ghcr.io/iot-rocket/devcontainer-features/mc:1": {} } ``` @@ -27,4 +27,4 @@ For complete usage instructions and documentation, visit the official MinIO Clie --- -_Note: This file was auto-generated from the [devcontainer-feature.json](https://github.com/tmaier/devcontainer-features/blob/main/src/mc/devcontainer-feature.json). Add additional notes to a `NOTES.md`._ +_Note: This file was auto-generated from the [devcontainer-feature.json](https://github.com/iot-rocket/devcontainer-features/blob/main/src/mc/devcontainer-feature.json). Add additional notes to a `NOTES.md`._ diff --git a/src/mcp-language-server/README.md b/src/mcp-language-server/README.md index db6034b..f23f5c5 100644 --- a/src/mcp-language-server/README.md +++ b/src/mcp-language-server/README.md @@ -7,7 +7,7 @@ Installs MCP Language Server - runs and exposes language servers to LLMs for sem ```json "features": { - "ghcr.io/tmaier/devcontainer-features/mcp-language-server:1": {} + "ghcr.io/iot-rocket/devcontainer-features/mcp-language-server:1": {} } ``` @@ -51,4 +51,4 @@ After installation, configure MCP Language Server in your MCP client settings: --- -_Note: This file was auto-generated from the [devcontainer-feature.json](https://github.com/tmaier/devcontainer-features/blob/main/src/mcp-language-server/devcontainer-feature.json). Add additional notes to a `NOTES.md`._ +_Note: This file was auto-generated from the [devcontainer-feature.json](https://github.com/iot-rocket/devcontainer-features/blob/main/src/mcp-language-server/devcontainer-feature.json). Add additional notes to a `NOTES.md`._ diff --git a/src/yek/README.md b/src/yek/README.md index 103a909..e625053 100644 --- a/src/yek/README.md +++ b/src/yek/README.md @@ -7,7 +7,7 @@ Installs yek, a tool for serializing repository files for LLM consumption ```json "features": { - "ghcr.io/tmaier/devcontainer-features/yek:1": {} + "ghcr.io/iot-rocket/devcontainer-features/yek:1": {} } ``` @@ -27,4 +27,4 @@ For complete usage instructions and documentation, visit the official yek reposi --- -_Note: This file was auto-generated from the [devcontainer-feature.json](https://github.com/tmaier/devcontainer-features/blob/main/src/yek/devcontainer-feature.json). Add additional notes to a `NOTES.md`._ +_Note: This file was auto-generated from the [devcontainer-feature.json](https://github.com/iot-rocket/devcontainer-features/blob/main/src/yek/devcontainer-feature.json). Add additional notes to a `NOTES.md`._ diff --git a/src/yq/README.md b/src/yq/README.md index d8dff4f..16c1064 100644 --- a/src/yq/README.md +++ b/src/yq/README.md @@ -7,7 +7,7 @@ Installs yq for yaml processing ```json "features": { - "ghcr.io/tmaier/devcontainer-features/yq:1": {} + "ghcr.io/iot-rocket/devcontainer-features/yq:1": {} } ``` @@ -27,4 +27,4 @@ For complete usage instructions and documentation, visit the official yq reposit --- -_Note: This file was auto-generated from the [devcontainer-feature.json](https://github.com/tmaier/devcontainer-features/blob/main/src/yq/devcontainer-feature.json). Add additional notes to a `NOTES.md`._ +_Note: This file was auto-generated from the [devcontainer-feature.json](https://github.com/iot-rocket/devcontainer-features/blob/main/src/yq/devcontainer-feature.json). Add additional notes to a `NOTES.md`._ From 7c894d98b466253e352a37f90bdc857bbcadcf2a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 24 Dec 2025 02:34:41 +0000 Subject: [PATCH 04/17] Automated documentation update [skip ci] --- src/imagemagick/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/imagemagick/README.md b/src/imagemagick/README.md index 3e9daea..905401b 100644 --- a/src/imagemagick/README.md +++ b/src/imagemagick/README.md @@ -21,6 +21,8 @@ Installs imagemagick This feature installs [ImageMagick](https://imagemagick.org/), a powerful image processing and manipulation library, along with Ghostscript for enhanced PDF support in container environments. +**Supported Versions**: This feature supports both ImageMagick 6 and ImageMagick 7, automatically detecting and configuring the installed version. + ## Key Configuration **PDF Processing Support**: The feature automatically configures ImageMagick to process PDF files by modifying the security policy, enabling PDF manipulation operations which are often restricted by default. From 8b994ee3b6907f4c07d53294eafa43030f78e699 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20Meichelb=C3=B6ck=20=5Biot=20rocket=5D?= Date: Sun, 8 Feb 2026 19:31:33 +0100 Subject: [PATCH 05/17] feat(claude): Migrated to new installer --- src/claude-code/NOTES.md | 19 ++++++++++++++++--- src/claude-code/devcontainer-feature.json | 8 +++++++- src/claude-code/install.sh | 18 +++++++++++++----- test/claude-code/claude_code_default.sh | 6 ++---- test/claude-code/claude_code_stable.sh | 16 ++++++++++++++++ .../claude_code_with_custom_user.sh | 5 +++-- test/claude-code/scenarios.json | 8 ++++++++ 7 files changed, 65 insertions(+), 15 deletions(-) create mode 100755 test/claude-code/claude_code_stable.sh diff --git a/src/claude-code/NOTES.md b/src/claude-code/NOTES.md index 1926254..2b682e4 100644 --- a/src/claude-code/NOTES.md +++ b/src/claude-code/NOTES.md @@ -1,14 +1,27 @@ # Claude Code Feature -This feature installs [Claude Code](https://www.anthropic.com/claude-code), Anthropic's official CLI for AI-powered development assistance. +This feature installs [Claude Code](https://www.anthropic.com/claude-code), Anthropic's official CLI for AI-powered development assistance, using the [native installer](https://code.claude.com/docs/en/setup). -Claude Code is installed using the [native installer](https://code.claude.com/docs/en/setup), which automatically keeps itself up to date. +No Node.js dependency is required. The native installer downloads a standalone binary. + +## Version Option + +By default, the `latest` release channel is installed. You can also specify: + +- `"stable"` — a release channel that is typically about one week behind latest, skipping releases with major regressions +- A specific semver version (e.g. `"1.0.58"`) + +The channel chosen at install time becomes the default for auto-updates. + +## Auto-Updates + +The native binary automatically updates in the background. Update checks are performed on startup and periodically while running. To disable auto-updates, set the `DISABLE_AUTOUPDATER=1` environment variable. ## Manual config for `devcontainer.json` Mount the local `~/.claude/` directory into the Dev Container. Add the following mount to the `devcontainer.json` file. -Replace `vscode` with the actual name of your user (see `remoteUser` property) +Replace `vscode` with the actual name of your user (see `remoteUser` property). ```json "mounts": [ diff --git a/src/claude-code/devcontainer-feature.json b/src/claude-code/devcontainer-feature.json index 8993c0f..91a7b63 100644 --- a/src/claude-code/devcontainer-feature.json +++ b/src/claude-code/devcontainer-feature.json @@ -3,7 +3,13 @@ "version": "2.0.0", "name": "Claude Code", "description": "Installs Claude Code CLI for AI-powered development assistance", - "options": {}, + "options": { + "version": { + "type": "string", + "default": "latest", + "description": "Version to install. Use 'latest', 'stable', or a specific semver (e.g. '1.0.58')." + } + }, "customizations": { "vscode": { "extensions": [ diff --git a/src/claude-code/install.sh b/src/claude-code/install.sh index 75e4969..acb7930 100755 --- a/src/claude-code/install.sh +++ b/src/claude-code/install.sh @@ -1,5 +1,6 @@ #!/bin/bash set -e +export DEBIAN_FRONTEND=noninteractive echo "Installing Claude Code..." @@ -7,26 +8,33 @@ echo "Installing Claude Code..." if ! command -v curl &> /dev/null; then echo "curl not found, installing..." if command -v apt-get &> /dev/null; then - apt-get update && apt-get install -y curl + apt-get update -y && apt-get install -y --no-install-recommends curl ca-certificates + rm -rf /var/lib/apt/lists/* elif command -v apk &> /dev/null; then - apk add --no-cache curl + apk add --no-cache curl ca-certificates elif command -v yum &> /dev/null; then - yum install -y curl + yum install -y curl ca-certificates else echo "Error: curl is required but could not be installed automatically." exit 1 fi fi +# Determine version argument for the installer +INSTALL_ARGS="" +if [ -n "$VERSION" ] && [ "$VERSION" != "latest" ]; then + INSTALL_ARGS="$VERSION" +fi + # Install Claude Code using the native installer # See https://code.claude.com/docs/en/setup if [ -n "$_REMOTE_USER" ] && [ "$_REMOTE_USER" != "root" ]; then echo "Installing Claude Code for user: $_REMOTE_USER" - su "$_REMOTE_USER" -c "curl -fsSL https://claude.ai/install.sh | bash" + su "$_REMOTE_USER" -c "curl -fsSL https://claude.ai/install.sh | bash -s $INSTALL_ARGS" INSTALL_HOME="$_REMOTE_USER_HOME" else echo "Installing Claude Code" - curl -fsSL https://claude.ai/install.sh | bash + curl -fsSL https://claude.ai/install.sh | bash -s $INSTALL_ARGS INSTALL_HOME="${HOME:-/root}" fi diff --git a/test/claude-code/claude_code_default.sh b/test/claude-code/claude_code_default.sh index e958bbd..f132388 100755 --- a/test/claude-code/claude_code_default.sh +++ b/test/claude-code/claude_code_default.sh @@ -5,14 +5,12 @@ set -e -# Optional: Import test library bundled with the devcontainer CLI +# Import test library bundled with the devcontainer CLI source dev-container-features-test-lib -# Feature-specific tests - simple smoke test -# The 'check' command comes from the dev-container-features-test-lib. +# Feature-specific tests check "claude command available" which claude check "claude shows version" bash -c "claude --version" # Report results -# If any of the checks above exited with a non-zero exit code, the test will fail. reportResults diff --git a/test/claude-code/claude_code_stable.sh b/test/claude-code/claude_code_stable.sh new file mode 100755 index 0000000..01c6099 --- /dev/null +++ b/test/claude-code/claude_code_stable.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +# This test file will be executed against the 'claude_code_stable' scenario +# to verify that Claude Code installs correctly with version set to 'stable'. + +set -e + +# Import test library bundled with the devcontainer CLI +source dev-container-features-test-lib + +# Feature-specific tests +check "claude command available" which claude +check "claude shows version" bash -c "claude --version" + +# Report results +reportResults diff --git a/test/claude-code/claude_code_with_custom_user.sh b/test/claude-code/claude_code_with_custom_user.sh index 9af9e29..deb49b9 100755 --- a/test/claude-code/claude_code_with_custom_user.sh +++ b/test/claude-code/claude_code_with_custom_user.sh @@ -1,14 +1,15 @@ #!/bin/bash # This test file will be executed against the 'claude_code_with_custom_user' scenario -# to verify that Claude Code is installed for the remote user. +# to verify that Claude Code is installed correctly for the remote user. set -e # Import test library bundled with the devcontainer CLI source dev-container-features-test-lib -# Basic functionality tests +# Feature-specific tests +check "claude binary exists in user home" test -f "$HOME/.local/bin/claude" check "claude command available" which claude check "claude shows version" bash -c "claude --version" diff --git a/test/claude-code/scenarios.json b/test/claude-code/scenarios.json index 40a82a9..7c4a444 100644 --- a/test/claude-code/scenarios.json +++ b/test/claude-code/scenarios.json @@ -5,6 +5,14 @@ "claude-code": {} } }, + "claude_code_stable": { + "image": "mcr.microsoft.com/devcontainers/base:ubuntu", + "features": { + "claude-code": { + "version": "stable" + } + } + }, "claude_code_with_custom_user": { "image": "mcr.microsoft.com/devcontainers/base:ubuntu", "remoteUser": "vscode", From 071138880fb7917447a53021db6e1f2f9346843f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20Meichelb=C3=B6ck=20=5Biot=20rocket=5D?= Date: Sun, 8 Feb 2026 20:02:09 +0100 Subject: [PATCH 06/17] feat(github): workflows improved --- .github/workflows/release.yaml | 1 + .github/workflows/test.yaml | 45 +++++++++++++++++-- .../workflows/{validate.yml => validate.yaml} | 8 ++++ 3 files changed, 51 insertions(+), 3 deletions(-) rename .github/workflows/{validate.yml => validate.yaml} (66%) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 0e9af1b..5669e9d 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -6,6 +6,7 @@ jobs: deploy: if: ${{ github.ref_type == 'branch' && github.ref_name == github.event.repository.default_branch }} runs-on: ubuntu-latest + timeout-minutes: 30 permissions: contents: write pull-requests: write diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 699ba69..686847b 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -6,11 +6,19 @@ on: pull_request: workflow_dispatch: +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + jobs: test-autogenerated: runs-on: ubuntu-latest - continue-on-error: true + timeout-minutes: 30 strategy: + fail-fast: false matrix: features: - adr-tools @@ -32,6 +40,16 @@ jobs: steps: - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 'lts/*' + + - name: Cache npm global packages + uses: actions/cache@v4 + with: + path: ~/.npm + key: npm-devcontainer-cli-${{ runner.os }} + - name: "Install latest devcontainer CLI" run: npm install -g @devcontainers/cli @@ -40,8 +58,9 @@ jobs: test-scenarios: runs-on: ubuntu-latest - continue-on-error: true + timeout-minutes: 30 strategy: + fail-fast: false matrix: features: - adr-tools @@ -60,6 +79,16 @@ jobs: steps: - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 'lts/*' + + - name: Cache npm global packages + uses: actions/cache@v4 + with: + path: ~/.npm + key: npm-devcontainer-cli-${{ runner.os }} + - name: "Install latest devcontainer CLI" run: npm install -g @devcontainers/cli @@ -68,10 +97,20 @@ jobs: test-global: runs-on: ubuntu-latest - continue-on-error: true + timeout-minutes: 30 steps: - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 'lts/*' + + - name: Cache npm global packages + uses: actions/cache@v4 + with: + path: ~/.npm + key: npm-devcontainer-cli-${{ runner.os }} + - name: "Install latest devcontainer CLI" run: npm install -g @devcontainers/cli diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yaml similarity index 66% rename from .github/workflows/validate.yml rename to .github/workflows/validate.yaml index 863418e..46c710f 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yaml @@ -3,9 +3,17 @@ on: workflow_dispatch: pull_request: +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + jobs: validate: runs-on: ubuntu-latest + timeout-minutes: 10 steps: - uses: actions/checkout@v4 From afff26fd69793b6db486a8b844eb63afa092b12c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20Meichelb=C3=B6ck=20=5Biot=20rocket=5D?= Date: Sun, 8 Feb 2026 20:30:22 +0100 Subject: [PATCH 07/17] feat(gitlab): vscode devcontainer feature for gitlab added --- .github/workflows/test.yaml | 2 ++ CLAUDE.md | 1 + README.md | 1 + src/gitlab/NOTES.md | 40 +++++++++++++++++++++++ src/gitlab/devcontainer-feature.json | 15 +++++++++ src/gitlab/install.sh | 49 ++++++++++++++++++++++++++++ test/gitlab/gitlab_test.sh | 15 +++++++++ test/gitlab/scenarios.json | 8 +++++ test/gitlab/test.sh | 16 +++++++++ 9 files changed, 147 insertions(+) create mode 100644 src/gitlab/NOTES.md create mode 100644 src/gitlab/devcontainer-feature.json create mode 100755 src/gitlab/install.sh create mode 100755 test/gitlab/gitlab_test.sh create mode 100644 test/gitlab/scenarios.json create mode 100755 test/gitlab/test.sh diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 686847b..394ef59 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -27,6 +27,7 @@ jobs: - codex - color - gemini-cli + - gitlab - hello - imagemagick - mc @@ -69,6 +70,7 @@ jobs: - codex - color - gemini-cli + - gitlab - hello - imagemagick - mc diff --git a/CLAUDE.md b/CLAUDE.md index 4422bb3..ad192cb 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -34,6 +34,7 @@ This is Tobias Maier's Dev Container Features collection - a repository for crea - `claude-code` - Claude Code CLI for AI-powered development assistance - `codex` - OpenAI Codex CLI for local AI-powered coding assistance - `gemini-cli` - Google Gemini CLI for AI-powered development assistance +- `gitlab` - GitLab CLI and workflow tools - `imagemagick` - ImageMagick image processing tools - `mc` - MinIO Client for object storage - `mcp-language-server` - MCP Language Server for semantic code navigation diff --git a/README.md b/README.md index 5a9d881..d829130 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ A collection of custom [Dev Container Features](https://containers.dev/implement | [claude-code](https://github.com/tmaier/devcontainer-features/tree/main/src/claude-code) | Claude Code CLI for AI-powered development assistance | `ghcr.io/tmaier/devcontainer-features/claude-code` | | [codex](https://github.com/tmaier/devcontainer-features/tree/main/src/codex) | OpenAI Codex CLI for local AI-powered coding assistance | `ghcr.io/tmaier/devcontainer-features/codex` | | [gemini-cli](https://github.com/tmaier/devcontainer-features/tree/main/src/gemini-cli) | Google Gemini CLI for AI-powered development assistance | `ghcr.io/tmaier/devcontainer-features/gemini-cli` | +| [gitlab](https://github.com/tmaier/devcontainer-features/tree/main/src/gitlab) | GitLab CLI and workflow extension for GitLab integration | `ghcr.io/tmaier/devcontainer-features/gitlab` | | [imagemagick](https://github.com/tmaier/devcontainer-features/tree/main/src/imagemagick) | ImageMagick image processing library with PDF support | `ghcr.io/tmaier/devcontainer-features/imagemagick` | | [mc](https://github.com/tmaier/devcontainer-features/tree/main/src/mc) | MinIO Client for object storage operations | `ghcr.io/tmaier/devcontainer-features/mc` | | [mcp-language-server](https://github.com/tmaier/devcontainer-features/tree/main/src/mcp-language-server) | MCP Language Server for semantic code navigation | `ghcr.io/tmaier/devcontainer-features/mcp-language-server` | diff --git a/src/gitlab/NOTES.md b/src/gitlab/NOTES.md new file mode 100644 index 0000000..e84db87 --- /dev/null +++ b/src/gitlab/NOTES.md @@ -0,0 +1,40 @@ +## What This Feature Installs + +1. **glab CLI** - The official GitLab command-line tool (latest version) +2. **GitLab Workflow VS Code extension** - GitLab integration for VS Code (`GitLab.gitlab-workflow`) + +## Authentication + +After the container starts, authenticate with your GitLab instance: + +```bash +# Interactive login (gitlab.com) +glab auth login + +# Login to a self-hosted instance +glab auth login --hostname gitlab.example.com +``` + +## Basic Usage + +```bash +# Clone a repository +glab repo clone owner/repo + +# Create a merge request +glab mr create --fill + +# List open issues +glab issue list + +# View CI/CD pipeline status +glab ci status + +# Browse the repository in the browser +glab repo view --web +``` + +## Further Reading + +- [glab CLI documentation](https://gitlab.com/gitlab-org/cli/-/blob/main/README.md) +- [GitLab Workflow VS Code extension](https://marketplace.visualstudio.com/items?itemName=GitLab.gitlab-workflow) diff --git a/src/gitlab/devcontainer-feature.json b/src/gitlab/devcontainer-feature.json new file mode 100644 index 0000000..307aea5 --- /dev/null +++ b/src/gitlab/devcontainer-feature.json @@ -0,0 +1,15 @@ +{ + "id": "gitlab", + "version": "1.0.0", + "name": "GitLab CLI & Workflow", + "description": "Installs glab CLI and GitLab Workflow VS Code extension for GitLab integration", + "options": {}, + "installsAfter": [], + "customizations": { + "vscode": { + "extensions": [ + "GitLab.gitlab-workflow" + ] + } + } +} diff --git a/src/gitlab/install.sh b/src/gitlab/install.sh new file mode 100755 index 0000000..f019881 --- /dev/null +++ b/src/gitlab/install.sh @@ -0,0 +1,49 @@ +#!/bin/bash +set -e + +echo "Installing glab CLI (latest version)..." + +# Ensure curl is available +if ! command -v curl &> /dev/null; then + apt-get update + apt-get install -y curl +fi + +# Detect architecture +ARCH=$(dpkg --print-architecture) +case "$ARCH" in + amd64|arm64) ;; + *) + echo "Unsupported architecture: $ARCH" + exit 1 + ;; +esac + +# Get latest version from GitLab API +LATEST_JSON=$(curl -fsSL "https://gitlab.com/api/v4/projects/34675721/releases/permalink/latest") +VERSION=$(echo "$LATEST_JSON" | sed -n 's/.*"tag_name"\s*:\s*"v\([^"]*\)".*/\1/p') + +if [ -z "$VERSION" ]; then + echo "Failed to determine latest glab version" + exit 1 +fi + +echo "Latest glab version: $VERSION" + +# Download and install the .deb package +DEB_URL="https://gitlab.com/gitlab-org/cli/-/releases/v${VERSION}/downloads/glab_${VERSION}_linux_${ARCH}.deb" +TMP_DEB=$(mktemp /tmp/glab_XXXXXX.deb) + +echo "Downloading glab from: $DEB_URL" +curl -fsSL -o "$TMP_DEB" "$DEB_URL" + +dpkg -i "$TMP_DEB" +rm -f "$TMP_DEB" + +# Verify installation +if command -v glab &> /dev/null; then + echo "glab CLI installed successfully: $(glab version)" +else + echo "glab CLI installation failed" + exit 1 +fi diff --git a/test/gitlab/gitlab_test.sh b/test/gitlab/gitlab_test.sh new file mode 100755 index 0000000..b157c6e --- /dev/null +++ b/test/gitlab/gitlab_test.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +# This test file will be executed against the 'gitlab_test' scenario. + +set -e + +# Import test library bundled with the devcontainer CLI +source dev-container-features-test-lib + +# Feature-specific tests +check "glab is installed" which glab +check "glab version" bash -c "glab version" + +# Report results +reportResults diff --git a/test/gitlab/scenarios.json b/test/gitlab/scenarios.json new file mode 100644 index 0000000..9435ba1 --- /dev/null +++ b/test/gitlab/scenarios.json @@ -0,0 +1,8 @@ +{ + "gitlab_test": { + "image": "mcr.microsoft.com/devcontainers/base:ubuntu", + "features": { + "gitlab": {} + } + } +} diff --git a/test/gitlab/test.sh b/test/gitlab/test.sh new file mode 100755 index 0000000..52fba7b --- /dev/null +++ b/test/gitlab/test.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +# This test file will be executed against an auto-generated devcontainer.json that +# includes the 'gitlab' Feature with no options. + +set -e + +# Import test library bundled with the devcontainer CLI +source dev-container-features-test-lib + +# Feature-specific tests +check "glab is installed" which glab +check "glab version" bash -c "glab version" + +# Report results +reportResults From 976bb72960bcae6abf6e873d1a171603708d9320 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20Meichelb=C3=B6ck=20=5Biot=20rocket=5D?= Date: Sun, 8 Feb 2026 23:11:55 +0100 Subject: [PATCH 08/17] fix(github): fixing and improving tests --- src/adr-tools/install.sh | 1 + src/chrome/install.sh | 12 ++-- src/claude-code/install.sh | 10 ++- src/codex/install.sh | 6 +- src/gemini-cli/install.sh | 6 +- src/gitlab/install.sh | 9 ++- src/mc/install.sh | 1 + src/mcp-language-server/NOTES.md | 2 +- src/mcp-language-server/install.sh | 61 ++++++++++++------- src/typst/install.sh | 1 + src/yek/install.sh | 23 +++++-- src/yq/install.sh | 1 + test/claude-code/test.sh | 31 +--------- test/codex/test.sh | 7 +-- test/color/scenarios.json | 2 +- test/gemini-cli/test.sh | 7 +-- .../mcp_language_server_without_go.sh | 19 ++++++ test/mcp-language-server/scenarios.json | 8 ++- test/mcp-language-server/test.sh | 13 ++++ 19 files changed, 136 insertions(+), 84 deletions(-) create mode 100755 test/mcp-language-server/mcp_language_server_without_go.sh create mode 100755 test/mcp-language-server/test.sh diff --git a/src/adr-tools/install.sh b/src/adr-tools/install.sh index 89ae2fa..90f91f7 100755 --- a/src/adr-tools/install.sh +++ b/src/adr-tools/install.sh @@ -1,5 +1,6 @@ #!/bin/sh set -e +export DEBIAN_FRONTEND=noninteractive # Update package list and install wget and ca-certificates if not available if ! command -v wget >/dev/null 2>&1; then diff --git a/src/chrome/install.sh b/src/chrome/install.sh index 4b62000..d8ee436 100755 --- a/src/chrome/install.sh +++ b/src/chrome/install.sh @@ -1,21 +1,21 @@ #!/bin/bash set -e +export DEBIAN_FRONTEND=noninteractive echo "Installing Google Chrome stable version..." # Install dependencies apt-get update -apt-get install -y wget gnupg2 apt-transport-https ca-certificates dbus-x11 +apt-get install -y --no-install-recommends wget gnupg2 apt-transport-https ca-certificates dbus-x11 -# Add Google Chrome repository using modern approach (apt-key is deprecated) +# Add Google Chrome repository (using modern signed-by approach) mkdir -p /etc/apt/keyrings -wget -q -O - https://dl.google.com/linux/linux_signing_key.pub | gpg --dearmor -o /etc/apt/keyrings/google-chrome.gpg -chmod a+r /etc/apt/keyrings/google-chrome.gpg -echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/google-chrome.gpg] http://dl.google.com/linux/chrome/deb/ stable main" > /etc/apt/sources.list.d/google-chrome.list +wget -q -O /etc/apt/keyrings/google-chrome.asc https://dl.google.com/linux/linux_signing_key.pub +echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/google-chrome.asc] http://dl.google.com/linux/chrome/deb/ stable main" > /etc/apt/sources.list.d/google-chrome.list apt-get update # Install Google Chrome stable -apt-get install -y google-chrome-stable +apt-get install -y --no-install-recommends google-chrome-stable echo "Creating Chrome wrapper script at /usr/local/bin/chrome..." diff --git a/src/claude-code/install.sh b/src/claude-code/install.sh index acb7930..a7e371b 100755 --- a/src/claude-code/install.sh +++ b/src/claude-code/install.sh @@ -4,7 +4,7 @@ export DEBIAN_FRONTEND=noninteractive echo "Installing Claude Code..." -# Ensure curl is available +# Ensure curl and other dependencies are available if ! command -v curl &> /dev/null; then echo "curl not found, installing..." if command -v apt-get &> /dev/null; then @@ -20,6 +20,14 @@ if ! command -v curl &> /dev/null; then fi fi +# Ensure tar and gzip are available (needed by the installer) +if ! command -v tar &> /dev/null || ! command -v gzip &> /dev/null; then + echo "Installing archive utilities..." + apt-get update -y + apt-get install -y --no-install-recommends tar gzip + rm -rf /var/lib/apt/lists/* +fi + # Determine version argument for the installer INSTALL_ARGS="" if [ -n "$VERSION" ] && [ "$VERSION" != "latest" ]; then diff --git a/src/codex/install.sh b/src/codex/install.sh index d0a8961..4f8e829 100755 --- a/src/codex/install.sh +++ b/src/codex/install.sh @@ -1,15 +1,17 @@ #!/bin/bash set -e +export DEBIAN_FRONTEND=noninteractive echo "Installing Codex CLI..." -# Check if Node.js is available, install if missing +# Install Node.js if not available if ! command -v node &> /dev/null; then echo "Node.js not found. Installing Node.js 22.x..." - apt-get update + apt-get update -y apt-get install -y --no-install-recommends curl ca-certificates curl -fsSL https://deb.nodesource.com/setup_22.x | bash - apt-get install -y --no-install-recommends nodejs + rm -rf /var/lib/apt/lists/* fi # Check Node.js version (requires 18+) diff --git a/src/gemini-cli/install.sh b/src/gemini-cli/install.sh index c864f78..ebfdb3e 100755 --- a/src/gemini-cli/install.sh +++ b/src/gemini-cli/install.sh @@ -1,15 +1,17 @@ #!/bin/bash set -e +export DEBIAN_FRONTEND=noninteractive echo "Installing Gemini CLI..." -# Check if Node.js is available, install if missing +# Install Node.js if not available if ! command -v node &> /dev/null; then echo "Node.js not found. Installing Node.js 22.x..." - apt-get update + apt-get update -y apt-get install -y --no-install-recommends curl ca-certificates curl -fsSL https://deb.nodesource.com/setup_22.x | bash - apt-get install -y --no-install-recommends nodejs + rm -rf /var/lib/apt/lists/* fi # Check Node.js version (requires 18+) diff --git a/src/gitlab/install.sh b/src/gitlab/install.sh index f019881..4a2bb66 100755 --- a/src/gitlab/install.sh +++ b/src/gitlab/install.sh @@ -1,13 +1,12 @@ #!/bin/bash set -e +export DEBIAN_FRONTEND=noninteractive echo "Installing glab CLI (latest version)..." -# Ensure curl is available -if ! command -v curl &> /dev/null; then - apt-get update - apt-get install -y curl -fi +# Ensure curl and git are available (git is a runtime dependency of glab) +apt-get update +apt-get install -y --no-install-recommends curl git # Detect architecture ARCH=$(dpkg --print-architecture) diff --git a/src/mc/install.sh b/src/mc/install.sh index cc6c097..9ce8364 100755 --- a/src/mc/install.sh +++ b/src/mc/install.sh @@ -1,5 +1,6 @@ #!/bin/sh set -e +export DEBIAN_FRONTEND=noninteractive # Update package list and install wget and ca-certificates if not available if ! command -v wget >/dev/null 2>&1; then diff --git a/src/mcp-language-server/NOTES.md b/src/mcp-language-server/NOTES.md index e36336c..13fd8a0 100644 --- a/src/mcp-language-server/NOTES.md +++ b/src/mcp-language-server/NOTES.md @@ -4,7 +4,7 @@ This feature installs [MCP Language Server](https://github.com/isaacphi/mcp-lang ## Requirements -- Go +- Go (automatically installed if not available) - A language server for your programming language (installed separately) ## Supported Language Servers diff --git a/src/mcp-language-server/install.sh b/src/mcp-language-server/install.sh index 67a7855..f22fc69 100755 --- a/src/mcp-language-server/install.sh +++ b/src/mcp-language-server/install.sh @@ -1,12 +1,37 @@ #!/bin/bash set -e +export DEBIAN_FRONTEND=noninteractive echo "Installing MCP Language Server..." -# Check if Go is available +# Install Go if not available if ! command -v go &> /dev/null; then - echo "Error: Go is required but not found. Please ensure the Go feature is installed." - exit 1 + echo "Go not found. Installing Go via official tarball..." + + apt-get update -y + apt-get install -y --no-install-recommends curl ca-certificates tar + + ARCH=$(dpkg --print-architecture) + case "$ARCH" in + amd64|arm64) ;; + *) + echo "Unsupported architecture: $ARCH" + exit 1 + ;; + esac + + GO_VERSION="1.24.1" + echo "Installing Go ${GO_VERSION} for ${ARCH}..." + curl -fsSL -o /tmp/go.tar.gz "https://go.dev/dl/go${GO_VERSION}.linux-${ARCH}.tar.gz" + tar -C /usr/local -xzf /tmp/go.tar.gz + rm -f /tmp/go.tar.gz + + ln -sf /usr/local/go/bin/go /usr/local/bin/go + ln -sf /usr/local/go/bin/gofmt /usr/local/bin/gofmt + export PATH="/usr/local/go/bin:$PATH" + + rm -rf /var/lib/apt/lists/* + echo "Go $(go version) installed successfully." fi # Install MCP Language Server @@ -19,25 +44,17 @@ else go install github.com/isaacphi/mcp-language-server@v${MCP_VERSION} fi -# Ensure Go bin directory is in PATH -GO_BIN_PATH=$(go env GOPATH)/bin -if ! echo "$PATH" | grep -q "$GO_BIN_PATH"; then - echo "Adding Go bin directory to PATH..." - echo "export PATH=\"\$PATH:$GO_BIN_PATH\"" >> /etc/bash.bashrc - export PATH="$PATH:$GO_BIN_PATH" -fi - -# Verify installation -if ! command -v mcp-language-server &> /dev/null; then - # Check if it's available in GOPATH/bin - if [ -f "$GO_BIN_PATH/mcp-language-server" ]; then - echo "MCP Language Server installed to $GO_BIN_PATH/mcp-language-server" - # Create a symlink to make it globally available - ln -sf "$GO_BIN_PATH/mcp-language-server" /usr/local/bin/mcp-language-server - else - echo "Error: MCP Language Server installation failed" - exit 1 - fi +# Copy binary to /usr/local/bin for system-wide access. +# go install places the binary in $GOPATH/bin (typically /root/go/bin), +# which is inaccessible to non-root users. We copy instead of symlink +# because /root has mode 700 (same approach as the yek feature). +GO_BIN_PATH="$(go env GOPATH)/bin" +if [ -f "$GO_BIN_PATH/mcp-language-server" ]; then + cp "$GO_BIN_PATH/mcp-language-server" /usr/local/bin/mcp-language-server + chmod 755 /usr/local/bin/mcp-language-server +else + echo "Error: MCP Language Server installation failed" + exit 1 fi echo "MCP Language Server installed successfully!" diff --git a/src/typst/install.sh b/src/typst/install.sh index 2884106..a3c423b 100755 --- a/src/typst/install.sh +++ b/src/typst/install.sh @@ -1,5 +1,6 @@ #!/bin/sh set -e +export DEBIAN_FRONTEND=noninteractive echo "Installing Typst..." diff --git a/src/yek/install.sh b/src/yek/install.sh index 1a520cd..b86dcfe 100755 --- a/src/yek/install.sh +++ b/src/yek/install.sh @@ -1,5 +1,6 @@ #!/bin/sh set -e +export DEBIAN_FRONTEND=noninteractive # Update package list and install curl and ca-certificates if not available if ! command -v curl >/dev/null 2>&1; then @@ -10,9 +11,21 @@ fi # Install yek curl -fsSL https://azimi.me/yek.sh | bash -# The installer places the binary in ~/.local/bin which may not be in PATH. -# Copy it to /usr/local/bin to ensure it's globally available. -if [ -f "$HOME/.local/bin/yek" ] && ! command -v yek >/dev/null 2>&1; then - cp "$HOME/.local/bin/yek" /usr/local/bin/yek - chmod +x /usr/local/bin/yek +# Ensure yek is accessible for all users by copying to /usr/local/bin. +# The upstream installer places the binary in a user-specific directory +# (e.g. /root/.local/bin) which is inaccessible to non-root users. +# We copy instead of symlink because /root has mode 700. +if [ ! -f /usr/local/bin/yek ]; then + for dir in "$HOME/.yek/bin" "/root/.yek/bin" "$HOME/.local/bin" "/root/.local/bin"; do + if [ -f "$dir/yek" ]; then + cp "$dir/yek" /usr/local/bin/yek + chmod 755 /usr/local/bin/yek + break + fi + done +fi + +if ! command -v yek >/dev/null 2>&1; then + echo "Error: yek installation failed" + exit 1 fi diff --git a/src/yq/install.sh b/src/yq/install.sh index 33d5775..788d37d 100755 --- a/src/yq/install.sh +++ b/src/yq/install.sh @@ -1,5 +1,6 @@ #!/bin/sh set -e +export DEBIAN_FRONTEND=noninteractive # Update package list and install wget and ca-certificates if not available if ! command -v wget >/dev/null 2>&1; then diff --git a/test/claude-code/test.sh b/test/claude-code/test.sh index e8811e0..a5405b7 100755 --- a/test/claude-code/test.sh +++ b/test/claude-code/test.sh @@ -1,37 +1,8 @@ #!/bin/bash -# This test file will be executed against an auto-generated devcontainer.json that -# includes the 'claude-code' Feature with no options. -# -# For more information, see: https://github.com/devcontainers/cli/blob/main/docs/features/test.md -# -# Eg: -# { -# "image": "<..some-base-image...>", -# "features": { -# "claude-code": {} -# }, -# "remoteUser": "root" -# } -# -# Thus, the value of all options will fall back to the default value in the -# Feature's 'devcontainer-feature.json'. -# -# These scripts are run as 'root' by default. Although that can be changed -# with the '--remote-user' flag. -# -# This test can be run with the following command: -# -# devcontainer features test \ -# --features claude-code \ -# --remote-user root \ -# --skip-scenarios \ -# --base-image mcr.microsoft.com/devcontainers/base:ubuntu \ -# /path/to/this/repo - set -e -# Optional: Import test library bundled with the devcontainer CLI +# Import test library bundled with the devcontainer CLI source dev-container-features-test-lib # Feature-specific tests diff --git a/test/codex/test.sh b/test/codex/test.sh index a3eac79..c245cb8 100755 --- a/test/codex/test.sh +++ b/test/codex/test.sh @@ -1,14 +1,13 @@ #!/bin/bash -# This test file will be executed against an auto-generated devcontainer.json that -# includes the 'codex' Feature with no options. - set -e -# Optional: Import test library bundled with the devcontainer CLI +# Import test library bundled with the devcontainer CLI source dev-container-features-test-lib # Feature-specific tests +check "node is available" which node +check "npm is available" which npm check "codex command available" which codex check "codex shows version" bash -c "codex --version" diff --git a/test/color/scenarios.json b/test/color/scenarios.json index bfd7952..3565284 100644 --- a/test/color/scenarios.json +++ b/test/color/scenarios.json @@ -1,6 +1,6 @@ { "my_favorite_color_is_green": { - "image": "mcr.microsoft.com/devcontainers/base:focal", + "image": "mcr.microsoft.com/devcontainers/base:noble", "features": { "ghcr.io/devcontainers/features/common-utils:1": { "installZsh": false, diff --git a/test/gemini-cli/test.sh b/test/gemini-cli/test.sh index 1726c7f..892852e 100755 --- a/test/gemini-cli/test.sh +++ b/test/gemini-cli/test.sh @@ -1,14 +1,13 @@ #!/bin/bash -# This test file will be executed against an auto-generated devcontainer.json that -# includes the 'gemini-cli' Feature with no options. - set -e -# Optional: Import test library bundled with the devcontainer CLI +# Import test library bundled with the devcontainer CLI source dev-container-features-test-lib # Feature-specific tests +check "node is available" which node +check "npm is available" which npm check "gemini command available" which gemini check "gemini shows version" bash -c "gemini --version" diff --git a/test/mcp-language-server/mcp_language_server_without_go.sh b/test/mcp-language-server/mcp_language_server_without_go.sh new file mode 100755 index 0000000..8f0b35c --- /dev/null +++ b/test/mcp-language-server/mcp_language_server_without_go.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +# This test file will be executed against the 'mcp_language_server_without_go' scenario +# to verify that MCP Language Server installs Go automatically when not available. + +set -e + +# Optional: Import test library bundled with the devcontainer CLI +source dev-container-features-test-lib + +# Feature-specific tests - verify Go fallback installation works +# The 'check' command comes from the dev-container-features-test-lib. +check "go is available" which go +check "mcp-language-server command available" which mcp-language-server +check "mcp-language-server runs help" bash -c "mcp-language-server --help 2>&1 | grep -E '(usage|help|Usage|Help)' || true" + +# Report results +# If any of the checks above exited with a non-zero exit code, the test will fail. +reportResults diff --git a/test/mcp-language-server/scenarios.json b/test/mcp-language-server/scenarios.json index dd4044e..7f1536c 100644 --- a/test/mcp-language-server/scenarios.json +++ b/test/mcp-language-server/scenarios.json @@ -8,8 +8,14 @@ "mcp-language-server": {} } }, + "mcp_language_server_without_go": { + "image": "mcr.microsoft.com/devcontainers/base:ubuntu", + "features": { + "mcp-language-server": {} + } + }, "mcp_language_server_with_go_image": { - "image": "mcr.microsoft.com/devcontainers/go:1-1.24-bookworm", + "image": "mcr.microsoft.com/devcontainers/go:1-bookworm", "features": { "mcp-language-server": {} } diff --git a/test/mcp-language-server/test.sh b/test/mcp-language-server/test.sh new file mode 100755 index 0000000..93b44f8 --- /dev/null +++ b/test/mcp-language-server/test.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +set -e + +# Import test library bundled with the devcontainer CLI +source dev-container-features-test-lib + +# Feature-specific tests +check "mcp-language-server command available" which mcp-language-server +check "mcp-language-server runs help" bash -c "mcp-language-server --help 2>&1 | grep -E '(usage|help|Usage|Help)' || true" + +# Report results +reportResults From 2501ab9e7fb644ee4c8f9540cea4b5761fc9f674 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20Meichelb=C3=B6ck=20=5Biot=20rocket=5D?= Date: Sun, 8 Feb 2026 23:23:26 +0100 Subject: [PATCH 09/17] fix(github): fixing and improving tests --- src/codex/install.sh | 3 +-- src/gemini-cli/install.sh | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/codex/install.sh b/src/codex/install.sh index 4f8e829..94940af 100755 --- a/src/codex/install.sh +++ b/src/codex/install.sh @@ -29,8 +29,7 @@ if ! command -v npm &> /dev/null; then exit 1 fi -# Install Codex CLI globally as root -# Note: Always install as root since npm's global dir requires root permissions +# Install Codex CLI globally (runs as root during container build) echo "Installing Codex CLI globally..." npm install -g @openai/codex diff --git a/src/gemini-cli/install.sh b/src/gemini-cli/install.sh index ebfdb3e..edab4c4 100755 --- a/src/gemini-cli/install.sh +++ b/src/gemini-cli/install.sh @@ -29,8 +29,7 @@ if ! command -v npm &> /dev/null; then exit 1 fi -# Install Gemini CLI globally as root -# Note: Always install as root since npm's global dir requires root permissions +# Install Gemini CLI globally (runs as root during container build) echo "Installing Gemini CLI globally..." npm install -g @google/gemini-cli From b988759a3c2c5ab0a73fbccc56e53d7124385a7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20Meichelb=C3=B6ck=20=5Biot=20rocket=5D?= Date: Sun, 8 Feb 2026 23:27:12 +0100 Subject: [PATCH 10/17] fix(github): fixing and improving tests --- src/gitlab/install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gitlab/install.sh b/src/gitlab/install.sh index 4a2bb66..457dd86 100755 --- a/src/gitlab/install.sh +++ b/src/gitlab/install.sh @@ -6,7 +6,7 @@ echo "Installing glab CLI (latest version)..." # Ensure curl and git are available (git is a runtime dependency of glab) apt-get update -apt-get install -y --no-install-recommends curl git +apt-get install -y --no-install-recommends curl git ca-certificates # Detect architecture ARCH=$(dpkg --print-architecture) From 24124cc0eab20bf0231ef4c3cfce2984d8547d2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20Meichelb=C3=B6ck?= Date: Mon, 16 Feb 2026 03:35:11 +0100 Subject: [PATCH 11/17] feat(chrome): add channel selection, display modes, and comprehensive test coverage Major enhancements to the Chrome devcontainer feature: - Add support for Chrome release channels (stable, beta, dev) - Add display mode options (headless, xvfb, vnc) with proper dependencies - Add VNC clipboard support configuration - Add optional font packages for CJK, emoji, and proper rendering - Add architecture detection (Chrome only supports amd64) - Install xdotool and wmctrl for X11 window automation - Improve wrapper script with better error handling and verification - Remove requirement for --add-host runArg (no longer needed) - Add shared memory configuration guidance for heavy workloads - Correct Puppeteer/Playwright auto-discovery documentation Test improvements: - Add 10+ new test scenarios covering all configuration options - Add tests for channel selection, display modes, extensions, VNC, and combined features - Expand scenarios.json with comprehensive test matrix --- src/chrome/NOTES.md | 273 ++++++++++- src/chrome/install.sh | 471 ++++++++++++++++--- test/chrome/chrome_channel_test.sh | 18 + test/chrome/chrome_combined_test.sh | 22 + test/chrome/chrome_debugging_port_test.sh | 14 + test/chrome/chrome_dev_channel_test.sh | 18 + test/chrome/chrome_extensions_inline_test.sh | 34 ++ test/chrome/chrome_extensions_test.sh | 36 ++ test/chrome/chrome_flags_test.sh | 15 + test/chrome/chrome_locale_test.sh | 14 + test/chrome/chrome_resolution_test.sh | 16 + test/chrome/chrome_test.sh | 2 + test/chrome/chrome_vnc_no_clipboard_test.sh | 19 + test/chrome/chrome_vnc_test.sh | 26 + test/chrome/chrome_xvfb_test.sh | 15 + test/chrome/scenarios.json | 104 +++- 16 files changed, 1018 insertions(+), 79 deletions(-) create mode 100755 test/chrome/chrome_channel_test.sh create mode 100755 test/chrome/chrome_combined_test.sh create mode 100755 test/chrome/chrome_debugging_port_test.sh create mode 100755 test/chrome/chrome_dev_channel_test.sh create mode 100755 test/chrome/chrome_extensions_inline_test.sh create mode 100755 test/chrome/chrome_extensions_test.sh create mode 100755 test/chrome/chrome_flags_test.sh create mode 100755 test/chrome/chrome_locale_test.sh create mode 100755 test/chrome/chrome_resolution_test.sh create mode 100755 test/chrome/chrome_vnc_no_clipboard_test.sh create mode 100755 test/chrome/chrome_vnc_test.sh create mode 100755 test/chrome/chrome_xvfb_test.sh diff --git a/src/chrome/NOTES.md b/src/chrome/NOTES.md index 351eeb4..184c114 100644 --- a/src/chrome/NOTES.md +++ b/src/chrome/NOTES.md @@ -2,19 +2,12 @@ Running Chrome in a container environment can be challenging due to sandbox, display, and security constraints. This feature installs: -1. Google Chrome stable version +1. Google Chrome (stable, beta, or dev channel) 2. A specialized wrapper script at `/usr/local/bin/chrome` that configures Chrome to work properly in containers 3. The necessary dependencies for Chrome to run in a container +4. `xdotool` for X11 automation (simulating keyboard/mouse input, managing windows) +5. `wmctrl` for X Window manager control (listing, moving, resizing windows) -## Required Configuration - -**Important**: Add the required run argument to your `devcontainer.json`: - -```json -"runArgs": ["--add-host=host.docker.internal:host-gateway"] -``` - -> The `--add-host=host.docker.internal:host-gateway` run argument is **required** for the Chrome wrapper to properly connect to the host machine for display forwarding and X11 connections. ## Using the Chrome Wrapper The key component is the Chrome wrapper script at `/usr/local/bin/chrome`. **Always use this wrapper** instead of calling `google-chrome-stable` directly. @@ -23,16 +16,16 @@ The key component is the Chrome wrapper script at `/usr/local/bin/chrome`. **Alw **Cline**: Automatically configured via VS Code settings. -**Playwright**: +**Puppeteer**: Auto-discovered via the `PUPPETEER_EXECUTABLE_PATH` environment variable set by this feature. No configuration needed: ```javascript -const browser = await playwright.chromium.launch({ - executablePath: '/usr/local/bin/chrome', -}) +const browser = await puppeteer.launch() ``` -**Puppeteer**: +**Karma / Angular CLI**: Auto-discovered via the `CHROME_BIN` environment variable. No configuration needed. + +**Playwright**: Does not support auto-discovery via environment variables. You must pass `executablePath` explicitly: ```javascript -const browser = await puppeteer.launch({ +const browser = await playwright.chromium.launch({ executablePath: '/usr/local/bin/chrome', }) ``` @@ -55,17 +48,257 @@ chrome --headless=new --screenshot=/tmp/screenshot.png https://example.com chrome --headless=new --print-to-pdf=/tmp/output.pdf https://example.com ``` +## Shared Memory for Heavy Workloads + +Docker containers default to 64MB of shared memory (`/dev/shm`), which can cause Chrome to crash or produce blank screenshots under heavy workloads (many tabs, large pages, parallel tests). Increase the shared memory size via `runArgs` in your `devcontainer.json`: + +```json +{ + "runArgs": ["--shm-size=2g"] +} +``` + +Alternatively, the wrapper script already passes `--disable-dev-shm-usage` to Chrome, which moves shared memory to `/tmp`. This works for most cases but may be slower under extreme load. + +## Chrome Release Channels + +Select which Chrome release channel to install using the `channel` option: + +```json +{ + "features": { + "ghcr.io/iot-rocket/devcontainer-features/chrome": { + "channel": "beta" + } + } +} +``` + +Available channels: `stable` (default), `beta`, `dev`. + +## Display Modes + +The `displayMode` option controls how Chrome handles display output. Three modes are available: + +### headless (default) + +No display server is installed. Chrome runs with `--headless=new` when no DISPLAY is set. Best for CI/CD, automated testing, and headless scraping. + +```json +{ + "features": { + "ghcr.io/iot-rocket/devcontainer-features/chrome": {} + } +} +``` + +### xvfb (Virtual Framebuffer) + +Installs Xvfb and auto-starts a virtual display on `:99` when no DISPLAY is set. Required for extensions that need a display server, and for automated testing that requires rendering. + +```json +{ + "features": { + "ghcr.io/iot-rocket/devcontainer-features/chrome": { + "displayMode": "xvfb", + "screenResolution": "1920x1080x24" + } + } +} +``` + +> **Note**: The Xvfb display is fixed at `:99`. If you need a different display number, set the `DISPLAY` environment variable before calling `chrome` — the wrapper will use it instead of starting its own Xvfb instance. + +### vnc (Desktop-Lite Integration) + +Delegates to the `ghcr.io/devcontainers/features/desktop-lite` feature for a full desktop environment with VNC access. Chrome runs in GUI mode on the desktop-lite display. View Chrome via: +- **noVNC** (browser): `http://localhost:6080` +- **VNC client**: port `5901` + +```json +{ + "features": { + "ghcr.io/devcontainers/features/desktop-lite:1": {}, + "ghcr.io/iot-rocket/devcontainer-features/chrome": { + "displayMode": "vnc" + } + } +} +``` + +> **Note**: VNC mode requires `desktop-lite` to be included in your devcontainer.json features. VNC password is configured via desktop-lite's `password` option (default: `vscode`). + +> **Important**: `desktop-lite` defaults to 16-bit color depth (`1440x768x16`). noVNC cursor rendering requires 24-bit depth. Override the resolution to use 24-bit: +> ```json +> "ghcr.io/devcontainers/features/desktop-lite:1": { +> "resolution": "1920x1080x24" +> } +> ``` + +### VNC Clipboard Sharing + +Clipboard copy/paste between the host and Chrome inside a VNC session is enabled automatically when `displayMode=vnc`. The clipboard bridge uses three components: + +- **vncconfig**: Bridges the VNC protocol clipboard with the X11 cutbuffer +- **autocutsel**: Syncs the X11 CLIPBOARD and PRIMARY selections with the cutbuffer +- **xclip**: Provides command-line clipboard access + +Together, these create a full clipboard pipeline: + +``` +Host clipboard ↔ VNC protocol ↔ vncconfig ↔ X11 cutbuffer ↔ autocutsel ↔ X11 CLIPBOARD ↔ Chrome +``` + +#### Using clipboard with noVNC (browser) + +1. Open the noVNC sidebar by clicking the arrow on the left edge of the screen +2. Open the **Clipboard** panel +3. Paste text into the clipboard panel — it becomes available to Chrome via Ctrl+V +4. Text copied in Chrome via Ctrl+C appears in the clipboard panel for copying to the host + +#### Using clipboard with a native VNC client + +Clipboard sharing is transparent with native VNC clients (e.g., TigerVNC Viewer, RealVNC). Ctrl+C/Ctrl+V work between the host and Chrome. + +#### Command-line clipboard + +`xclip` is installed for scripting clipboard access: + +```bash +# Copy to clipboard +echo "text" | xclip -selection clipboard + +# Paste from clipboard +xclip -selection clipboard -o +``` + +## Extension Management + +Chrome extensions can be pre-installed via managed enterprise policies using the `extensions` option. Extensions are configured through Chrome's `ExtensionSettings` policy, which handles installation mode, toolbar pinning, and incognito access. + +The `extensions` option takes a JSON object where keys are Chrome Web Store extension IDs and values are settings objects. An empty object `{}` applies the hardcoded defaults. + +### Default Settings + +| Key | Values | Default | +|-----|--------|---------| +| `mode` | `force_installed`, `normal_installed` | `force_installed` | +| `pin` | `true`, `false` | `true` | +| `incognito` | `force_allowed`, `allowed`, `not_allowed` | `allowed` | + +### Basic Usage + +Install one or more extensions with default settings: + +```json +{ + "features": { + "ghcr.io/iot-rocket/devcontainer-features/chrome": { + "extensions": "{\"fcoeoabgfenejglbffodgkkbkcdhcgfn\": {}, \"cjpalhdlnbpafiamejdnhcphjbkeiagm\": {}}" + } + } +} +``` + +### Per-Extension Settings + +Override settings for individual extensions by providing values in the settings object: + +```json +{ + "features": { + "ghcr.io/iot-rocket/devcontainer-features/chrome": { + "extensions": "{\"fcoeoabgfenejglbffodgkkbkcdhcgfn\": {}, \"cjpalhdlnbpafiamejdnhcphjbkeiagm\": {\"mode\": \"normal_installed\", \"pin\": false, \"incognito\": \"not_allowed\"}}" + } + } +} +``` + +In this example: +- `fcoeoabgfenejglbffodgkkbkcdhcgfn` (Claude) uses the default settings (force installed, pinned, incognito allowed) +- `cjpalhdlnbpafiamejdnhcphjbkeiagm` (uBlock Origin) uses custom settings (normal install, unpinned, no incognito) + +### Technical Details + +- Extension policies are written to `/etc/opt/chrome/policies/managed/extension_settings.json` (Chrome) and `/etc/chromium/policies/managed/extension_settings.json` (Chromium) +- The wrapper script automatically detects managed policy files at runtime: if policy files exist, extensions are enabled; otherwise `--disable-extensions` is used +- Extensions are downloaded from the Chrome Web Store update URL (`https://clients2.google.com/service/update2/crx`) + +## Additional Options + +### Remote Debugging Port + +Set a default remote debugging port that's always passed to Chrome: + +```json +{ + "features": { + "ghcr.io/iot-rocket/devcontainer-features/chrome": { + "debuggingPort": "9222" + } + } +} +``` + +### Extra Chrome Flags + +Pass additional Chrome flags to every invocation: + +```json +{ + "features": { + "ghcr.io/iot-rocket/devcontainer-features/chrome": { + "chromeFlags": "--disable-web-security --allow-running-insecure-content" + } + } +} +``` + +### Fonts + +Install additional font packages for proper rendering of international text, emoji, and symbols. This prevents tofu (missing glyph boxes) in screenshots and PDFs: + +```json +{ + "features": { + "ghcr.io/iot-rocket/devcontainer-features/chrome": { + "fonts": true + } + } +} +``` + +Installs: `fonts-noto-cjk` (Chinese/Japanese/Korean), `fonts-noto-color-emoji` (emoji), `fonts-liberation` (metric-compatible with Arial/Times/Courier), `fonts-dejavu-core` (extended Latin/Greek/Cyrillic). + +### Locale + +Set the Chrome UI language: + +```json +{ + "features": { + "ghcr.io/iot-rocket/devcontainer-features/chrome": { + "locale": "de-DE" + } + } +} +``` + ## Technical Details The wrapper script handles common issues with running Chrome in containers by: -- Disabling the sandbox for container compatibility -- Setting proper display configurations +- Automatically disabling the sandbox when running as root for container compatibility +- Setting proper display configurations based on display mode - Suppressing error messages for cleaner output - Optimizing for both headless and interactive use - Providing special handling for debugging/Puppeteer modes - Logging command arguments, errors, and exit codes -- Ensuring the --no-sandbox flag is always included +- Sourcing runtime config from `/etc/chrome-wrapper/config` + +### Runtime Configuration + +The install script writes a configuration file at `/etc/chrome-wrapper/config` that the wrapper sources at runtime. This contains the Chrome binary path, display mode, screen resolution, debugging port, extra flags, and locale settings. ### Logging @@ -81,4 +314,4 @@ Log files are stored in one of the following locations (in order of preference): - `$HOME/.local/state/chrome-wrapper/chrome-wrapper.log` - `/tmp/chrome-wrapper/chrome-wrapper.log` -For more detailed documentation, see `/usr/local/share/chrome-wrapper/README.md` after installation. \ No newline at end of file +For more detailed documentation, see `/usr/local/share/chrome-wrapper/README.md` after installation. diff --git a/src/chrome/install.sh b/src/chrome/install.sh index d8ee436..a6c8369 100755 --- a/src/chrome/install.sh +++ b/src/chrome/install.sh @@ -2,11 +2,28 @@ set -e export DEBIAN_FRONTEND=noninteractive -echo "Installing Google Chrome stable version..." +# ============================================================================= +# Section 1: Package Installation +# ============================================================================= + +# Chrome only ships for amd64 — detect arch and exit early on unsupported platforms +ARCH=$(dpkg --print-architecture) +case "$ARCH" in + amd64) ;; + *) + echo "ERROR: Google Chrome is only available for amd64, but this system is ${ARCH}." + exit 1 + ;; +esac + +CHANNEL="${CHANNEL:-stable}" +CHROME_PACKAGE="google-chrome-${CHANNEL}" + +echo "Installing Google Chrome (${CHANNEL} channel)..." # Install dependencies apt-get update -apt-get install -y --no-install-recommends wget gnupg2 apt-transport-https ca-certificates dbus-x11 +apt-get install -y --no-install-recommends wget gnupg2 apt-transport-https ca-certificates dbus-x11 xdotool wmctrl jq # Add Google Chrome repository (using modern signed-by approach) mkdir -p /etc/apt/keyrings @@ -14,18 +31,171 @@ wget -q -O /etc/apt/keyrings/google-chrome.asc https://dl.google.com/linux/linux echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/google-chrome.asc] http://dl.google.com/linux/chrome/deb/ stable main" > /etc/apt/sources.list.d/google-chrome.list apt-get update -# Install Google Chrome stable -apt-get install -y --no-install-recommends google-chrome-stable +# Install Google Chrome +apt-get install -y --no-install-recommends "$CHROME_PACKAGE" + +# Verify Chrome installed correctly +if ! command -v "$CHROME_PACKAGE" >/dev/null 2>&1; then + echo "ERROR: ${CHROME_PACKAGE} binary not found after installation." + exit 1 +fi +echo "Chrome version: $("$CHROME_PACKAGE" --version)" + +# Install display-mode specific packages +case "${DISPLAYMODE:-headless}" in + xvfb) + echo "Installing Xvfb for virtual display support..." + apt-get install -y --no-install-recommends xvfb + echo "Xvfb installed successfully." + ;; + vnc) + echo "Installing cursor theme for VNC display..." + apt-get install -y --no-install-recommends adwaita-icon-theme + # Configure system-wide default cursor theme + mkdir -p /usr/share/icons/default + cat > /usr/share/icons/default/index.theme << CURSOREOF +[Icon Theme] +Inherits=Adwaita +CURSOREOF + # Configure GTK3 cursor theme for Chrome + mkdir -p /etc/gtk-3.0 + cat > /etc/gtk-3.0/settings.ini << GTKEOF +[Settings] +gtk-cursor-theme-name=Adwaita +gtk-cursor-theme-size=24 +GTKEOF + if [ "${VNCCLIPBOARD:-true}" = "true" ]; then + echo "Installing VNC clipboard support packages..." + apt-get install -y --no-install-recommends autocutsel xclip + echo "VNC clipboard packages installed successfully." + fi + # Desktop-lite provides VNC infrastructure + if [ ! -f /usr/local/share/desktop-init.sh ]; then + echo "WARNING: displayMode=vnc requires the 'ghcr.io/devcontainers/features/desktop-lite' feature." + echo "Add it to your devcontainer.json features to enable VNC access." + fi + ;; +esac + +# Install optional font packages for proper rendering in screenshots/PDFs +if [ "${FONTS:-false}" = "true" ]; then + echo "Installing font packages for CJK, emoji, and Latin rendering..." + apt-get install -y --no-install-recommends fonts-noto-cjk fonts-noto-color-emoji fonts-liberation fonts-dejavu-core + echo "Font packages installed successfully." +fi + +# Clean up APT cache to reduce image size +apt-get autoremove -y && rm -rf /var/lib/apt/lists/* + +# ============================================================================= +# Section 2: Runtime Config File +# ============================================================================= + +# Validate screenResolution format when xvfb mode is selected +if [ "${DISPLAYMODE:-headless}" = "xvfb" ]; then + if ! echo "${SCREENRESOLUTION:-1920x1080x24}" | grep -qE '^[0-9]+x[0-9]+x[0-9]+$'; then + echo "ERROR: screenResolution '${SCREENRESOLUTION}' does not match expected WIDTHxHEIGHTxDEPTH format (e.g. 1920x1080x24)." + exit 1 + fi +fi + +echo "Writing runtime configuration..." +mkdir -p /etc/chrome-wrapper +cat > /etc/chrome-wrapper/config << CONFIGEOF +CHROME_BINARY="${CHROME_PACKAGE}" +DISPLAY_MODE="${DISPLAYMODE:-headless}" +SCREEN_RESOLUTION="${SCREENRESOLUTION:-1920x1080x24}" +DEBUGGING_PORT="${DEBUGGINGPORT:-}" +EXTRA_FLAGS="${CHROMEFLAGS:-}" +LOCALE="${LOCALE:-}" +VNC_CLIPBOARD="$([ "${DISPLAYMODE:-headless}" = "vnc" ] && [ "${VNCCLIPBOARD:-true}" = "true" ] && echo "true" || echo "false")" +CONFIGEOF + +echo "Runtime config written to /etc/chrome-wrapper/config" + +# ============================================================================= +# Section 3: Extension Policy Generation +# ============================================================================= + +if [ -n "${EXTENSIONS}" ]; then + echo "Configuring Chrome extension policies..." + + # The devcontainer CLI may strip double quotes from JSON option values when + # generating the install wrapper script. Detect and repair corrupted JSON. + if ! echo "$EXTENSIONS" | jq empty 2>/dev/null; then + echo "Repairing extension JSON (quotes stripped by devcontainer CLI)..." + EXTENSIONS=$(echo "$EXTENSIONS" | sed -E \ + -e 's/([{,]) *([a-zA-Z_][a-zA-Z0-9_]*) *:/\1"\2":/g' \ + -e 's/: *([a-zA-Z_][a-zA-Z0-9_]*) *([,}])/: "\1"\2/g' \ + -e 's/"(true|false|null)"/\1/g') + fi + + CHROME_UPDATE_URL="https://clients2.google.com/service/update2/crx" + + # Build ExtensionSettings policy from JSON input using jq + POLICY_JSON=$(echo "$EXTENSIONS" | jq -r --arg update_url "$CHROME_UPDATE_URL" ' + { + "ExtensionSettings": ( + to_entries | map( + { + key: .key, + value: { + "installation_mode": (.value.mode // "force_installed"), + "update_url": $update_url, + "toolbar_pin": (if (.value | has("pin")) then (if .value.pin then "force_pinned" else "unpinned" end) else "force_pinned" end), + "incognito": (.value.incognito // "allowed") + } + } + ) | from_entries + ) + } + ') + + # Validate extension ID formats + for ext_id in $(echo "$EXTENSIONS" | jq -r 'keys[]'); do + if ! echo "$ext_id" | grep -qE '^[a-z]{32}$'; then + echo "========================================================================" + echo "WARNING: Extension ID '$ext_id' does not match expected format" + echo " Expected: 32 lowercase letters (e.g. fcoeoabgfenejglbffodgkkbkcdhcgfn)" + echo " Got: '$ext_id'" + echo " This may be valid for enterprise or custom extensions." + echo "========================================================================" + fi + done + + # Write Chrome managed policy + mkdir -p /etc/opt/chrome/policies/managed + echo "$POLICY_JSON" > /etc/opt/chrome/policies/managed/extension_settings.json + echo "Chrome extension policy written to /etc/opt/chrome/policies/managed/extension_settings.json" + + # Copy to Chromium policy directory for compatibility + mkdir -p /etc/chromium/policies/managed + cp /etc/opt/chrome/policies/managed/extension_settings.json /etc/chromium/policies/managed/extension_settings.json + echo "Chromium extension policy written to /etc/chromium/policies/managed/extension_settings.json" +fi + +# ============================================================================= +# Section 4: Wrapper Script +# ============================================================================= echo "Creating Chrome wrapper script at /usr/local/bin/chrome..." -# Create wrapper script cat > /usr/local/bin/chrome << 'EOF' #!/bin/bash # Wrapper script to run Chrome in a container environment # addressing sandbox and display issues +# Source runtime config +if [ -f /etc/chrome-wrapper/config ]; then + source /etc/chrome-wrapper/config +fi + +# Defaults if config is missing +CHROME_BINARY="${CHROME_BINARY:-google-chrome-stable}" +DISPLAY_MODE="${DISPLAY_MODE:-headless}" +SCREEN_RESOLUTION="${SCREEN_RESOLUTION:-1920x1080x24}" + # Logging setup LOG_DIR="/var/log/chrome-wrapper" # Fallback to user directory if system directory is not writable @@ -44,12 +214,116 @@ log() { # Log the command invocation log "Command: chrome $*" +log "Display mode: $DISPLAY_MODE" + +# Virtual display management +XVFB_DISPLAY=":99" +XVFB_PIDFILE="/tmp/.xvfb-display99.pid" +XVFB_LOCKFILE="/tmp/.X99-lock" + +is_xvfb_running() { + if [ -f "$XVFB_PIDFILE" ]; then + local pid + pid=$(cat "$XVFB_PIDFILE" 2>/dev/null) + if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then + return 0 + fi + rm -f "$XVFB_PIDFILE" + fi + if [ -f "$XVFB_LOCKFILE" ]; then + local pid + pid=$(cat "$XVFB_LOCKFILE" 2>/dev/null | tr -d ' ') + if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then + return 0 + fi + rm -f "$XVFB_LOCKFILE" + fi + return 1 +} -# Check if DISPLAY is set -if [ -z "$DISPLAY" ]; then - echo "DISPLAY environment variable is not set. Setting to host.docker.internal:0.0" - export DISPLAY="host.docker.internal:0.0" -fi +start_xvfb() { + log "Starting Xvfb on display $XVFB_DISPLAY with resolution $SCREEN_RESOLUTION" + Xvfb "$XVFB_DISPLAY" -screen 0 "$SCREEN_RESOLUTION" -ac +extension GLX +render -noreset & + local xvfb_pid=$! + echo "$xvfb_pid" > "$XVFB_PIDFILE" + local wait_count=0 + while [ $wait_count -lt 10 ]; do + if [ -f "$XVFB_LOCKFILE" ]; then + log "Xvfb started successfully (PID: $xvfb_pid)" + return 0 + fi + sleep 0.1 + wait_count=$((wait_count + 1)) + done + if kill -0 "$xvfb_pid" 2>/dev/null; then + log "Xvfb running (PID: $xvfb_pid)" + return 0 + fi + log "WARNING: Xvfb failed to start" + rm -f "$XVFB_PIDFILE" + return 1 +} + +# Display setup based on DISPLAY_MODE +case "$DISPLAY_MODE" in + xvfb) + if [ -z "$DISPLAY" ]; then + if command -v Xvfb >/dev/null 2>&1; then + if is_xvfb_running; then + log "Reusing existing Xvfb on $XVFB_DISPLAY" + export DISPLAY="$XVFB_DISPLAY" + elif start_xvfb; then + export DISPLAY="$XVFB_DISPLAY" + else + log "Xvfb failed, falling back to headless mode" + fi + else + log "Xvfb not available, falling back to headless mode" + fi + else + log "Using existing DISPLAY=$DISPLAY" + fi + ;; + vnc) + # VNC mode: desktop-lite sets DISPLAY=:1 + if [ -n "$DISPLAY" ]; then + log "VNC mode: using existing DISPLAY=$DISPLAY (set by desktop-lite)" + # Configure cursor theme for VNC display + export XCURSOR_THEME="${XCURSOR_THEME:-Adwaita}" + export XCURSOR_SIZE="${XCURSOR_SIZE:-24}" + # Initialize root window cursor so VNC server has a cursor to broadcast + if command -v xsetroot >/dev/null 2>&1; then + xsetroot -cursor_name left_ptr 2>/dev/null || true + fi + else + log "WARNING: VNC mode but no DISPLAY set. Is desktop-lite running?" + log "Falling back to headless mode" + fi + # Start VNC clipboard daemons if clipboard support was installed + if [ "$VNC_CLIPBOARD" = "true" ] && [ -n "$DISPLAY" ]; then + # vncconfig: bridges VNC protocol clipboard with X11 cutbuffer + if command -v vncconfig >/dev/null 2>&1 && ! pgrep -x vncconfig >/dev/null 2>&1; then + vncconfig -nowin & + log "Started vncconfig for VNC clipboard support (PID: $!)" + fi + # autocutsel: syncs X11 CLIPBOARD selection with cutbuffer + if command -v autocutsel >/dev/null 2>&1; then + if ! pgrep -f "autocutsel.*CLIPBOARD" >/dev/null 2>&1; then + autocutsel -selection CLIPBOARD -fork + log "Started autocutsel for CLIPBOARD selection sync" + fi + if ! pgrep -f "autocutsel.*PRIMARY" >/dev/null 2>&1; then + autocutsel -selection PRIMARY -fork + log "Started autocutsel for PRIMARY selection sync" + fi + fi + fi + ;; + headless|*) + # Headless mode: no display needed + log "Headless mode: skipping display setup" + ;; +esac # Create empty .Xauthority file if it doesn't exist if [ ! -f ~/.Xauthority ]; then @@ -59,30 +333,59 @@ fi # Disable DBus for Chrome to reduce error messages export DBUS_SESSION_BUS_ADDRESS="disabled:" -# Check if --no-sandbox is already in the arguments -if [[ "$*" != *"--no-sandbox"* ]]; then - log "Adding --no-sandbox flag as it was not provided" +# Add --no-sandbox only when running as root (uid 0). +# Chrome's sandbox works for non-root users; the flag triggers a warning bar. +if [ "$(id -u)" -eq 0 ] && [[ "$*" != *"--no-sandbox"* ]]; then + log "Running as root (uid 0), adding --no-sandbox flag" set -- --no-sandbox "$@" fi +# Add locale flag if configured and not already in args +if [ -n "$LOCALE" ] && [[ "$*" != *"--lang="* ]]; then + set -- --lang="$LOCALE" "$@" +fi + +# Add debugging port if configured and not already in args +if [ -n "$DEBUGGING_PORT" ] && [[ "$*" != *"--remote-debugging-port"* ]]; then + set -- --remote-debugging-port="$DEBUGGING_PORT" "$@" +fi + +# Add extra flags from config +if [ -n "$EXTRA_FLAGS" ]; then + # shellcheck disable=SC2086 + set -- $EXTRA_FLAGS "$@" +fi + +# Detect if a real desktop environment is available (e.g., desktop-lite VNC) +has_real_desktop() { + # If DISPLAY is the Xvfb virtual display we manage, it's not a real desktop + if [ "$DISPLAY" = "$XVFB_DISPLAY" ]; then + return 1 + fi + # Check if a display is set and accessible + if [ -n "$DISPLAY" ] && command -v xdpyinfo >/dev/null 2>&1 && xdpyinfo >/dev/null 2>&1; then + return 0 + fi + return 1 +} + # Check if this is being called from browser_action if [[ "$*" == *"--remote-debugging-port"* ]]; then # Running in Puppeteer/browser_action mode - keep remote debugging output visible stderr_log=$(mktemp) - # Run Chrome and capture stderr - google-chrome-stable \ - --no-sandbox \ + # Run Chrome and capture stderr to a temp file for reliable exit code + "$CHROME_BINARY" \ --disable-dev-shm-usage \ --disable-gpu \ - --disable-setuid-sandbox \ --disable-features=VizDisplayCompositor \ - "$@" 2> >(tee "$stderr_log" >&2) + "$@" 2>"$stderr_log" exit_code=$? - # Log any errors + # Replay stderr so callers can see it, then log if [ -s "$stderr_log" ]; then + cat "$stderr_log" >&2 log "STDERR: $(cat "$stderr_log")" fi @@ -99,30 +402,66 @@ else # Redirect stderr to a temporary file local_stderr_file=$(mktemp) - # Log the final command with all flags - log "Final command: google-chrome-stable --no-sandbox --disable-dev-shm-usage --disable-gpu --disable-software-rasterizer --disable-setuid-sandbox --no-first-run --no-default-browser-check --no-zygote --disable-features=VizDisplayCompositor --disable-extensions --disable-background-networking --disable-sync --disable-translate --hide-scrollbars --metrics-recording-only --mute-audio --disable-dbus --headless=$([[ "$*" == *--headless* ]] || echo "new") $*" + # Determine extensions flag: disable extensions unless managed policy files exist + EXTENSIONS_FLAG="--disable-extensions" + BACKGROUND_NETWORKING_FLAG="--disable-background-networking" + if [ -d /etc/opt/chrome/policies/managed ] && ls /etc/opt/chrome/policies/managed/*.json >/dev/null 2>&1; then + EXTENSIONS_FLAG="" + BACKGROUND_NETWORKING_FLAG="" + log "Extension policies detected, extensions and background networking enabled" + fi + + # Determine if user explicitly passed a --headless variant + USER_WANTS_HEADLESS=false + HEADLESS_FLAG="--headless=new" + if [[ "$*" == *"--headless"* ]]; then + USER_WANTS_HEADLESS=true + # User already passed their own --headless flag, don't add another + HEADLESS_FLAG="" + fi - # Run Chrome with all the flags, redirect stderr to our temp file - google-chrome-stable \ - --no-sandbox \ - --disable-dev-shm-usage \ - --disable-gpu \ - --disable-software-rasterizer \ - --disable-setuid-sandbox \ - --no-first-run \ - --no-default-browser-check \ - --no-zygote \ - --disable-features=VizDisplayCompositor \ - --disable-extensions \ - --disable-background-networking \ - --disable-sync \ - --disable-translate \ - --hide-scrollbars \ - --metrics-recording-only \ - --mute-audio \ - --disable-dbus \ - --headless="$([[ "$*" == *--headless* ]] || echo "new")" \ - "$@" 2>"$local_stderr_file" + # Choose GUI or headless mode based on desktop environment availability and display mode + if [ "$USER_WANTS_HEADLESS" = false ] && { [ "$DISPLAY_MODE" = "vnc" ] || [ "$DISPLAY_MODE" = "xvfb" ]; } && has_real_desktop; then + log "Real desktop detected (DISPLAY=$DISPLAY), launching in GUI mode" + log "Final command: $CHROME_BINARY --disable-dev-shm-usage --no-first-run --no-default-browser-check --disable-features=VizDisplayCompositor ${EXTENSIONS_FLAG} ${BACKGROUND_NETWORKING_FLAG} --disable-sync --disable-translate --mute-audio --disable-dbus $*" + + # GUI mode: launch Chrome with a visible window on the real desktop + "$CHROME_BINARY" \ + --disable-dev-shm-usage \ + --no-first-run \ + --no-default-browser-check \ + --disable-features=VizDisplayCompositor \ + ${EXTENSIONS_FLAG:+$EXTENSIONS_FLAG} \ + ${BACKGROUND_NETWORKING_FLAG:+$BACKGROUND_NETWORKING_FLAG} \ + --disable-sync \ + --disable-translate \ + --mute-audio \ + --disable-dbus \ + "$@" 2>"$local_stderr_file" + else + log "Launching in headless mode" + log "Final command: $CHROME_BINARY --disable-dev-shm-usage --disable-gpu --disable-software-rasterizer --no-first-run --no-default-browser-check --no-zygote --disable-features=VizDisplayCompositor ${EXTENSIONS_FLAG} ${BACKGROUND_NETWORKING_FLAG} --disable-sync --disable-translate --hide-scrollbars --metrics-recording-only --mute-audio --disable-dbus ${HEADLESS_FLAG} $*" + + # Headless mode: no visible window + "$CHROME_BINARY" \ + --disable-dev-shm-usage \ + --disable-gpu \ + --disable-software-rasterizer \ + --no-first-run \ + --no-default-browser-check \ + --no-zygote \ + --disable-features=VizDisplayCompositor \ + ${EXTENSIONS_FLAG:+$EXTENSIONS_FLAG} \ + ${BACKGROUND_NETWORKING_FLAG:+$BACKGROUND_NETWORKING_FLAG} \ + --disable-sync \ + --disable-translate \ + --hide-scrollbars \ + --metrics-recording-only \ + --mute-audio \ + --disable-dbus \ + ${HEADLESS_FLAG:+$HEADLESS_FLAG} \ + "$@" 2>"$local_stderr_file" + fi # Get the exit code local_exit_code=$? @@ -146,7 +485,10 @@ EOF # Make the wrapper script executable chmod +x /usr/local/bin/chrome -# Create documentation +# ============================================================================= +# Section 5: Embedded Documentation +# ============================================================================= + mkdir -p /usr/local/share/chrome-wrapper cat > /usr/local/share/chrome-wrapper/README.md << 'EOF' # Chrome in Dev Container @@ -168,24 +510,39 @@ chrome --headless=new --screenshot=/tmp/screenshot.png https://example.com chrome --headless=new --print-to-pdf=/tmp/output.pdf https://example.com ``` -### Interactive Mode (requires X server on host) +### Interactive Mode -Make sure you have an X server running on your host machine and that your DISPLAY variable is properly set. +#### Xvfb (Virtual Framebuffer) +With `displayMode: "xvfb"`, Chrome runs on a virtual display `:99`. Useful for +automated testing that requires a display server. -```bash -# Open Chrome browser (GUI mode) -chrome https://example.com -``` +#### VNC (Browser-Based Viewing) +With `displayMode: "vnc"` and the `desktop-lite` feature, Chrome runs in a +full desktop environment accessible via: +- noVNC (browser): http://localhost:6080 +- VNC client: port 5901 + +Clipboard sharing between host and Chrome is enabled automatically via +vncconfig and autocutsel. In noVNC, use the clipboard panel in the sidebar. ## Technical Details The wrapper script applies the following configurations to Chrome: -- Disables sandbox for container compatibility -- Optimizes for headless operation -- Suppresses common error messages related to dbus and display -- Sets up proper display configuration +- Automatically adds --no-sandbox when running as root (uid 0) for container compatibility +- Sources runtime config from /etc/chrome-wrapper/config +- Handles display setup based on configured display mode +- Supports channel selection (stable, beta, dev) - Logs command arguments, errors, and exit codes to a log file -- Ensures the --no-sandbox flag is always included + +### Configuration + +Runtime configuration is stored at `/etc/chrome-wrapper/config` and includes: +- CHROME_BINARY: Which Chrome binary to use +- DISPLAY_MODE: headless, xvfb, or vnc +- SCREEN_RESOLUTION: Virtual display resolution +- DEBUGGING_PORT: Remote debugging port +- EXTRA_FLAGS: Additional Chrome flags +- LOCALE: Chrome UI locale ### Logging @@ -200,7 +557,5 @@ Log file location: - Last resort: `/tmp/chrome-wrapper/chrome-wrapper.log` EOF -echo "Google Chrome installation and configuration complete." -echo "NOTE: To use this feature properly, ensure your devcontainer.json includes:" -echo " \"runArgs\": [\"--add-host=host.docker.internal:host-gateway\"]" -echo " This is required for host.docker.internal to resolve correctly." +echo "Google Chrome (${CHANNEL}) installation and configuration complete." +echo "Display mode: ${DISPLAYMODE:-headless}" diff --git a/test/chrome/chrome_channel_test.sh b/test/chrome/chrome_channel_test.sh new file mode 100755 index 0000000..6cb452b --- /dev/null +++ b/test/chrome/chrome_channel_test.sh @@ -0,0 +1,18 @@ +#!/bin/bash +set -e +source dev-container-features-test-lib + +# Basic checks +check "chrome wrapper exists" test -f /usr/local/bin/chrome +check "chrome wrapper is executable" test -x /usr/local/bin/chrome + +# Beta channel checks +check "google-chrome-beta is installed" bash -c "command -v google-chrome-beta" +check "google-chrome-beta shows version" bash -c "google-chrome-beta --version" +check "config file exists" test -f /etc/chrome-wrapper/config +check "config has beta binary" bash -c "grep -q 'CHROME_BINARY=\"google-chrome-beta\"' /etc/chrome-wrapper/config" + +# Wrapper should use the beta binary +check "wrapper uses beta channel" bash -c "chrome --version | grep -i beta" + +reportResults diff --git a/test/chrome/chrome_combined_test.sh b/test/chrome/chrome_combined_test.sh new file mode 100755 index 0000000..1f385fe --- /dev/null +++ b/test/chrome/chrome_combined_test.sh @@ -0,0 +1,22 @@ +#!/bin/bash +set -e +source dev-container-features-test-lib + +# Basic checks +check "chrome wrapper exists" test -f /usr/local/bin/chrome +check "chrome wrapper is executable" test -x /usr/local/bin/chrome +check "chrome version works" bash -c "chrome --version" + +# Combined config checks - debugging port + locale + chromeFlags +check "config file exists" test -f /etc/chrome-wrapper/config +check "config has debugging port 9222" bash -c "grep -q 'DEBUGGING_PORT=\"9222\"' /etc/chrome-wrapper/config" +check "config has locale fr-FR" bash -c "grep -q 'LOCALE=\"fr-FR\"' /etc/chrome-wrapper/config" +check "config has disable-web-security flag" bash -c "grep -q 'disable-web-security' /etc/chrome-wrapper/config" + +# Environment variable checks +check "CHROME_BIN env var is set" bash -c "test -n \"\$CHROME_BIN\"" +check "CHROME_BIN points to wrapper" bash -c "test \"\$CHROME_BIN\" = '/usr/local/bin/chrome'" +check "PUPPETEER_EXECUTABLE_PATH is set" bash -c "test -n \"\$PUPPETEER_EXECUTABLE_PATH\"" +check "PUPPETEER_EXECUTABLE_PATH points to wrapper" bash -c "test \"\$PUPPETEER_EXECUTABLE_PATH\" = '/usr/local/bin/chrome'" + +reportResults diff --git a/test/chrome/chrome_debugging_port_test.sh b/test/chrome/chrome_debugging_port_test.sh new file mode 100755 index 0000000..0c94955 --- /dev/null +++ b/test/chrome/chrome_debugging_port_test.sh @@ -0,0 +1,14 @@ +#!/bin/bash +set -e +source dev-container-features-test-lib + +# Basic checks +check "chrome wrapper exists" test -f /usr/local/bin/chrome +check "chrome wrapper is executable" test -x /usr/local/bin/chrome +check "chrome version works" bash -c "chrome --version" + +# Config file checks +check "config file exists" test -f /etc/chrome-wrapper/config +check "config contains debugging port 9222" bash -c "grep -q 'DEBUGGING_PORT=\"9222\"' /etc/chrome-wrapper/config" + +reportResults diff --git a/test/chrome/chrome_dev_channel_test.sh b/test/chrome/chrome_dev_channel_test.sh new file mode 100755 index 0000000..93fcb2f --- /dev/null +++ b/test/chrome/chrome_dev_channel_test.sh @@ -0,0 +1,18 @@ +#!/bin/bash +set -e +source dev-container-features-test-lib + +# Basic checks +check "chrome wrapper exists" test -f /usr/local/bin/chrome +check "chrome wrapper is executable" test -x /usr/local/bin/chrome + +# Dev channel checks +check "google-chrome-dev is installed" bash -c "command -v google-chrome-dev" +check "google-chrome-dev shows version" bash -c "google-chrome-dev --version" +check "config file exists" test -f /etc/chrome-wrapper/config +check "config has dev binary" bash -c "grep -q 'CHROME_BINARY=\"google-chrome-dev\"' /etc/chrome-wrapper/config" + +# Wrapper should use the dev binary +check "wrapper uses dev channel" bash -c "chrome --version | grep -i dev" + +reportResults diff --git a/test/chrome/chrome_extensions_inline_test.sh b/test/chrome/chrome_extensions_inline_test.sh new file mode 100755 index 0000000..5541f69 --- /dev/null +++ b/test/chrome/chrome_extensions_inline_test.sh @@ -0,0 +1,34 @@ +#!/bin/bash + +# This test file will be executed against the 'chrome_extensions_inline_test' scenario +# to verify that inline per-extension config is properly applied. + +set -e + +# Optional: Import test library bundled with the devcontainer CLI +source dev-container-features-test-lib + +POLICY_FILE="/etc/opt/chrome/policies/managed/extension_settings.json" + +# Basic Chrome checks +check "chrome wrapper script exists" test -f /usr/local/bin/chrome +check "chrome shows version" bash -c "chrome --version" + +# Extension policy file exists +check "extension policy file exists" test -f "$POLICY_FILE" + +# Claude extension (fcoeoabgfenejglbffodgkkbkcdhcgfn) should have global defaults +check "Claude extension has force_installed" bash -c "grep -A5 'fcoeoabgfenejglbffodgkkbkcdhcgfn' $POLICY_FILE | grep -q 'force_installed'" +check "Claude extension has force_pinned" bash -c "grep -A5 'fcoeoabgfenejglbffodgkkbkcdhcgfn' $POLICY_FILE | grep -q 'force_pinned'" +check "Claude extension has incognito allowed" bash -c "grep -A5 'fcoeoabgfenejglbffodgkkbkcdhcgfn' $POLICY_FILE | grep -q '\"incognito\": \"allowed\"'" + +# uBlock Origin (cjpalhdlnbpafiamejdnhcphjbkeiagm) should have inline overrides +check "uBlock Origin has normal_installed" bash -c "grep -A5 'cjpalhdlnbpafiamejdnhcphjbkeiagm' $POLICY_FILE | grep -q 'normal_installed'" +check "uBlock Origin has unpinned" bash -c "grep -A5 'cjpalhdlnbpafiamejdnhcphjbkeiagm' $POLICY_FILE | grep -q 'unpinned'" +check "uBlock Origin has not_allowed incognito" bash -c "grep -A5 'cjpalhdlnbpafiamejdnhcphjbkeiagm' $POLICY_FILE | grep -q 'not_allowed'" + +# Chromium policy should also exist +check "chromium policy file also exists" test -f /etc/chromium/policies/managed/extension_settings.json + +# Report results +reportResults diff --git a/test/chrome/chrome_extensions_test.sh b/test/chrome/chrome_extensions_test.sh new file mode 100755 index 0000000..aea8761 --- /dev/null +++ b/test/chrome/chrome_extensions_test.sh @@ -0,0 +1,36 @@ +#!/bin/bash + +# This test file will be executed against the 'chrome_extensions_test' scenario +# to verify that Chrome extension policies are properly configured. + +set -e + +# Optional: Import test library bundled with the devcontainer CLI +source dev-container-features-test-lib + +# Basic Chrome checks +check "chrome wrapper script exists" test -f /usr/local/bin/chrome +check "chrome wrapper script is executable" test -x /usr/local/bin/chrome +check "chrome shows version" bash -c "chrome --version" + +# Config file checks +check "config file exists" test -f /etc/chrome-wrapper/config +check "config has correct binary" bash -c "grep -q 'CHROME_BINARY=\"google-chrome-stable\"' /etc/chrome-wrapper/config" + +# Extension policy directory checks +check "chrome policy directory exists" test -d /etc/opt/chrome/policies/managed +check "chromium policy directory exists" test -d /etc/chromium/policies/managed + +# Extension policy file checks +check "chrome extension policy file exists" test -f /etc/opt/chrome/policies/managed/extension_settings.json +check "chromium extension policy file exists" test -f /etc/chromium/policies/managed/extension_settings.json + +# Policy content checks - Claude extension +check "policy contains Claude extension ID" bash -c "grep -q 'fcoeoabgfenejglbffodgkkbkcdhcgfn' /etc/opt/chrome/policies/managed/extension_settings.json" +check "policy contains force_installed" bash -c "grep -q 'force_installed' /etc/opt/chrome/policies/managed/extension_settings.json" +check "policy contains force_pinned" bash -c "grep -q 'force_pinned' /etc/opt/chrome/policies/managed/extension_settings.json" +check "policy contains update_url" bash -c "grep -q 'https://clients2.google.com/service/update2/crx' /etc/opt/chrome/policies/managed/extension_settings.json" +check "policy contains incognito allowed" bash -c "grep -q '\"incognito\": \"allowed\"' /etc/opt/chrome/policies/managed/extension_settings.json" + +# Report results +reportResults diff --git a/test/chrome/chrome_flags_test.sh b/test/chrome/chrome_flags_test.sh new file mode 100755 index 0000000..1b8b0fc --- /dev/null +++ b/test/chrome/chrome_flags_test.sh @@ -0,0 +1,15 @@ +#!/bin/bash +set -e +source dev-container-features-test-lib + +# Basic checks +check "chrome wrapper exists" test -f /usr/local/bin/chrome +check "chrome wrapper is executable" test -x /usr/local/bin/chrome +check "chrome version works" bash -c "chrome --version" + +# Extra flags config checks +check "config file exists" test -f /etc/chrome-wrapper/config +check "config has disable-web-security flag" bash -c "grep -q 'disable-web-security' /etc/chrome-wrapper/config" +check "config has allow-running-insecure-content flag" bash -c "grep -q 'allow-running-insecure-content' /etc/chrome-wrapper/config" + +reportResults diff --git a/test/chrome/chrome_locale_test.sh b/test/chrome/chrome_locale_test.sh new file mode 100755 index 0000000..04615cb --- /dev/null +++ b/test/chrome/chrome_locale_test.sh @@ -0,0 +1,14 @@ +#!/bin/bash +set -e +source dev-container-features-test-lib + +# Basic checks +check "chrome wrapper exists" test -f /usr/local/bin/chrome +check "chrome wrapper is executable" test -x /usr/local/bin/chrome +check "chrome version works" bash -c "chrome --version" + +# Locale config checks +check "config file exists" test -f /etc/chrome-wrapper/config +check "config has locale de-DE" bash -c "grep -q 'LOCALE=\"de-DE\"' /etc/chrome-wrapper/config" + +reportResults diff --git a/test/chrome/chrome_resolution_test.sh b/test/chrome/chrome_resolution_test.sh new file mode 100755 index 0000000..52f8702 --- /dev/null +++ b/test/chrome/chrome_resolution_test.sh @@ -0,0 +1,16 @@ +#!/bin/bash +set -e +source dev-container-features-test-lib + +# Basic checks +check "chrome wrapper exists" test -f /usr/local/bin/chrome +check "chrome wrapper is executable" test -x /usr/local/bin/chrome +check "chrome version works" bash -c "chrome --version" + +# Xvfb and resolution checks +check "xvfb is installed" bash -c "command -v Xvfb" +check "config file exists" test -f /etc/chrome-wrapper/config +check "config has xvfb display mode" bash -c "grep -q 'DISPLAY_MODE=\"xvfb\"' /etc/chrome-wrapper/config" +check "config has custom resolution 1280x720x16" bash -c "grep -q 'SCREEN_RESOLUTION=\"1280x720x16\"' /etc/chrome-wrapper/config" + +reportResults diff --git a/test/chrome/chrome_test.sh b/test/chrome/chrome_test.sh index 3dc2ef0..fd46968 100644 --- a/test/chrome/chrome_test.sh +++ b/test/chrome/chrome_test.sh @@ -13,6 +13,8 @@ source dev-container-features-test-lib check "chrome wrapper script exists" test -f /usr/local/bin/chrome check "chrome wrapper script is executable" test -x /usr/local/bin/chrome check "chrome shows version" bash -c "chrome --version" +check "config file exists" test -f /etc/chrome-wrapper/config +check "config has headless display mode" bash -c "grep -q 'DISPLAY_MODE=\"headless\"' /etc/chrome-wrapper/config" # Report results # If any of the checks above exited with a non-zero exit code, the test will fail. diff --git a/test/chrome/chrome_vnc_no_clipboard_test.sh b/test/chrome/chrome_vnc_no_clipboard_test.sh new file mode 100755 index 0000000..a3af648 --- /dev/null +++ b/test/chrome/chrome_vnc_no_clipboard_test.sh @@ -0,0 +1,19 @@ +#!/bin/bash +set -e +source dev-container-features-test-lib + +# Basic Chrome checks +check "chrome wrapper exists" test -f /usr/local/bin/chrome +check "chrome wrapper is executable" test -x /usr/local/bin/chrome +check "chrome version works" bash -c "chrome --version" + +# Config file checks +check "config file exists" test -f /etc/chrome-wrapper/config +check "config has vnc display mode" bash -c "grep -q 'DISPLAY_MODE=\"vnc\"' /etc/chrome-wrapper/config" +check "config has VNC_CLIPBOARD=false" bash -c "grep -q 'VNC_CLIPBOARD=\"false\"' /etc/chrome-wrapper/config" + +# Negative test: clipboard tools should NOT be installed +check "autocutsel is NOT installed" bash -c "! command -v autocutsel" +check "xclip is NOT installed" bash -c "! command -v xclip" + +reportResults diff --git a/test/chrome/chrome_vnc_test.sh b/test/chrome/chrome_vnc_test.sh new file mode 100755 index 0000000..947aeb4 --- /dev/null +++ b/test/chrome/chrome_vnc_test.sh @@ -0,0 +1,26 @@ +#!/bin/bash +set -e +source dev-container-features-test-lib + +# Basic Chrome checks +check "chrome wrapper exists" test -f /usr/local/bin/chrome +check "chrome wrapper is executable" test -x /usr/local/bin/chrome +check "chrome version works" bash -c "chrome --version" + +# Config file checks +check "config file exists" test -f /etc/chrome-wrapper/config +check "config has vnc display mode" bash -c "grep -q 'DISPLAY_MODE=\"vnc\"' /etc/chrome-wrapper/config" + +# Desktop-lite integration checks +check "desktop-lite init script exists" test -f /usr/local/share/desktop-init.sh + +# Xvfb should NOT be installed (vnc mode relies on desktop-lite) +check "xvfb is not installed by chrome feature" bash -c "! dpkg -l xvfb 2>/dev/null | grep -q '^ii'" + +# VNC clipboard support checks +check "autocutsel is installed" bash -c "command -v autocutsel" +check "xclip is installed" bash -c "command -v xclip" +check "vncconfig is available" bash -c "command -v vncconfig" +check "config has VNC_CLIPBOARD=true" bash -c "grep -q 'VNC_CLIPBOARD=\"true\"' /etc/chrome-wrapper/config" + +reportResults diff --git a/test/chrome/chrome_xvfb_test.sh b/test/chrome/chrome_xvfb_test.sh new file mode 100755 index 0000000..553a059 --- /dev/null +++ b/test/chrome/chrome_xvfb_test.sh @@ -0,0 +1,15 @@ +#!/bin/bash +set -e +source dev-container-features-test-lib + +check "chrome wrapper exists" test -f /usr/local/bin/chrome +check "chrome wrapper is executable" test -x /usr/local/bin/chrome +check "chrome version works" bash -c "chrome --version" +check "xvfb is installed" bash -c "command -v Xvfb" +check "xdotool is installed" bash -c "command -v xdotool" +check "wmctrl is installed" bash -c "command -v wmctrl" +check "config file exists" test -f /etc/chrome-wrapper/config +check "config has xvfb display mode" bash -c "grep -q 'DISPLAY_MODE=\"xvfb\"' /etc/chrome-wrapper/config" +check "chrome runs with virtual display" bash -c "unset DISPLAY && chrome --headless=new --dump-dom https://example.com 2>/dev/null | head -1" + +reportResults diff --git a/test/chrome/scenarios.json b/test/chrome/scenarios.json index b177651..83630e6 100644 --- a/test/chrome/scenarios.json +++ b/test/chrome/scenarios.json @@ -4,5 +4,107 @@ "features": { "chrome": {} } + }, + "chrome_channel_test": { + "image": "mcr.microsoft.com/devcontainers/base:ubuntu", + "features": { + "chrome": { + "channel": "beta" + } + } + }, + "chrome_extensions_test": { + "image": "mcr.microsoft.com/devcontainers/base:ubuntu", + "features": { + "chrome": { + "extensions": "{\"fcoeoabgfenejglbffodgkkbkcdhcgfn\": {}}" + } + } + }, + "chrome_extensions_inline_test": { + "image": "mcr.microsoft.com/devcontainers/base:ubuntu", + "features": { + "chrome": { + "extensions": "{\"fcoeoabgfenejglbffodgkkbkcdhcgfn\": {}, \"cjpalhdlnbpafiamejdnhcphjbkeiagm\": {\"mode\": \"normal_installed\", \"pin\": false, \"incognito\": \"not_allowed\"}}" + } + } + }, + "chrome_xvfb_test": { + "image": "mcr.microsoft.com/devcontainers/base:ubuntu", + "features": { + "chrome": { + "displayMode": "xvfb" + } + } + }, + "chrome_vnc_test": { + "image": "mcr.microsoft.com/devcontainers/base:ubuntu", + "features": { + "ghcr.io/devcontainers/features/desktop-lite:1": {}, + "chrome": { + "displayMode": "vnc" + } + } + }, + "chrome_debugging_port_test": { + "image": "mcr.microsoft.com/devcontainers/base:ubuntu", + "features": { + "chrome": { + "debuggingPort": "9222" + } + } + }, + "chrome_locale_test": { + "image": "mcr.microsoft.com/devcontainers/base:ubuntu", + "features": { + "chrome": { + "locale": "de-DE" + } + } + }, + "chrome_flags_test": { + "image": "mcr.microsoft.com/devcontainers/base:ubuntu", + "features": { + "chrome": { + "chromeFlags": "--disable-web-security --allow-running-insecure-content" + } + } + }, + "chrome_resolution_test": { + "image": "mcr.microsoft.com/devcontainers/base:ubuntu", + "features": { + "chrome": { + "displayMode": "xvfb", + "screenResolution": "1280x720x16" + } + } + }, + "chrome_dev_channel_test": { + "image": "mcr.microsoft.com/devcontainers/base:ubuntu", + "features": { + "chrome": { + "channel": "dev" + } + } + }, + "chrome_combined_test": { + "image": "mcr.microsoft.com/devcontainers/base:ubuntu", + "features": { + "chrome": { + "debuggingPort": "9222", + "locale": "fr-FR", + "chromeFlags": "--disable-web-security" + } + } + }, + "chrome_vnc_no_clipboard_test": { + "image": "mcr.microsoft.com/devcontainers/base:ubuntu", + "features": { + "ghcr.io/devcontainers/features/desktop-lite:1": {}, + "chrome": { + "displayMode": "vnc", + "vncClipboard": false + } + } } -} \ No newline at end of file +} From c87bf6203ff07a455b64e9f59b04177700f87f40 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 8 Feb 2026 22:31:39 +0000 Subject: [PATCH 12/17] Automated documentation update [skip ci] --- src/claude-code/README.md | 27 ++++++++---- src/gitlab/README.md | 70 +++++++++++++++++++++++++++++++ src/mcp-language-server/README.md | 2 +- 3 files changed, 90 insertions(+), 9 deletions(-) create mode 100644 src/gitlab/README.md diff --git a/src/claude-code/README.md b/src/claude-code/README.md index b02125b..d99e093 100644 --- a/src/claude-code/README.md +++ b/src/claude-code/README.md @@ -7,7 +7,7 @@ Installs Claude Code CLI for AI-powered development assistance ```json "features": { - "ghcr.io/iot-rocket/devcontainer-features/claude-code:1": {} + "ghcr.io/iot-rocket/devcontainer-features/claude-code:2": {} } ``` @@ -15,7 +15,7 @@ Installs Claude Code CLI for AI-powered development assistance | Options Id | Description | Type | Default Value | |-----|-----|-----|-----| - +| version | Version to install. Use 'latest', 'stable', or a specific semver (e.g. '1.0.58'). | string | latest | ## Customizations @@ -25,17 +25,28 @@ Installs Claude Code CLI for AI-powered development assistance # Claude Code Feature -This feature installs [Claude Code](https://www.anthropic.com/claude-code), Anthropic's official CLI for AI-powered development assistance. +This feature installs [Claude Code](https://www.anthropic.com/claude-code), Anthropic's official CLI for AI-powered development assistance, using the [native installer](https://code.claude.com/docs/en/setup). + +No Node.js dependency is required. The native installer downloads a standalone binary. + +## Version Option + +By default, the `latest` release channel is installed. You can also specify: + +- `"stable"` — a release channel that is typically about one week behind latest, skipping releases with major regressions +- A specific semver version (e.g. `"1.0.58"`) + +The channel chosen at install time becomes the default for auto-updates. -## Requirements +## Auto-Updates -- Node.js +The native binary automatically updates in the background. Update checks are performed on startup and periodically while running. To disable auto-updates, set the `DISABLE_AUTOUPDATER=1` environment variable. ## Manual config for `devcontainer.json` -Mount the local `~/.claude/` directory into the Dev Contaier. +Mount the local `~/.claude/` directory into the Dev Container. Add the following mount to the `devcontainer.json` file. -Replace `vscode` with the actual name of your user (see `remoteUser` property) +Replace `vscode` with the actual name of your user (see `remoteUser` property). ```json "mounts": [ @@ -51,7 +62,7 @@ Replace `vscode` with the actual name of your user (see `remoteUser` property) After installation, run `claude` in your project directory to get started. -For detailed documentation, see the [Claude Code documentation](https://docs.anthropic.com/en/docs/claude-code/overview). +For detailed documentation, see the [Claude Code documentation](https://code.claude.com/docs/en/setup). --- diff --git a/src/gitlab/README.md b/src/gitlab/README.md new file mode 100644 index 0000000..38bdfc6 --- /dev/null +++ b/src/gitlab/README.md @@ -0,0 +1,70 @@ + +# GitLab CLI & Workflow (gitlab) + +Installs glab CLI and GitLab Workflow VS Code extension for GitLab integration + +## Example Usage + +```json +"features": { + "ghcr.io/iot-rocket/devcontainer-features/gitlab:1": {} +} +``` + +## Options + +| Options Id | Description | Type | Default Value | +|-----|-----|-----|-----| + + +## Customizations + +### VS Code Extensions + +- `GitLab.gitlab-workflow` + +## What This Feature Installs + +1. **glab CLI** - The official GitLab command-line tool (latest version) +2. **GitLab Workflow VS Code extension** - GitLab integration for VS Code (`GitLab.gitlab-workflow`) + +## Authentication + +After the container starts, authenticate with your GitLab instance: + +```bash +# Interactive login (gitlab.com) +glab auth login + +# Login to a self-hosted instance +glab auth login --hostname gitlab.example.com +``` + +## Basic Usage + +```bash +# Clone a repository +glab repo clone owner/repo + +# Create a merge request +glab mr create --fill + +# List open issues +glab issue list + +# View CI/CD pipeline status +glab ci status + +# Browse the repository in the browser +glab repo view --web +``` + +## Further Reading + +- [glab CLI documentation](https://gitlab.com/gitlab-org/cli/-/blob/main/README.md) +- [GitLab Workflow VS Code extension](https://marketplace.visualstudio.com/items?itemName=GitLab.gitlab-workflow) + + +--- + +_Note: This file was auto-generated from the [devcontainer-feature.json](https://github.com/iot-rocket/devcontainer-features/blob/main/src/gitlab/devcontainer-feature.json). Add additional notes to a `NOTES.md`._ diff --git a/src/mcp-language-server/README.md b/src/mcp-language-server/README.md index f23f5c5..ff6cfc2 100644 --- a/src/mcp-language-server/README.md +++ b/src/mcp-language-server/README.md @@ -23,7 +23,7 @@ This feature installs [MCP Language Server](https://github.com/isaacphi/mcp-lang ## Requirements -- Go +- Go (automatically installed if not available) - A language server for your programming language (installed separately) ## Supported Language Servers From 9d03ccf3877e2205c18054ad04c6bfdb37a23ac9 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 16 Feb 2026 02:50:19 +0000 Subject: [PATCH 13/17] Automated documentation update [skip ci] --- src/chrome/README.md | 272 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 253 insertions(+), 19 deletions(-) diff --git a/src/chrome/README.md b/src/chrome/README.md index c15d495..827f14b 100644 --- a/src/chrome/README.md +++ b/src/chrome/README.md @@ -17,19 +17,12 @@ Installs Google Chrome with container-specific configurations and wrapper script Running Chrome in a container environment can be challenging due to sandbox, display, and security constraints. This feature installs: -1. Google Chrome stable version +1. Google Chrome (stable, beta, or dev channel) 2. A specialized wrapper script at `/usr/local/bin/chrome` that configures Chrome to work properly in containers 3. The necessary dependencies for Chrome to run in a container +4. `xdotool` for X11 automation (simulating keyboard/mouse input, managing windows) +5. `wmctrl` for X Window manager control (listing, moving, resizing windows) -## Required Configuration - -**Important**: Add the required run argument to your `devcontainer.json`: - -```json -"runArgs": ["--add-host=host.docker.internal:host-gateway"] -``` - -> The `--add-host=host.docker.internal:host-gateway` run argument is **required** for the Chrome wrapper to properly connect to the host machine for display forwarding and X11 connections. ## Using the Chrome Wrapper The key component is the Chrome wrapper script at `/usr/local/bin/chrome`. **Always use this wrapper** instead of calling `google-chrome-stable` directly. @@ -38,16 +31,16 @@ The key component is the Chrome wrapper script at `/usr/local/bin/chrome`. **Alw **Cline**: Automatically configured via VS Code settings. -**Playwright**: +**Puppeteer**: Auto-discovered via the `PUPPETEER_EXECUTABLE_PATH` environment variable set by this feature. No configuration needed: ```javascript -const browser = await playwright.chromium.launch({ - executablePath: '/usr/local/bin/chrome', -}) +const browser = await puppeteer.launch() ``` -**Puppeteer**: +**Karma / Angular CLI**: Auto-discovered via the `CHROME_BIN` environment variable. No configuration needed. + +**Playwright**: Does not support auto-discovery via environment variables. You must pass `executablePath` explicitly: ```javascript -const browser = await puppeteer.launch({ +const browser = await playwright.chromium.launch({ executablePath: '/usr/local/bin/chrome', }) ``` @@ -70,17 +63,257 @@ chrome --headless=new --screenshot=/tmp/screenshot.png https://example.com chrome --headless=new --print-to-pdf=/tmp/output.pdf https://example.com ``` +## Shared Memory for Heavy Workloads + +Docker containers default to 64MB of shared memory (`/dev/shm`), which can cause Chrome to crash or produce blank screenshots under heavy workloads (many tabs, large pages, parallel tests). Increase the shared memory size via `runArgs` in your `devcontainer.json`: + +```json +{ + "runArgs": ["--shm-size=2g"] +} +``` + +Alternatively, the wrapper script already passes `--disable-dev-shm-usage` to Chrome, which moves shared memory to `/tmp`. This works for most cases but may be slower under extreme load. + +## Chrome Release Channels + +Select which Chrome release channel to install using the `channel` option: + +```json +{ + "features": { + "ghcr.io/iot-rocket/devcontainer-features/chrome": { + "channel": "beta" + } + } +} +``` + +Available channels: `stable` (default), `beta`, `dev`. + +## Display Modes + +The `displayMode` option controls how Chrome handles display output. Three modes are available: + +### headless (default) + +No display server is installed. Chrome runs with `--headless=new` when no DISPLAY is set. Best for CI/CD, automated testing, and headless scraping. + +```json +{ + "features": { + "ghcr.io/iot-rocket/devcontainer-features/chrome": {} + } +} +``` + +### xvfb (Virtual Framebuffer) + +Installs Xvfb and auto-starts a virtual display on `:99` when no DISPLAY is set. Required for extensions that need a display server, and for automated testing that requires rendering. + +```json +{ + "features": { + "ghcr.io/iot-rocket/devcontainer-features/chrome": { + "displayMode": "xvfb", + "screenResolution": "1920x1080x24" + } + } +} +``` + +> **Note**: The Xvfb display is fixed at `:99`. If you need a different display number, set the `DISPLAY` environment variable before calling `chrome` — the wrapper will use it instead of starting its own Xvfb instance. + +### vnc (Desktop-Lite Integration) + +Delegates to the `ghcr.io/devcontainers/features/desktop-lite` feature for a full desktop environment with VNC access. Chrome runs in GUI mode on the desktop-lite display. View Chrome via: +- **noVNC** (browser): `http://localhost:6080` +- **VNC client**: port `5901` + +```json +{ + "features": { + "ghcr.io/devcontainers/features/desktop-lite:1": {}, + "ghcr.io/iot-rocket/devcontainer-features/chrome": { + "displayMode": "vnc" + } + } +} +``` + +> **Note**: VNC mode requires `desktop-lite` to be included in your devcontainer.json features. VNC password is configured via desktop-lite's `password` option (default: `vscode`). + +> **Important**: `desktop-lite` defaults to 16-bit color depth (`1440x768x16`). noVNC cursor rendering requires 24-bit depth. Override the resolution to use 24-bit: +> ```json +> "ghcr.io/devcontainers/features/desktop-lite:1": { +> "resolution": "1920x1080x24" +> } +> ``` + +### VNC Clipboard Sharing + +Clipboard copy/paste between the host and Chrome inside a VNC session is enabled automatically when `displayMode=vnc`. The clipboard bridge uses three components: + +- **vncconfig**: Bridges the VNC protocol clipboard with the X11 cutbuffer +- **autocutsel**: Syncs the X11 CLIPBOARD and PRIMARY selections with the cutbuffer +- **xclip**: Provides command-line clipboard access + +Together, these create a full clipboard pipeline: + +``` +Host clipboard ↔ VNC protocol ↔ vncconfig ↔ X11 cutbuffer ↔ autocutsel ↔ X11 CLIPBOARD ↔ Chrome +``` + +#### Using clipboard with noVNC (browser) + +1. Open the noVNC sidebar by clicking the arrow on the left edge of the screen +2. Open the **Clipboard** panel +3. Paste text into the clipboard panel — it becomes available to Chrome via Ctrl+V +4. Text copied in Chrome via Ctrl+C appears in the clipboard panel for copying to the host + +#### Using clipboard with a native VNC client + +Clipboard sharing is transparent with native VNC clients (e.g., TigerVNC Viewer, RealVNC). Ctrl+C/Ctrl+V work between the host and Chrome. + +#### Command-line clipboard + +`xclip` is installed for scripting clipboard access: + +```bash +# Copy to clipboard +echo "text" | xclip -selection clipboard + +# Paste from clipboard +xclip -selection clipboard -o +``` + +## Extension Management + +Chrome extensions can be pre-installed via managed enterprise policies using the `extensions` option. Extensions are configured through Chrome's `ExtensionSettings` policy, which handles installation mode, toolbar pinning, and incognito access. + +The `extensions` option takes a JSON object where keys are Chrome Web Store extension IDs and values are settings objects. An empty object `{}` applies the hardcoded defaults. + +### Default Settings + +| Key | Values | Default | +|-----|--------|---------| +| `mode` | `force_installed`, `normal_installed` | `force_installed` | +| `pin` | `true`, `false` | `true` | +| `incognito` | `force_allowed`, `allowed`, `not_allowed` | `allowed` | + +### Basic Usage + +Install one or more extensions with default settings: + +```json +{ + "features": { + "ghcr.io/iot-rocket/devcontainer-features/chrome": { + "extensions": "{\"fcoeoabgfenejglbffodgkkbkcdhcgfn\": {}, \"cjpalhdlnbpafiamejdnhcphjbkeiagm\": {}}" + } + } +} +``` + +### Per-Extension Settings + +Override settings for individual extensions by providing values in the settings object: + +```json +{ + "features": { + "ghcr.io/iot-rocket/devcontainer-features/chrome": { + "extensions": "{\"fcoeoabgfenejglbffodgkkbkcdhcgfn\": {}, \"cjpalhdlnbpafiamejdnhcphjbkeiagm\": {\"mode\": \"normal_installed\", \"pin\": false, \"incognito\": \"not_allowed\"}}" + } + } +} +``` + +In this example: +- `fcoeoabgfenejglbffodgkkbkcdhcgfn` (Claude) uses the default settings (force installed, pinned, incognito allowed) +- `cjpalhdlnbpafiamejdnhcphjbkeiagm` (uBlock Origin) uses custom settings (normal install, unpinned, no incognito) + +### Technical Details + +- Extension policies are written to `/etc/opt/chrome/policies/managed/extension_settings.json` (Chrome) and `/etc/chromium/policies/managed/extension_settings.json` (Chromium) +- The wrapper script automatically detects managed policy files at runtime: if policy files exist, extensions are enabled; otherwise `--disable-extensions` is used +- Extensions are downloaded from the Chrome Web Store update URL (`https://clients2.google.com/service/update2/crx`) + +## Additional Options + +### Remote Debugging Port + +Set a default remote debugging port that's always passed to Chrome: + +```json +{ + "features": { + "ghcr.io/iot-rocket/devcontainer-features/chrome": { + "debuggingPort": "9222" + } + } +} +``` + +### Extra Chrome Flags + +Pass additional Chrome flags to every invocation: + +```json +{ + "features": { + "ghcr.io/iot-rocket/devcontainer-features/chrome": { + "chromeFlags": "--disable-web-security --allow-running-insecure-content" + } + } +} +``` + +### Fonts + +Install additional font packages for proper rendering of international text, emoji, and symbols. This prevents tofu (missing glyph boxes) in screenshots and PDFs: + +```json +{ + "features": { + "ghcr.io/iot-rocket/devcontainer-features/chrome": { + "fonts": true + } + } +} +``` + +Installs: `fonts-noto-cjk` (Chinese/Japanese/Korean), `fonts-noto-color-emoji` (emoji), `fonts-liberation` (metric-compatible with Arial/Times/Courier), `fonts-dejavu-core` (extended Latin/Greek/Cyrillic). + +### Locale + +Set the Chrome UI language: + +```json +{ + "features": { + "ghcr.io/iot-rocket/devcontainer-features/chrome": { + "locale": "de-DE" + } + } +} +``` + ## Technical Details The wrapper script handles common issues with running Chrome in containers by: -- Disabling the sandbox for container compatibility -- Setting proper display configurations +- Automatically disabling the sandbox when running as root for container compatibility +- Setting proper display configurations based on display mode - Suppressing error messages for cleaner output - Optimizing for both headless and interactive use - Providing special handling for debugging/Puppeteer modes - Logging command arguments, errors, and exit codes -- Ensuring the --no-sandbox flag is always included +- Sourcing runtime config from `/etc/chrome-wrapper/config` + +### Runtime Configuration + +The install script writes a configuration file at `/etc/chrome-wrapper/config` that the wrapper sources at runtime. This contains the Chrome binary path, display mode, screen resolution, debugging port, extra flags, and locale settings. ### Logging @@ -98,6 +331,7 @@ Log files are stored in one of the following locations (in order of preference): For more detailed documentation, see `/usr/local/share/chrome-wrapper/README.md` after installation. + --- _Note: This file was auto-generated from the [devcontainer-feature.json](https://github.com/iot-rocket/devcontainer-features/blob/main/src/chrome/devcontainer-feature.json). Add additional notes to a `NOTES.md`._ From 3fe5d35b7417c12a3af3ef5b8c0b157f3194fb14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20Meichelb=C3=B6ck?= Date: Mon, 16 Feb 2026 03:54:53 +0100 Subject: [PATCH 14/17] feat(chrome): add forgotten devcontainer-feature.json --- src/chrome/devcontainer-feature.json | 57 +++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 2 deletions(-) diff --git a/src/chrome/devcontainer-feature.json b/src/chrome/devcontainer-feature.json index 899a77b..85b915b 100644 --- a/src/chrome/devcontainer-feature.json +++ b/src/chrome/devcontainer-feature.json @@ -1,8 +1,61 @@ { "id": "chrome", - "version": "1.0.2", + "version": "2.3.0", "name": "Google Chrome for Containers", "description": "Installs Google Chrome with container-specific configurations and wrapper script", + "options": { + "channel": { + "type": "string", + "enum": ["stable", "beta", "dev"], + "default": "stable", + "description": "Chrome release channel to install" + }, + "extensions": { + "type": "string", + "default": "", + "description": "JSON object mapping extension IDs to settings: {\"extId\": {}, \"extId2\": {\"mode\": \"normal_installed\", \"pin\": false, \"incognito\": \"not_allowed\"}}. Empty object {} uses defaults: mode=force_installed, pin=true, incognito=allowed." + }, + "displayMode": { + "type": "string", + "enum": ["headless", "xvfb", "vnc"], + "default": "headless", + "description": "Display server: headless (no display), xvfb (virtual framebuffer :99), vnc (uses desktop-lite feature for browser-based VNC viewing)" + }, + "screenResolution": { + "type": "string", + "default": "1920x1080x24", + "description": "Virtual display resolution for xvfb mode (WIDTHxHEIGHTxDEPTH). VNC mode uses desktop-lite's resolution." + }, + "debuggingPort": { + "type": "string", + "default": "", + "description": "Default remote debugging port (e.g. 9222). Empty = not set by default." + }, + "chromeFlags": { + "type": "string", + "default": "", + "description": "Space-separated additional Chrome flags always passed to the browser" + }, + "locale": { + "type": "string", + "default": "", + "description": "Chrome locale/language (e.g. en-US, de-DE). Empty = system default." + }, + "vncClipboard": { + "type": "boolean", + "default": true, + "description": "Enable clipboard sharing between host and Chrome in VNC mode (installs autocutsel and xclip). Only applies when displayMode=vnc." + }, + "fonts": { + "type": "boolean", + "default": false, + "description": "Install additional font packages (Noto CJK, Noto Color Emoji, Liberation, DejaVu) for proper rendering of international text, emoji, and symbols in screenshots and PDFs." + } + }, + "containerEnv": { + "CHROME_BIN": "/usr/local/bin/chrome", + "PUPPETEER_EXECUTABLE_PATH": "/usr/local/bin/chrome" + }, "customizations": { "vscode": { "settings": { @@ -10,5 +63,5 @@ } } }, - "installsAfter": [] + "installsAfter": ["ghcr.io/devcontainers/features/desktop-lite"] } From d40ec2240e674e4d5b1a172ca43eded096afa18e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 16 Feb 2026 03:05:42 +0000 Subject: [PATCH 15/17] Automated documentation update [skip ci] --- src/chrome/README.md | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/chrome/README.md b/src/chrome/README.md index 827f14b..06d0aa4 100644 --- a/src/chrome/README.md +++ b/src/chrome/README.md @@ -7,11 +7,23 @@ Installs Google Chrome with container-specific configurations and wrapper script ```json "features": { - "ghcr.io/iot-rocket/devcontainer-features/chrome:1": {} + "ghcr.io/iot-rocket/devcontainer-features/chrome:2": {} } ``` - +## Options + +| Options Id | Description | Type | Default Value | +|-----|-----|-----|-----| +| channel | Chrome release channel to install | string | stable | +| extensions | JSON object mapping extension IDs to settings: {"extId": {}, "extId2": {"mode": "normal_installed", "pin": false, "incognito": "not_allowed"}}. Empty object {} uses defaults: mode=force_installed, pin=true, incognito=allowed. | string | - | +| displayMode | Display server: headless (no display), xvfb (virtual framebuffer :99), vnc (uses desktop-lite feature for browser-based VNC viewing) | string | headless | +| screenResolution | Virtual display resolution for xvfb mode (WIDTHxHEIGHTxDEPTH). VNC mode uses desktop-lite's resolution. | string | 1920x1080x24 | +| debuggingPort | Default remote debugging port (e.g. 9222). Empty = not set by default. | string | - | +| chromeFlags | Space-separated additional Chrome flags always passed to the browser | string | - | +| locale | Chrome locale/language (e.g. en-US, de-DE). Empty = system default. | string | - | +| vncClipboard | Enable clipboard sharing between host and Chrome in VNC mode (installs autocutsel and xclip). Only applies when displayMode=vnc. | boolean | true | +| fonts | Install additional font packages (Noto CJK, Noto Color Emoji, Liberation, DejaVu) for proper rendering of international text, emoji, and symbols in screenshots and PDFs. | boolean | false | ## What This Feature Installs From 71dab6e765e11a3d5ac4492402294563cac6ef09 Mon Sep 17 00:00:00 2001 From: "Tobias L. Maier" Date: Mon, 16 Feb 2026 20:14:46 +0000 Subject: [PATCH 16/17] fix: replace iot-rocket references with tmaier in auto-generated docs --- src/adr-tools/README.md | 4 ++-- src/chrome/NOTES.md | 20 ++++++++++---------- src/chrome/README.md | 24 ++++++++++++------------ src/claude-code/README.md | 4 ++-- src/color/README.md | 4 ++-- src/gitlab/README.md | 4 ++-- src/hello/README.md | 4 ++-- src/imagemagick/README.md | 4 ++-- src/mc/README.md | 4 ++-- src/mcp-language-server/README.md | 4 ++-- src/yek/README.md | 4 ++-- src/yq/README.md | 4 ++-- 12 files changed, 42 insertions(+), 42 deletions(-) diff --git a/src/adr-tools/README.md b/src/adr-tools/README.md index 2f1ac23..a79bf61 100644 --- a/src/adr-tools/README.md +++ b/src/adr-tools/README.md @@ -7,7 +7,7 @@ Installs ADR Tools for managing Architecture Decision Records ```json "features": { - "ghcr.io/iot-rocket/devcontainer-features/adr-tools:1": {} + "ghcr.io/tmaier/devcontainer-features/adr-tools:1": {} } ``` @@ -25,4 +25,4 @@ For usage instructions and documentation, see the [official ADR Tools repository --- -_Note: This file was auto-generated from the [devcontainer-feature.json](https://github.com/iot-rocket/devcontainer-features/blob/main/src/adr-tools/devcontainer-feature.json). Add additional notes to a `NOTES.md`._ +_Note: This file was auto-generated from the [devcontainer-feature.json](https://github.com/tmaier/devcontainer-features/blob/main/src/adr-tools/devcontainer-feature.json). Add additional notes to a `NOTES.md`._ diff --git a/src/chrome/NOTES.md b/src/chrome/NOTES.md index 184c114..61b4fec 100644 --- a/src/chrome/NOTES.md +++ b/src/chrome/NOTES.md @@ -67,7 +67,7 @@ Select which Chrome release channel to install using the `channel` option: ```json { "features": { - "ghcr.io/iot-rocket/devcontainer-features/chrome": { + "ghcr.io/tmaier/devcontainer-features/chrome": { "channel": "beta" } } @@ -87,7 +87,7 @@ No display server is installed. Chrome runs with `--headless=new` when no DISPLA ```json { "features": { - "ghcr.io/iot-rocket/devcontainer-features/chrome": {} + "ghcr.io/tmaier/devcontainer-features/chrome": {} } } ``` @@ -99,7 +99,7 @@ Installs Xvfb and auto-starts a virtual display on `:99` when no DISPLAY is set. ```json { "features": { - "ghcr.io/iot-rocket/devcontainer-features/chrome": { + "ghcr.io/tmaier/devcontainer-features/chrome": { "displayMode": "xvfb", "screenResolution": "1920x1080x24" } @@ -119,7 +119,7 @@ Delegates to the `ghcr.io/devcontainers/features/desktop-lite` feature for a ful { "features": { "ghcr.io/devcontainers/features/desktop-lite:1": {}, - "ghcr.io/iot-rocket/devcontainer-features/chrome": { + "ghcr.io/tmaier/devcontainer-features/chrome": { "displayMode": "vnc" } } @@ -193,7 +193,7 @@ Install one or more extensions with default settings: ```json { "features": { - "ghcr.io/iot-rocket/devcontainer-features/chrome": { + "ghcr.io/tmaier/devcontainer-features/chrome": { "extensions": "{\"fcoeoabgfenejglbffodgkkbkcdhcgfn\": {}, \"cjpalhdlnbpafiamejdnhcphjbkeiagm\": {}}" } } @@ -207,7 +207,7 @@ Override settings for individual extensions by providing values in the settings ```json { "features": { - "ghcr.io/iot-rocket/devcontainer-features/chrome": { + "ghcr.io/tmaier/devcontainer-features/chrome": { "extensions": "{\"fcoeoabgfenejglbffodgkkbkcdhcgfn\": {}, \"cjpalhdlnbpafiamejdnhcphjbkeiagm\": {\"mode\": \"normal_installed\", \"pin\": false, \"incognito\": \"not_allowed\"}}" } } @@ -233,7 +233,7 @@ Set a default remote debugging port that's always passed to Chrome: ```json { "features": { - "ghcr.io/iot-rocket/devcontainer-features/chrome": { + "ghcr.io/tmaier/devcontainer-features/chrome": { "debuggingPort": "9222" } } @@ -247,7 +247,7 @@ Pass additional Chrome flags to every invocation: ```json { "features": { - "ghcr.io/iot-rocket/devcontainer-features/chrome": { + "ghcr.io/tmaier/devcontainer-features/chrome": { "chromeFlags": "--disable-web-security --allow-running-insecure-content" } } @@ -261,7 +261,7 @@ Install additional font packages for proper rendering of international text, emo ```json { "features": { - "ghcr.io/iot-rocket/devcontainer-features/chrome": { + "ghcr.io/tmaier/devcontainer-features/chrome": { "fonts": true } } @@ -277,7 +277,7 @@ Set the Chrome UI language: ```json { "features": { - "ghcr.io/iot-rocket/devcontainer-features/chrome": { + "ghcr.io/tmaier/devcontainer-features/chrome": { "locale": "de-DE" } } diff --git a/src/chrome/README.md b/src/chrome/README.md index 06d0aa4..02fd6a8 100644 --- a/src/chrome/README.md +++ b/src/chrome/README.md @@ -7,7 +7,7 @@ Installs Google Chrome with container-specific configurations and wrapper script ```json "features": { - "ghcr.io/iot-rocket/devcontainer-features/chrome:2": {} + "ghcr.io/tmaier/devcontainer-features/chrome:2": {} } ``` @@ -94,7 +94,7 @@ Select which Chrome release channel to install using the `channel` option: ```json { "features": { - "ghcr.io/iot-rocket/devcontainer-features/chrome": { + "ghcr.io/tmaier/devcontainer-features/chrome": { "channel": "beta" } } @@ -114,7 +114,7 @@ No display server is installed. Chrome runs with `--headless=new` when no DISPLA ```json { "features": { - "ghcr.io/iot-rocket/devcontainer-features/chrome": {} + "ghcr.io/tmaier/devcontainer-features/chrome": {} } } ``` @@ -126,7 +126,7 @@ Installs Xvfb and auto-starts a virtual display on `:99` when no DISPLAY is set. ```json { "features": { - "ghcr.io/iot-rocket/devcontainer-features/chrome": { + "ghcr.io/tmaier/devcontainer-features/chrome": { "displayMode": "xvfb", "screenResolution": "1920x1080x24" } @@ -146,7 +146,7 @@ Delegates to the `ghcr.io/devcontainers/features/desktop-lite` feature for a ful { "features": { "ghcr.io/devcontainers/features/desktop-lite:1": {}, - "ghcr.io/iot-rocket/devcontainer-features/chrome": { + "ghcr.io/tmaier/devcontainer-features/chrome": { "displayMode": "vnc" } } @@ -220,7 +220,7 @@ Install one or more extensions with default settings: ```json { "features": { - "ghcr.io/iot-rocket/devcontainer-features/chrome": { + "ghcr.io/tmaier/devcontainer-features/chrome": { "extensions": "{\"fcoeoabgfenejglbffodgkkbkcdhcgfn\": {}, \"cjpalhdlnbpafiamejdnhcphjbkeiagm\": {}}" } } @@ -234,7 +234,7 @@ Override settings for individual extensions by providing values in the settings ```json { "features": { - "ghcr.io/iot-rocket/devcontainer-features/chrome": { + "ghcr.io/tmaier/devcontainer-features/chrome": { "extensions": "{\"fcoeoabgfenejglbffodgkkbkcdhcgfn\": {}, \"cjpalhdlnbpafiamejdnhcphjbkeiagm\": {\"mode\": \"normal_installed\", \"pin\": false, \"incognito\": \"not_allowed\"}}" } } @@ -260,7 +260,7 @@ Set a default remote debugging port that's always passed to Chrome: ```json { "features": { - "ghcr.io/iot-rocket/devcontainer-features/chrome": { + "ghcr.io/tmaier/devcontainer-features/chrome": { "debuggingPort": "9222" } } @@ -274,7 +274,7 @@ Pass additional Chrome flags to every invocation: ```json { "features": { - "ghcr.io/iot-rocket/devcontainer-features/chrome": { + "ghcr.io/tmaier/devcontainer-features/chrome": { "chromeFlags": "--disable-web-security --allow-running-insecure-content" } } @@ -288,7 +288,7 @@ Install additional font packages for proper rendering of international text, emo ```json { "features": { - "ghcr.io/iot-rocket/devcontainer-features/chrome": { + "ghcr.io/tmaier/devcontainer-features/chrome": { "fonts": true } } @@ -304,7 +304,7 @@ Set the Chrome UI language: ```json { "features": { - "ghcr.io/iot-rocket/devcontainer-features/chrome": { + "ghcr.io/tmaier/devcontainer-features/chrome": { "locale": "de-DE" } } @@ -346,4 +346,4 @@ For more detailed documentation, see `/usr/local/share/chrome-wrapper/README.md` --- -_Note: This file was auto-generated from the [devcontainer-feature.json](https://github.com/iot-rocket/devcontainer-features/blob/main/src/chrome/devcontainer-feature.json). Add additional notes to a `NOTES.md`._ +_Note: This file was auto-generated from the [devcontainer-feature.json](https://github.com/tmaier/devcontainer-features/blob/main/src/chrome/devcontainer-feature.json). Add additional notes to a `NOTES.md`._ diff --git a/src/claude-code/README.md b/src/claude-code/README.md index d99e093..7559aa9 100644 --- a/src/claude-code/README.md +++ b/src/claude-code/README.md @@ -7,7 +7,7 @@ Installs Claude Code CLI for AI-powered development assistance ```json "features": { - "ghcr.io/iot-rocket/devcontainer-features/claude-code:2": {} + "ghcr.io/tmaier/devcontainer-features/claude-code:2": {} } ``` @@ -67,4 +67,4 @@ For detailed documentation, see the [Claude Code documentation](https://code.cla --- -_Note: This file was auto-generated from the [devcontainer-feature.json](https://github.com/iot-rocket/devcontainer-features/blob/main/src/claude-code/devcontainer-feature.json). Add additional notes to a `NOTES.md`._ +_Note: This file was auto-generated from the [devcontainer-feature.json](https://github.com/tmaier/devcontainer-features/blob/main/src/claude-code/devcontainer-feature.json). Add additional notes to a `NOTES.md`._ diff --git a/src/color/README.md b/src/color/README.md index 6327772..9741712 100644 --- a/src/color/README.md +++ b/src/color/README.md @@ -7,7 +7,7 @@ A feature to remind you of your favorite color ```json "features": { - "ghcr.io/iot-rocket/devcontainer-features/color:1": {} + "ghcr.io/tmaier/devcontainer-features/color:1": {} } ``` @@ -21,4 +21,4 @@ A feature to remind you of your favorite color --- -_Note: This file was auto-generated from the [devcontainer-feature.json](https://github.com/iot-rocket/devcontainer-features/blob/main/src/color/devcontainer-feature.json). Add additional notes to a `NOTES.md`._ +_Note: This file was auto-generated from the [devcontainer-feature.json](https://github.com/tmaier/devcontainer-features/blob/main/src/color/devcontainer-feature.json). Add additional notes to a `NOTES.md`._ diff --git a/src/gitlab/README.md b/src/gitlab/README.md index 38bdfc6..0390f37 100644 --- a/src/gitlab/README.md +++ b/src/gitlab/README.md @@ -7,7 +7,7 @@ Installs glab CLI and GitLab Workflow VS Code extension for GitLab integration ```json "features": { - "ghcr.io/iot-rocket/devcontainer-features/gitlab:1": {} + "ghcr.io/tmaier/devcontainer-features/gitlab:1": {} } ``` @@ -67,4 +67,4 @@ glab repo view --web --- -_Note: This file was auto-generated from the [devcontainer-feature.json](https://github.com/iot-rocket/devcontainer-features/blob/main/src/gitlab/devcontainer-feature.json). Add additional notes to a `NOTES.md`._ +_Note: This file was auto-generated from the [devcontainer-feature.json](https://github.com/tmaier/devcontainer-features/blob/main/src/gitlab/devcontainer-feature.json). Add additional notes to a `NOTES.md`._ diff --git a/src/hello/README.md b/src/hello/README.md index 98e8b19..51a0e86 100644 --- a/src/hello/README.md +++ b/src/hello/README.md @@ -7,7 +7,7 @@ A hello world feature ```json "features": { - "ghcr.io/iot-rocket/devcontainer-features/hello:1": {} + "ghcr.io/tmaier/devcontainer-features/hello:1": {} } ``` @@ -21,4 +21,4 @@ A hello world feature --- -_Note: This file was auto-generated from the [devcontainer-feature.json](https://github.com/iot-rocket/devcontainer-features/blob/main/src/hello/devcontainer-feature.json). Add additional notes to a `NOTES.md`._ +_Note: This file was auto-generated from the [devcontainer-feature.json](https://github.com/tmaier/devcontainer-features/blob/main/src/hello/devcontainer-feature.json). Add additional notes to a `NOTES.md`._ diff --git a/src/imagemagick/README.md b/src/imagemagick/README.md index 905401b..f860690 100644 --- a/src/imagemagick/README.md +++ b/src/imagemagick/README.md @@ -7,7 +7,7 @@ Installs imagemagick ```json "features": { - "ghcr.io/iot-rocket/devcontainer-features/imagemagick:1": {} + "ghcr.io/tmaier/devcontainer-features/imagemagick:1": {} } ``` @@ -33,4 +33,4 @@ For complete usage instructions and documentation, visit the official ImageMagic --- -_Note: This file was auto-generated from the [devcontainer-feature.json](https://github.com/iot-rocket/devcontainer-features/blob/main/src/imagemagick/devcontainer-feature.json). Add additional notes to a `NOTES.md`._ +_Note: This file was auto-generated from the [devcontainer-feature.json](https://github.com/tmaier/devcontainer-features/blob/main/src/imagemagick/devcontainer-feature.json). Add additional notes to a `NOTES.md`._ diff --git a/src/mc/README.md b/src/mc/README.md index 1596c35..5504562 100644 --- a/src/mc/README.md +++ b/src/mc/README.md @@ -7,7 +7,7 @@ Installs the MinIO Client (mc) ```json "features": { - "ghcr.io/iot-rocket/devcontainer-features/mc:1": {} + "ghcr.io/tmaier/devcontainer-features/mc:1": {} } ``` @@ -27,4 +27,4 @@ For complete usage instructions and documentation, visit the official MinIO Clie --- -_Note: This file was auto-generated from the [devcontainer-feature.json](https://github.com/iot-rocket/devcontainer-features/blob/main/src/mc/devcontainer-feature.json). Add additional notes to a `NOTES.md`._ +_Note: This file was auto-generated from the [devcontainer-feature.json](https://github.com/tmaier/devcontainer-features/blob/main/src/mc/devcontainer-feature.json). Add additional notes to a `NOTES.md`._ diff --git a/src/mcp-language-server/README.md b/src/mcp-language-server/README.md index ff6cfc2..22681a3 100644 --- a/src/mcp-language-server/README.md +++ b/src/mcp-language-server/README.md @@ -7,7 +7,7 @@ Installs MCP Language Server - runs and exposes language servers to LLMs for sem ```json "features": { - "ghcr.io/iot-rocket/devcontainer-features/mcp-language-server:1": {} + "ghcr.io/tmaier/devcontainer-features/mcp-language-server:1": {} } ``` @@ -51,4 +51,4 @@ After installation, configure MCP Language Server in your MCP client settings: --- -_Note: This file was auto-generated from the [devcontainer-feature.json](https://github.com/iot-rocket/devcontainer-features/blob/main/src/mcp-language-server/devcontainer-feature.json). Add additional notes to a `NOTES.md`._ +_Note: This file was auto-generated from the [devcontainer-feature.json](https://github.com/tmaier/devcontainer-features/blob/main/src/mcp-language-server/devcontainer-feature.json). Add additional notes to a `NOTES.md`._ diff --git a/src/yek/README.md b/src/yek/README.md index e625053..103a909 100644 --- a/src/yek/README.md +++ b/src/yek/README.md @@ -7,7 +7,7 @@ Installs yek, a tool for serializing repository files for LLM consumption ```json "features": { - "ghcr.io/iot-rocket/devcontainer-features/yek:1": {} + "ghcr.io/tmaier/devcontainer-features/yek:1": {} } ``` @@ -27,4 +27,4 @@ For complete usage instructions and documentation, visit the official yek reposi --- -_Note: This file was auto-generated from the [devcontainer-feature.json](https://github.com/iot-rocket/devcontainer-features/blob/main/src/yek/devcontainer-feature.json). Add additional notes to a `NOTES.md`._ +_Note: This file was auto-generated from the [devcontainer-feature.json](https://github.com/tmaier/devcontainer-features/blob/main/src/yek/devcontainer-feature.json). Add additional notes to a `NOTES.md`._ diff --git a/src/yq/README.md b/src/yq/README.md index 16c1064..d8dff4f 100644 --- a/src/yq/README.md +++ b/src/yq/README.md @@ -7,7 +7,7 @@ Installs yq for yaml processing ```json "features": { - "ghcr.io/iot-rocket/devcontainer-features/yq:1": {} + "ghcr.io/tmaier/devcontainer-features/yq:1": {} } ``` @@ -27,4 +27,4 @@ For complete usage instructions and documentation, visit the official yq reposit --- -_Note: This file was auto-generated from the [devcontainer-feature.json](https://github.com/iot-rocket/devcontainer-features/blob/main/src/yq/devcontainer-feature.json). Add additional notes to a `NOTES.md`._ +_Note: This file was auto-generated from the [devcontainer-feature.json](https://github.com/tmaier/devcontainer-features/blob/main/src/yq/devcontainer-feature.json). Add additional notes to a `NOTES.md`._ From 05e3083b1118ab74bc84b986a47a70e1d3e769ca Mon Sep 17 00:00:00 2001 From: "Tobias L. Maier" Date: Mon, 16 Feb 2026 21:07:52 +0000 Subject: [PATCH 17/17] fix(chrome): map dev channel to google-chrome-unstable package Google's apt repository names the dev channel package "google-chrome-unstable", not "google-chrome-dev". Map the user-facing "dev" option value to the correct package name. Co-Authored-By: Claude Opus 4.6 --- src/chrome/install.sh | 7 ++++++- test/chrome/chrome_dev_channel_test.sh | 12 ++++++------ 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/chrome/install.sh b/src/chrome/install.sh index a6c8369..0cb9921 100755 --- a/src/chrome/install.sh +++ b/src/chrome/install.sh @@ -17,7 +17,12 @@ case "$ARCH" in esac CHANNEL="${CHANNEL:-stable}" -CHROME_PACKAGE="google-chrome-${CHANNEL}" +# The apt package for the "dev" channel is named "google-chrome-unstable" +if [ "$CHANNEL" = "dev" ]; then + CHROME_PACKAGE="google-chrome-unstable" +else + CHROME_PACKAGE="google-chrome-${CHANNEL}" +fi echo "Installing Google Chrome (${CHANNEL} channel)..." diff --git a/test/chrome/chrome_dev_channel_test.sh b/test/chrome/chrome_dev_channel_test.sh index 93fcb2f..076438e 100755 --- a/test/chrome/chrome_dev_channel_test.sh +++ b/test/chrome/chrome_dev_channel_test.sh @@ -6,13 +6,13 @@ source dev-container-features-test-lib check "chrome wrapper exists" test -f /usr/local/bin/chrome check "chrome wrapper is executable" test -x /usr/local/bin/chrome -# Dev channel checks -check "google-chrome-dev is installed" bash -c "command -v google-chrome-dev" -check "google-chrome-dev shows version" bash -c "google-chrome-dev --version" +# Dev channel checks — the apt package is "google-chrome-unstable" +check "google-chrome-unstable is installed" bash -c "command -v google-chrome-unstable" +check "google-chrome-unstable shows version" bash -c "google-chrome-unstable --version" check "config file exists" test -f /etc/chrome-wrapper/config -check "config has dev binary" bash -c "grep -q 'CHROME_BINARY=\"google-chrome-dev\"' /etc/chrome-wrapper/config" +check "config has unstable binary" bash -c "grep -q 'CHROME_BINARY=\"google-chrome-unstable\"' /etc/chrome-wrapper/config" -# Wrapper should use the dev binary -check "wrapper uses dev channel" bash -c "chrome --version | grep -i dev" +# Wrapper should use the dev (unstable) binary +check "wrapper uses dev channel" bash -c "chrome --version | grep -i -E 'dev|canary|unstable'" reportResults