diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..0502b5fe3 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,28 @@ +# 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 + diff --git a/.github/workflows/Merge.yml b/.github/workflows/Merge.yml old mode 100644 new mode 100755 index a8b49d3ad..327137e63 --- a/.github/workflows/Merge.yml +++ b/.github/workflows/Merge.yml @@ -20,3 +20,10 @@ jobs: if: ${{ always() && contains(join(needs.*.result, ','), 'success') }} needs: [build, vet, unit-test] uses: ./.github/workflows/integration.yml + docker: + if: ${{ always() && contains(join(needs.*.result, ','), 'success') }} + needs: [build, vet, unit-test] + uses: ./.github/workflows/docker.yml + secrets: + DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }} + DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }} diff --git a/.github/workflows/PR.yml b/.github/workflows/PR.yml old mode 100644 new mode 100755 index d4c5905e5..e79fdb188 --- a/.github/workflows/PR.yml +++ b/.github/workflows/PR.yml @@ -23,3 +23,10 @@ jobs: if: ${{ always() && contains(join(needs.*.result, ','), 'success') }} needs: [build, vet, unit-test] uses: ./.github/workflows/integration.yml + docker: + if: ${{ always() && contains(join(needs.*.result, ','), 'success') }} + needs: [build, vet, unit-test] + uses: ./.github/workflows/docker.yml + secrets: + DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }} + DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }} diff --git a/.github/workflows/Release.yml b/.github/workflows/Release.yml old mode 100644 new mode 100755 index 06de89e61..f008cd46a --- a/.github/workflows/Release.yml +++ b/.github/workflows/Release.yml @@ -29,6 +29,13 @@ jobs: if: ${{ always() && contains(join(needs.*.result, ','), 'success') }} needs: [build, e2e, integration, unit-test, vet] uses: ./.github/workflows/publish.yml + docker: + if: ${{ always() && contains(join(needs.*.result, ','), 'success') }} + needs: [build, e2e, integration, unit-test, vet] + uses: ./.github/workflows/docker.yml + secrets: + DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }} + DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }} docs: if: ${{ always() && contains(join(needs.*.result, ','), 'success') }} needs: [build, e2e, integration, unit-test, vet] diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 000000000..f1e89f2b1 --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,127 @@ +name: Docker Test, Build & Push + +on: + workflow_call: + secrets: + DOCKERHUB_USERNAME: + required: true + description: "Docker Hub username" + DOCKERHUB_TOKEN: + required: true + description: "Docker Hub access token" + +env: + IMAGE_NAME: mirantis/cri-dockerd + +jobs: + # ----------------------------------------------------------- + # Stage 1 – Run unit tests inside a Docker container + # ----------------------------------------------------------- + docker-test: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Load environment + uses: c-py/action-dotenv-to-setenv@v4 + with: + env-file: .github/.env + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build test image + uses: docker/build-push-action@v6 + with: + context: . + file: ./Dockerfile.test + build-args: | + GO_VERSION=${{ env.GO_VERSION }} + push: false + load: true + tags: cri-dockerd-test:ci + cache-from: type=gha + cache-to: type=gha,mode=max + + - name: Run unit tests in container + run: docker run --rm cri-dockerd-test:ci + + # ----------------------------------------------------------- + # Stage 2 – Build multi-arch image & push to Docker Hub + # ----------------------------------------------------------- + docker-build-push: + runs-on: ubuntu-latest + needs: [docker-test] + permissions: + contents: read + packages: write + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: 0 # needed for git describe + + - name: Load environment + uses: c-py/action-dotenv-to-setenv@v4 + with: + env-file: .github/.env + + - name: Set version metadata + id: meta + run: | + VERSION=$(git describe --tags 2>/dev/null | sed 's/^v//' || echo "dev") + REVISION=$(git log -1 --pretty='%h') + PRERELEASE=$(echo "${VERSION}" | grep -q dev && echo "pre" || echo "") + echo "version=${VERSION}" >> "$GITHUB_OUTPUT" + echo "revision=${REVISION}" >> "$GITHUB_OUTPUT" + echo "prerelease=${PRERELEASE}" >> "$GITHUB_OUTPUT" + + - 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 + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Docker metadata (tags & labels) + id: docker_meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.IMAGE_NAME }} + tags: | + # tag semver on release tags + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + # branch name + type=ref,event=branch + # short SHA + type=sha,prefix= + # "latest" on default branch + type=raw,value=latest,enable={{is_default_branch}} + + - name: Build and push + uses: docker/build-push-action@v6 + with: + context: . + file: ./Dockerfile + platforms: linux/amd64,linux/arm64 + push: true + tags: ${{ steps.docker_meta.outputs.tags }} + labels: ${{ steps.docker_meta.outputs.labels }} + build-args: | + GO_VERSION=${{ env.GO_VERSION }} + VERSION=${{ steps.meta.outputs.version }} + REVISION=${{ steps.meta.outputs.revision }} + PRERELEASE=${{ steps.meta.outputs.prerelease }} + cache-from: type=gha + cache-to: type=gha,mode=max + + - name: Print image digest + run: echo "Image pushed with digest ${{ steps.docker_meta.outputs.digest }}" + diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 000000000..27ee3b9ab --- /dev/null +++ b/Dockerfile @@ -0,0 +1,44 @@ +# syntax=docker/dockerfile:1 + +# ---- Build Stage ---- +ARG GO_VERSION=1.24.9 +FROM golang:${GO_VERSION}-bookworm AS builder + +ARG VERSION="" +ARG REVISION="" +ARG PRERELEASE="" +ARG TARGETOS=linux +ARG TARGETARCH=amd64 + +WORKDIR /go/src/github.com/Mirantis/cri-dockerd + +# Cache Go modules +COPY go.mod go.sum ./ +RUN go mod download + +# Copy source +COPY . . + +# Build the binary +RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -trimpath \ + -ldflags "-s -w \ + -X github.com/Mirantis/cri-dockerd/cmd/version.Version=${VERSION} \ + -X github.com/Mirantis/cri-dockerd/cmd/version.PreRelease=${PRERELEASE} \ + -X github.com/Mirantis/cri-dockerd/cmd/version.GitCommit=${REVISION}" \ + -o /usr/local/bin/cri-dockerd + +# ---- Test Stage ---- +FROM builder AS test +RUN go test ./... + +# ---- Final Stage ---- +FROM debian:bookworm-slim AS runtime + +RUN apt-get update && apt-get install -y --no-install-recommends \ + ca-certificates \ + && rm -rf /var/lib/apt/lists/* + +COPY --from=builder /usr/local/bin/cri-dockerd /usr/local/bin/cri-dockerd + +ENTRYPOINT ["cri-dockerd"] + diff --git a/Dockerfile.test b/Dockerfile.test new file mode 100644 index 000000000..5f407db48 --- /dev/null +++ b/Dockerfile.test @@ -0,0 +1,18 @@ +# syntax=docker/dockerfile:1 + +# Dockerfile for running unit tests in CI +ARG GO_VERSION=1.24.9 +FROM golang:${GO_VERSION}-bookworm + +WORKDIR /go/src/github.com/Mirantis/cri-dockerd + +# Cache Go modules +COPY go.mod go.sum ./ +RUN go mod download + +# Copy source +COPY . . + +# Default command: run all unit tests +CMD ["go", "test", "-v", "./..."] +