Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions .github/workflows/clean_docker.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
name: Clean docker

on:
schedule:
- cron: "0 3 * * 0" # weekly, Sunday 03:00 UTC
workflow_dispatch:

jobs:
prune:
name: Delete stale per-run tags from GHCR
runs-on: ubuntu-latest
permissions:
packages: write
steps:
- name: Delete old, untagged/non-latest package versions
uses: actions/delete-package-versions@v5
with:
package-name: mrpro_runner
package-type: container
min-versions-to-keep: 10
delete-only-untagged-versions: false
ignore-versions: '^latest$'
112 changes: 26 additions & 86 deletions .github/workflows/docker.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Build and test docker
name: Docker

on:
pull_request:
Expand All @@ -8,68 +8,36 @@ on:
branches:
- main


jobs:
get_dockerfiles:
name: Get list of Docker files for different containers
changes:
name: Check if Docker files changed
runs-on: ubuntu-latest
outputs:
docker_toml: ${{ steps.filter.outputs.docker_toml }}
docker_tasks: ${{ steps.set-matrix.outputs.docker_tasks }}
docker_changed: ${{ steps.filter.outputs.docker }}
steps:
- name: Checkout mrpro repo
uses: actions/checkout@v6

- name: Check if files in docker or the toml file has been modified
- name: Check if files in docker or the workflow file have been modified
uses: dorny/paths-filter@v3
id: filter
with:
filters: |
docker_toml:
docker:
- 'docker/*'
- '.github/workflows/docker.yml'

- name: Do we need to do something?
run: |
echo "Push to main? ${{ github.event_name }} "
echo "Rebuild containers? ${{ steps.filter.outputs.docker_toml }}"

- name: Define docker image names
id: set-matrix
if: steps.filter.outputs.docker_toml == 'true' || github.event_name == 'push'
run: |
# docker_tasks is a list of pairs (dictionaries) with keys 'filepath' and 'image_name' like:
# [{"filepath": "docker/Dockerfile_x1", "image_name": "ghcr.io/ptb-mr/mrpro_x1"}, ...]
docker_tasks=$(find docker -type f -name 'Dockerfile_*' | jq -R -s -c 'split("\n")[:-1]' | \
jq -r -c 'map({filepath: ., image_name: . | sub("docker/Dockerfile"; "ghcr.io\/ptb-mr\/mrpro")})')
echo "docker_tasks: $docker_tasks"
echo "docker_tasks=$docker_tasks" >> $GITHUB_OUTPUT

- name: Dockerfile overview
if: steps.filter.outputs.docker_toml == 'true' || github.event_name == 'push'
run: |
echo "final list of docker_tasks: ${{ steps.set-matrix.outputs.docker_tasks }}"

push_test:
name: Create test images and push to GCR
needs: get_dockerfiles
if: needs.get_dockerfiles.outputs.docker_toml == 'true' || github.event_name == 'push'
name: Build test image and push to GHCR
needs: changes
if: needs.changes.outputs.docker_changed == 'true' || github.event_name == 'push'
runs-on: ubuntu-latest
permissions:
packages: write
strategy:
fail-fast: false
matrix:
docker_task: ${{ fromJson(needs.get_dockerfiles.outputs.docker_tasks) }}
steps:
- name: Checkout
uses: actions/checkout@v6

- name: Get image basename
id: image_name
run: |
echo "dockerfile_basename=$(basename ${{ matrix.docker_task.filepath }})" >> $GITHUB_OUTPUT

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

Expand All @@ -84,66 +52,38 @@ jobs:
uses: docker/build-push-action@v6
with:
context: .
cache-from: type=gha,scope=$${{ steps.image_name.outputs.dockerfile_basename }})
cache-to: type=gha,mode=max,scope=${{ steps.image_name.outputs.dockerfile_basename }}
file: ${{ matrix.docker_task.filepath }}
cache-from: type=gha
cache-to: type=gha,mode=max
file: docker/Dockerfile
push: true
tags: ${{ matrix.docker_task.image_name }}:test
tags: ghcr.io/ptb-mr/mrpro_runner:${{ github.run_id }}

test:
name: Test docker containers
needs: [get_dockerfiles, push_test]
if: needs.get_dockerfiles.outputs.docker_toml == 'true' || github.event_name == 'push'
name: Test docker container
needs: [changes, push_test]
if: needs.changes.outputs.docker_changed == 'true' || github.event_name == 'push'
runs-on: ubuntu-latest
permissions:
pull-requests: write
contents: write
strategy:
matrix:
docker_task: ${{ fromJson(needs.get_dockerfiles.outputs.docker_tasks) }}
# runs within Docker container
container:
image: ${{ matrix.docker_task.image_name }}:test
image: ghcr.io/ptb-mr/mrpro_runner:${{ github.run_id }}
options: --user runner

steps:
- name: Checkout repo
uses: actions/checkout@v6

- name: Install mrpro and dependencies
run: |
# for the test of minimal requirements we do not want to update dependencies
# the image_url is like ghcr.io/ptb-mr/mrpro_py310_minimal, we want to check the last part after the last slash
image_url="${{ matrix.docker_task.image_name }}"
if [ "${image_url##*/}" = "mrpro_py310_minimal" ]; then
pip install .[tests]
elif [ "${image_url##*/}" = "mrpro_runner" ]; then
pip install --upgrade --upgrade-strategy eager .[tests]
else
pip install --index-url=https://download.pytorch.org/whl/cpu --extra-index-url https://pypi.org/simple/ --upgrade --upgrade-strategy eager .[tests]
fi

