99 pull_request :
1010 branches :
1111 - " master"
12+ workflow_dispatch :
13+ inputs :
14+ ref :
15+ description : " Tag to build (e.g. v0.1.0)"
16+ required : true
1217
1318env :
1419 IMAGE_NAME : ${{ github.repository }}
1520 REGISTRY : ghcr.io
1621
1722jobs :
18- build :
19- name : Build Docker image
20- runs-on : ubuntu-latest
23+ build-riverui :
24+ name : " Build Docker image: riverui "
25+ runs-on : ${{ matrix.runner }}
2126 strategy :
2227 matrix :
23- docker_platform :
24- - linux/amd64
25- - linux/arm64
26- - linux/arm64/v8
28+ include :
29+ - docker_platform : linux/amd64
30+ runner : ubuntu-latest
31+ - docker_platform : linux/arm64
32+ runner : ubuntu-24.04-arm64
2733 outputs :
2834 tags : ${{ steps.meta.outputs.tags }}
2935 labels : ${{ steps.meta.outputs.labels }}
@@ -36,15 +42,14 @@ jobs:
3642 steps :
3743 - name : Checkout
3844 uses : actions/checkout@v4
45+ with :
46+ ref : ${{ inputs.ref || github.ref }}
3947
4048 - name : Prepare
4149 run : |
4250 platform=${{ matrix.docker_platform }}
4351 echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
4452
45- - name : Set up QEMU
46- uses : docker/setup-qemu-action@v3
47-
4853 - name : Set up Docker Buildx
4954 uses : docker/setup-buildx-action@v3
5055
5964 id : meta
6065 uses : docker/metadata-action@v5
6166 with :
62- images : ghcr.io/${{ github.repository }}
67+ images : ghcr.io/${{ env.IMAGE_NAME }}
6368 labels : |
6469 org.opencontainers.image.source=https://github.com/riverqueue/riverui
6570 org.opencontainers.image.description="River UI is a web-based user interface for River, a fast and reliable background job system."
6974 type=ref,event=pr
7075 type=semver,pattern={{version}}
7176 type=semver,pattern={{major}}.{{minor}}
77+ type=raw,value=latest,enable=${{ startsWith(github.ref, 'refs/tags/v') }}
7278
7379 - name : Build and push to GitHub Container Registry
7480 id : build
@@ -98,30 +104,107 @@ jobs:
98104 - name : Upload digest
99105 uses : actions/upload-artifact@v4
100106 with :
101- name : digests-${{ env.PLATFORM_PAIR }}
107+ name : digests-oss- ${{ env.PLATFORM_PAIR }}
102108 path : /tmp/digests/*
103109 if-no-files-found : error
104110 retention-days : 1
105111
106- merge :
107- name : Merge and publish image manifests
108- runs-on : ubuntu-latest
109- needs :
110- - build
112+ build-riverproui :
113+ name : " Build Docker image: riverproui"
114+ runs-on : ${{ matrix.runner }}
115+ strategy :
116+ matrix :
117+ include :
118+ - docker_platform : linux/amd64
119+ runner : ubuntu-latest
120+ - docker_platform : linux/arm64
121+ runner : ubuntu-24.04-arm64
122+
111123 permissions :
124+ attestations : write
112125 contents : read
113126 id-token : write
114127 packages : write
115128
116129 steps :
117130 - name : Checkout
118131 uses : actions/checkout@v4
132+ with :
133+ ref : ${{ inputs.ref || github.ref }}
134+
135+ - name : Prepare
136+ run : |
137+ platform=${{ matrix.docker_platform }}
138+ echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
139+
140+ - name : Set TAG
141+ run : echo "TAG=${{ inputs.ref || github.ref_name }}" >> $GITHUB_ENV
142+
143+ - name : Validate TAG
144+ if : startsWith(github.ref, 'refs/tags/v') || github.event_name == 'workflow_dispatch'
145+ run : |
146+ if [[ ! "$TAG" == v* ]]; then
147+ echo "Tag must start with 'v'"
148+ exit 1
149+ fi
150+
151+ - name : Set up Docker Buildx
152+ uses : docker/setup-buildx-action@v3
153+
154+ - name : Build and push by digest
155+ id : build
156+ uses : docker/build-push-action@v6
157+ with :
158+ context : .
159+ file : Dockerfile.pro
160+ pull : true
161+ platforms : ${{ matrix.docker_platform }}
162+ labels : |
163+ org.opencontainers.image.source=https://github.com/riverqueue/riverui
164+ org.opencontainers.image.description=River UI Pro is a web-based user interface for River, with pro features.
165+ org.opencontainers.image.licenses=MPL-2.0
166+ # cache-from: type=registry,ref=riverqueue.com/riverproui:cache
167+ # cache-to: type=registry,ref=riverqueue.com/riverproui:cache,mode=max
168+ outputs : type=image,name=riverqueue.com/riverproui,push-by-digest=true,name-canonical=true,push=false,annotation-index.org.opencontainers.image.description=River UI Pro
169+ secrets : |
170+ "riverpro_credential=${{ secrets.RIVERPRO_GO_MOD_CREDENTIAL }}"
171+
172+ - name : Generate artifact attestation
173+ uses : actions/attest-build-provenance@v1
174+ with :
175+ push-to-registry : false
176+ subject-digest : ${{ steps.build.outputs.digest }}
177+ subject-name : riverqueue.com/riverproui
119178
179+ - name : Export digest
180+ run : |
181+ mkdir -p /tmp/digests
182+ digest="${{ steps.build.outputs.digest }}"
183+ touch "/tmp/digests/${digest#sha256:}"
184+
185+ - name : Upload digest
186+ uses : actions/upload-artifact@v4
187+ with :
188+ name : digests-pro-${{ env.PLATFORM_PAIR }}
189+ path : /tmp/digests/*
190+ if-no-files-found : error
191+ retention-days : 1
192+
193+ merge-riverui :
194+ name : " Merge Docker manifests: riverui"
195+ runs-on : ubuntu-latest
196+ needs :
197+ - build-riverui
198+ permissions :
199+ contents : read
200+ packages : write
201+
202+ steps :
120203 - name : Download digests
121204 uses : actions/download-artifact@v4
122205 with :
123206 path : /tmp/digests
124- pattern : digests-*
207+ pattern : digests-oss- *
125208 merge-multiple : true
126209
127210 - name : Set up Docker Buildx
@@ -138,7 +221,7 @@ jobs:
138221 id : meta
139222 uses : docker/metadata-action@v5
140223 with :
141- images : ghcr.io /${{ env.IMAGE_NAME }}
224+ images : ${{ env.REGISTRY }} /${{ env.IMAGE_NAME }}
142225 labels : |
143226 org.opencontainers.image.source=https://github.com/riverqueue/riverui
144227 org.opencontainers.image.description="River UI is a web-based user interface for River, a fast and reliable background job system."
@@ -148,13 +231,140 @@ jobs:
148231 type=ref,event=pr
149232 type=semver,pattern={{version}}
150233 type=semver,pattern={{major}}.{{minor}}
234+ type=raw,value=latest,enable=${{ startsWith(github.ref, 'refs/tags/v') }}
151235
152236 - name : Create manifest list and push
153237 working-directory : /tmp/digests
154238 run : |
155- docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
239+ docker buildx imagetools create \
240+ --annotation "index:org.opencontainers.image.source=https://github.com/riverqueue/riverui" \
241+ --annotation "index:org.opencontainers.image.description=River UI is a web-based user interface for River, a fast and reliable background job system." \
242+ --annotation "index:org.opencontainers.image.licenses=MPL-2.0" \
243+ $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
156244 $(printf '${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@sha256:%s ' *)
157245
158- - name : Inspect image
246+ merge-riverproui :
247+ name : " Merge Docker manifests: riverproui"
248+ runs-on : ubuntu-latest
249+ needs :
250+ - build-riverproui
251+ outputs :
252+ image-name : ${{ steps.meta-pro.outputs.tags }}
253+ digest : ${{ steps.merge.outputs.digest }}
254+ permissions :
255+ attestations : write
256+ contents : read
257+ id-token : write
258+
259+ steps :
260+ - name : Download digests
261+ uses : actions/download-artifact@v4
262+ with :
263+ path : /tmp/digests
264+ pattern : digests-pro-*
265+ merge-multiple : true
266+
267+ - name : Set up Docker Buildx
268+ uses : docker/setup-buildx-action@v3
269+
270+ - name : Docker meta for Pro
271+ id : meta-pro
272+ uses : docker/metadata-action@v5
273+ with :
274+ images : riverqueue.com/riverproui
275+ labels : |
276+ org.opencontainers.image.source=https://github.com/riverqueue/riverui
277+ org.opencontainers.image.description="River UI Pro is a web-based user interface for River, with pro features."
278+ org.opencontainers.image.licenses=MPL-2.0
279+ # TODO: type=ref are temporary for testing
280+ tags : |
281+ type=ref,event=branch
282+ type=ref,event=pr
283+ type=semver,pattern={{version}}
284+ type=semver,pattern={{major}}.{{minor}}
285+ type=raw,value=latest,enable=${{ startsWith(github.ref, 'refs/tags/v') }}
286+
287+ - name : Create manifest list and export to OCI
288+ id : merge
289+ run : |
290+ docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
291+ $(printf 'riverqueue.com/riverproui@sha256:%s ' /tmp/digests/*)
292+ docker buildx imagetools create --dry-run $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
293+ $(printf 'riverqueue.com/riverproui@sha256:%s ' /tmp/digests/*) > /tmp/oci
294+ echo "digest=$(sha256sum /tmp/oci | cut -d' ' -f1)" >> $GITHUB_OUTPUT
295+
296+ - name : Upload OCI artifact
297+ uses : actions/upload-artifact@v4
298+ with :
299+ name : pro-oci
300+ path : /tmp/oci
301+ retention-days : 1
302+
303+ publish-riverproui :
304+ name : " Publish Docker image: riverproui"
305+ runs-on : ubuntu-latest
306+ needs :
307+ - merge-riverproui
308+
309+ steps :
310+ - name : Checkout
311+ uses : actions/checkout@v4
312+ with :
313+ ref : ${{ inputs.ref || github.ref }}
314+
315+ - name : Download OCI artifact
316+ uses : actions/download-artifact@v4
317+ with :
318+ name : pro-oci
319+ path : /tmp/oci
320+
321+ - name : Install jq
322+ run : sudo apt-get update && sudo apt-get install -y jq
323+
324+ - name : Install AWS CLI
325+ uses : unfor19/install-aws-cli-action@v1
326+ with :
327+ version : 2
328+ verbose : false
329+ arch : amd64
330+
331+ - name : Configure AWS CLI
332+ run : |
333+ aws configure set aws_access_key_id ${{ secrets.R2_ACCESS_KEY_ID }}
334+ aws configure set aws_secret_access_key ${{ secrets.R2_SECRET_ACCESS_KEY }}
335+ aws configure set default.region auto
336+
337+ - name : Sync blobs to R2
338+ # TODO: temporary for testing
339+ # if: startsWith(github.ref, 'refs/tags/v') || github.event_name == 'workflow_dispatch'
340+ env :
341+ DOCKER_RELEASE_STORAGE_BUCKET : ${{ vars.DOCKER_RELEASE_STORAGE_BUCKET }}
342+ REPO_NAME : riverqueue.com/riverproui
343+ ENDPOINT_URL : ${{ secrets.R2_DOCKER_RELEASES_ENDPOINT_URL }}
344+ run : |
345+ aws s3 cp /tmp/oci/blobs/sha256/ s3://$DOCKER_RELEASE_STORAGE_BUCKET/$REPO_NAME/blobs/sha256/ --recursive --endpoint-url $ENDPOINT_URL
346+
347+ - name : Upload manifest by digest
348+ # TODO: temporary for testing
349+ # if: startsWith(github.ref, 'refs/tags/v') || github.event_name == 'workflow_dispatch'
350+ env :
351+ DOCKER_RELEASE_STORAGE_BUCKET : ${{ vars.DOCKER_RELEASE_STORAGE_BUCKET }}
352+ REPO_NAME : riverqueue.com/riverproui
353+ ENDPOINT_URL : ${{ secrets.R2_DOCKER_RELEASES_ENDPOINT_URL }}
354+ run : |
355+ MANIFEST_DIGEST=$(sha256sum /tmp/oci/index.json | awk '{print $1}')
356+ aws s3 cp /tmp/oci/index.json s3://$DOCKER_RELEASE_STORAGE_BUCKET/$REPO_NAME/manifests/sha256:$MANIFEST_DIGEST --content-type "application/vnd.docker.distribution.manifest.list.v2+json" --endpoint-url $ENDPOINT_URL
357+
358+ - name : Upload tagged manifests
359+ # TODO: temporary for testing
360+ # if: startsWith(github.ref, 'refs/tags/v') || github.event_name == 'workflow_dispatch'
361+ env :
362+ DOCKER_RELEASE_STORAGE_BUCKET : ${{ vars.DOCKER_RELEASE_STORAGE_BUCKET }}
363+ REPO_NAME : riverqueue.com/riverproui
364+ ENDPOINT_URL : ${{ secrets.R2_DOCKER_RELEASES_ENDPOINT_URL }}
159365 run : |
160- docker buildx imagetools inspect ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.version }}
366+ # Extract tags from the merge job output
367+ TAGS=$(echo '${{ needs.merge-riverproui.outputs.image-name }}' | tr ' ' '\n' | sed 's|riverqueue.com/riverproui:||' | grep -v '^$')
368+ for TAG in $TAGS; do
369+ aws s3 cp /tmp/oci/index.json s3://$DOCKER_RELEASE_STORAGE_BUCKET/$REPO_NAME/manifests/$TAG --content-type "application/vnd.docker.distribution.manifest.list.v2+json" --endpoint-url $ENDPOINT_URL
370+ done
0 commit comments