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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 65 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ A similar [Azure DevOps Task](./docs/azure-devops-task.md) is also available!
Note that this project builds on top of [@devcontainers/cli](https://www.npmjs.com/package/@devcontainers/cli) which can be used in other automation systems.

## Quick start
Here are three examples of using the Action for common scenarios. See the [documentation](./docs/github-action.md) for more details and a list of available [inputs](./docs/github-action.md#inputs).
Here are examples of using the Action for common scenarios. See the [documentation](./docs/github-action.md) for more details and a list of available [inputs](./docs/github-action.md#inputs).

**Pre-building an image:**
**Pre-building an image (legacy format):**

```yaml
- name: Pre-build dev container image
Expand All @@ -22,6 +22,28 @@ Here are three examples of using the Action for common scenarios. See the [docum
push: always
```

**Pre-building an image (new format with docker-metadata-action):**

```yaml
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/example/example-devcontainer
tags: |
type=ref,event=branch
type=ref,event=pr
type=sha

- name: Pre-build dev container image
uses: devcontainers/[email protected]
with:
images: ${{ steps.meta.outputs.images }}
tags: ${{ steps.meta.outputs.tags }}
cacheFrom: ghcr.io/example/example-devcontainer
push: always
```

**Using a Dev Container for a CI build:**

```yaml
Expand Down Expand Up @@ -49,6 +71,47 @@ Here are three examples of using the Action for common scenarios. See the [docum
runCmd: make ci-build
```

**Using with docker-metadata-action for advanced tagging:**

```yaml
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: |
ghcr.io/example/example-devcontainer
example/example-devcontainer
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=sha

- name: Pre-build image and run make ci-build in dev container
uses: devcontainers/[email protected]
with:
images: ${{ steps.meta.outputs.images }}
tags: ${{ steps.meta.outputs.tags }}
cacheFrom: ghcr.io/example/example-devcontainer
push: always
runCmd: make ci-build
```

## Input compatibility

This action now supports two input formats for specifying images and tags:

### New format (recommended)
- `images`: List of Docker images to use as base name for tags (compatible with docker-metadata-action)
- `tags`: List of tags (compatible with docker-metadata-action output)

### Legacy format (still supported)
- `imageName`: Single image name (including registry)
- `imageTag`: Comma-separated list of image tags

The new format takes precedence when both are provided. This ensures compatibility with docker-metadata-action while maintaining backwards compatibility with existing workflows.

## CHANGELOG

### Version 0.3.0 (24th February 2023)
Expand Down
9 changes: 9 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ inputs:
imageTag:
required: false
description: One or more comma-separated image tags (defaults to latest)
images:
required: false
description: List of Docker images to use as base name for tags. Takes precedence over imageName if both are provided.
tags:
required: false
description: List of tags. Takes precedence over imageTag if both are provided. Can accept output from docker/metadata-action.
platform:
require: false
description: Platforms for which the image should be built. If omitted, defaults to the platform of the GitHub Actions Runner. Multiple platforms should be comma separated.
Expand Down Expand Up @@ -66,6 +72,9 @@ inputs:
cacheTo:
required: false
description: Specify the image to cache the built image to
mounts:
required: false
description: List of additional mounts to be added to the dev container
outputs:
runCmdOutput:
description: The output of the command specified in the runCmd input
Expand Down
243 changes: 238 additions & 5 deletions docs/github-action.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ jobs:

With the example above, each time the action runs it will rebuild the Docker image for the dev container. To save time in builds, you can push the dev container image to a container registry (e.g. [GitHub Container Registry](https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry)) and re-used the cached image in future builds.

To enable pushing the dev container image to a container registry you need to specify a qualified `imageName` and ensure that your GitHub workflow is signed in to that registry.
To enable pushing the dev container image to a container registry you need to specify image information and ensure that your GitHub workflow is signed in to that registry.

The example below shows installing Docker BuildKit, logging in to GitHub Container Registry, and then building and running the dev container with the devcontainer-build-run action:

Expand Down Expand Up @@ -95,6 +95,28 @@ The [`devcontainers/ci` action](https://github.com/marketplace/actions/devcontai
push: always
```

**Pre-building an image with docker-metadata-action:**

```yaml
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/example/example-devcontainer
tags: |
type=ref,event=branch
type=ref,event=pr
type=sha

- name: Pre-build dev container image
uses: devcontainers/[email protected]
with:
images: ${{ steps.meta.outputs.images }}
tags: ${{ steps.meta.outputs.tags }}
cacheFrom: ghcr.io/example/example-devcontainer
push: always
```

**Using a Dev Container for a CI build:**

```yaml
Expand Down Expand Up @@ -122,27 +144,111 @@ The [`devcontainers/ci` action](https://github.com/marketplace/actions/devcontai
runCmd: make ci-build
```

**Advanced tagging with docker-metadata-action:**

```yaml
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: |
ghcr.io/example/example-devcontainer
example/example-devcontainer
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=sha

- name: Pre-build image and run make ci-build in dev container
uses: devcontainers/[email protected]
with:
images: ${{ steps.meta.outputs.images }}
tags: ${{ steps.meta.outputs.tags }}
cacheFrom: ghcr.io/example/example-devcontainer
push: always
runCmd: make ci-build
```

## Inputs

This action supports two input formats for specifying images and tags:

### New format (recommended for new workflows)
The new format is compatible with outputs from [docker/metadata-action](https://github.com/docker/metadata-action) and follows the same pattern as [docker/build-push-action](https://github.com/docker/build-push-action).

### Legacy format (backwards compatible)
The legacy format continues to work for existing workflows.

| Name | Required | Description |
| ------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| imageName | true | Image name to use when building the dev container image (including registry) |
| imageTag | false | One or more comma-separated image tags (defaults to `latest`) |
| **New format inputs** | | |
| images | false | List of Docker images to use as base name for tags. Takes precedence over `imageName` if both are provided. Compatible with docker-metadata-action outputs. |
| tags | false | List of tags. Takes precedence over `imageTag` if both are provided. Compatible with docker-metadata-action outputs. Can be newline-delimited string. |
| **Legacy format inputs** | | |
| imageName | false | Image name to use when building the dev container image (including registry). Superseded by `images` input if provided. |
| imageTag | false | One or more comma-separated image tags (defaults to `latest`). Superseded by `tags` input if provided. |
| **Other inputs** | | |
| subFolder | false | Use this to specify the repo-relative path to the folder containing the dev container (i.e. the folder that contains the `.devcontainer` folder). Defaults to repo root |
| configFile | false | Use this to specify the repo-relative path to the devcontainer.json file. Defaults to `./.devcontainer/devcontainer.json` and `./.devcontainer.json`. |
| runCmd | true | The command to run after building the dev container image |
| runCmd | false | The command to run after building the dev container image |
| env | false | Specify environment variables to pass to the dev container when run |
| inheritEnv | false | Inherit all environment variables of the runner CI machine |
| checkoutPath | false | Only used for development/testing |
| push | false | Control when images are pushed. Options are `never`, `filter`, `always`. For `filter`, images are pushed if the `refFilterForPush` and `eventFilterForPush` conditions are met. Defaults to `filter` if `imageName` is set, `never` otherwise. |
| push | false | Control when images are pushed. Options are `never`, `filter`, `always`. For `filter`, images are pushed if the `refFilterForPush` and `eventFilterForPush` conditions are met. Defaults to `filter` if image inputs are set, `never` otherwise. |
| refFilterForPush | false | Set the source branches (e.g. `refs/heads/main`) that are allowed to trigger a push of the dev container image. Leave empty to allow all (default) |
| eventFilterForPush | false | Set the event names (e.g. `pull_request`, `push`) that are allowed to trigger a push of the dev container image. Defaults to `push`. Leave empty for all |
| skipContainerUserIdUpdate | false | For non-root Dev Containers (i.e. where `remoteUser` is specified), the action attempts to make the container user UID and GID match those of the host user. Set this to true to skip this step (defaults to false) |
| cacheFrom | false | Specify additional images to use for build caching |
| noCache | false | Builds the image with `--no-cache` (takes precedence over `cacheFrom`) |
| cacheTo | false | Specify the image to cache the built image to |
| platform | false | Platforms for which the image should be built. If omitted, defaults to the platform of the GitHub Actions Runner. Multiple platforms should be comma separated. |
| mounts | false | List of additional mounts to be added to the dev container |

## Input format compatibility

The action supports both legacy and new input formats with the following precedence:

1. If `images` or `tags` inputs are provided, they take precedence over `imageName` and `imageTag`
2. If only legacy inputs (`imageName`, `imageTag`) are provided, they are used for backwards compatibility
3. The new format accepts newline-delimited strings (compatible with docker-metadata-action outputs)
4. The legacy format accepts comma-separated values for `imageTag`

### Examples of input combinations

**Using new format:**
```yaml
with:
images: |
ghcr.io/example/app
example/app
tags: |
v1.0.0
latest
```

**Using legacy format:**
```yaml
with:
imageName: ghcr.io/example/app
imageTag: v1.0.0,latest
```

**Using docker-metadata-action outputs:**
```yaml
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/example/app

- name: Build dev container
uses: devcontainers/[email protected]
with:
images: ${{ steps.meta.outputs.images }}
tags: ${{ steps.meta.outputs.tags }}
```

## Outputs

Expand Down Expand Up @@ -237,3 +343,130 @@ You should set the `HELLO` environment variable using the `env` property on the
## Multi-Platform Builds

Builds for multiple platforms have special considerations, detailed at [mutli-platform-builds.md](multi-platform-builds.md).

## Complete workflow example with docker-metadata-action

Here's a comprehensive example that demonstrates using this action with docker-metadata-action for advanced image management:

```yaml
name: Dev Container CI

on:
push:
branches:
- main
- develop
tags:
- 'v*'
pull_request:
branches:
- main

env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}

jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=sha

- name: Log in to Container Registry
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Log in to Docker Hub
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

- name: Setup Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Build and test in dev container
uses: devcontainers/[email protected]
with:
images: ${{ steps.meta.outputs.images }}
tags: ${{ steps.meta.outputs.tags }}
cacheFrom: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
push: ${{ github.event_name != 'pull_request' && 'always' || 'never' }}
runCmd: |
npm test
npm run lint
npm run build

multi-platform:
runs-on: ubuntu-latest
if: github.event_name != 'pull_request'
needs: test
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}

- name: Log in to Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Setup Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Build multi-platform dev container
uses: devcontainers/[email protected]
with:
images: ${{ steps.meta.outputs.images }}
tags: ${{ steps.meta.outputs.tags }}
platform: linux/amd64,linux/arm64
cacheFrom: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
push: always
```

This example demonstrates:

1. **Multi-registry support**: Pushes to both GitHub Container Registry and Docker Hub
2. **Conditional pushing**: Only pushes on non-PR events
3. **Comprehensive tagging**: Uses semantic versioning, branch names, and SHA-based tags
4. **Multi-platform builds**: Separate job for building ARM64 and AMD64 images
5. **Build caching**: Reuses previously built layers for faster builds
6. **Integration testing**: Runs tests inside the dev container before pushing

The workflow creates tags like:
- `main` (for main branch pushes)
- `pr-123` (for pull request #123)
- `v1.2.3`, `v1.2`, `v1` (for version tags)
- `sha-abc1234` (for specific commits)
Loading