Conversation
- Dockerfile: Multi-stage build with builder, test, and runtime stages
- Builder stage compiles cri-dockerd as a static Go binary with
version/revision ldflags using golang:1.24.9-bookworm
- Test stage runs the full unit test suite (targetable via --target test)
- Runtime stage produces a minimal debian:bookworm-slim image with
only ca-certificates and the cri-dockerd binary
- Supports multi-arch builds (linux/amd64, linux/arm64) via TARGETOS
and TARGETARCH build args
- Dockerfile.test: Lightweight standalone test image that runs
'go test -v ./...' for isolated CI test execution
- .dockerignore: Excludes .git, docs, packaging artifacts, IDE files,
and markdown from the Docker build context to optimize build speed
Add a reusable GitHub Actions workflow (.github/workflows/docker.yml)
that provides a complete Docker CI/CD pipeline with two stages:
Stage 1 - docker-test:
- Builds the Dockerfile.test image using Docker Buildx
- Runs the full unit test suite inside an isolated container
- Uses GitHub Actions cache (GHA) for layer caching
Stage 2 - docker-build-push:
- Runs only after tests pass successfully
- Extracts version metadata from git tags (semver, revision, prerelease)
- Sets up QEMU and Buildx for multi-architecture builds
- Authenticates to Docker Hub via repository secrets
- Generates smart image tags using docker/metadata-action:
semver, major.minor, branch name, short SHA, and latest
- Builds and pushes linux/amd64 and linux/arm64 images
- Passes Go version and build metadata as build args
Requires DOCKERHUB_USERNAME and DOCKERHUB_TOKEN repository secrets.
Wire the new docker.yml reusable workflow into the three main CI entry points so Docker images are automatically tested, built, and pushed to Docker Hub as part of the standard development lifecycle: - Release.yml: Adds docker job after all quality gates pass (build, e2e, integration, unit-test, vet). Docker images are tagged with semver from the release tag and pushed to Docker Hub. - Merge.yml: Adds docker job on push to master branch after build, vet, and unit-test succeed. Pushes images tagged with branch name (master) and latest. - PR.yml: Adds docker job on pull requests to run containerized tests and validate the Docker build without pushing. All three pass DOCKERHUB_USERNAME and DOCKERHUB_TOKEN secrets to the reusable workflow.
There was a problem hiding this comment.
Pull request overview
This PR introduces Docker-based CI/CD support for cri-dockerd, adding Dockerfiles for building/testing, a reusable GitHub Actions workflow for Docker test/build/push, and wiring that workflow into the existing PR, Merge, and Release pipelines.
Changes:
- Added
Dockerfile(multi-stage: builder → test → runtime) andDockerfile.testfor containerized unit testing - Added
.github/workflows/docker.ymlas a reusable workflow that runs unit tests in Docker and then builds/pushes a multi-arch image to Docker Hub - Integrated the new
docker.ymlworkflow intoPR.yml,Merge.yml, andRelease.yml, with a.dockerignoreto trim the build context
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
Dockerfile |
Multi-stage build: Go builder, unused test stage, and debian-slim runtime |
Dockerfile.test |
Standalone Dockerfile for running unit tests in CI |
.dockerignore |
Reduces build context by excluding non-source files |
.github/workflows/docker.yml |
Reusable workflow: tests via Dockerfile.test, then builds & pushes multi-arch image |
.github/workflows/PR.yml |
Adds docker job call, gated on build/vet/unit-test success |
.github/workflows/Merge.yml |
Adds docker job call, gated on build/vet/unit-test success |
.github/workflows/Release.yml |
Adds docker job call, gated on all pre-release tests |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| - name: Print image digest | ||
| run: echo "Image pushed with digest ${{ steps.docker_meta.outputs.digest }}" |
There was a problem hiding this comment.
The "Print image digest" step incorrectly references steps.docker_meta.outputs.digest. The docker_meta step id refers to the docker/metadata-action@v5 step, which does not produce a digest output. The image digest is an output of the docker/build-push-action@v6 "Build and push" step. To fix this, add an id (e.g., id: build) to the "Build and push" step and then reference it as steps.build.outputs.digest.
| context: . | ||
| file: ./Dockerfile | ||
| platforms: linux/amd64,linux/arm64 | ||
| push: true |
There was a problem hiding this comment.
The docker-build-push job unconditionally sets push: true, which means it will push an image to Docker Hub on every PR event (since docker.yml is also called from PR.yml). This is likely unintended — PR builds should not publish images to the public registry. Consider conditionally setting push based on the GitHub event type, for example by adding push: ${{ github.event_name != 'pull_request' }}.
| push: true | |
| push: ${{ github.event_name != 'pull_request' }} |
| # ---- Test Stage ---- | ||
| FROM builder AS test | ||
| RUN go test ./... | ||
|
|
There was a problem hiding this comment.
The "Test Stage" (FROM builder AS test) defined in Dockerfile is never actually used. The CI pipeline runs tests via Dockerfile.test in the docker-test job, and the docker-build-push job builds only the final runtime stage. If the intent is to run tests as part of the main Docker build, then the workflow should add a --target test step; otherwise this stage is unused dead code and should be removed to avoid confusion.
| # ---- Test Stage ---- | |
| FROM builder AS test | |
| RUN go test ./... |
| - name: Set version metadata | ||
| id: meta | ||
| run: | | ||
| VERSION=$(git describe --tags 2>/dev/null | sed 's/^v//' || echo "dev") |
There was a problem hiding this comment.
The VERSION detection logic is broken when no git tags exist. The command git describe --tags 2>/dev/null | sed 's/^v//' || echo "dev" evaluates the exit code of sed (the last command in the pipe), not git describe. Since sed always succeeds even on empty input, || echo "dev" is never reached, and VERSION will be an empty string instead of "dev" when no tags exist. To fix this, either capture git describe output in a variable first (e.g., VERSION=$(git describe --tags 2>/dev/null) && VERSION=${VERSION#v} || VERSION=dev) or use set -o pipefail at the start of the run block.
| VERSION=$(git describe --tags 2>/dev/null | sed 's/^v//' || echo "dev") | |
| VERSION=$(git describe --tags 2>/dev/null) && VERSION=${VERSION#v} || VERSION=dev |
| # Git | ||
| .git | ||
| .github | ||
| .gitignore | ||
|
|
||
| # Build artifacts | ||
| build/ | ||
| packaging/deb/ | ||
| packaging/rpm/ | ||
| packaging/windows/ | ||
|
|
||
| # Documentation | ||
| docs/ | ||
|
|
||
| # IDE & editor | ||
| .idea/ | ||
| *.swp | ||
| *.swo | ||
| *~ | ||
|
|
||
| # OS files | ||
| .DS_Store | ||
| Thumbs.db | ||
|
|
||
| # Markdown (not needed in image) | ||
| *.md | ||
| LICENSE | ||
|
|
There was a problem hiding this comment.
The vendor/ directory is not excluded from the Docker build context in .dockerignore. Since the Dockerfiles use go mod download to fetch dependencies, the vendor/ directory is not needed and unnecessarily inflates the build context. Adding vendor/ to .dockerignore would speed up Docker builds significantly.
Fixes #
Proposed Changes