diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 00b3feb..4251ce8 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -1,4 +1,4 @@ -name: "Release Dev Container Templates" +name: "Release Dev Container Features And Templates" on: workflow_dispatch: @@ -13,6 +13,15 @@ jobs: steps: - uses: actions/checkout@v6 + - name: "Publish Features" + uses: devcontainers/action@v1 + with: + publish-features: "true" + base-path-to-features: "./features/src" + + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: "Publish Templates" uses: devcontainers/action@v1 with: diff --git a/.github/workflows/test-pr.yaml b/.github/workflows/test-pr.yaml index db4f7fb..a975c73 100644 --- a/.github/workflows/test-pr.yaml +++ b/.github/workflows/test-pr.yaml @@ -1,4 +1,4 @@ -name: "CI - Test Templates" +name: "CI - Test Templates And Features" on: pull_request: @@ -18,6 +18,14 @@ jobs: with: template: "gz" + feature-tests: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + + - name: Validate feature metadata + run: bash features/test/test_all.sh + gen-docs: if: ${{ github.event.pull_request.head.repo.full_name == github.repository }} runs-on: ubuntu-latest @@ -31,13 +39,18 @@ jobs: ref: ${{ github.head_ref }} token: ${{ secrets.CI_BOT_TOKEN }} - - name: "Publish Templates" + - name: "Generate Template Docs" uses: devcontainers/action@v1 with: - publish-templates: "false" base-path-to-templates: "./templates/src" generate-docs: "true" + - name: "Generate Feature Docs" + uses: devcontainers/action@v1 + with: + base-path-to-features: "./features/src" + generate-docs: "true" + - uses: stefanzweifel/git-auto-commit-action@v7 with: commit_message: Update generated docs @@ -45,6 +58,7 @@ jobs: complete: needs: - gen-docs + - feature-tests - smoke-test if: always() runs-on: ubuntu-latest @@ -52,11 +66,13 @@ jobs: - name: Verify required jobs succeeded env: SMOKE_TEST_RESULT: ${{ needs.smoke-test.result }} + FEATURE_TESTS_RESULT: ${{ needs.feature-tests.result }} GEN_DOCS_RESULT: ${{ needs.gen-docs.result }} run: | set -euo pipefail echo "smoke_test: ${SMOKE_TEST_RESULT}" + echo "feature_tests: ${FEATURE_TESTS_RESULT}" echo "gen_docs: ${GEN_DOCS_RESULT}" if [[ "${SMOKE_TEST_RESULT}" != "success" ]]; then @@ -64,6 +80,11 @@ jobs: exit 1 fi + if [[ "${FEATURE_TESTS_RESULT}" != "success" ]]; then + echo "Required job 'feature_tests' did not succeed." + exit 1 + fi + # gen_docs is skipped for fork PRs by design; treat that as acceptable. if [[ "${GEN_DOCS_RESULT}" != "success" && "${GEN_DOCS_RESULT}" != "skipped" ]]; then echo "Required job 'gen_docs' failed or was cancelled." diff --git a/README.md b/README.md index fcfb820..152f9a1 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,12 @@ Devcontainer templates repository focused on robotics and simulation. - `templates/src/gz` +## Included features + +- `features/src/x11` +- `features/src/wayland` + ## Workflows -- `.github/workflows/test-pr.yaml`: smoke tests template changes on pull requests -- `.github/workflows/release.yaml`: publishes templates to GHCR on manual dispatch from `main` +- `.github/workflows/test-pr.yaml`: smoke tests template changes, validates features, and refreshes generated docs on pull requests +- `.github/workflows/release.yaml`: publishes features and templates to GHCR on manual dispatch from `main` diff --git a/features/src/wayland/README.md b/features/src/wayland/README.md new file mode 100644 index 0000000..30c1a2b --- /dev/null +++ b/features/src/wayland/README.md @@ -0,0 +1,24 @@ + +# Wayland GUI Forwarding (wayland) + +Configure container environment and mounts for host Wayland display forwarding. + +## Example Usage + +```json +"features": { + "ghcr.io/althack/devcontainers/wayland:0": {} +} +``` + +## Options + +| Options Id | Description | Type | Default Value | +|-----|-----|-----|-----| +| softwareGL | Set LIBGL_ALWAYS_SOFTWARE for GPU compatibility. | string | 1 | + + + +--- + +_Note: This file was auto-generated from the [devcontainer-feature.json](https://github.com/althack/devcontainers/blob/main/features/src/wayland/devcontainer-feature.json). Add additional notes to a `NOTES.md`._ diff --git a/features/src/wayland/devcontainer-feature.json b/features/src/wayland/devcontainer-feature.json new file mode 100644 index 0000000..c718490 --- /dev/null +++ b/features/src/wayland/devcontainer-feature.json @@ -0,0 +1,31 @@ +{ + "id": "wayland", + "version": "0.1.0", + "name": "Wayland GUI Forwarding", + "description": "Configure container environment and mounts for host Wayland display forwarding.", + "documentationURL": "https://github.com/athackst/devcontainer-templates/tree/main/features/src/wayland", + "licenseURL": "https://github.com/athackst/devcontainer-templates/blob/main/LICENSE", + "options": { + "softwareGL": { + "type": "string", + "default": "1", + "proposals": [ + "0", + "1" + ], + "description": "Set LIBGL_ALWAYS_SOFTWARE for GPU compatibility." + } + }, + "containerEnv": { + "WAYLAND_DISPLAY": "${localEnv:WAYLAND_DISPLAY}", + "XDG_RUNTIME_DIR": "${localEnv:XDG_RUNTIME_DIR}", + "PULSE_SERVER": "${localEnv:PULSE_SERVER}", + "LIBGL_ALWAYS_SOFTWARE": "${feature:softwareGL}" + }, + "mounts": [ + "source=${localEnv:XDG_RUNTIME_DIR},target=${localEnv:XDG_RUNTIME_DIR},type=bind" + ], + "installsAfter": [ + "ghcr.io/devcontainers/features/common-utils" + ] +} diff --git a/features/src/wayland/install.sh b/features/src/wayland/install.sh new file mode 100755 index 0000000..51daee1 --- /dev/null +++ b/features/src/wayland/install.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Runtime forwarding is configured through feature metadata (containerEnv/mounts). +# Keep install lightweight and deterministic. +echo "Wayland feature metadata configured." diff --git a/features/src/x11/README.md b/features/src/x11/README.md new file mode 100644 index 0000000..313e323 --- /dev/null +++ b/features/src/x11/README.md @@ -0,0 +1,24 @@ + +# X11 GUI Forwarding (x11) + +Configure container environment and mounts for host X11 display forwarding. + +## Example Usage + +```json +"features": { + "ghcr.io/althack/devcontainers/x11:0": {} +} +``` + +## Options + +| Options Id | Description | Type | Default Value | +|-----|-----|-----|-----| +| softwareGL | Set LIBGL_ALWAYS_SOFTWARE for GPU compatibility. | string | 1 | + + + +--- + +_Note: This file was auto-generated from the [devcontainer-feature.json](https://github.com/althack/devcontainers/blob/main/features/src/x11/devcontainer-feature.json). Add additional notes to a `NOTES.md`._ diff --git a/features/src/x11/devcontainer-feature.json b/features/src/x11/devcontainer-feature.json new file mode 100644 index 0000000..297db48 --- /dev/null +++ b/features/src/x11/devcontainer-feature.json @@ -0,0 +1,31 @@ +{ + "id": "x11", + "version": "0.1.0", + "name": "X11 GUI Forwarding", + "description": "Configure container environment and mounts for host X11 display forwarding.", + "documentationURL": "https://github.com/athackst/devcontainer-templates/tree/main/features/src/x11", + "licenseURL": "https://github.com/athackst/devcontainer-templates/blob/main/LICENSE", + "options": { + "softwareGL": { + "type": "string", + "default": "1", + "proposals": [ + "0", + "1" + ], + "description": "Set LIBGL_ALWAYS_SOFTWARE for GPU compatibility." + } + }, + "containerEnv": { + "DISPLAY": "${localEnv:DISPLAY}", + "XAUTHORITY": "${localEnv:XAUTHORITY}", + "PULSE_SERVER": "${localEnv:PULSE_SERVER}", + "LIBGL_ALWAYS_SOFTWARE": "${feature:softwareGL}" + }, + "mounts": [ + "source=/tmp/.X11-unix,target=/tmp/.X11-unix,type=bind" + ], + "installsAfter": [ + "ghcr.io/devcontainers/features/common-utils" + ] +} diff --git a/features/src/x11/install.sh b/features/src/x11/install.sh new file mode 100755 index 0000000..8eed45b --- /dev/null +++ b/features/src/x11/install.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Runtime forwarding is configured through feature metadata (containerEnv/mounts). +# Keep install lightweight and deterministic. +echo "X11 feature metadata configured." diff --git a/features/test/test_all.sh b/features/test/test_all.sh new file mode 100755 index 0000000..d1ea3c3 --- /dev/null +++ b/features/test/test_all.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +set -euo pipefail + +repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" + +for test_script in "${repo_root}"/features/test/*/test.sh; do + bash "${test_script}" +done diff --git a/features/test/wayland/test.sh b/features/test/wayland/test.sh new file mode 100755 index 0000000..058c89f --- /dev/null +++ b/features/test/wayland/test.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +set -euo pipefail + +repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)" +feature_dir="${repo_root}/features/src/wayland" +feature_metadata="${feature_dir}/devcontainer-feature.json" +feature_install="${feature_dir}/install.sh" +feature_readme="${feature_dir}/README.md" + +test -f "${feature_metadata}" +test -f "${feature_install}" +test -f "${feature_readme}" + +jq -e '.id == "wayland"' "${feature_metadata}" >/dev/null +jq -e '.name == "Wayland GUI Forwarding"' "${feature_metadata}" >/dev/null +jq -e '.options.softwareGL.default == "1"' "${feature_metadata}" >/dev/null +jq -e '.containerEnv.WAYLAND_DISPLAY == "${localEnv:WAYLAND_DISPLAY}"' "${feature_metadata}" >/dev/null +jq -e '.containerEnv.XDG_RUNTIME_DIR == "${localEnv:XDG_RUNTIME_DIR}"' "${feature_metadata}" >/dev/null +jq -e '.containerEnv.PULSE_SERVER == "${localEnv:PULSE_SERVER}"' "${feature_metadata}" >/dev/null +jq -e '.containerEnv.LIBGL_ALWAYS_SOFTWARE == "${feature:softwareGL}"' "${feature_metadata}" >/dev/null +jq -e '.mounts | index("source=${localEnv:XDG_RUNTIME_DIR},target=${localEnv:XDG_RUNTIME_DIR},type=bind") != null' "${feature_metadata}" >/dev/null +jq -e '.containerEnv.DISPLAY == null' "${feature_metadata}" >/dev/null +jq -e '.containerEnv.XAUTHORITY == null' "${feature_metadata}" >/dev/null + +grep -q 'Wayland feature metadata configured.' "${feature_install}" diff --git a/features/test/x11/test.sh b/features/test/x11/test.sh new file mode 100755 index 0000000..b5d09b1 --- /dev/null +++ b/features/test/x11/test.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +set -euo pipefail + +repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)" +feature_dir="${repo_root}/features/src/x11" +feature_metadata="${feature_dir}/devcontainer-feature.json" +feature_install="${feature_dir}/install.sh" +feature_readme="${feature_dir}/README.md" + +test -f "${feature_metadata}" +test -f "${feature_install}" +test -f "${feature_readme}" + +jq -e '.id == "x11"' "${feature_metadata}" >/dev/null +jq -e '.name == "X11 GUI Forwarding"' "${feature_metadata}" >/dev/null +jq -e '.options.softwareGL.default == "1"' "${feature_metadata}" >/dev/null +jq -e '.containerEnv.DISPLAY == "${localEnv:DISPLAY}"' "${feature_metadata}" >/dev/null +jq -e '.containerEnv.XAUTHORITY == "${localEnv:XAUTHORITY}"' "${feature_metadata}" >/dev/null +jq -e '.containerEnv.PULSE_SERVER == "${localEnv:PULSE_SERVER}"' "${feature_metadata}" >/dev/null +jq -e '.containerEnv.LIBGL_ALWAYS_SOFTWARE == "${feature:softwareGL}"' "${feature_metadata}" >/dev/null +jq -e '.mounts | index("source=/tmp/.X11-unix,target=/tmp/.X11-unix,type=bind") != null' "${feature_metadata}" >/dev/null +jq -e '.containerEnv.WAYLAND_DISPLAY == null' "${feature_metadata}" >/dev/null +jq -e '.containerEnv.XDG_RUNTIME_DIR == null' "${feature_metadata}" >/dev/null + +grep -q 'X11 feature metadata configured.' "${feature_install}"