From 33cc73eaa4e4d2c4fe6ee51932331d155bb2ccb7 Mon Sep 17 00:00:00 2001 From: Josef Haupt Date: Mon, 27 Jul 2026 21:57:55 +0200 Subject: [PATCH 1/3] CI model cache --- .github/workflows/ci.yml | 19 +++++++++++++++++++ .gitignore | 3 +++ 2 files changed, 22 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dd51b22cd..8b92507e8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,6 +20,14 @@ jobs: runs-on: ${{ matrix.os }} env: IS_GITHUB_RUNNER: "true" + # Fixed, cacheable location for birdnet's model/label downloads. The acoustic + # (~120 MB), geo (~35 MB) and perch-v2 (~380 MB) models all land here, and + # birdnet reads this env var at import time and skips the download when the + # files already exist - so a cache hit avoids re-fetching them from tuc.cloud + # on every job. Placed under the workspace because the `runner` context isn't + # available in job-level env but `github.workspace` is; checkout runs before + # the cache is restored, so it never wipes the restored models. + BIRDNET_APP_DATA: ${{ github.workspace }}/.birdnet-app-data strategy: matrix: os: [ubuntu-latest, macos-latest, windows-latest] @@ -37,6 +45,17 @@ jobs: with: enable-cache: true cache-dependency-glob: "pyproject.toml" + # Cache the downloaded models across runs. Keyed on the birdnet dependency + # (via pyproject.toml) and the OS only - the model files are identical across + # Python versions, so all matrix jobs on an OS share one cache. birdnet + # re-downloads only missing/updated models, so restoring an older cache is safe. + - name: Cache birdnet models + uses: actions/cache@v4 + with: + path: ${{ env.BIRDNET_APP_DATA }} + key: birdnet-models-${{ runner.os }}-${{ hashFiles('pyproject.toml') }} + restore-keys: | + birdnet-models-${{ runner.os }}- - name: Install dependencies run: uv pip install --system .[embeddings,train,tests,gui-tests] - name: Run tests diff --git a/.gitignore b/.gitignore index 84c8345d7..ba9a9a55f 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,9 @@ uv.lock # Ruff .ruff_cache +# CI model cache location (BIRDNET_APP_DATA in .github/workflows/ci.yml) +.birdnet-app-data + # Models checkpoints From 4224a9569cd4e42b899817bdfab8f24e3cc73087 Mon Sep 17 00:00:00 2001 From: Josef Haupt Date: Tue, 28 Jul 2026 15:02:18 +0200 Subject: [PATCH 2/3] . --- .github/workflows/ci.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8b92507e8..82cbd4d6c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,7 +13,9 @@ on: concurrency: group: "${{ github.event.pull_request.number }}-${{ github.ref_name }}-${{ github.workflow }}" - cancel-in-progress: true + # Cancel superseded PR runs when new commits are pushed, but let pushes to main + # run to completion so a merge's CI is never cancelled by the next merge. + cancel-in-progress: ${{ github.event_name == 'pull_request' }} jobs: running-tests: From b7e4a54f583354133c30c7908ea3a07426b31732 Mon Sep 17 00:00:00 2001 From: Josef Haupt Date: Tue, 28 Jul 2026 16:56:53 +0200 Subject: [PATCH 3/3] . --- .github/workflows/ci.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 82cbd4d6c..3d07dd9b5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,6 +20,10 @@ concurrency: jobs: running-tests: runs-on: ${{ matrix.os }} + # Backstop for a wedged run (e.g. a hung model download or a native deadlock + # that pytest-timeout can't interrupt). Healthy jobs finish in ~2-6 min, plus a + # few for a cold-cache pre-download, so 30 caps a hang's cost with wide margin. + timeout-minutes: 30 env: IS_GITHUB_RUNNER: "true" # Fixed, cacheable location for birdnet's model/label downloads. The acoustic @@ -60,6 +64,15 @@ jobs: birdnet-models-${{ runner.os }}- - name: Install dependencies run: uv pip install --system .[embeddings,train,tests,gui-tests] + # Pre-download the models outside pytest's per-test 120s timeout. On a cold + # cache the acoustic-model download from tuc.cloud can exceed 120s on slower + # (Windows/macOS) runners and trip the timeout mid-download - which also means + # the model cache never captures a complete model, so the next run is cold + # again and the flake is permanent. Fetching them here (no per-test timeout) + # breaks that cycle and lets the cache actually populate. Mirrors the models + # baked into the Docker image. + - name: Pre-download birdnet models + run: python -c "import birdnet; birdnet.load('acoustic', '2.4', 'tf', lang='en_us'); birdnet.load('geo', '2.4', 'tf', lang='en_us')" - name: Run tests run: | python -m pytest