- name: Install pytest-github-actions-annotate-failures plugin
run: pip install pytest-github-actions-annotate-failures
- name: Install mrpro and test dependencies
run: pip install ".[tests]" pytest-github-actions-annotate-failures

- name: Run PyTest
run: |
pytest -n 4 -m "not cuda"
run: pytest -n 4 -m "not cuda"

push_latest:
name: Pull latest images and push to GCR with new tag
needs: [get_dockerfiles, test]
if: needs.get_dockerfiles.outputs.docker_toml == 'true' || github.event_name == 'push'
name: Pull test image and push with new tag
needs: [changes, test]
if: needs.changes.outputs.docker_changed == 'true' || github.event_name == 'push'
runs-on: ubuntu-latest
permissions:
packages: write
strategy:
matrix:
docker_task: ${{ fromJson(needs.get_dockerfiles.outputs.docker_tasks) }}
steps:
- name: Checkout
uses: actions/checkout@v6

- name: Login to GitHub Packages
uses: docker/login-action@v3
with:
Expand All @@ -153,6 +93,6 @@ jobs:

- name: Pull and push Docker image with new tag
run: |
docker pull ${{ matrix.docker_task.image_name }}:test
docker tag ${{ matrix.docker_task.image_name }}:test ${{ matrix.docker_task.image_name }}:latest
docker push ${{ matrix.docker_task.image_name}}:latest
docker pull ghcr.io/ptb-mr/mrpro_runner:${{ github.run_id }}
docker tag ghcr.io/ptb-mr/mrpro_runner:${{ github.run_id }} ghcr.io/ptb-mr/mrpro_runner:latest
docker push ghcr.io/ptb-mr/mrpro_runner:latest
49 changes: 29 additions & 20 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ jobs:
pull-requests: write
contents: write
container:
image: ghcr.io/ptb-mr/mrpro_py311:latest
image: ghcr.io/ptb-mr/mrpro_runner:latest
options: --user root
strategy:
fail-fast: false
Expand All @@ -51,29 +51,38 @@ jobs:
uses: actions/checkout@v6

- name: Install mrpro and dependencies
run: pip install --index-url=https://download.pytorch.org/whl/cpu --extra-index-url https://pypi.org/simple/ --upgrade --upgrade-strategy "eager" -e .[notebooks]
run: pip install --upgrade --upgrade-strategy "eager" .[notebooks]

- name: Notebook name
run: |
echo "current jupyter-notebook: ${{ matrix.notebook_path }}"

- name: Add nb-myst download badge
env:
NOTEBOOK: ${{ matrix.notebook_path }}
run: |
notebook=${{ matrix.notebook_path }}
notebook_name=$(basename $notebook)
download_badge_md="[![Download notebook](https://img.shields.io/badge/Download-notebook-blue?logo=jupyter)](path:$notebook_name)"
python_command="import nbformat as nbf\n\
nb = nbf.read(open('$notebook'), as_version=4)\n\
# if the 1st cell is md and has colab text => add space after\n\
if nb['cells'][0]['cell_type'] == 'markdown' and 'colab' in nb['cells'][0]['source'].lower():\n\
nb['cells'][0]['source'] += ' '\n\
# if there is no md cell with colab => create empty md cell on top\n\
else:\n\
nb['cells'].insert(0, nbf.v4.new_markdown_cell())\n\
nb['cells'][0]['source'] += '$download_badge_md'\n\
nbf.write(nb, open('$notebook', 'w'))"

python -c "exec (\"$python_command\")"
python - <<'EOF'
import os
import nbformat as nbf

notebook = os.environ["NOTEBOOK"]
notebook_name = os.path.basename(notebook)
badge_md = (
f"[![Download notebook](https://img.shields.io/badge/Download-notebook-blue?logo=jupyter)]"
f"(path:{notebook_name})"
)

nb = nbf.read(open(notebook), as_version=4)
first_cell = nb["cells"][0] if nb["cells"] else None

if first_cell and first_cell["cell_type"] == "markdown" and "colab" in first_cell["source"].lower():
first_cell["source"] += " "
else:
nb["cells"].insert(0, nbf.v4.new_markdown_cell())

nb["cells"][0]["source"] += badge_md
nbf.write(nb, open(notebook, "w"))
EOF

- name: Run notebook
uses: fzimmermann89/run-notebook@v3
Expand Down Expand Up @@ -103,7 +112,7 @@ jobs:
needs: run_notebook
runs-on: ubuntu-latest
container:
image: ghcr.io/ptb-mr/mrpro_py311:latest
image: ghcr.io/ptb-mr/mrpro_runner:latest
options: --user runner
steps:
- name: Checkout
Expand All @@ -113,7 +122,7 @@ jobs:
fetch-tags: true

- name: Install mrpro and dependencies
run: pip install --index-url=https://download.pytorch.org/whl/cpu --extra-index-url https://pypi.org/simple/ --upgrade --upgrade-strategy "eager" -e .[docs]
run: pip install .[docs]

- name: Download executed notebook ipynb files
id: download
Expand All @@ -134,7 +143,7 @@ jobs:
name: Documentation
path: docs/build/html/

# if the one of above steps fails the "artifact-url" will be an empty string
# if one of the above steps fails the "artifact-url" will be an empty string
- name: Dump documentation info
if: always()
run: |
Expand Down
Loading
Loading