devcontainers/ci

repository·main·Indexed 19 days ago

https://github.com/devcontainers/ci

A GitHub Action and Azure DevOps Task (DevcontainersCi@0) designed to facilitate the use of Dev Containers in CI/CD workflows. It supports pre-building Dev Container images with Dev Container Features, executing commands within containerized environments, and pushing images to registries for caching. The project is built on top of @devcontainers/cli and includes utilities for managing DevContainerConfig, building images with UID/GID alignment, and running containers with workspace mounting.

Tokens
13.3K
Snippets
44
Records
53
Agent score
66%

What's inside devcontainers-ci

  1. Use the Dev Container Build and Run GitHub Action

    main

    The devcontainers/ci GitHub Action allows you to reuse Dev Containers within a GitHub workflow. You can use it to pre-build Dev Container images (supporting Dev Container Features and automatic metadata labeling) or to run commands (like CI tests or builds) inside a Dev Container.

    This project is built on top of @devcontainers/cli, which can also be used for automation in other systems.

    - name: Pre-build dev container image
      uses: devcontainers/ci@v0.3
      with:
        imageName: ghcr.io/example/example-devcontainer
        cacheFrom: ghcr.io/example/example-devcontainer
        push: always
  2. Configure environment variables for remoteEnv and localEnv

    main

    If your devcontainer.json uses localEnv references within the remoteEnv section, you must define those variables using the env property on the GitHub Action step itself, rather than the env property nested under the with block.

    Because localEnv references are resolved by the devcontainer CLI, setting them in the action's env block ensures they are available in the correct context for resolution. If you attempt to set them inside the with.env block, they may be overridden by the localEnv resolution process when the CLI starts the container.

    // devcontainer.json
    {
        "remoteEnv": {
            "HELLO": "${localEnv:HELLO}"
        }
    }
    # GitHub Action Workflow
          - name: Build and run dev container task
            uses: devcontainers/ci@v0.3
            env:
              # Set HELLO here so that it is resolved via the localEnv context
              HELLO: hello
            with:
              imageName: ghcr.io/example/example-devcontainer
              runCmd: echo "$HELLO"
              # Don't use the env block here to set the HELLO environment variable
              # as it will be overridden by the value from localEnv context
  3. Perform native multi-platform builds using a matrix strategy

    main

    For faster and more reliable builds, use a matrix strategy to run builds on native runners for each architecture. This avoids the overhead and compatibility issues of QEMU emulation.

    Workflow Pattern

    1. Build Jobs: Run parallel jobs on native runners (e.g., ubuntu-latest for AMD64 and ubuntu-24.04-arm for ARM64).
      • Set useNativeRunner: true.
      • Provide a single platform value (e.g., linux/arm64).
      • Set push: always to ensure platform-specific images are available for the merge step.
      • The system automatically tags images with a suffix derived from the platform (e.g., linux/arm64 becomes linux-arm64).
    2. Merge Job: Runs after all build jobs complete. It uses a dedicated merge tool to combine the individual platform images into a single multi-arch manifest.

    Benefits

    • Speed: Native compilation is much faster than emulation.
    • Reliability: Avoids QEMU-related bugs.
    • Flexibility: Works with GitHub hosted ARM runners or self-hosted ARM agents in Azure DevOps.
    # GitHub Actions Matrix Example
    jobs:
      build:
        strategy:
          matrix:
            include:
              - runner: ubuntu-latest
                platform: linux/amd64
              - runner: ubuntu-24.04-arm
                platform: linux/arm64
        runs-on: ${{ matrix.runner }}
        steps:
          - uses: devcontainers/ci@v0.3
            with:
              imageName: ghcr.io/example/myimage
              platform: ${{ matrix.platform }}
              useNativeRunner: true
              push: always
    
      manifest:
        needs: build
        runs-on: ubuntu-latest
        steps:
          - uses: devcontainers/ci/merge@v0.3
            with:
              imageName: ghcr.io/example/myimage
              platforms: linux/amd64,linux/arm64
  4. How multi-platform builds work with DevcontainersCi

    main

    For native multi-platform builds, the process involves two main steps:

    1. Per-platform builds: Each build job is configured with useNativeRunner: true and a specific platform value.
    2. Manifest merging: A separate merge task, DevcontainersMerge@0, is used to combine the individual per-platform images into a single multi-arch manifest.
  5. Configure environment variables for localEnv references

    main

    If your devcontainer.json uses localEnv references in the remoteEnv section, you must define those variables using the task's top-level env property rather than the env input nested under the with block.

    Defining them at the task level ensures the devcontainer CLI can resolve the ${localEnv:VAR_NAME} syntax correctly. If you define them inside the inputs.env block, they may be overridden by the localEnv resolution process when the container starts.

    // devcontainer.json
    {
        "remoteEnv": {
            "HELLO": "${localEnv:HELLO}"
        }
    }
    # Azure DevOps Task Configuration
    - task: DevcontainersCi@0
      env:
        # Set HELLO here so that it is resolved via the localEnv context
        HELLO: hello
      inputs:
        imageName: 'yourregistry.azurecr.io/example-dev-container'
        runCmd: echo "$HELLO"
        # Don't use the env block here to set the HELLO environment variable
        # as it will be overridden by the value from localEnv context
        # when the CLI starts the container
  6. Perform multi-platform builds using a matrix strategy

    main

    When performing native multi-platform builds using a GitHub Actions matrix strategy, you must configure each build job with useNativeRunner: true and provide a single platform value per job.

    After the individual platform builds are complete, use the devcontainers/ci/merge action to combine the resulting per-platform images into a single multi-arch manifest.

  7. Use PR Comment bot commands

    main

    The pr-comment-bot allows maintainers to trigger CI tasks via GitHub PR comments. These commands are not immediate; you must wait for the associated GitHub Action to start.

    Security Note: Commands only work if the user is a direct repo collaborator with write permission. Builds triggered via these commands use workflow definitions from the main branch. To test workflow changes before merging, push changes to a branch and use the ci_branch workflow.

    Safety Warning for Fork PRs: When running tests on PRs from forks, the bot makes deployment secrets available. Before using /test <sha>, verify that the PR does not contain malicious changes to workflows, scripts, or new package installations that could leak secrets or perform unauthorized operations.

  8. Pass environment variables to the Dev Container Build and Run Task

    main

    You can pass additional environment variables to the dev container during execution using the env input.

    Variables defined in the task's env block are passed to the run-command and will override any environment variables with the same name defined in the Dockerfile or the containerEnv key in devcontainer.json.

    If a variable is specified without a value in the env block (e.g., WORLD), it will inherit the value from the standard Azure DevOps pipeline environment variables.

    - task: DevcontainersCi@0
      env:
        WORLD: World
      inputs:
        imageName: 'yourregistry.azurecr.io/example-dev-container'
        runCmd: echo "$HELLO - $WORLD"
        env: |
          HELLO=Hello
          WORLD
  9. Specify a sub-folder for the Dev Container

    main

    If your .devcontainer folder is not at the repository root, use the subFolder input to specify the repo-relative path to the directory containing the .devcontainer folder.

    - task: DevcontainersCi@0
      inputs:
        imageName: 'yourregistry.azurecr.io/example-dev-container'
        subFolder: folderB
        runCmd: 'make ci-build'
  10. Pre-build a Dev Container image

    main

    To pre-build a Dev Container image and push it to a registry, use the imageName input to specify the target image, cacheFrom to specify existing images for layer caching, and set push: always. This is useful for speeding up subsequent CI steps that run inside the container.

    - name: Pre-build dev container image
      uses: devcontainers/ci@v0.3
      with:
        imageName: ghcr.io/example/example-devcontainer
        cacheFrom: ghcr.io/example/example-devcontainer
        push: always
  11. Optimize builds by pushing Dev Container images to a registry

    main

    To avoid rebuilding the image from scratch on every run, you can push the built image to a container registry (e.g., GitHub Container Registry) and use it as a cache.

    When imageName is provided, the action defaults to a push strategy of filter. This means if the run succeeds (and is not from a PR branch), the image is pushed to the registry. Subsequent builds will then use these layers as a cache to improve performance.

          - name: Login to GitHub Container Registry
            uses: docker/login-action@v2 
            with:
              registry: ghcr.io
              username: ${{ github.repository_owner }}
              password: ${{ secrets.GITHUB_TOKEN }}
    
          - name: Build and run Dev Container task
            uses: devcontainers/ci@v0.3
            with:
              # Specify the full image name including registry
              imageName: ghcr.io/example/example-devcontainer
              runCmd: |
                make install-packages
                make ci-build
  12. Use the Dev Container Build and Run Azure DevOps task

    main

    The DevcontainersCi@0 task allows you to reuse a Dev Container in an Azure DevOps pipeline to run CI, testing, or other commands. It supports building Dev Container images with Dev Container Features and automatically adds metadata labels to the image.

    By default, the task rebuilds the image every time. To optimize build times, you can push the image to a container registry (like Azure Container Registry) and reuse it as a cache in subsequent runs. The task uses Docker BuildKit to support layer cache metadata.

    Note: If using custom agents, ensure Docker BuildKit is available.

    trigger:
    - main
    
    pool:
      vmImage: ubuntu-latest
    
    steps:
    - task: DevcontainersCi@0
      inputs:
        runCmd: 'make ci-build'