1717 - .github/workflows/release.yml
1818 - .github/workflows/auto-merge.yml
1919 - .github/workflows/ruff.yml
20- release :
21- types : [published]
20+ workflow_run :
21+ workflows : [ "Release" ]
22+ types :
23+ - completed
2224 workflow_dispatch :
2325
26+ env :
27+ LATEST_PYTHON_VERSION : " 3.13"
28+
2429jobs :
25- build-and-push :
30+ metadata :
2631 runs-on : ubuntu-latest
32+ if : |-
33+ github.event_name != 'workflow_run' ||
34+ (github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'success')
2735 permissions :
2836 contents : read
29- packages : write
37+ outputs :
38+ LATEST_RELEASE : ${{ steps.workflow.outputs.LATEST_RELEASE || steps.api.outputs.LATEST_RELEASE }}
3039
3140 steps :
32- - name : Checkout
41+ - name : Get latest release from previous workflow
42+ id : workflow
43+ if : github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'success'
44+ run : |
45+ tag=${{ github.event.workflow_run.head_branch }}
46+ echo "LATEST_RELEASE=${tag#refs/tags/}" >> $GITHUB_OUTPUT
47+
48+ - name : Get latest release from api
49+ id : api
50+ if : github.event_name != 'workflow_run'
51+ run : |
52+ gh api repos/${{ github.repository }}/releases/latest | jq -r '.tag_name' | xargs -0 printf "LATEST_RELEASE=%s" >> $GITHUB_OUTPUT
53+ env :
54+ GITHUB_TOKEN : ${{ secrets.GITHUB_TOKEN }}
55+
56+
57+ build :
58+ runs-on : ubuntu-latest
59+ needs : metadata
60+ permissions :
61+ contents : read
62+ packages : write
63+ concurrency :
64+ group : ${{ github.workflow }}-${{ needs.metadata.outputs.LATEST_RELEASE }}-${{ matrix.python_version }}${{ matrix.python_variant }}-${{ matrix.platform }}
65+ cancel-in-progress : true
66+ strategy :
67+ fail-fast : true
68+ matrix :
69+ python_version :
70+ - " 3.9"
71+ - " 3.10"
72+ - " 3.11"
73+ - " 3.12"
74+ - " 3.13"
75+ python_variant :
76+ - " "
77+ - " -slim"
78+ platform :
79+ - linux/amd64
80+ - linux/arm64
81+ - linux/arm
82+ steps :
83+ - name : Checkout (Latest Release)
84+ if : github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'success'
85+ uses : actions/checkout@v5
86+ with :
87+ ref : ${{ needs.metadata.outputs.LATEST_RELEASE}}
88+
89+ - name : Checkout (Other)
90+ if : github.event_name != 'workflow_run'
3391 uses : actions/checkout@v5
3492
3593 - name : Set up QEMU
@@ -46,22 +104,154 @@ jobs:
46104 username : ${{ github.repository_owner }}
47105 password : ${{ secrets.GIT_TOKEN }}
48106
49- - name : Extract metadata
50- id : meta
107+ - name : Generate Image Name and Scope
108+ id : image
109+ run : |
110+ echo "IMAGE=ghcr.io/${{ github.repository }}" >> $GITHUB_OUTPUT
111+ echo "SCOPE=${{ hashFiles('**/pdm.lock') }}-${{ matrix.python_version }}${{ matrix.python_variant }}-${{ matrix.platform }}" >> $GITHUB_OUTPUT
112+ platform="${{ matrix.platform }}"
113+ echo "ARTIFACT=${{ matrix.python_version }}${{ matrix.python_variant }}-${platform/\//-}-digests" >> $GITHUB_OUTPUT
114+
115+ - name : Generate Labels
51116 uses : docker/metadata-action@v5
117+ id : metadata
52118 with :
53- images : ghcr.io/${{ github.repository }}
54- tags : |
55- type=edge,value=nightly
56- type=sha,event=branch
57- type=ref,event=tag
58- type=ref,event=pr
59-
60- - name : Build and push
119+ images : ${{ steps.image.outputs.IMAGE }}
120+
121+ - name : Build and Publish
61122 uses : docker/build-push-action@v6
123+ id : build
62124 with :
63125 context : .
64- file : Dockerfile
126+ platforms : ${{ matrix.platform }}
65127 push : ${{ github.event_name != 'pull_request' }}
66- tags : ${{ steps.meta.outputs.tags }}
67- labels : ${{ steps.meta.outputs.labels }}
128+ build-args : |
129+ PYTHON_IMAGE=${{ matrix.python_version }}
130+ VARIANT=${{ matrix.python_variant }}
131+ labels : ${{ steps.metadata.outputs.labels }}
132+ cache-from : type=gha,scope=${{ steps.image.outputs.SCOPE }}
133+ cache-to : type=gha,scope=${{ steps.image.outputs.SCOPE }},mode=max
134+ outputs : type=image,name=${{ steps.image.outputs.IMAGE }},push-by-digest=true,name-canonical=true,push=true
135+
136+ - name : Export digest
137+ run : |
138+ mkdir -p /tmp/digests/
139+ digest="${{ steps.build.outputs.digest }}"
140+ touch "/tmp/digests/${digest#sha256:}"
141+
142+ - name : Upload digest
143+ uses : actions/upload-artifact@v4
144+ with :
145+ name : ${{ steps.image.outputs.ARTIFACT }}
146+ path : /tmp/digests/*
147+ if-no-files-found : error
148+ retention-days : 1
149+
150+ push :
151+ runs-on : ubuntu-latest
152+ needs : [metadata, build]
153+ strategy :
154+ matrix :
155+ python_version :
156+ - " 3.9"
157+ - " 3.10"
158+ - " 3.11"
159+ - " 3.12"
160+ - " 3.13"
161+ python_variant :
162+ - " "
163+ - " -slim"
164+
165+ steps :
166+ - name : Checkout (Release)
167+ if : github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'success'
168+ uses : actions/checkout@v5
169+ with :
170+ ref : ${{ needs.metadata.outputs.LATEST_RELEASE }}
171+
172+ - name : Checkout (Other)
173+ if : github.event_name != 'workflow_run'
174+ uses : actions/checkout@v5
175+
176+ - name : Download digests
177+ uses : actions/download-artifact@v5
178+ with :
179+ path : /tmp/artifacts
180+ pattern : " *-digests"
181+
182+ - name : Copy digests
183+ run : |
184+ mkdir -p /tmp/digests
185+ cp /tmp/artifacts/${{ matrix.python_version }}${{ matrix.python_variant }}-*-digests/* /tmp/digests
186+
187+ - name : Set up Docker Buildx
188+ uses : docker/setup-buildx-action@v3
189+
190+ - name : Login to GitHub Container Registry
191+ uses : docker/login-action@v3
192+ if : github.event_name != 'pull_request'
193+ with :
194+ registry : ghcr.io
195+ username : ${{ github.repository_owner }}
196+ password : ${{ secrets.GIT_TOKEN }}
197+
198+ - name : Generate Image Name
199+ id : image
200+ run : |
201+ echo "IMAGE=ghcr.io/${{ github.repository }}" >> $GITHUB_OUTPUT
202+
203+ - name : Generate Tags
204+ uses : docker/metadata-action@v5
205+ id : metadata
206+ with :
207+ context : git
208+ images : |
209+ ghcr.io/${{ github.repository }}
210+ flavor : |
211+ suffix=-py${{ matrix.python_version }}${{ matrix.python_variant }},onlatest=true
212+ tags : |
213+ type=edge,value=nightly
214+ type=ref,event=pr
215+ type=sha,event=branch
216+ type=semver,pattern={{version}}
217+ type=semver,pattern={{major}}.{{minor}}
218+ type=semver,pattern={{major}}
219+
220+ - name : Create manifest list and push
221+ working-directory : /tmp/digests
222+ run : |
223+ docker buildx imagetools create --dry-run $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
224+ $(printf '${{ steps.image.outputs.IMAGE }}@sha256:%s ' *)
225+ docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
226+ $(printf '${{ steps.image.outputs.IMAGE }}@sha256:%s ' *)
227+
228+ - name : Generate Tags
229+ uses : docker/metadata-action@v5
230+ id : metadata-latest
231+ if : matrix.python_version == env.LATEST_PYTHON_VERSION
232+ with :
233+ context : git
234+ images : |
235+ ghcr.io/${{ github.repository }}
236+ flavor : |
237+ suffix=${{ matrix.python_variant }},onlatest=true
238+ tags : |
239+ type=semver,pattern={{version}}
240+ type=semver,pattern={{major}}.{{minor}}
241+ type=semver,pattern={{major}}
242+
243+ - name : Create manifest list and push for latest python version
244+ if : matrix.python_version == env.LATEST_PYTHON_VERSION
245+ working-directory : /tmp/digests
246+ run : |
247+ docker buildx imagetools create --dry-run $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
248+ $(printf '${{ steps.image.outputs.IMAGE }}@sha256:%s ' *)
249+ docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
250+ $(printf '${{ steps.image.outputs.IMAGE }}@sha256:%s ' *)
251+
252+ - name : Docker Hub Description
253+ uses : peter-evans/dockerhub-description@v4
254+ with :
255+ username : ${{ secrets.DOCKERHUB_USERNAME }}
256+ password : ${{ secrets.DOCKERHUB_PASSWORD }}
257+ short-description : ${{ github.event.repository.description }}
0 commit comments