Skip to content

Commit fbba5b4

Browse files
committed
Merge branch 'master' into sunt05/gh3-xsmd-input
Resolve conflict in test_df_state_conversion.py by adopting semantic assertion approach from master (checking specific field presence/absence) rather than brittle column count assertions.
2 parents 0ede9a4 + 2407c29 commit fbba5b4

File tree

109 files changed

+5603
-8824
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

109 files changed

+5603
-8824
lines changed

.claude/reference/quick-start.md

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,24 @@ make test
5353
## Makefile Commands
5454

5555
- `make setup` - Create virtual environment with uv (if available)
56-
- `make dev` - Install SUEWS in editable mode
56+
- `make dev` - Install SUEWS in editable mode (self-healing, works after clean)
5757
- `make test` - Run test suite
5858
- `make clean` - Smart clean (keeps .venv if active)
5959
- `make format` - Format Python and Fortran code
6060

61+
## Common Workflows
62+
63+
```bash
64+
# Fresh start (most common for troubleshooting)
65+
make clean && make dev
66+
67+
# Update code and rebuild
68+
git pull && make dev
69+
70+
# Build and test changes
71+
make dev && make test
72+
```
73+
6174
## Package Name Differences
6275

6376
When migrating from mamba:

.github/ISSUE_TEMPLATE/release-checklist.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,12 @@ assignees: ''
1515
- [ ] **Clean state**: `git checkout master && git pull && git status` (clean)
1616
- [ ] **Tests pass**: `make test` (all pass)
1717
- [ ] **Docs build**: `make docs` (no errors)
18+
- [ ] **Update CHANGELOG.md**: Add entry under today's date with `[feature]`, `[bugfix]`, `[change]` tags + issue links
19+
- [ ] **Create version history page**: Create `docs/source/version-history/vYYYY.M.D.rst` and add to toctree in `version-history.rst` (remove `new_latest` label from previous version)
1820

1921
## Create Release
2022

