1
+ name : Build Container Image
2
+
3
+ on :
4
+ workflow_call :
5
+ inputs :
6
+ registry :
7
+ description : ' Container registry'
8
+ required : true
9
+ type : string
10
+ name :
11
+ description : ' Image name, without registry or tags (repo/name)'
12
+ required : true
13
+ type : string
14
+ tag :
15
+ description : ' Tag for manifest creation'
16
+ required : true
17
+ type : string
18
+ build-runners :
19
+ description : ' JSON array of runners to build on (e.g. ["ubuntu-24.04", "ubuntu-24.04-arm"])'
20
+ required : false
21
+ type : string
22
+ default : ' ["ubuntu-24.04", "ubuntu-24.04-arm"]'
23
+ merge-runner :
24
+ description : ' Runner for merge job'
25
+ required : false
26
+ type : string
27
+ default : ' ubuntu-24.04'
28
+ dockerfile :
29
+ description : ' Path to Dockerfile'
30
+ required : false
31
+ type : string
32
+ default : ' Dockerfile'
33
+ context :
34
+ description : ' Build context path'
35
+ required : false
36
+ type : string
37
+ default : ' .'
38
+ build-args :
39
+ description : ' Build arguments as multi-line string (e.g. "ARG1=value1\nARG2=value2")'
40
+ required : false
41
+ type : string
42
+ default : ' '
43
+ build-args-for-arch :
44
+ description : ' Architecture-specific build arguments as JSON object (e.g. {"x86_64": "ARG1=value1\nARG2=value2", "aarch64": "ARG3=value3"})'
45
+ required : false
46
+ type : string
47
+ default : ' {}'
48
+ target :
49
+ description : ' Target stage in multi-stage Dockerfile'
50
+ required : false
51
+ type : string
52
+ default : ' '
53
+ buildkit-config :
54
+ description : ' BuildKit daemon configuration'
55
+ required : false
56
+ type : string
57
+ default : ' '
58
+ cache-prefix :
59
+ description : ' Prefix for cache image names'
60
+ required : false
61
+ type : string
62
+ default : ' '
63
+ update-cache :
64
+ description : ' Enable cache-to for pushing updated cache layers'
65
+ required : false
66
+ type : boolean
67
+ default : false
68
+ artifact-name :
69
+ description : ' Name of the artifact to be downloaded before build'
70
+ required : false
71
+ type : string
72
+ default : ' '
73
+ artifact-path :
74
+ description : ' Directory where the artifact should be unpacked'
75
+ required : false
76
+ type : string
77
+ default : ' .'
78
+ checkout-submodules :
79
+ description : ' Whether to checkout git submodules'
80
+ required : false
81
+ type : boolean
82
+ default : false
83
+ checkout-ref :
84
+ description : ' Git ref to checkout'
85
+ required : false
86
+ type : string
87
+ default : ' '
88
+ checkout-token :
89
+ description : ' GitHub token for checkout'
90
+ required : false
91
+ type : string
92
+ default : ' '
93
+ checkout-path :
94
+ description : ' Path to checkout'
95
+ required : false
96
+ type : string
97
+ default : ' .'
98
+
99
+ outputs :
100
+ digest :
101
+ description : ' Digest of the multi-arch image'
102
+ value : ${{ jobs.merge.outputs.digest }}
103
+ ref-with-digest :
104
+ description : ' Full image reference with digest'
105
+ value : ${{ jobs.merge.outputs.ref-with-digest }}
106
+
107
+ secrets :
108
+ registry-user :
109
+ required : false
110
+ registry-password :
111
+ required : false
112
+ build-secrets :
113
+ description : ' Build secrets as a multi-line string (e.g. "SECRET1=value1\nSECRET2=value2")'
114
+ required : false
115
+
116
+ permissions : {}
117
+
118
+ defaults :
119
+ run :
120
+ shell : bash -euo pipefail {0}
121
+
122
+ jobs :
123
+ build-image :
124
+ runs-on : ${{ matrix.runner }}
125
+
126
+ strategy :
127
+ fail-fast : false
128
+ matrix :
129
+ runner : ${{ fromJSON(inputs.build-runners) }}
130
+
131
+ permissions :
132
+ contents : read
133
+ packages : write
134
+
135
+ outputs :
136
+ digest_x86_64 : ${{ steps.digest.outputs.digest_x86_64 }}
137
+ digest_aarch64 : ${{ steps.digest.outputs.digest_aarch64 }}
138
+
139
+ steps :
140
+ - name : Checkout
141
+ uses : actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
142
+ with :
143
+ submodules : ${{ inputs.checkout-submodules }}
144
+ ref : ${{ inputs.checkout-ref }}
145
+ token : ${{ inputs.checkout-token || github.token }}
146
+ path : ${{ inputs.checkout-path }}
147
+
148
+ - name : Download artifact
149
+ if : ${{ inputs.artifact-name != '' }}
150
+ uses : actions/download-artifact@v4
151
+ with :
152
+ name : ${{ inputs.artifact-name }}
153
+ path : ${{ inputs.artifact-path }}
154
+
155
+ - name : Set up Docker Buildx
156
+ uses : docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0
157
+ with :
158
+ cache-binary : false
159
+ buildkitd-config-inline : ${{ inputs.buildkit-config || '' }}
160
+
161
+ - name : Login to Container Registry
162
+ uses : docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
163
+ with :
164
+ registry : ${{ inputs.registry }}
165
+ username : ${{ secrets.registry-user != '' && secrets.registry-user || (inputs.registry == 'ghcr.io' && github.actor) }}
166
+ password : ${{ secrets.registry-password != '' && secrets.registry-password || (inputs.registry == 'ghcr.io' && github.token) }}
167
+
168
+ - name : Detect architecture
169
+ id : arch
170
+ run : |
171
+ arch=$(uname -m)
172
+ echo "arch=${arch}" | tee -a "$GITHUB_OUTPUT"
173
+
174
+ - name : Build and push architecture-specific image
175
+ id : build
176
+ env :
177
+ CACHE_KEY : ${{ inputs.cache-prefix && format('type=registry,ref={0}/{1}:{2}-{3}', inputs.registry, inputs.name, inputs.cache-prefix, steps.arch.outputs.arch) || '' }}
178
+ uses : docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
179
+ with :
180
+ context : ${{ inputs.context }}
181
+ file : ${{ inputs.dockerfile }}
182
+ push : true
183
+ pull : true
184
+ target : ${{ inputs.target }}
185
+ build-args : ${{ format('{0}\n{1}', inputs.build-args, fromJson(inputs.build-args-for-arch)[steps.arch.outputs.arch] || '') }}
186
+ secrets : ${{ secrets.build-secrets }}
187
+ cache-from : ${{ env.CACHE_KEY }}
188
+ cache-to : ${{ inputs.update-cache == 'true' && format('{0},image-manifest=true,oci-mediatypes=true,mode=max', env.CACHE_KEY) || '' }}
189
+ attests : |
190
+ type=provenance,mode=max
191
+ type=sbom,generator=${{ contains(inputs.registry, 'databricks.com') && format('{0}/brickstore/neon/docker/buildkit-syft-scanner:1', inputs.registry) || 'docker.io/docker/buildkit-syft-scanner:1' }}
192
+ outputs : type=registry,name=${{ inputs.registry }}/${{ inputs.name }},push-by-digest=true,name-canonical=true
193
+
194
+ - name : Export digest for architecture
195
+ id : digest
196
+ run : |
197
+ digest="${{ steps.build.outputs.digest }}"
198
+ echo "digest_$(uname -m)=${digest}" | tee -a "$GITHUB_OUTPUT"
199
+
200
+ merge :
201
+ runs-on : ${{ inputs.merge-runner }}
202
+ needs : [build-image]
203
+ if : always() && !cancelled() && !failure()
204
+
205
+ permissions :
206
+ contents : read
207
+ packages : write
208
+
209
+ env :
210
+ IMAGE_REF : ${{ inputs.registry }}/${{ inputs.name }}
211
+
212
+ outputs :
213
+ digest : ${{ steps.merge.outputs.digest }}
214
+ ref-with-digest : ${{ steps.merge.outputs.ref-with-digest }}
215
+
216
+ steps :
217
+ - name : Checkout
218
+ uses : actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
219
+
220
+ - name : Set up Docker Buildx
221
+ uses : docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0
222
+
223
+ - name : Login to Container Registry
224
+ uses : docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
225
+ with :
226
+ registry : ${{ inputs.registry }}
227
+ username : ${{ secrets.registry-user != '' && secrets.registry-user || (inputs.registry == 'ghcr.io' && github.actor) }}
228
+ password : ${{ secrets.registry-password != '' && secrets.registry-password || (inputs.registry == 'ghcr.io' && github.token) }}
229
+
230
+ - name : Prepare references
231
+ id : prepare
232
+ env :
233
+ DIGEST_X86_64 : ${{ needs.build-image.outputs.digest_x86_64 }}
234
+ DIGEST_AARCH64 : ${{ needs.build-image.outputs.digest_aarch64 }}
235
+ run : |
236
+ # Parse environment variables and create references in one go
237
+ references_data=$(printenv | jq -Rsc '[split("\n")[] | capture("^DIGEST_(?<arch>[^=]+)=(?<digest>.+)$") | .arch |= ascii_downcase]')
238
+
239
+ # Verify we have at least one digest
240
+ if [[ "$(echo "$references_data" | jq -r 'length')" -eq 0 ]]; then
241
+ echo "::error::No digest values found! Cannot create manifest list without at least one input reference."
242
+ exit 1
243
+ fi
244
+
245
+ # Log found architectures and their digests
246
+ echo "$references_data" | jq -r '.[] | "Found digest for \(.arch): \(.digest)"'
247
+
248
+ # Create space-separated references string for the composite action
249
+ references_string=$(echo "$references_data" | jq -r --arg ref "$IMAGE_REF" 'map($ref + "@" + .digest) | join(" ")')
250
+ echo "references=${references_string}" >> "$GITHUB_OUTPUT"
251
+
252
+ - name : Merge OCI manifest lists
253
+ id : merge
254
+ uses : ./merge-oci-manifest-lists
255
+ with :
256
+ target : ${{ inputs.registry }}/${{ inputs.name }}:${{ inputs.tag }}
257
+ references : ${{ steps.prepare.outputs.references }}
258
+
259
+ - name : Fetch manifest digest references
260
+ id : digests
261
+ uses : ./fetch-oci-manifest-list-digest-references
0 commit comments