base: refreeze requirements.txt #95
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: Build | |
on: | |
pull_request: | |
paths-ignore: | |
- "docs/**" | |
push: | |
paths-ignore: | |
- "docs/**" | |
branches-ignore: | |
- "dependabot/**" | |
- "pre-commit-ci-update-config" | |
- "update-requirements/**" | |
workflow_dispatch: | |
# Tags are calculated based on the existing tags in the repository | |
# so we can only run builds consecutively, otherwise two images in | |
# different builds may have the same tag. | |
# If there are multiple pending jobs older pending jobs will be cancelled. | |
concurrency: | |
group: ${{ github.workflow }}-${{ github.ref }} | |
env: | |
# If we're on this branch don't bother fetching every tag since we know | |
# this will be the most recent tag, i.e. builds on this branch should always | |
# update the `latest` tag | |
LATEST_BRANCH: main | |
# Only push images if this workflow is run on this branch | |
# This ensures if a a new branch is created in this repo it won't automatically | |
# push an image. | |
# If this is a backport then change this to the name of the backports branch | |
PUBLISH_BRANCH: main | |
IMAGE: jupyterhub/jupyterhub | |
SINGLEUSER: jupyterhub/singleuser | |
PUBLISH_DOCKERIO: "true" | |
# Enable caching across builds, set to "" to disable | |
CACHE_FROM: type=gha | |
CACHE_TO: type=gha,mode=max | |
permissions: | |
contents: read | |
jobs: | |
tag: | |
runs-on: ubuntu-24.04 | |
timeout-minutes: 2 | |
outputs: | |
existing-tags: ${{ steps.quayio.outputs.tags }} | |
new-tag: ${{ steps.new.outputs.TAG }} | |
jupyterhub-version: ${{ steps.version.outputs.VERSION }} | |
steps: | |
- uses: actions/checkout@v4 | |
with: | |
persist-credentials: false | |
- name: JupyterHub version | |
id: version | |
run: | | |
VERSION=$(grep '^jupyterhub==' base/requirements.txt | cut -d= -f3) | |
if [[ $VERSION =~ [0-9]+\.[0-9]+\.[0-9]+ ]]; then | |
echo "VERSION=$VERSION" >> $GITHUB_OUTPUT | |
else | |
echo "Failed to get JupyterHub version" | |
exit 1 | |
fi | |
- name: Get build-number by looking at existing tags | |
id: quayio | |
uses: jupyterhub/[email protected] | |
with: | |
repository: ${{ env.IMAGE }} | |
version: ${{ steps.version.outputs.VERSION }} | |
# https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/accessing-contextual-information-about-workflow-runs#github-context | |
# If this is not LATEST_BRANCH, nor is it a pull request against LATEST_BRANCH | |
# assume it's a backport branch which means we need to get all tags to work out | |
# which MAJOR and MAJOR.MINOR aliases are needed. | |
allTags: ${{ (github.ref != format('refs/heads/{0}', env.LATEST_BRANCH)) && (github.base_ref != format('refs/heads/{0}', env.LATEST_BRANCH)) }} | |
# can't use strict when building prereleases | |
strict: "false" | |
- name: Get new tag # zizmor: ignore[template-injection] | |
id: new | |
run: | | |
echo "TAG=${{ steps.version.outputs.VERSION }}-${{ steps.quayio.outputs.buildNumber }}" >> $GITHUB_OUTPUT | |
publish-docker: | |
runs-on: ubuntu-24.04 | |
timeout-minutes: 30 | |
needs: | |
- tag | |
services: | |
# So that we can test this in PRs/branches | |
local-registry: | |
image: registry:2 | |
ports: | |
- 5000:5000 | |
steps: | |
- uses: actions/checkout@v4 | |
with: | |
persist-credentials: false | |
- name: Should we push this image to a public registry? | |
run: | | |
if [ "${{ github.ref == format('refs/heads/{0}', env.PUBLISH_BRANCH) }}" = "true" ]; then | |
echo "REGISTRY=quay.io/" >> $GITHUB_ENV | |
echo "PUBLIC=true" >> $GITHUB_ENV | |
else | |
echo "REGISTRY=localhost:5000/" >> $GITHUB_ENV | |
echo "PUBLIC=false" >> $GITHUB_ENV | |
fi | |
# Setup docker to build for multiple platforms, see: | |
# https://github.com/docker/build-push-action/tree/v6.9.0?tab=readme-ov-file#usage | |
# https://docs.docker.com/build/ci/github-actions/multi-platform/ | |
- name: Set up QEMU (for docker buildx) | |
uses: docker/setup-qemu-action@v3 | |
# We never run or use untrusted code so cache-poisoning shouldn't be | |
# possible other than if a dependency or action is compromised | |
- name: Set up Docker Buildx (for multi-arch builds) | |
uses: docker/setup-buildx-action@v3 # zizmor: ignore[cache-poisoning] | |
with: | |
# Allows pushing to registry on localhost:5000 | |
driver-opts: network=host | |
- name: Setup push rights to Docker Hub | |
# This was setup by... | |
# 1. Creating a [Robot Account](https://quay.io/organization/jupyterhub?tab=robots) in the JupyterHub | |
# . Quay.io org | |
# 2. Giving it enough permissions to push to the jupyterhub and singleuser images | |
# 3. Putting the robot account's username and password in GitHub actions environment | |
if: env.PUBLIC == 'true' | |
run: | | |
docker login -u "${{ secrets.QUAY_USERNAME }}" -p "${{ secrets.QUAY_PASSWORD }}" "${{ env.REGISTRY }}" | |
if [ "${{ env.PUBLISH_DOCKERIO }}" = "true" ]; then | |
docker login -u "${{ secrets.DOCKERHUB_USERNAME }}" -p "${{ secrets.DOCKERHUB_TOKEN }}" docker.io | |
fi | |
# image: env.IMAGE | |
# | |
# https://github.com/jupyterhub/action-major-minor-tag-calculator | |
# If this is a tagged build this will return additional parent tags. | |
# E.g. 1.2.3 is expanded to Docker tags | |
# [{prefix}:1.2.3, {prefix}:1.2, {prefix}:1, {prefix}:latest] unless | |
# this is a backported tag in which case the newer tags aren't updated. | |
- name: Calculate tags | |
id: jupyterhubtags | |
uses: jupyterhub/action-major-minor-tag-calculator@v3 | |
with: | |
existingTags: ${{ needs.tag.outputs.existing-tags }} | |
newTag: ${{ needs.tag.outputs.new-tag }} | |
prereleaseHasBuild: "true" | |
prefix: >- | |
${{ env.REGISTRY }}${{ env.IMAGE }}: | |
${{ (env.PUBLIC == 'true' && env.PUBLISH_DOCKERIO == 'true') && format('{0}:', env.IMAGE) || '' }} | |
- name: Print tags # zizmor: ignore[template-injection] | |
run: | | |
echo "Existing tags: ${{ needs.tag.outputs.existing-tags }}" | |
echo "New tags: ${{ needs.tag.outputs.new-tag }}" | |
echo "Image tags: ${{ steps.jupyterhubtags.outputs.tags }}" | |
- name: Build and push jupyterhub | |
uses: docker/build-push-action@v6 # zizmor: ignore[cache-poisoning] | |
with: | |
context: base | |
platforms: linux/amd64,linux/arm64 | |
push: true | |
# tags parameter must be a string input so convert `gettags` JSON | |
# array into a comma separated list of tags | |
tags: ${{ join(fromJson(steps.jupyterhubtags.outputs.tags)) }} | |
cache-from: ${{ env.CACHE_FROM }} | |
cache-to: ${{ env.CACHE_TO }} | |
# image: env.IMAGE-demo | |
# | |
- name: Get list of jupyterhub-demo tags | |
id: demotags | |
uses: jupyterhub/action-major-minor-tag-calculator@v3 | |
with: | |
existingTags: ${{ needs.tag.outputs.existing-tags }} | |
newTag: ${{ needs.tag.outputs.new-tag }} | |
prereleaseHasBuild: "true" | |
prefix: >- | |
${{ env.REGISTRY }}${{ env.IMAGE }}-demo: | |
${{ (env.PUBLIC == 'true' && env.PUBLISH_DOCKERIO == 'true') && format('{0}-demo:', env.IMAGE) || '' }} | |
- name: Build and push jupyterhub-demo | |
uses: docker/build-push-action@v6 | |
with: | |
build-args: | | |
BASE_IMAGE=${{ fromJson(steps.jupyterhubtags.outputs.tags)[0] }} | |
context: demo-image | |
platforms: linux/amd64,linux/arm64 | |
push: true | |
tags: ${{ join(fromJson(steps.demotags.outputs.tags)) }} | |
cache-from: ${{ env.CACHE_FROM }} | |
cache-to: ${{ env.CACHE_TO }} | |
# image: env.SINGLEUSER | |
# | |
- name: Get list of jupyterhub/singleuser tags | |
id: singleusertags | |
uses: jupyterhub/action-major-minor-tag-calculator@v3 | |
with: | |
existingTags: ${{ needs.tag.outputs.existing-tags }} | |
newTag: ${{ needs.tag.outputs.new-tag }} | |
prereleaseHasBuild: "true" | |
prefix: >- | |
${{ env.REGISTRY }}${{ env.SINGLEUSER }}: | |
${{ (env.PUBLIC == 'true' && env.PUBLISH_DOCKERIO == 'true') && format('{0}:', env.SINGLEUSER) || '' }} | |
- name: Build and push jupyterhub/singleuser | |
uses: docker/build-push-action@v6 | |
with: | |
build-args: | | |
JUPYTERHUB_VERSION=${{ needs.tag.outputs.jupyterhub-version }} | |
context: singleuser | |
platforms: linux/amd64,linux/arm64 | |
push: true | |
tags: ${{ join(fromJson(steps.singleusertags.outputs.tags)) }} | |
cache-from: ${{ env.CACHE_FROM }} | |
cache-to: ${{ env.CACHE_TO }} | |
- name: Setup Python | |
uses: actions/setup-python@v5 | |
with: | |
python-version: "3.13" | |
cache: pip | |
cache-dependency-path: tests/dev-requirements.txt | |
- name: Install test dependencies | |
run: | | |
python -mpip install -r tests/dev-requirements.txt | |
python -mplaywright install chromium | |
- name: Test demo image # zizmor: ignore[template-injection] | |
run: | | |
DEMO_IMAGE=${{ fromJson(steps.demotags.outputs.tags)[0] }} | |
docker run -d --name hub -p8000:8000 "$DEMO_IMAGE" | |
sleep 5 | |
python -mpytest | |
- name: Get logs, including on failure | |
if: always() | |
run: docker logs hub |