1
- # This is a GitHub workflow defining a set of jobs with a set of steps.
2
- # ref: https://docs.github.com/en/actions/learn-github-actions/workflow-syntax-for-github-actions
3
- #
4
- # Test build release artifacts (PyPI package, Docker images) and publish them on
5
- # pushed git tags.
6
- #
7
- name : Release
1
+ name : Build
8
2
9
3
on :
10
4
pull_request :
11
5
paths-ignore :
12
6
- " docs/**"
13
- - " **.md"
14
- - " **.rst"
15
- - " .github/workflows/*"
16
- - " !.github/workflows/release.yml"
17
7
push :
18
8
paths-ignore :
19
9
- " docs/**"
20
- - " **.md"
21
- - " **.rst"
22
- - " .github/workflows/*"
23
- - " !.github/workflows/release.yml"
24
10
branches-ignore :
25
11
- " dependabot/**"
26
12
- " pre-commit-ci-update-config"
27
- tags :
28
- - " **"
29
13
workflow_dispatch :
30
14
15
+ # Tags are calculated based on the existing tags in the repository
16
+ # so we can only run builds consecutively, otherwise two images in
17
+ # different builds may have the same tag.
18
+ # If there are multiple pending jobs older pending jobs will be cancelled.
19
+ concurrency :
20
+ group : ${{ github.workflow }}-${{ github.ref }}
21
+
22
+ env :
23
+ # If we're on this branch don't bother fetching every tag since we know
24
+ # this will be the most recent tag, i.e. builds on this branch should always
25
+ # update the `latest` tag
26
+ LATEST_BRANCH : main
27
+ # Only push images if this workflow is run on this branch
28
+ # This ensures if a a new branch is created in this repo it won't automatically
29
+ # push an image.
30
+ # If this is a backport then change this to the name of the backports branch
31
+ PUBLISH_BRANCH : main
32
+
33
+ # IMAGE: jupyterhub/jupyterhub
34
+ IMAGE : manics/jupyterhub-image-test
35
+ SINGLEUSER : manics/jupyterhub-singleuser-image-test
36
+ PUBLISH_DOCKERIO : " false"
37
+
38
+ # Enable caching across builds, set to "" to disable
39
+ CACHE_FROM : type=gha
40
+ CACHE_TO : type=gha,mode=max
41
+
31
42
jobs :
32
- build-release :
43
+ tag :
33
44
runs-on : ubuntu-22.04
45
+ timeout-minutes : 2
46
+ outputs :
47
+ existing-tags : ${{ steps.quayio.outputs.tags }}
48
+ new-tag : ${{ steps.new.outputs.TAG }}
49
+ jupyterhub-version : ${{ steps.version.outputs.VERSION }}
50
+
34
51
steps :
35
52
- uses : actions/checkout@v4
36
- - uses : actions/setup-python@v5
37
- with :
38
- python-version : " 3.11"
39
- cache : pip
40
-
41
- - uses : actions/setup-node@v4
42
- with :
43
- node-version : " 20"
44
-
45
- - name : install build requirements
46
- run : |
47
- npm install -g yarn
48
- pip install --upgrade pip
49
- pip install build
50
- pip freeze
51
-
52
- - name : build release
53
- run : |
54
- python -m build --sdist --wheel .
55
- ls -l dist
56
-
57
- - name : verify sdist
58
- run : |
59
- ./ci/check_sdist.py dist/jupyterhub-*.tar.gz
60
-
61
- - name : verify data-files are installed where they are found
62
- run : |
63
- pip install dist/*.whl
64
- ./ci/check_installed_data.py
65
53
66
- - name : verify sdist can be installed without npm/yarn
54
+ - name : JupyterHub version
55
+ id : version
67
56
run : |
68
- docker run --rm -v $PWD/dist:/dist:ro docker.io/library/python:3.9-slim-bullseye bash -c 'pip install /dist/jupyterhub-*.tar.gz'
57
+ VERSION=$(grep '^jupyterhub==' base/requirements.txt | cut -d= -f3)
58
+ if [[ $VERSION =~ [0-9]+\.[0-9]+\.[0-9]+ ]]; then
59
+ echo "VERSION=$VERSION" >> $GITHUB_OUTPUT
60
+ else
61
+ echo "Failed to get JupyterHub version"
62
+ exit 1
63
+ fi
69
64
70
- # ref: https://github.com/actions/upload-artifact#readme
71
- - uses : actions/upload-artifact@v4
65
+ - name : Get build-number by looking at existing tags
66
+ id : quayio
67
+ uses : manics/action-get-quayio-tags@main
72
68
with :
73
- name : jupyterhub-${{ github.sha }}
74
- path : " dist/*"
75
- if-no-files-found : error
76
-
77
- - name : Publish to PyPI
78
- if : startsWith(github.ref, 'refs/tags/')
79
- env :
80
- TWINE_USERNAME : __token__
81
- TWINE_PASSWORD : ${{ secrets.PYPI_PASSWORD }}
69
+ repository : ${{ env.IMAGE }}
70
+ version : ${{ steps.version.outputs.VERSION }}
71
+ # https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/accessing-contextual-information-about-workflow-runs#github-context
72
+ # If this is not LATEST_BRANCH, nor is it a pull request against LATEST_BRANCH
73
+ # assume it's a backport branch which means we need to get all tags to work out
74
+ # which MAJOR and MAJOR.MINOR aliases are needed.
75
+ allTags : ${{ (github.ref != format('refs/heads/{0}', env.LATEST_BRANCH)) && (github.base_ref != format('refs/heads/{0}', env.LATEST_BRANCH)) }}
76
+ strict : " true"
77
+
78
+ - name : Get new tag
79
+ id : new
82
80
run : |
83
- pip install twine
84
- twine upload --skip-existing dist/*
81
+ echo "TAG=${{ steps.version.outputs.VERSION }}-${{ steps.quayio.outputs.buildNumber }}" >> $GITHUB_OUTPUT
85
82
86
83
publish-docker :
87
84
runs-on : ubuntu-22.04
88
85
timeout-minutes : 30
86
+ needs :
87
+ - tag
89
88
90
89
services :
91
90
# So that we can test this in PRs/branches
@@ -95,19 +94,23 @@ jobs:
95
94
- 5000:5000
96
95
97
96
steps :
97
+ - uses : actions/checkout@v4
98
+
98
99
- name : Should we push this image to a public registry?
99
100
run : |
100
- if [ "${{ startsWith( github.ref, 'refs/tags/') || (github.ref == 'refs/heads/main' ) }}" = "true" ]; then
101
+ if [ "${{ github.ref == format( 'refs/heads/{0}', env.PUBLISH_BRANCH ) }}" = "true" ]; then
101
102
echo "REGISTRY=quay.io/" >> $GITHUB_ENV
103
+ echo "PUBLIC=true" >> $GITHUB_ENV
102
104
else
103
105
echo "REGISTRY=localhost:5000/" >> $GITHUB_ENV
106
+ echo "PUBLIC=false" >> $GITHUB_ENV
104
107
fi
105
108
106
109
- uses : actions/checkout@v4
107
110
108
111
# Setup docker to build for multiple platforms, see:
109
- # https://github.com/docker/build-push-action/tree/v2.4.0 #usage
110
- # https://github. com/docker/ build-push-action/blob/v2.4.0/docs/advanced/ multi-platform.md
112
+ # https://github.com/docker/build-push-action/tree/v6.9.0?tab=readme-ov-file #usage
113
+ # https://docs.docker. com/build/ci/github-actions/ multi-platform/
111
114
- name : Set up QEMU (for docker buildx)
112
115
uses : docker/setup-qemu-action@v3
113
116
@@ -123,53 +126,59 @@ jobs:
123
126
# . Quay.io org
124
127
# 2. Giving it enough permissions to push to the jupyterhub and singleuser images
125
128
# 3. Putting the robot account's username and password in GitHub actions environment
126
- if : env.REGISTRY != 'localhost:5000/ '
129
+ if : env.PUBLIC == 'true '
127
130
run : |
128
131
docker login -u "${{ secrets.QUAY_USERNAME }}" -p "${{ secrets.QUAY_PASSWORD }}" "${{ env.REGISTRY }}"
129
- docker login -u "${{ secrets.DOCKERHUB_USERNAME }}" -p "${{ secrets.DOCKERHUB_TOKEN }}" docker.io
132
+ if [ "${{ env.PUBLISH_DOCKERIO }}" = "true" ]; then
133
+ docker login -u "${{ secrets.DOCKERHUB_USERNAME }}" -p "${{ secrets.DOCKERHUB_TOKEN }}" docker.io
134
+ fi
130
135
131
- # image: jupyterhub/jupyterhub
136
+ # image: env.IMAGE
132
137
#
133
138
# https://github.com/jupyterhub/action-major-minor-tag-calculator
134
139
# If this is a tagged build this will return additional parent tags.
135
140
# E.g. 1.2.3 is expanded to Docker tags
136
141
# [{prefix}:1.2.3, {prefix}:1.2, {prefix}:1, {prefix}:latest] unless
137
142
# this is a backported tag in which case the newer tags aren't updated.
138
- # For branches this will return the branch name.
139
- # If GITHUB_TOKEN isn't available (e.g. in PRs) returns no tags [].
140
- - name : Get list of jupyterhub tags
143
+ - name : Calculate tags
141
144
id : jupyterhubtags
142
- uses : jupyterhub /action-major-minor-tag-calculator@v3
145
+ uses : manics /action-major-minor-tag-calculator@allow-external-tag
143
146
with :
144
- githubToken : ${{ secrets.GITHUB_TOKEN }}
147
+ tagList : ${{ needs.tag.outputs.existing-tags }}
148
+ currentTag : ${{ needs.tag.outputs.new-tag }}
145
149
prefix : >-
146
- ${{ env.REGISTRY }}jupyterhub/jupyterhub:
147
- jupyterhub/jupyterhub:
148
- defaultTag : " ${{ env.REGISTRY }}jupyterhub/jupyterhub:noref"
149
- branchRegex : ^\w[\w-.]*$
150
+ ${{ env.REGISTRY }}${{ env.IMAGE }}:
151
+ ${{ (env.PUBLIC == 'true' && env.PUBLISH_DOCKERIO == 'true') && format('{0}:', env.IMAGE) || '' }}
152
+
153
+ - name : Print tags
154
+ run : |
155
+ echo "Existing tags: ${{ needs.tag.outputs.existing-tags }}"
156
+ echo "New tags: ${{ needs.tag.outputs.new-tag }}"
157
+ echo "Image tags: ${{ steps.jupyterhubtags.outputs.tags }}"
150
158
151
159
- name : Build and push jupyterhub
152
160
uses : docker/build-push-action@v6
153
161
with :
154
- context : .
162
+ context : base
155
163
platforms : linux/amd64,linux/arm64
156
164
push : true
157
165
# tags parameter must be a string input so convert `gettags` JSON
158
166
# array into a comma separated list of tags
159
167
tags : ${{ join(fromJson(steps.jupyterhubtags.outputs.tags)) }}
168
+ cache-from : ${{ env.CACHE_FROM }}
169
+ cache-to : ${{ env.CACHE_TO }}
160
170
161
- # image: jupyterhub/jupyterhub -onbuild
171
+ # image: env.IMAGE -onbuild
162
172
#
163
173
- name : Get list of jupyterhub-onbuild tags
164
174
id : onbuildtags
165
- uses : jupyterhub /action-major-minor-tag-calculator@v3
175
+ uses : manics /action-major-minor-tag-calculator@allow-external-tag
166
176
with :
167
- githubToken : ${{ secrets.GITHUB_TOKEN }}
177
+ tagList : ${{ needs.tag.outputs.existing-tags }}
178
+ currentTag : ${{ needs.tag.outputs.new-tag }}
168
179
prefix : >-
169
- ${{ env.REGISTRY }}jupyterhub/jupyterhub-onbuild:
170
- jupyterhub/jupyterhub-onbuild:
171
- defaultTag : " ${{ env.REGISTRY }}jupyterhub/jupyterhub-onbuild:noref"
172
- branchRegex : ^\w[\w-.]*$
180
+ ${{ env.REGISTRY }}${{ env.IMAGE }}-onbuild:
181
+ ${{ (env.PUBLIC == 'true' && env.PUBLISH_DOCKERIO == 'true') && format('{0}-onbuild:', env.IMAGE) || '' }}
173
182
174
183
- name : Build and push jupyterhub-onbuild
175
184
uses : docker/build-push-action@v6
@@ -180,19 +189,20 @@ jobs:
180
189
platforms : linux/amd64,linux/arm64
181
190
push : true
182
191
tags : ${{ join(fromJson(steps.onbuildtags.outputs.tags)) }}
192
+ cache-from : ${{ env.CACHE_FROM }}
193
+ cache-to : ${{ env.CACHE_TO }}
183
194
184
- # image: jupyterhub/jupyterhub -demo
195
+ # image: env.IMAGE -demo
185
196
#
186
197
- name : Get list of jupyterhub-demo tags
187
198
id : demotags
188
- uses : jupyterhub /action-major-minor-tag-calculator@v3
199
+ uses : manics /action-major-minor-tag-calculator@allow-external-tag
189
200
with :
190
- githubToken : ${{ secrets.GITHUB_TOKEN }}
201
+ tagList : ${{ needs.tag.outputs.existing-tags }}
202
+ currentTag : ${{ needs.tag.outputs.new-tag }}
191
203
prefix : >-
192
- ${{ env.REGISTRY }}jupyterhub/jupyterhub-demo:
193
- jupyterhub/jupyterhub-demo:
194
- defaultTag : " ${{ env.REGISTRY }}jupyterhub/jupyterhub-demo:noref"
195
- branchRegex : ^\w[\w-.]*$
204
+ ${{ env.REGISTRY }}${{ env.IMAGE }}-demo:
205
+ ${{ (env.PUBLIC == 'true' && env.PUBLISH_DOCKERIO == 'true') && format('{0}-demo:', env.IMAGE) || '' }}
196
206
197
207
- name : Build and push jupyterhub-demo
198
208
uses : docker/build-push-action@v6
@@ -206,26 +216,29 @@ jobs:
206
216
platforms : linux/amd64
207
217
push : true
208
218
tags : ${{ join(fromJson(steps.demotags.outputs.tags)) }}
219
+ cache-from : ${{ env.CACHE_FROM }}
220
+ cache-to : ${{ env.CACHE_TO }}
209
221
210
- # image: jupyterhub/singleuser
222
+ # image: env.SINGLEUSER
211
223
#
212
224
- name : Get list of jupyterhub/singleuser tags
213
225
id : singleusertags
214
- uses : jupyterhub /action-major-minor-tag-calculator@v3
226
+ uses : manics /action-major-minor-tag-calculator@allow-external-tag
215
227
with :
216
- githubToken : ${{ secrets.GITHUB_TOKEN }}
228
+ tagList : ${{ needs.tag.outputs.existing-tags }}
229
+ currentTag : ${{ needs.tag.outputs.new-tag }}
217
230
prefix : >-
218
- ${{ env.REGISTRY }}jupyterhub/singleuser:
219
- jupyterhub/singleuser:
220
- defaultTag : " ${{ env.REGISTRY }}jupyterhub/singleuser:noref"
221
- branchRegex : ^\w[\w-.]*$
231
+ ${{ env.REGISTRY }}${{ env.SINGLEUSER }}:
232
+ ${{ (env.PUBLIC == 'true' && env.PUBLISH_DOCKERIO == 'true') && format('{0}:', env.SINGLEUSER) || '' }}
222
233
223
234
- name : Build and push jupyterhub/singleuser
224
235
uses : docker/build-push-action@v6
225
236
with :
226
237
build-args : |
227
- JUPYTERHUB_VERSION=${{ github.ref_type == ' tag' && github.ref_name || format('git:{0}', github.sha) }}
238
+ JUPYTERHUB_VERSION=${{ needs. tag.outputs.jupyterhub-version }}
228
239
context : singleuser
229
240
platforms : linux/amd64,linux/arm64
230
241
push : true
231
242
tags : ${{ join(fromJson(steps.singleusertags.outputs.tags)) }}
243
+ cache-from : ${{ env.CACHE_FROM }}
244
+ cache-to : ${{ env.CACHE_TO }}
0 commit comments