Skip to content

Commit e9ff89c

Browse files
committed
chore: Add docker build step
1 parent 7edc3db commit e9ff89c

4 files changed

Lines changed: 133 additions & 0 deletions

File tree

.dockerignore

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
.git
2+
.github
3+
.omc
4+
.venv
5+
.ruff_cache
6+
.pytest_cache
7+
.ipynb_checkpoints
8+
__pycache__
9+
*.py[cod]
10+
*.egg-info
11+
build
12+
dist
13+
climate_ref_client
14+
notebooks/output
15+
notebooks/data
16+
tests/test-data/sample-data
17+
.DS_Store

.github/workflows/docker.yaml

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
name: Build and publish Docker image
2+
3+
on:
4+
push:
5+
branches: [main]
6+
tags: ["v*"]
7+
pull_request:
8+
paths:
9+
- Dockerfile
10+
- .dockerignore
11+
- .binder/requirements.txt
12+
- .github/workflows/docker.yaml
13+
workflow_dispatch:
14+
15+
env:
16+
REGISTRY: ghcr.io
17+
IMAGE_NAME: ${{ github.repository }}
18+
19+
jobs:
20+
build:
21+
runs-on: ubuntu-latest
22+
permissions:
23+
contents: read
24+
packages: write
25+
steps:
26+
- uses: actions/checkout@v6.0.2
27+
28+
- name: Set up Docker Buildx
29+
uses: docker/setup-buildx-action@v3
30+
31+
- name: Log in to GHCR
32+
if: github.event_name != 'pull_request'
33+
uses: docker/login-action@v3
34+
with:
35+
registry: ${{ env.REGISTRY }}
36+
username: ${{ github.actor }}
37+
password: ${{ secrets.GITHUB_TOKEN }}
38+
39+
- name: Extract image metadata
40+
id: meta
41+
uses: docker/metadata-action@v5
42+
with:
43+
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
44+
tags: |
45+
type=ref,event=branch
46+
type=ref,event=pr
47+
type=semver,pattern={{version}}
48+
type=semver,pattern={{major}}.{{minor}}
49+
type=sha,format=short
50+
type=raw,value=latest,enable={{is_default_branch}}
51+
52+
- name: Build and push
53+
uses: docker/build-push-action@v6
54+
with:
55+
context: .
56+
push: ${{ github.event_name != 'pull_request' }}
57+
tags: ${{ steps.meta.outputs.tags }}
58+
labels: ${{ steps.meta.outputs.labels }}
59+
cache-from: type=gha
60+
cache-to: type=gha,mode=max

Dockerfile

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Pre-built JupyterLab image for the Climate REF training notebooks.
2+
#
3+
# Mirrors the Binder/repo2docker setup in .binder/ so users can pull a ready
4+
# environment instead of waiting for Binder to build from scratch.
5+
#
6+
# Build:
7+
# docker build -t ghcr.io/climate-ref/climate-ref-tutorials:dev .
8+
# Run:
9+
# docker run --rm -p 8888:8888 ghcr.io/climate-ref/climate-ref-tutorials:dev
10+
11+
FROM quay.io/jupyter/minimal-notebook:python-3.12
12+
13+
LABEL org.opencontainers.image.source="https://github.com/Climate-REF/climate-ref-tutorials"
14+
LABEL org.opencontainers.image.description="JupyterLab environment for the Climate REF training notebooks"
15+
LABEL org.opencontainers.image.licenses="Apache-2.0"
16+
17+
# Cartopy needs GEOS + PROJ system libraries (matches .binder/apt.txt).
18+
USER root
19+
RUN apt-get update \
20+
&& apt-get install -y --no-install-recommends \
21+
libgeos-dev \
22+
libproj-dev \
23+
proj-bin \
24+
proj-data \
25+
&& apt-get clean \
26+
&& rm -rf /var/lib/apt/lists/*
27+
28+
USER ${NB_UID}
29+
WORKDIR /home/${NB_USER}/work
30+
31+
# Install pinned Python deps first so this layer caches across source edits.
32+
COPY --chown=${NB_UID}:${NB_GID} .binder/requirements.txt /tmp/requirements.txt
33+
RUN pip install --no-cache-dir uv \
34+
&& pip install --no-cache-dir -r /tmp/requirements.txt
35+
36+
# Copy the rest of the repo and install the helper package + generated client.
37+
COPY --chown=${NB_UID}:${NB_GID} . .
38+
RUN pip install --no-cache-dir --no-deps . \
39+
&& bash scripts/generate_client.sh \
40+
&& python -c "from ref_tutorials import fetch_sample_data; fetch_sample_data()" \
41+
&& python -c "import matplotlib.pyplot as plt; plt.figure(); plt.text(0, 0, 'warm fonts'); plt.close('all')"
42+
43+
# Use the non-interactive Agg backend by default (matches .binder/start).
44+
ENV MPLBACKEND=Agg

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,18 @@ Binder builds the environment and opens JupyterLab; no local setup is required.
1616

1717
This may take several minutes
1818

19+
## Run with Docker (pre-built image)
20+
21+
A pre-built JupyterLab image is published to the GitHub Container Registry
22+
on every push to `main` and on every release tag.
23+
No local Python setup is required.
24+
25+
```bash
26+
docker run --rm -p 8888:8888 ghcr.io/climate-ref/climate-ref-tutorials:latest
27+
```
28+
29+
Open the JupyterLab URL printed in the terminal (includes the access token).
30+
1931
## Run locally
2032

2133
Requires [uv](https://docs.astral.sh/uv/), a Python Package Manager to create a local virtual environment.

0 commit comments

Comments
 (0)