Skip to content

Commit f251620

Browse files
authored
ci: Refactor OCI image workflows to use multi-runner for cross-arch builds (#246)
Replaces `build-push-action` buildx QEMU building for multiple `arch` with a multi-runner matrix that builds each image on native runners and combines digests before pushing to registries. * Use arm public preview runner * Refactor publish image workflow to use multi-runner * Refactor PR workflow to use multi-runner
1 parent f8902e0 commit f251620

File tree

3 files changed

+333
-102
lines changed

3 files changed

+333
-102
lines changed

.github/act/multiRunnerTest.yml

+34-31
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,10 @@ jobs:
3737
- name: Prepare
3838
run: |
3939
platform=${{ matrix.platform }}
40-
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
40+
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
4141
4242
- name: Check out the repo
43-
uses: actions/checkout@v4
43+
uses: actions/checkout@v5
4444

4545
- name: Set short git commit SHA
4646
id: appvars
@@ -64,17 +64,17 @@ jobs:
6464
run: |
6565
echo appversion=$APP_VERSION >>${GITHUB_OUTPUT}
6666
67-
- name: Extract metadata (tags, labels) for Docker
68-
id: meta
69-
uses: docker/metadata-action@v5
70-
with:
71-
images: |
72-
${{ env.DOCKERHUB_SLUG }}
73-
${{ env.GHCR_SLUG }}
74-
labels: |
75-
org.opencontainers.image.title=Multi-Scrobbler
76-
org.opencontainers.image.description=Scrobble from many sources to many clients
77-
org.opencontainers.image.vendor=FoxxMD
67+
# - name: Extract metadata (tags, labels) for Docker
68+
# id: meta
69+
# uses: docker/metadata-action@v5
70+
# with:
71+
# images: |
72+
# ${{ env.DOCKERHUB_SLUG }}
73+
# ${{ env.GHCR_SLUG }}
74+
# labels: |
75+
# org.opencontainers.image.title=Multi-Scrobbler
76+
# org.opencontainers.image.description=Scrobble from many sources to many clients
77+
# org.opencontainers.image.vendor=FoxxMD
7878

7979
- name: Set up QEMU
8080
uses: docker/setup-qemu-action@v3
@@ -138,6 +138,19 @@ jobs:
138138
path: /tmp/digests
139139
pattern: digests-*
140140
merge-multiple: true
141+
142+
- name: Login to Docker Hub
143+
uses: docker/login-action@v3
144+
with:
145+
username: ${{ secrets.DOCKER_USERNAME }}
146+
password: ${{ secrets.DOCKER_PASSWORD }}
147+
148+
- name: Login to GitHub Container Registry
149+
uses: docker/login-action@v3
150+
with:
151+
registry: ghcr.io
152+
username: ${{ github.repository_owner }}
153+
password: ${{ secrets.GITHUB_TOKEN }}
141154

142155
- name: Set up Docker Buildx
143156
uses: docker/setup-buildx-action@v3
@@ -155,7 +168,7 @@ jobs:
155168
type=edge
156169
157170
# maybe re-enable branch-named tags in the futures
158-
#type=ref,event=branch,enable=${{ !endsWith(github.ref, 'master') }}
171+
type=ref,event=branch,enable=${{ !endsWith(github.ref, 'master') }}
159172
160173
# tag non-prelease as latest -- has a higher priority than regular tag so it shows first in registries
161174
type=match,pattern=\d.\d.\d$,priority=901
@@ -169,27 +182,17 @@ jobs:
169182
org.opencontainers.image.description=Scrobble from many sources to many clients
170183
org.opencontainers.image.vendor=FoxxMD
171184
172-
- name: Login to Docker Hub
173-
uses: docker/login-action@v3
174-
with:
175-
username: ${{ secrets.DOCKER_USERNAME }}
176-
password: ${{ secrets.DOCKER_PASSWORD }}
177185

178-
- name: Login to GitHub Container Registry
179-
uses: docker/login-action@v3
180-
with:
181-
registry: ghcr.io
182-
username: ${{ github.repository_owner }}
183-
password: ${{ secrets.GITHUB_TOKEN }}
184186

185187
- name: Create manifest list and push
186188
working-directory: /tmp/digests
187189
run: |
188-
docker buildx imagetools create $(jq -cr '.tags | map(select(startswith("${{ env.DOCKERHUB_SLUG }}")) | "-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
190+
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
189191
$(printf '${{ env.DOCKERHUB_SLUG }}@sha256:%s ' *)
190-
docker buildx imagetools create $(jq -cr '.tags | map(select(startswith("${{ env.GHCR_SLUG }}")) | "-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
191-
$(printf '${{ env.GHCR_SLUG }}@sha256:%s ' *)
192+
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
193+
$(printf '${{ env.GHCR_SLUG }}@sha256:%s ' *)
192194
193-
# - name: Inspect image
194-
# run: |
195-
# docker buildx imagetools inspect ${{ env.REGISTRY_IMAGE }}:${{ steps.meta.outputs.version }}
195+
- name: Inspect image
196+
run: |
197+
docker buildx imagetools inspect ${{ env.DOCKERHUB_SLUG }}:${{ steps.meta.outputs.version }}
198+
docker buildx imagetools inspect ${{ env.GHCR_SLUG }}:${{ steps.meta.outputs.version }}

.github/workflows/pr.yml

+143-35
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
name: PR Workflow
22

3+
concurrency:
4+
group: ${{ github.workflow }}-${{ github.event.pull_request.head.sha }}
5+
cancel-in-progress: true
6+
37
on:
48
pull_request_target:
59
types:
@@ -17,76 +21,180 @@ jobs:
1721
with:
1822
ref: ${{ github.event.pull_request.head.sha }}
1923

20-
release-snapshot:
21-
name: Release snapshot
22-
runs-on: ubuntu-latest
24+
build-snapshot:
25+
name: Build OCI snapshot
2326
needs: test
2427
if: contains(github.event.pull_request.labels.*.name, 'safe to test')
28+
runs-on: ${{ matrix.os }}
2529
permissions:
2630
packages: write
2731
contents: read
2832
strategy:
2933
fail-fast: false
3034
matrix:
3135
include:
32-
- dockerfile: ./Dockerfile
33-
suffix: ''
34-
platforms: 'linux/amd64,linux/arm64'
36+
- os: ubuntu-latest
37+
arch: amd64
38+
platform: linux/amd64
39+
- os: ubuntu-24.04-arm
40+
arch: arm64
41+
platform: linux/arm64
3542
steps:
36-
- name: Set up QEMU
37-
uses: docker/setup-qemu-action@v3
43+
- name: Prepare
44+
run: |
45+
platform=${{ matrix.platform }}
46+
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
47+
48+
# list all registries to push to and join all non-empty with comma
49+
# https://unix.stackexchange.com/a/693165/116849
50+
# https://stackoverflow.com/a/9429887/1469797
51+
strings=("${{vars.DOCKERHUB_SLUG}}" "${{vars.GHCR_SLUG}}")
52+
for i in ${!strings[@]}; do [[ -z ${strings[i]} ]] && unset strings[i]; done
53+
joined_string=$(IFS=, ; echo "${strings[*]}")
54+
echo "REGISTRIES_JOINED=$joined_string" >> $GITHUB_ENV
55+
56+
- uses: actions/checkout@v4
57+
with:
58+
ref: ${{ github.event.pull_request.head.sha }}
59+
60+
- name: Set short git commit SHA
61+
run: |
62+
short=$(echo "${{ github.event.pull_request.head.sha }}" | head -c7)
63+
echo "COMMIT_SHORT_SHA=$short" >> $GITHUB_ENV
64+
65+
- name: Get App Version
66+
id: appversion
67+
env:
68+
APP_VERSION: ${{ format('pr{0}-{1}', github.event.number, env.COMMIT_SHORT_SHA ) }}
69+
run: |
70+
echo appversion=$APP_VERSION >>${GITHUB_OUTPUT}
71+
72+
- name: Login to Docker Hub
73+
if: ${{ vars.DOCKERHUB_SLUG != '' }}
74+
uses: docker/login-action@v3
75+
with:
76+
username: ${{ secrets.DOCKER_USERNAME }}
77+
password: ${{ secrets.DOCKER_PASSWORD }}
78+
79+
- name: Login to GitHub Container Registry
80+
if: ${{ vars.GHCR_SLUG != '' }}
81+
uses: docker/login-action@v3
82+
with:
83+
registry: ghcr.io
84+
username: ${{ github.repository_owner }}
85+
password: ${{ secrets.GITHUB_TOKEN }}
86+
87+
# metadata extract for docker labels/image names is done in merge job
3888

3989
- name: Set up Buildx
4090
uses: docker/setup-buildx-action@v3
4191

42-
- name: Log in to Docker Hub
92+
# https://github.com/docker/build-push-action/issues/671#issuecomment-1619353328
93+
# for caching
94+
- name: Build and push by digest
95+
id: build
96+
uses: docker/build-push-action@v6
97+
with:
98+
build-args: |
99+
APP_BUILD_VERSION=${{steps.appversion.outputs.appversion}}
100+
platforms: ${{ matrix.platform }}
101+
labels: ${{ steps.meta.outputs.labels }}
102+
outputs: type=image,"name=${{ env.REGISTRIES_JOINED }}",push-by-digest=true,name-canonical=true,push=true
103+
#cache-from: type=gha,scope=build-${{ env.PLATFORM_PAIR }}
104+
#cache-to: type=gha,scope=build-${{ env.PLATFORM_PAIR }}
105+
106+
- name: Export digest
107+
run: |
108+
mkdir -p /tmp/digests
109+
digest="${{ steps.build.outputs.digest }}"
110+
touch "/tmp/digests/${digest#sha256:}"
111+
112+
- name: Upload digest
113+
uses: actions/upload-artifact@v4
114+
with:
115+
name: digests-${{ env.PLATFORM_PAIR }}
116+
path: /tmp/digests/*
117+
if-no-files-found: error
118+
retention-days: 1
119+
120+
release-snapshot:
121+
name: Merge OCI Images and Push
122+
if: ${{ vars.DOCKERHUB_SLUG != '' || vars.GHCR_SLUG != '' }}
123+
runs-on: ubuntu-latest
124+
permissions:
125+
packages: write
126+
contents: read
127+
needs:
128+
- build-snapshot
129+
- test
130+
steps:
131+
- name: Download digests
132+
uses: actions/download-artifact@v4
133+
with:
134+
path: /tmp/digests
135+
pattern: digests-*
136+
merge-multiple: true
137+
138+
- name: Login to Docker Hub
139+
if: ${{ vars.DOCKERHUB_SLUG != '' }}
43140
uses: docker/login-action@v3
44141
with:
45142
username: ${{ secrets.DOCKER_USERNAME }}
46143
password: ${{ secrets.DOCKER_PASSWORD }}
47144

48145
- name: Login to GitHub Container Registry
146+
if: ${{ vars.GHCR_SLUG != '' }}
49147
uses: docker/login-action@v3
50148
with:
51149
registry: ghcr.io
52150
username: ${{ github.repository_owner }}
53151
password: ${{ secrets.GITHUB_TOKEN }}
54-
55-
- name: Extract metadata (tags, labels) for Docker
152+
153+
- name: Set up Docker Buildx
154+
uses: docker/setup-buildx-action@v3
155+
156+
- name: Extract metadata (tags, labels)
56157
id: meta
57158
uses: docker/metadata-action@v5
58159
with:
59160
images: |
60-
foxxmd/multi-scrobbler
61-
ghcr.io/foxxmd/multi-scrobbler
161+
${{ vars.DOCKERHUB_SLUG }}
162+
${{ vars.GHCR_SLUG }}
163+
# generate Docker tags based on the following events/attributes
164+
# https://github.com/docker/metadata-action/issues/247#issuecomment-1511259674 for NOT is default branch, eventually
62165
tags: |
63-
type=ref,event=pr,suffix=${{ matrix.suffix }}
166+
type=ref,event=pr
64167
flavor: |
65168
latest=false
169+
labels: |
170+
org.opencontainers.image.title=Multi-Scrobbler
171+
org.opencontainers.image.description=Scrobble from many sources to many clients
172+
org.opencontainers.image.vendor=FoxxMD
173+
174+
- name: Create manifest list and push dockerhub
175+
if: ${{ vars.DOCKERHUB_SLUG != '' }}
176+
working-directory: /tmp/digests
177+
run: |
178+
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
179+
$(printf '${{ vars.DOCKERHUB_SLUG }}@sha256:%s ' *)
180+
181+
- name: Create manifest list and push gchr
182+
if: ${{ vars.GHCR_SLUG != '' }}
183+
working-directory: /tmp/digests
184+
run: |
185+
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
186+
$(printf '${{ vars.GHCR_SLUG }}@sha256:%s ' *)
187+
188+
- name: Inspect image dockerhub
189+
if: ${{ vars.DOCKERHUB_SLUG != '' }}
190+
run: |
191+
docker buildx imagetools inspect ${{ vars.DOCKERHUB_SLUG }}:${{ steps.meta.outputs.version }}
66192
67-
- uses: actions/checkout@v4
68-
with:
69-
ref: ${{ github.event.pull_request.head.sha }}
70-
71-
- name: Set short git commit SHA
193+
- name: Inspect image ghcr
194+
if: ${{ vars.GHCR_SLUG != '' }}
72195
run: |
73-
short=$(echo "${{ github.event.pull_request.head.sha }}" | head -c7)
74-
echo "COMMIT_SHORT_SHA=$short" >> $GITHUB_ENV
196+
docker buildx imagetools inspect ${{ vars.GHCR_SLUG }}:${{ steps.meta.outputs.version }}
75197
76-
- name: Build and push
77-
id: docker_build
78-
uses: docker/build-push-action@v5
79-
env:
80-
APP_VERSION: ${{ format('pr{0}-{1}', github.event.number, env.COMMIT_SHORT_SHA ) }}
81-
with:
82-
context: .
83-
build-args: |
84-
APP_BUILD_VERSION=${{env.APP_VERSION}}
85-
file: ${{ matrix.dockerfile }}
86-
push: ${{ !env.ACT}}
87-
tags: ${{ steps.meta.outputs.tags }}
88-
labels: ${{ steps.meta.outputs.labels }}
89-
platforms: ${{ matrix.platforms }}
90198
91199
combine-and-comment:
92200
name: Leave comment

0 commit comments

Comments
 (0)