name: Devcontainer Smoke # Smoke-tests the .devcontainer/ on changes to it: unit-tests the pure # host->container config transforms (plugin-registry path translation + # the $HOME/.claude.json machine-field strip) and builds the devcontainer # image via the canonical @devcontainers/cli path (which reads build.args # from devcontainer.json, enforcing the "single source of truth" pin). on: push: branches: [main] paths: - '.devcontainer/**' - '.github/workflows/ci-devcontainer.yml' pull_request: paths: - '.devcontainer/**' - '.github/workflows/ci-devcontainer.yml' permissions: contents: read # Concurrency convention: see CONTRIBUTING.md → "GitHub Actions — Concurrency Convention". # Branch/tag scope; cancel superseded PR runs, never cancel push-to-main runs. concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: ${{ github.event_name == 'pull_request' }} jobs: config-transforms: name: Config-transform unit tests runs-on: ubuntu-latest timeout-minutes: 5 steps: # persist-credentials: false — read-only job (tests + syntax checks), # never pushes; keeps GITHUB_TOKEN out of .git/config (zizmor artipacked). - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: 22 - name: Unit-test the host->container config transforms run: node --test .devcontainer/translate-plugin-registries.test.cjs - name: Syntax-check the lifecycle shell scripts run: | bash -n .devcontainer/install-deps.sh bash -n .devcontainer/post-create.sh build: name: Build devcontainer image runs-on: ubuntu-latest timeout-minutes: 30 steps: # persist-credentials: false — read-only build smoke, never pushes; # keeps GITHUB_TOKEN out of .git/config (zizmor artipacked). - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: 22 # Builds the image exactly as a developer's "Reopen in Container" would: # @devcontainers/cli parses devcontainer.json (jsonc), resolves build.args # (the CLAUDE_CODE_VERSION / CODEX_VERSION pins), and runs the Dockerfile. # This is the smoke that catches Dockerfile regressions + drift from the # canonical version pins. Lifecycle hooks (post-create.sh) are not run # here — they need the host config mounts, which CI has none of. - name: Build devcontainer via @devcontainers/cli run: npx --yes @devcontainers/cli build --workspace-folder .