Skip to content

Build DevContainer

Build DevContainer #23

name: Build DevContainer
on:
workflow_dispatch:
inputs:
devcontainer_choice:
description: 'DevContainer to build (only those with Dockerfiles)'
required: true
type: choice
options:
- 'just'
- 'default'
default: 'just'
image_tag:
description: 'Image tag (e.g., latest, v1.0.0)'
required: false
default: 'latest'
build_args:
description: 'Build arguments (comma-separated, e.g., JUST_RECIPE=install-all,GO_VERSION=1.25.1)'
required: false
type: string
concurrency:
group: build-${{ github.event.inputs.devcontainer_choice }}-${{ github.event.inputs.image_tag }}
cancel-in-progress: false
jobs:
build:
runs-on: ubuntu-latest
timeout-minutes: 120
permissions:
contents: read
packages: write
strategy:
fail-fast: false
matrix:
platform:
- linux/amd64
- linux/arm64/v8
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Display build parameters
run: |
devcontainer="${{ github.event.inputs.devcontainer_choice }}"
image_name="${devcontainer%-container}"
image_tag="${{ github.event.inputs.image_tag || 'latest' }}"
image_url="ghcr.io/${{ github.repository }}/${image_name}:${image_tag}"
echo "::group::🔧 Build Configuration"
echo "DevContainer: ${{ github.event.inputs.devcontainer_choice }}"
echo "Image Tag: ${image_tag}"
echo "Platform: ${{ matrix.platform }}"
echo "Build Arguments: ${{ github.event.inputs.build_args || '(none)' }}"
echo "Image: ${image_url}"
echo "::endgroup::"
# Add to job summary
echo "## 🔧 Build Configuration" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Parameter | Value |" >> $GITHUB_STEP_SUMMARY
echo "| --- | --- |" >> $GITHUB_STEP_SUMMARY
echo "| **DevContainer** | \`${{ github.event.inputs.devcontainer_choice }}\` |" >> $GITHUB_STEP_SUMMARY
echo "| **Image Tag** | \`${image_tag}\` |" >> $GITHUB_STEP_SUMMARY
echo "| **Platform** | \`${{ matrix.platform }}\` |" >> $GITHUB_STEP_SUMMARY
echo "| **Build Arguments** | \`${{ github.event.inputs.build_args || '(none)' }}\` |" >> $GITHUB_STEP_SUMMARY
echo "| **Image** | \`${image_url}\` |" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Set image name
id: image-name
run: |
devcontainer="${{ github.event.inputs.devcontainer_choice }}"
# Remove -container suffix if it exists
image_name="${devcontainer%-container}"
echo "name=$image_name" >> $GITHUB_OUTPUT
- name: Prepare build arguments
id: build-args
run: |
build_args="${{ github.event.inputs.build_args }}"
# Convert comma-separated format to newline-separated for Docker
if [[ -n "$build_args" ]]; then
build_args=$(echo "$build_args" | tr ',' '\n' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
echo "args<<EOF" >> $GITHUB_OUTPUT
echo "$build_args" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
fi
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository }}/${{ steps.image-name.outputs.name }}
tags: |
type=raw,value=${{ github.event.inputs.image_tag || 'latest' }}
type=raw,value=latest,enable=${{ github.event.inputs.image_tag == 'latest' || github.event.inputs.image_tag == '' }}
- name: Build and push by digest
id: build
uses: docker/build-push-action@v5
with:
context: .
file: ./.devcontainer/${{ github.event.inputs.devcontainer_choice }}/Dockerfile
platforms: ${{ matrix.platform }}
labels: ${{ steps.meta.outputs.labels }}
build-args: ${{ steps.build-args.outputs.args }}
cache-from: type=gha,scope=${{ github.event.inputs.devcontainer_choice }}-${{ matrix.platform }}
cache-to: type=gha,mode=max,scope=${{ github.event.inputs.devcontainer_choice }}-${{ matrix.platform }}
outputs: type=image,name=ghcr.io/${{ github.repository }}/${{ steps.image-name.outputs.name }},push-by-digest=true,name-canonical=true,push=true
sbom: true
provenance: mode=max
- name: Export digest
run: |
mkdir -p /tmp/digests
digest="${{ steps.build.outputs.digest }}"
touch "/tmp/digests/${digest#sha256:}"
- name: Upload digests
uses: actions/upload-artifact@v4
with:
name: digests-${{ github.run_id }}-${{ strategy.job-index }}
path: /tmp/digests/*
if-no-files-found: error
retention-days: 1
compression-level: 6
merge:
runs-on: ubuntu-latest
needs: build
permissions:
contents: read
packages: write
steps:
- name: Download digests
uses: actions/download-artifact@v4
with:
path: /tmp/digests
pattern: digests-*
merge-multiple: true
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set image name
id: image-name
run: |
devcontainer="${{ github.event.inputs.devcontainer_choice }}"
image_name="${devcontainer%-container}"
echo "name=$image_name" >> $GITHUB_OUTPUT
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository }}/${{ steps.image-name.outputs.name }}
tags: |
type=raw,value=${{ github.event.inputs.image_tag || 'latest' }}
type=raw,value=latest,enable=${{ github.event.inputs.image_tag == 'latest' || github.event.inputs.image_tag == '' }}
- name: Create multi-arch manifest
run: |
docker buildx imagetools create \
$(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
$(for digest in /tmp/digests/*; do echo "ghcr.io/${{ github.repository }}/${{ steps.image-name.outputs.name }}@sha256:$(basename $digest)"; done)
env:
DOCKER_METADATA_OUTPUT_JSON: ${{ steps.meta.outputs.json }}
- name: Inspect multi-arch image
run: |
docker buildx imagetools inspect ghcr.io/${{ github.repository }}/${{ steps.image-name.outputs.name }}:${{ github.event.inputs.image_tag || 'latest' }}