Skip to content

base: refreeze requirements.txt #95

base: refreeze requirements.txt

base: refreeze requirements.txt #95

Workflow file for this run

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