From fb92553766771ee19e4b216fffc8a17c52f9952c Mon Sep 17 00:00:00 2001 From: j-atkins <106238905+j-atkins@users.noreply.github.com> Date: Fri, 28 Aug 2026 12:17:36 +0100 Subject: [PATCH 1/2] add workflow/tests for checking notebook links --- .github/workflows/check-binder-files.yml | 36 +++++++++++++++++ .gitignore | 1 + tests/test_binder_files.py | 49 ++++++++++++++++++++++++ 3 files changed, 86 insertions(+) create mode 100644 .github/workflows/check-binder-files.yml create mode 100644 .gitignore create mode 100644 tests/test_binder_files.py diff --git a/.github/workflows/check-binder-files.yml b/.github/workflows/check-binder-files.yml new file mode 100644 index 0000000..5cc35ae --- /dev/null +++ b/.github/workflows/check-binder-files.yml @@ -0,0 +1,36 @@ +name: Test Binder Notebook Links + +on: + push: + branches: [ main ] + paths: + - '.binder/files_to_fetch.txt' + pull_request: + branches: [ main ] + paths: + - '.binder/files_to_fetch.txt' + schedule: + # run every Monday at 08:00 UTC to catch stale links automatically + - cron: '0 8 * * 1' + workflow_dispatch: # allow manual triggering from the GitHub Actions tab + +jobs: + test-notebook-links: + runs-on: ubuntu-latest + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.12' + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install pytest + + - name: Run notebook URL unit tests + run: | + pytest tests/test_binder_files.py -v \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7e99e36 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.pyc \ No newline at end of file diff --git a/tests/test_binder_files.py b/tests/test_binder_files.py new file mode 100644 index 0000000..4ae277f --- /dev/null +++ b/tests/test_binder_files.py @@ -0,0 +1,49 @@ +from pathlib import Path +import urllib.request +import urllib.error +import pytest + +REPO_OWNER = "Parcels-code" +REPO_NAME = "virtualship" +BRANCH = "main" + +# path to the list file relative to the repository root +LIST_FILE = Path(__file__).parent.parent / ".binder" / "files_to_fetch.txt" + + +def get_target_files(): + """Reads non-empty, non-comment lines from files_to_fetch.txt.""" + if not LIST_FILE.exists(): + pytest.fail(f"List file not found at: {LIST_FILE}") + + files = [] + with open(LIST_FILE, "r", encoding="utf-8") as f: + for line in f: + line = line.strip() + if line and not line.startswith("#"): + files.append(line) + return files + + +@pytest.mark.parametrize("file_path", get_target_files()) +def test_remote_notebook_exists(file_path): + """Sends a HEAD request to verify each notebook URL is active (HTTP 200).""" + raw_url = f"https://raw.githubusercontent.com/{REPO_OWNER}/{REPO_NAME}/{BRANCH}/{file_path}" + + req = urllib.request.Request(raw_url, method="HEAD") + + try: + with urllib.request.urlopen(req) as response: + assert response.status == 200, ( + f"Expected status 200, got {response.status} for {raw_url}" + ) + except urllib.error.HTTPError as e: + pytest.fail( + f"Failed to reach notebook at '{file_path}'.\n" + f"URL: {raw_url}\n" + f"HTTP Error: {e.code} {e.reason}\n" + f"Please update '.binder/files_to_fetch.txt' or report on the issue tracker: " + f"https://github.com/{REPO_OWNER}/{REPO_NAME}/issues" + ) + except urllib.error.URLError as e: + pytest.fail(f"Network error while connecting to {raw_url}: {e.reason}") From 8e4c9995ffd14d078c0a3c2bf39c92d54c5ae6bf Mon Sep 17 00:00:00 2001 From: j-atkins <106238905+j-atkins@users.noreply.github.com> Date: Fri, 28 Aug 2026 12:21:06 +0100 Subject: [PATCH 2/2] update workflow --- .github/workflows/check-binder-files.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/check-binder-files.yml b/.github/workflows/check-binder-files.yml index 5cc35ae..96f27af 100644 --- a/.github/workflows/check-binder-files.yml +++ b/.github/workflows/check-binder-files.yml @@ -6,13 +6,12 @@ on: paths: - '.binder/files_to_fetch.txt' pull_request: + types: [ opened, synchronize, reopened ] branches: [ main ] - paths: - - '.binder/files_to_fetch.txt' schedule: - # run every Monday at 08:00 UTC to catch stale links automatically + # run every Monday at 08:00 UTC to catch broken links automatically - cron: '0 8 * * 1' - workflow_dispatch: # allow manual triggering from the GitHub Actions tab + workflow_dispatch: # allow manual triggering from the Actions tab jobs: test-notebook-links: