The resolve-meta action resolves Docker build metadata and generates a build matrix for multi-variant image builds.
Overview
This action:
- Detects which apps and variants changed
- Resolves Docker metadata (images, tags, labels, platforms)
- Generates a build matrix for parallel builds
- Outputs configuration for docker/build-push-action
Usage
Basic Setup
.github/workflows/build-image.yaml
name: Build Docker Image
on:
workflow_dispatch:
inputs:
context:
description: 'App context (e.g., apps/icones)'
required: true
variants:
description: 'Variants to build (e.g., latest,dev)'
default: latest
jobs:
metadata:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.metadata.outputs.matrix }}
steps:
- uses: actions/checkout@v4
- name: Resolve metadata
id: metadata
uses: ./action/resolve-meta
with:
context: ${{ inputs.context }}
variants: ${{ inputs.variants }}
build:
needs: metadata
runs-on: ubuntu-latest
if: needs.metadata.outputs.matrix
strategy:
matrix: ${{ fromJson(needs.metadata.outputs.matrix) }}
steps:
- uses: actions/checkout@v4
- name: Build & Push
uses: docker/build-push-action@v6
with:
context: ${{ matrix.build.context }}
file: ${{ matrix.build.file }}
platforms: ${{ matrix.build.platformLines }}
tags: ${{ matrix.metadata.tagLines }}
labels: ${{ matrix.metadata.labelLines }}
App context to build (e.g., apps/icones).with:
context: apps/icones
Comma-separated list of variants to build.Examples:
latest - Build only latest variant
latest,dev - Build latest and dev variants
stable,nightly - Build custom variants
with:
variants: latest,dev
Outputs
Build matrix for GitHub Actions strategy.{
"include": [
{
"name": "icones",
"variant": "latest",
"metadata": {
"images": ["user/icones"],
"imageLines": "user/icones",
"tags": ["latest", "0bc5918"],
"tagLines": "type=raw,value=latest\ntype=raw,value=0bc5918",
"labels": [...],
"labelLines": "..."
},
"build": {
"platforms": ["linux/amd64", "linux/arm64"],
"platformLines": "linux/amd64\nlinux/arm64",
"file": "apps/icones/Dockerfile",
"context": "apps/icones",
"push": true
},
"readme": {
"push": true,
"path": "apps/icones/README.md",
"repo": "user/icones"
},
"pushDocker": true,
"pushGhcr": true,
"pushAli": false,
"hasPreScript": false,
"hasPostScript": false
}
]
}
Latest variant configuration (for convenience).{
"name": "icones",
"variant": "latest",
"metadata": { ... },
"build": { ... }
}
Complete Example
.github/workflows/build-image.yaml
name: Build Docker Image
on:
workflow_dispatch:
inputs:
context:
description: 'App Context (e.g., apps/icones)'
type: choice
required: true
options:
- apps/cobalt
- apps/icones
- apps/haitang
variants:
description: 'Build variants (e.g., latest,stable)'
default: latest
type: string
debug:
description: 'Debug mode'
default: false
type: boolean
pull_request:
branches:
- master
jobs:
metadata:
name: Resolve Docker Metadata
runs-on: ubuntu-latest
permissions:
pull-requests: read
outputs:
matrix: ${{ steps.metadata.outputs.matrix }}
steps:
- uses: actions/checkout@v6
- name: Resolve metadata
id: metadata
uses: ./action/resolve-meta
env:
TZ: Asia/Shanghai
DOCKERHUB_USERNAME: myuser
GHCR_USERNAME: myuser
ALI_ACR_REGISTRY: registry.cn-hangzhou.aliyuncs.com
ALI_ACR_USERNAME: myuser
with:
context: ${{ inputs.context }}
variants: ${{ inputs.variants }}
debug: ${{ inputs.debug }}
docker-build:
needs: metadata
name: Build Variant (${{ matrix.variant }})
runs-on: ubuntu-latest
if: needs.metadata.outputs.matrix
permissions:
contents: read
packages: write
strategy:
matrix: ${{ fromJson(needs.metadata.outputs.matrix) }}
steps:
- uses: actions/checkout@v6
- name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ matrix.metadata.imageLines }}
tags: ${{ matrix.metadata.tagLines }}
labels: ${{ matrix.metadata.labelLines }}
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
if: ${{ matrix.pushDocker }}
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GHCR
if: ${{ matrix.pushGhcr }}
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build & Push
uses: docker/build-push-action@v6
with:
context: ${{ matrix.build.context }}
file: ${{ matrix.build.file }}
platforms: ${{ matrix.build.platformLines }}
push: ${{ matrix.build.push }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Push README
if: ${{ matrix.readme.push }}
uses: peter-evans/dockerhub-description@v4
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
repository: ${{ matrix.readme.repo }}
readme-filepath: ${{ matrix.readme.path }}
How It Works
1. Detect Changed Context
On pull requests:
- Detects which apps changed based on modified files
- Automatically selects the changed app
On manual trigger:
- Uses the specified
context input
2. Load App Configuration
Reads meta.json from the app context:
{
"name": "icones",
"variants": {
"latest": { ... },
"dev": { ... }
}
}
3. Filter Variants
Filters variants based on:
variants input parameter
- Variant
enabled status
- Existence of required files (Dockerfile, version, sha)
For each variant, generates:
Image names with placeholders:
["dockerhub-user/icones", "ghcr.io/user/icones"]
Tags with version placeholders:
["type=raw,value=latest", "type=raw,value=0bc5918"]
Labels (OCI annotations):
{
"org.opencontainers.image.version": "0bc5918",
"org.opencontainers.image.source": "https://github.com/...",
"org.opencontainers.image.revision": "abc123..."
}
Build configuration:
{
"platforms": ["linux/amd64", "linux/arm64"],
"file": "apps/icones/Dockerfile",
"context": "apps/icones",
"push": true
}
5. Generate Matrix
Creates GitHub Actions matrix:
{
"include": [
{ "variant": "latest", ... },
{ "variant": "dev", ... }
]
}
Environment Variables
Docker Hub username for image names.env:
DOCKERHUB_USERNAME: myuser
Generates: myuser/{app-name}
GitHub Container Registry username.env:
GHCR_USERNAME: myorg
Generates: ghcr.io/myorg/{app-name}
Aliyun Container Registry URL.env:
ALI_ACR_REGISTRY: registry.cn-hangzhou.aliyuncs.com
Aliyun Container Registry username.env:
ALI_ACR_USERNAME: myuser
Generates: {registry}/{username}/{app-name}
Matrix Fields
Each matrix item contains:
Variant name (e.g., latest, dev)
Docker metadata:
images: Array of image names
imageLines: Newline-separated image names
tags: Array of tags
tagLines: Newline-separated tags
labels: Array of OCI labels
labelLines: Newline-separated labels
Build configuration:
platforms: Array of platforms
platformLines: Newline-separated platforms
file: Dockerfile path
context: Build context path
push: Whether to push image
README push configuration:
push: Whether to push README
path: README file path
repo: Docker Hub repository
Whether to push to Docker Hub
Whether to push to GitHub Container Registry
Whether to push to Aliyun Container Registry
Whether pre-build script exists
Whether post-build script exists
Integration with Build Workflow
Pre-build Scripts
- name: Execute Pre-Build Commands
if: matrix.hasPreScript
working-directory: ${{ matrix.build.context }}
run: bash ${{ matrix.variant != 'latest' && format('pre.{0}.sh', matrix.variant) || 'pre.sh' }}
Post-build Scripts
- name: Execute Post-Build Commands
if: matrix.hasPostScript
working-directory: ${{ matrix.build.context }}
run: bash ${{ matrix.variant != 'latest' && format('post.{0}.sh', matrix.variant) || 'post.sh' }}
Best Practices
-
Set registry usernames in environment:
env:
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
GHCR_USERNAME: ${{ github.repository_owner }}
-
Use matrix conditionals for registry-specific steps:
if: ${{ matrix.pushDocker }}
-
Validate variants exist before building:
if: needs.metadata.outputs.matrix
-
Use proper permissions:
permissions:
contents: read
packages: write # For GHCR
-
Enable debug for troubleshooting:
Troubleshooting
No matrix output
- Verify
context points to valid app directory
- Check that variant has
version and sha set
- Ensure Dockerfile exists
- Enable
debug: true for details
Missing images in matrix
- Set registry usernames in environment variables
- Check
docker.images in meta.json
- Verify platforms are supported by buildx
- Check QEMU setup for multi-arch builds
- Consider reducing platforms for faster builds