1- name : Docker
1+ name : Docker - riverproui
22
33on :
44 push :
55 branches :
66 - " master"
77 tags :
8- - " v*"
8+ - " riverproui/ v*"
99 pull_request :
1010 branches :
1111 - " master"
1919 ECR_REGION : us-east-2
2020
2121jobs :
22- build-riverui :
23- name : " Build image: riverui"
24- runs-on : ubuntu-latest
25- env :
26- IMAGE_NAME : ${{ github.repository }}
27- REGISTRY : ghcr.io
28- strategy :
29- matrix :
30- docker_platform :
31- - linux/amd64
32- - linux/arm64
33- outputs :
34- tags : ${{ steps.meta.outputs.tags }}
35- labels : ${{ steps.meta.outputs.labels }}
36- permissions :
37- attestations : write
38- contents : read
39- id-token : write
40- packages : write
41-
42- steps :
43- - name : Checkout
44- uses : actions/checkout@v4
45- with :
46- ref : ${{ inputs.ref || github.ref }}
47-
48- - name : Prepare
49- run : |
50- platform=${{ matrix.docker_platform }}
51- echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
52-
53- - name : Set up QEMU
54- uses : docker/setup-qemu-action@v3
55-
56- - name : Set up Docker Buildx
57- uses : docker/setup-buildx-action@v3
58-
59- - name : Login to GitHub Container Registry
60- uses : docker/login-action@v3
61- with :
62- registry : ${{ env.REGISTRY }}
63- username : ${{ github.actor }}
64- password : ${{ secrets.GITHUB_TOKEN }}
65-
66- - name : Docker meta
67- id : meta
68- uses : docker/metadata-action@v5
69- with :
70- images : ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
71- labels : |
72- org.opencontainers.image.source=https://github.com/riverqueue/riverui
73- org.opencontainers.image.description="River UI is a web-based user interface for River, a fast and reliable background job system."
74- org.opencontainers.image.licenses=MPL-2.0
75- tags : |
76- type=ref,event=branch
77- type=ref,event=pr
78- type=semver,pattern={{version}}
79- type=semver,pattern={{major}}.{{minor}}
80- type=raw,value=latest,enable=${{ startsWith(github.ref, 'refs/tags/v') }}
81-
82- - name : Build and push to GitHub Container Registry
83- id : build
84- uses : docker/build-push-action@v6
85- with :
86- context : .
87- pull : true
88- platforms : ${{ matrix.docker_platform }}
89- labels : ${{ steps.meta.outputs.labels }}
90- cache-from : type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:cache-${{ env.PLATFORM_PAIR }}
91- cache-to : type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:cache-${{ env.PLATFORM_PAIR }},mode=max
92- outputs : type=image,name=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }},push-by-digest=true,name-canonical=true,push=true,annotation-index.org.opencontainers.image.description=River UI
93-
94- - name : Generate artifact attestation
95- uses : actions/attest-build-provenance@v2
96- with :
97- push-to-registry : true
98- subject-digest : ${{ steps.build.outputs.digest }}
99- subject-name : ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
100-
101- - name : Export digest
102- run : |
103- mkdir -p /tmp/digests
104- digest="${{ steps.build.outputs.digest }}"
105- touch "/tmp/digests/${digest#sha256:}"
106-
107- - name : Upload digest
108- uses : actions/upload-artifact@v4
109- with :
110- name : digests-oss-${{ env.PLATFORM_PAIR }}
111- path : /tmp/digests/*
112- if-no-files-found : error
113- retention-days : 1
114-
11522 build-riverproui :
11623 name : " Build image: riverproui"
11724 runs-on : ubuntu-latest
@@ -194,62 +101,6 @@ jobs:
194101 if-no-files-found : error
195102 retention-days : 1
196103
197- merge-riverui :
198- name : " Merge manifests: riverui"
199- runs-on : ubuntu-latest
200- env :
201- IMAGE_NAME : ${{ github.repository }}
202- REGISTRY : ghcr.io
203- needs :
204- - build-riverui
205- permissions :
206- contents : read
207- packages : write
208-
209- steps :
210- - name : Download digests
211- uses : actions/download-artifact@v4
212- with :
213- path : /tmp/digests
214- pattern : digests-oss-*
215- merge-multiple : true
216-
217- - name : Set up Docker Buildx
218- uses : docker/setup-buildx-action@v3
219-
220- - name : Login to GitHub Container Registry
221- uses : docker/login-action@v3
222- with :
223- registry : ${{ env.REGISTRY }}
224- username : ${{ github.actor }}
225- password : ${{ secrets.GITHUB_TOKEN }}
226-
227- - name : Docker meta
228- id : meta
229- uses : docker/metadata-action@v5
230- with :
231- images : ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
232- labels : |
233- org.opencontainers.image.source=https://github.com/riverqueue/riverui
234- org.opencontainers.image.description="River UI is a web-based user interface for River, a fast and reliable background job system."
235- org.opencontainers.image.licenses=MPL-2.0
236- tags : |
237- type=ref,event=branch
238- type=ref,event=pr
239- type=semver,pattern={{version}}
240- type=semver,pattern={{major}}.{{minor}}
241- type=raw,value=latest,enable=${{ startsWith(github.ref, 'refs/tags/v') }}
242-
243- - name : Create manifest list and push
244- working-directory : /tmp/digests
245- run : |
246- docker buildx imagetools create \
247- --annotation "index:org.opencontainers.image.source=https://github.com/riverqueue/riverui" \
248- --annotation "index:org.opencontainers.image.description=River UI is a web-based user interface for River, a fast and reliable background job system." \
249- --annotation "index:org.opencontainers.image.licenses=MPL-2.0" \
250- $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
251- $(printf '${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@sha256:%s ' *)
252-
253104 merge-riverproui :
254105 name : " Merge manifests: riverproui"
255106 runs-on : ubuntu-latest
@@ -267,6 +118,9 @@ jobs:
267118 outputs :
268119 manifest_digest : ${{ steps.manifest_digest.outputs.digest }}
269120 tag : ${{ steps.compute_tag.outputs.tag }}
121+ immutable_tags : ${{ steps.export_immutable_tags.outputs.tags }}
122+ mutable_tags : ${{ steps.tag_mutable.outputs.tags }}
123+
270124 steps :
271125 - name : Checkout full history (no tags yet)
272126 uses : actions/checkout@v4
@@ -306,8 +160,8 @@ jobs:
306160 - name : Compute TAG
307161 id : compute_tag
308162 run : |
309- if [[ "${{ github.ref }}" =~ ^refs/tags/riverproui/v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
310- TAG="${GITHUB_REF_NAME#riverproui/}"
163+ if [[ "${{ github.ref }}" =~ ^refs/tags/riverproui/v[0-9]+\.[0-9]+\.[0-9]+(-[a-z0-9\.-]+)? $ ]]; then
164+ TAG="${GITHUB_REF_NAME#riverproui/v }"
311165 elif [ "${{ github.event_name }}" = "pull_request" ]; then
312166 TAG="pr-${{ github.event.pull_request.number }}"
313167 else
@@ -330,7 +184,7 @@ jobs:
330184 type=ref,event=branch
331185 type=ref,event=pr
332186 type=sha,pattern=sha-{{sha}}
333- type=semver,pattern=v {{version}},match=riverproui/(v .*)
187+ type=semver,pattern={{version}},match=riverproui/v( .*)
334188
335189 - name : Create manifest list and push immutable tags
336190 working-directory : /tmp/digests
@@ -342,27 +196,34 @@ jobs:
342196 `echo "${{ steps.meta.outputs.tags }}" | xargs -n1 echo -t` \
343197 $(printf "$ECR_IMAGE@sha256:%s " *)
344198
199+ - name : Export immutable tags (short) for downstream jobs
200+ id : export_immutable_tags
201+ run : |
202+ TAGS=$(jq -r '.tags[] | split(":") | .[1]' <<< "$DOCKER_METADATA_OUTPUT_JSON")
203+ echo "tags<<EOF" >> $GITHUB_OUTPUT
204+ echo "$TAGS" >> $GITHUB_OUTPUT
205+ echo "EOF" >> $GITHUB_OUTPUT
206+
345207 - name : Tag mutable on release if latest in series
208+ id : tag_mutable
346209 if : startsWith(github.ref, 'refs/tags/riverproui/v')
347210 working-directory : /tmp/digests
348211 run : |
349- STRIPPED_VERSION="${{ github.ref_name }}"
350- STRIPPED_VERSION="${STRIPPED_VERSION#riverproui/}"
351- MAJOR="${STRIPPED_VERSION#v}"
352- MAJOR="${MAJOR%%.*}"
353- MINOR_PATCH="${STRIPPED_VERSION#v${MAJOR}.}"
212+ STRIPPED_VERSION="${GITHUB_REF_NAME#riverproui/v}"
213+ MAJOR="${STRIPPED_VERSION%%.*}"
214+ MINOR_PATCH="${STRIPPED_VERSION#${MAJOR}.}"
354215 MINOR="${MINOR_PATCH%%.*}"
355216 declare -a mutable_tags=()
356- GLOBAL_LATEST=$(git tag --list 'riverproui/v*' --sort=-v:refname | grep -E '^riverproui/v[0-9]+\.[0-9]+\.[0-9]+$' | head -n1)
357- if [ "$GLOBAL_LATEST" = "${{ github.ref_name } }" ]; then
217+ GLOBAL_LATEST=$(git tag --list 'riverproui/v*' --sort=-v:refname | grep -E '^riverproui/v[0-9]+\.[0-9]+\.[0-9]+(-[a-z0-9\.-]+)? $' | head -n1)
218+ if [ "$GLOBAL_LATEST" = "riverproui/v${STRIPPED_VERSION }" ]; then
358219 mutable_tags+=("latest")
359220 fi
360221 LATEST_IN_MAJOR=$(git tag --list "riverproui/v${MAJOR}.*" --sort=-v:refname | head -n1)
361- if [ "$LATEST_IN_MAJOR" = "${{ github.ref_name } }" ]; then
222+ if [ "$LATEST_IN_MAJOR" = "riverproui/v${STRIPPED_VERSION }" ]; then
362223 mutable_tags+=("v${MAJOR}")
363224 fi
364225 LATEST_IN_MINOR=$(git tag --list "riverproui/v${MAJOR}.${MINOR}.*" --sort=-v:refname | head -n1)
365- if [ "$LATEST_IN_MINOR" = "${{ github.ref_name } }" ]; then
226+ if [ "$LATEST_IN_MINOR" = "riverproui/v${STRIPPED_VERSION }" ]; then
366227 mutable_tags+=("v${MAJOR}.${MINOR}")
367228 fi
368229 for tag in "${mutable_tags[@]}"; do
@@ -374,6 +235,12 @@ jobs:
374235 $(printf "$ECR_IMAGE@sha256:%s " *)
375236 done
376237
238+ if [ ${#mutable_tags[@]} -gt 0 ]; then
239+ echo "tags<<EOF" >> $GITHUB_OUTPUT
240+ printf "%s\n" "${mutable_tags[@]}" >> $GITHUB_OUTPUT
241+ echo "EOF" >> $GITHUB_OUTPUT
242+ fi
243+
377244 - name : Install crane
378245 uses : imjasonh/setup-crane@v0.4
379246
@@ -434,6 +301,8 @@ jobs:
434301 IMAGE_NAME : riverqueue.com/riverproui
435302 MANIFEST_DIGEST : ${{ needs.merge-riverproui.outputs.manifest_digest }}
436303 TAG : ${{ needs.merge-riverproui.outputs.tag }}
304+ IMMUTABLE_TAGS : ${{ needs.merge-riverproui.outputs.immutable_tags }}
305+ MUTABLE_TAGS : ${{ needs.merge-riverproui.outputs.mutable_tags }}
437306 if : startsWith(github.ref, 'refs/tags/riverproui/v')
438307 permissions :
439308 contents : read
@@ -447,38 +316,35 @@ jobs:
447316 - name : Fetch tags
448317 run : git fetch --tags --force
449318
450- - name : Force refresh mutable tags
451- if : startsWith(github.ref, 'refs/tags/riverproui/v')
319+ - name : Force refresh pushed tags
452320 run : |
453- STRIPPED_VERSION="${{ github.ref_name }}"
454- STRIPPED_VERSION="${STRIPPED_VERSION#riverproui/}"
455- MAJOR="${STRIPPED_VERSION#v}"
456- MAJOR="${MAJOR%%.*}"
457- MINOR_PATCH="${STRIPPED_VERSION#v${MAJOR}.}"
458- MINOR="${MINOR_PATCH%%.*}"
459- declare -a mutable_tags=()
460- GLOBAL_LATEST=$(git tag --list 'riverproui/v*' --sort=-v:refname | grep -E '^riverproui/v[0-9]+\.[0-9]+\.[0-9]+$' | head -n1)
461- if [ "$GLOBAL_LATEST" = "riverproui/${STRIPPED_VERSION}" ]; then
462- mutable_tags+=("latest")
463- fi
464- LATEST_IN_MAJOR=$(git tag --list "riverproui/v${MAJOR}.*" --sort=-v:refname | head -n1)
465- if [ "$LATEST_IN_MAJOR" = "riverproui/${STRIPPED_VERSION}" ]; then
466- mutable_tags+=("v${MAJOR}")
467- fi
468- LATEST_IN_MINOR=$(git tag --list "riverproui/v${MAJOR}.${MINOR}.*" --sort=-v:refname | head -n1)
469- if [ "$LATEST_IN_MINOR" = "riverproui/${STRIPPED_VERSION}" ]; then
470- mutable_tags+=("v${MAJOR}.${MINOR}")
321+ pushed_tags="$IMMUTABLE_TAGS"
322+ if [ -n "$MUTABLE_TAGS" ]; then
323+ if [ -n "$pushed_tags" ]; then
324+ pushed_tags="$pushed_tags,$MUTABLE_TAGS"
325+ else
326+ pushed_tags="$MUTABLE_TAGS"
327+ fi
471328 fi
472- for tag in "${mutable_tags[@]}"; do
473- echo "Force refreshing mutable tag: $tag"
474- curl -f -u river:"${{ secrets.RIVERPRO_GO_MOD_CREDENTIAL }}" \
475- -H "X-Force-Fetch-From-Upstream: ${{ secrets.FORCE_FETCH_SECRET }}" \
476- -H "Accept: application/vnd.docker.distribution.manifest.list.v2+json" \
477- "https://riverqueue.com/v2/riverproui/manifests/${tag}" -o /dev/null
478- curl -f -u river:"${{ secrets.RIVERPRO_GO_MOD_CREDENTIAL }}" \
479- -H "X-Force-Fetch-From-Upstream: ${{ secrets.FORCE_FETCH_SECRET }}" \
480- -H "Accept: application/vnd.oci.image.index.v1+json" \
481- "https://riverqueue.com/v2/riverproui/manifests/${tag}" -o /dev/null
329+ IFS=',' read -r -a tags <<< "$pushed_tags"
330+ declare -a unique_tags=()
331+ for t in "${tags[@]}"; do
332+ if [[ ! " ${unique_tags[*]} " =~ " ${t} " ]]; then
333+ unique_tags+=("$t")
334+ fi
335+ done
336+ for tag in "${unique_tags[@]}"; do
337+ if [ -n "$tag" ]; then
338+ echo "Force refreshing tag: $tag"
339+ curl -f -u river:"${{ secrets.RIVERPRO_GO_MOD_CREDENTIAL }}" \
340+ -H "X-Force-Fetch-From-Upstream: ${{ secrets.FORCE_FETCH_SECRET }}" \
341+ -H "Accept: application/vnd.docker.distribution.manifest.list.v2+json" \
342+ "https://riverqueue.com/v2/riverproui/manifests/${tag}" -o /dev/null
343+ curl -f -u river:"${{ secrets.RIVERPRO_GO_MOD_CREDENTIAL }}" \
344+ -H "X-Force-Fetch-From-Upstream: ${{ secrets.FORCE_FETCH_SECRET }}" \
345+ -H "Accept: application/vnd.oci.image.index.v1+json" \
346+ "https://riverqueue.com/v2/riverproui/manifests/${tag}" -o /dev/null
347+ fi
482348 done
483349
484350 - name : Login to live registry
0 commit comments