21-
- [ ] **Update CHANGELOG.md**: Add entry under today's date with `[feature]`, `[bugfix]`, `[change]` tags + issue links
22-
- [ ] **Commit & push**: `git add CHANGELOG.md && git commit -m "docs: update changelog for YYYY.M.D" && git push`
23+
- [ ] **Commit & push**: `git add CHANGELOG.md docs/source/version-history/ && git commit -m "docs: update changelog and version history for YYYY.M.D" && git push`
2324
- [ ] **Tag & push**:
2425
```bash
2526
VERSION="YYYY.M.D"
@@ -50,7 +51,7 @@ git push origin $VERSION
5051

5152
## Announce (Optional)
5253

53-
- [ ] [UMEP Discussions](https://github.com/UMEP-dev/UMEP/discussions)
54+
- [ ] [SUEWS Discussions](https://github.com/UMEP-dev/SUEWS/discussions)
5455

5556
---
5657

.github/actions/build-suews/action.yml

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ inputs:
2222
description: 'Suffix for wheel artifact name (e.g., "", "-umep")'
2323
required: false
2424
default: ''
25+
test_tier:
26+
description: 'Test tier: smoke, core, cfg, standard, or all'
27+
required: false
28+
default: 'standard'
2529

2630
runs:
2731
using: 'composite'
@@ -129,7 +133,14 @@ runs:
129133
pip install delvewheel
130134
CIBW_REPAIR_WHEEL_COMMAND_WINDOWS: "delvewheel repair -w {dest_dir} {wheel}"
131135
CIBW_TEST_REQUIRES: ${{ inputs.is_umep_variant == 'true' && 'pytest oldest-supported-numpy' || 'pytest' }}
132-
CIBW_TEST_COMMAND: "python -m pytest {project}/test -v --tb=short --durations=10"
136+
# Test tiers: choose subset based on path detection and trigger
137+
CIBW_TEST_COMMAND: >
138+
python -m pytest {project}/test -v --tb=short
139+
${{ inputs.test_tier == 'smoke' && '-m smoke' ||
140+
inputs.test_tier == 'core' && '-m "smoke or core"' ||
141+
inputs.test_tier == 'cfg' && '-m "smoke or cfg"' ||
142+
inputs.test_tier == 'standard' && '-m "not slow"' ||
143+
'--durations=10' }}
133144
MACOSX_DEPLOYMENT_TARGET: ${{ inputs.buildplat == 'macos-13' && '13.0' || '15.0' }}
134145

135146
- name: Upload wheels

.github/pages/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,7 @@
235235
</div>
236236
</a>
237237

238-
<a href="https://github.com/UMEP-dev/umep/discussions" class="nav-card">
238+
<a href="https://github.com/UMEP-dev/SUEWS/discussions" class="nav-card">
239239
<span class="nav-icon">💬</span>
240240
<div class="nav-title">Community</div>
241241
<div class="nav-description">

.github/workflows/build-publish_to_pypi.yml

Lines changed: 95 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,9 @@ name: Build and Publish Python wheels to PyPI and TestPyPI
8686
# - Both wheel sets deployed to PyPI together (production tags only)
8787
# - UMEP builds use Python 3.12 only (aligned with QGIS 3.40 LTR bundled Python)
8888
# - UMEP build triggers (runs on ALL events for continuous NumPy 1.x testing):
89-
# * PRs: manylinux cp312 (NumPy 1.x validation before merge)
90-
# * Master/manual: manylinux cp312 (continuous UMEP compatibility testing)
91-
# * Nightly/dev tags: manylinux cp312 (daily testing, uses .dev1 to avoid version collision)
89+
# * PRs: win cp312 (NumPy 1.x validation before merge, Windows = QGIS primary platform)
90+
# * Master/manual: win cp312 (continuous UMEP compatibility testing)
91+
# * Nightly/dev tags: win cp312 (daily testing, uses .dev1 to avoid version collision)
9292
# * Production tags: all platforms cp312 (complete distribution → PyPI, uses rc1 suffix)
9393
#
9494
# UMEP INTEGRATION:
@@ -178,39 +178,72 @@ jobs:
178178
# Only run for pull requests (push/tag/schedule always build)
179179
if: github.event_name == 'pull_request'
180180
outputs:
181-
needs-build: ${{ steps.filter.outputs.code }}
181+
needs-build: ${{ steps.filter.outputs.core == 'true' || steps.filter.outputs.util == 'true' || steps.filter.outputs.cfg == 'true' || steps.filter.outputs.tests == 'true' }}
182+
core-changed: ${{ steps.filter.outputs.core }}
183+
util-changed: ${{ steps.filter.outputs.util }}
184+
cfg-changed: ${{ steps.filter.outputs.cfg }}
185+
tests-changed: ${{ steps.filter.outputs.tests }}
182186
steps:
183187
- uses: actions/checkout@v4
184188

185189
- uses: dorny/paths-filter@v3
186190
id: filter
187191
with:
188192
filters: |
189-
code:
193+
core:
194+
# Fortran physics
195+
- 'src/suews/src/**'
196+
# Fortran driver interface
197+
- 'src/supy_driver/**'
198+
- 'src/supy/supy_driver/**'
199+
# Core Python files that affect scientific rigour
200+
- 'src/supy/_run.py'
201+
- 'src/supy/_supy_module.py'
202+
- 'src/supy/_load.py'
203+
- 'src/supy/_post.py'
204+
- 'src/supy/_check.py'
205+
- 'src/supy/_save.py'
206+
- 'src/supy/suews_sim.py'
207+
# Catch-all for core package changes
190208
- 'src/suews/**'
209+
util:
210+
# Utility modules (nice-to-have)
211+
- 'src/supy/util/**'
212+
# Default category for remaining supy changes
191213
- 'src/supy/**'
192-
- 'src/supy_driver/**'
193-
- '.github/workflows/build-publish_to_pypi.yml'
194-
- '.github/actions/build-suews/**'
195-
- 'test/**'
196-
- 'scripts/**'
197-
# NOTE: schemas/** excluded - JSON metadata files don't require builds
198-
# Bot-created schema PRs use schema-pr-validation.yml instead
214+
cfg:
215+
# Config validation
216+
- 'src/supy/data_model/**'
217+
# CLI commands
218+
- 'src/supy/cmd/**'
219+
# JSON configs
220+
- 'src/supy/*.json'
221+
# Schemas
222+
- 'schemas/**'
223+
# Build system / infra
224+
- '.github/**'
199225
- 'pyproject.toml'
200226
- 'meson.build'
201227
- 'meson_options.txt'
202228
- 'Makefile'
203229
- 'get_ver_git.py'
230+
- 'scripts/**'
231+
tests:
232+
- 'test/**'
204233
205234
# Determine if we should run limited or full matrix
206235
determine_matrix:
207236
name: Determine build matrix
208237
runs-on: ubuntu-latest
238+
needs: [detect-changes]
239+
# Always run - detect-changes is skipped for non-PR events, but we still need matrix
240+
if: always() && (needs.detect-changes.result == 'success' || needs.detect-changes.result == 'skipped')
209241
outputs:
210242
buildplat: ${{ steps.set-matrix.outputs.buildplat }}
211243
python: ${{ steps.set-matrix.outputs.python }}
212244
umep_buildplat: ${{ steps.set-matrix.outputs.umep_buildplat }}
213245
umep_python: ${{ steps.set-matrix.outputs.umep_python }}
246+
test_tier: ${{ steps.set-matrix.outputs.test_tier }}
214247
steps:
215248
- name: Set matrix based on trigger type
216249
id: set-matrix
@@ -220,30 +253,67 @@ jobs:
220253
PR_PLATFORMS='[["ubuntu-latest", "manylinux", "x86_64"], ["macos-latest", "macosx", "arm64"], ["windows-2025", "win", "AMD64"]]'
221254
MINIMAL_PLATFORMS='[["ubuntu-latest", "manylinux", "x86_64"]]'
222255
256+
CORE_CHANGED="${{ needs.detect-changes.outputs.core-changed || 'false' }}"
257+
UTIL_CHANGED="${{ needs.detect-changes.outputs.util-changed || 'false' }}"
258+
CFG_CHANGED="${{ needs.detect-changes.outputs.cfg-changed || 'false' }}"
259+
TESTS_CHANGED="${{ needs.detect-changes.outputs.tests-changed || 'false' }}"
260+
223261
# Python version strategies:
224262
# - Draft PR: minimal (1 platform, bookend versions) for fastest feedback
225263
# - PR/Master: reduced (ARM Mac + Linux + Windows, bookend versions) for speed with good coverage
226264
# - Tags/Schedule/Manual: full (all platforms including x86_64 Mac, all Python versions) for comprehensive testing
227265
228266
if [[ "${{ github.event_name }}" == "pull_request" ]] && [[ "${{ github.event.pull_request.draft }}" == "true" ]]; then
229-
echo "Running minimal matrix for draft PR (1 platform, bookend Python versions)"
230-
echo "buildplat=$MINIMAL_PLATFORMS" >> $GITHUB_OUTPUT
231-
echo 'python=["cp39", "cp314"]' >> $GITHUB_OUTPUT
232-
elif [[ "${{ github.event_name }}" == "pull_request" ]] || [[ "${{ github.event_name }}" == "push" && "${{ github.ref }}" == "refs/heads/master" ]]; then
233-
echo "Running reduced matrix for PR/master (ARM Mac + Linux + Windows, bookend Python versions)"
234-
echo "buildplat=$PR_PLATFORMS" >> $GITHUB_OUTPUT
235-
echo 'python=["cp39", "cp314"]' >> $GITHUB_OUTPUT
267+
if [[ "$CORE_CHANGED" == "true" ]]; then
268+
echo "Draft PR with core changes - reduced platforms, core tests"
269+
echo "buildplat=$PR_PLATFORMS" >> $GITHUB_OUTPUT
270+
echo 'python=["cp39", "cp314"]' >> $GITHUB_OUTPUT
271+
echo "test_tier=core" >> $GITHUB_OUTPUT
272+
elif [[ "$CFG_CHANGED" == "true" ]]; then
273+
echo "Draft PR with cfg changes - reduced platforms, cfg tests"
274+
echo "buildplat=$PR_PLATFORMS" >> $GITHUB_OUTPUT
275+
echo 'python=["cp39", "cp314"]' >> $GITHUB_OUTPUT
276+
echo "test_tier=cfg" >> $GITHUB_OUTPUT
277+
else
278+
echo "Draft PR with util/tests changes - minimal platform, smoke tests"
279+
echo "buildplat=$MINIMAL_PLATFORMS" >> $GITHUB_OUTPUT
280+
echo 'python=["cp39", "cp314"]' >> $GITHUB_OUTPUT
281+
echo "test_tier=smoke" >> $GITHUB_OUTPUT
282+
fi
283+
elif [[ "${{ github.event_name }}" == "pull_request" ]]; then
284+
if [[ "$CORE_CHANGED" == "true" ]]; then
285+
echo "Ready PR with core changes - reduced platforms, standard tests"
286+
echo "buildplat=$PR_PLATFORMS" >> $GITHUB_OUTPUT
287+
echo 'python=["cp39", "cp314"]' >> $GITHUB_OUTPUT
288+
echo "test_tier=standard" >> $GITHUB_OUTPUT
289+
elif [[ "$CFG_CHANGED" == "true" ]]; then
290+
echo "Ready PR with cfg changes - reduced platforms, standard tests"
291+
echo "buildplat=$PR_PLATFORMS" >> $GITHUB_OUTPUT
292+
echo 'python=["cp39", "cp314"]' >> $GITHUB_OUTPUT
293+
echo "test_tier=standard" >> $GITHUB_OUTPUT
294+
else
295+
echo "Ready PR with util/tests changes - reduced platforms, smoke tests"
296+
echo "buildplat=$PR_PLATFORMS" >> $GITHUB_OUTPUT
297+
echo 'python=["cp39", "cp314"]' >> $GITHUB_OUTPUT
298+
echo "test_tier=smoke" >> $GITHUB_OUTPUT
299+
fi
300+
elif [[ "${{ github.event_name }}" == "schedule" ]]; then
301+
echo "Running full matrix for nightly (all platforms, all Python versions, all tests)"
302+
echo "buildplat=$FULL_PLATFORMS" >> $GITHUB_OUTPUT
303+
echo 'python=["cp39", "cp310", "cp311", "cp312", "cp313", "cp314"]' >> $GITHUB_OUTPUT
304+
echo "test_tier=all" >> $GITHUB_OUTPUT
236305
else
237-
echo "Running full matrix for tags/schedule/manual (all platforms including x86_64 Mac, all Python versions)"
306+
echo "Running full matrix for tags/manual (all platforms, all Python versions, all tests)"
238307
echo "buildplat=$FULL_PLATFORMS" >> $GITHUB_OUTPUT
239308
echo 'python=["cp39", "cp310", "cp311", "cp312", "cp313", "cp314"]' >> $GITHUB_OUTPUT
309+
echo "test_tier=all" >> $GITHUB_OUTPUT
240310
fi
241311
242312
# UMEP builds: Python 3.12 only (aligned with QGIS 3.40 LTR bundled Python)
243-
# PR: manylinux only for quick validation
313+
# PR/nightly: Windows only (QGIS primary platform) for quick validation
244314
# Production: all platforms for complete UMEP distribution
245315
echo "UMEP matrix: cp312 only (QGIS 3.40 LTR uses Python 3.12)"
246-
echo 'umep_buildplat=[["ubuntu-latest", "manylinux", "x86_64"]]' >> $GITHUB_OUTPUT
316+
echo 'umep_buildplat=[["windows-2025", "win", "AMD64"]]' >> $GITHUB_OUTPUT
247317
echo 'umep_python=["cp312"]' >> $GITHUB_OUTPUT
248318
249319
build_wheels:
@@ -280,6 +350,7 @@ jobs:
280350
python: ${{ matrix.python }}
281351
is_umep_variant: 'false'
282352
wheel_name_suffix: ''
353+
test_tier: ${{ needs.determine_matrix.outputs.test_tier }}
283354

284355
build_umep:
285356
name: Build UMEP wheel (rc1/dev1) for ${{ matrix.python }}-${{ matrix.buildplat[1] }} ${{ matrix.buildplat[2] }}
@@ -321,6 +392,7 @@ jobs:
321392
python: ${{ matrix.python }}
322393
is_umep_variant: 'true'
323394
wheel_name_suffix: '-umep'
395+
test_tier: ${{ needs.determine_matrix.outputs.test_tier }}
324396

325397
# PR gate: single consolidated check for branch protection
326398
# This job ALWAYS runs for PRs and reports pass/fail based on build results
@@ -467,7 +539,7 @@ jobs:
467539
needs:
468540
- deploy_pypi
469541
# Run whenever PyPI deployment succeeds (already ensures it's a production tag)
470-
if: needs.deploy_pypi.result == 'success'
542+
if: always() && needs.deploy_pypi.result == 'success'
471543

472544
permissions:
473545
contents: write # Required to create releases

0 commit comments

Comments
 (0)