1+ name : Build and Publish Docker Image
2+
3+ on :
4+ push :
5+ branches :
6+ - ' main'
7+ pull_request :
8+ branches :
9+ - ' main'
10+ workflow_dispatch :
11+ inputs :
12+ tag :
13+ description : ' Git ref (branch or tag) to build'
14+ required : true
15+ type : string
16+ pkcs11 :
17+ description : ' Bundle PKCS#11 provider libs (SoftHSM2 + OpenSC) in the runtime image'
18+ required : false
19+ default : false
20+ type : boolean
21+
22+ env :
23+ REGISTRY : ghcr.io
24+ ORG : ${{ github.repository_owner }}
25+ IMAGE_NAME : kms
26+
27+ concurrency :
28+ group : ${{ github.workflow }}-${{ github.ref }}
29+ cancel-in-progress : ${{ github.ref != 'refs/heads/main' }}
30+
31+ jobs :
32+ build :
33+ permissions :
34+ packages : write
35+ contents : read
36+ timeout-minutes : 60
37+ strategy :
38+ fail-fast : false
39+ matrix :
40+ include :
41+ - platform : linux/amd64
42+ runner : ubuntu-24.04
43+ - platform : linux/arm64
44+ runner : ubuntu-24.04-arm
45+ runs-on : ${{ matrix.runner }}
46+ name : build (${{ matrix.platform }})
47+ steps :
48+ - name : Checkout repository
49+ uses : actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
50+ with :
51+ # On workflow_dispatch, build the caller-specified ref. On push events
52+ # inputs.tag is empty, which checkout treats as the triggering commit.
53+ ref : ${{ inputs.tag }}
54+ # Full history + tags so the Makefile's git-describe version stamp works.
55+ fetch-depth : 0
56+
57+ - name : Prepare platform pair
58+ run : |
59+ platform="${{ matrix.platform }}"
60+ echo "PLATFORM_PAIR=${platform//\//-}" >> "$GITHUB_ENV"
61+
62+ - name : Resolve KMS_VERSION
63+ run : |
64+ # Same default as the Makefile's KMS_VERSION.
65+ echo "KMS_VERSION=$(git describe --tags --always --dirty 2>/dev/null || echo '0.1.0-dev')" >> "$GITHUB_ENV"
66+
67+ - name : Set up Docker Buildx
68+ uses : docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4.3.0
69+
70+ - name : Log in to Container Registry
71+ uses : docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
72+ with :
73+ registry : ${{ env.REGISTRY }}
74+ username : ${{ github.actor }}
75+ password : ${{ secrets.GITHUB_TOKEN }}
76+
77+ - name : Build and push by digest
78+ id : build
79+ uses : docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0
80+ with :
81+ context : .
82+ file : Dockerfile
83+ platforms : ${{ matrix.platform }}
84+ # Mirrors `make docker-build`.
85+ build-args : |
86+ KMS_VERSION=${{ env.KMS_VERSION }}
87+ PKCS11=${{ inputs.pkcs11 || false }}
88+ outputs : type=image,name=${{ env.REGISTRY }}/${{ env.ORG }}/${{ env.IMAGE_NAME }},push-by-digest=true,name-canonical=true,push=true
89+ cache-from : type=gha,scope=kms-${{ env.PLATFORM_PAIR }}
90+ cache-to : type=gha,mode=max,scope=kms-${{ env.PLATFORM_PAIR }}
91+
92+ - name : Export digest
93+ run : |
94+ mkdir -p "${{ runner.temp }}/digests"
95+ digest="${{ steps.build.outputs.digest }}"
96+ touch "${{ runner.temp }}/digests/${digest#sha256:}"
97+
98+ - name : Upload digest
99+ uses : actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
100+ with :
101+ name : digests-${{ env.PLATFORM_PAIR }}
102+ path : ${{ runner.temp }}/digests/*
103+ if-no-files-found : error
104+ retention-days : 1
105+
106+ merge :
107+ needs : build
108+ permissions :
109+ packages : write
110+ contents : read
111+ runs-on : ubuntu-latest
112+ timeout-minutes : 5
113+ env :
114+ TAG : ${{ github.event.inputs.tag || (github.event_name == 'push' && github.ref == 'refs/heads/main' && 'latest') || github.ref_name }}
115+ steps :
116+ - name : Download digests
117+ uses : actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
118+ with :
119+ path : ${{ runner.temp }}/digests
120+ pattern : digests-*
121+ merge-multiple : true
122+
123+ - name : Set up Docker Buildx
124+ uses : docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4.3.0
125+
126+ - name : Log in to Container Registry
127+ uses : docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
128+ with :
129+ registry : ${{ env.REGISTRY }}
130+ username : ${{ github.actor }}
131+ password : ${{ secrets.GITHUB_TOKEN }}
132+
133+ - name : Prepare Docker tag
134+ id : tags
135+ env :
136+ EVENT_NAME : ${{ github.event_name }}
137+ GIT_REF : ${{ github.ref }}
138+ run : |
139+ set -euo pipefail
140+ DOCKER_TAG="${TAG//[^a-zA-Z0-9._-]/-}"
141+ # Sanitizing can also yield `latest` from a ref named `latest`, so
142+ # re-check ownership after rewriting rather than before.
143+ if [[ "$DOCKER_TAG" == "latest" && ! ( "$EVENT_NAME" == "push" && "$GIT_REF" == "refs/heads/main" ) ]]; then
144+ echo "::error::refusing to publish 'latest' from $EVENT_NAME on $GIT_REF"
145+ exit 1
146+ fi
147+ # Reject what Docker would not accept as a tag, rather than letting
148+ # imagetools fail later with an opaque reference-parse error.
149+ if [[ ! "$DOCKER_TAG" =~ ^[a-zA-Z0-9_][a-zA-Z0-9._-]{0,127}$ ]]; then
150+ echo "::error::ref did not yield a usable image tag: $DOCKER_TAG"
151+ exit 1
152+ fi
153+ echo "DOCKER_TAG=$DOCKER_TAG" >> "$GITHUB_OUTPUT"
154+
155+ - name : Create manifest list and push
156+ working-directory : ${{ runner.temp }}/digests
157+ run : |
158+ docker buildx imagetools create \
159+ -t "${{ env.REGISTRY }}/${{ env.ORG }}/${{ env.IMAGE_NAME }}:${{ steps.tags.outputs.DOCKER_TAG }}" \
160+ $(printf '${{ env.REGISTRY }}/${{ env.ORG }}/${{ env.IMAGE_NAME }}@sha256:%s ' *)
161+
162+ - name : Inspect manifest
163+ run : |
164+ docker buildx imagetools inspect \
165+ "${{ env.REGISTRY }}/${{ env.ORG }}/${{ env.IMAGE_NAME }}:${{ steps.tags.outputs.DOCKER_TAG }}"
0 commit comments