Skip to content

Hm/GitHub workflows - #46

Merged
Hendrik-code merged 17 commits into
mainfrom
hm/github-workflows
Aug 7, 2026
Merged

Hm/GitHub workflows#46
Hendrik-code merged 17 commits into
mainfrom
hm/github-workflows

Conversation

@Hendrik-code

@Hendrik-code Hendrik-code commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator

Requires review

Hendrik-code and others added 6 commits August 5, 2026 07:54
Addresses part of #34. Sets up the linting/formatting toolchain, modelled on
Hendrik-code/TPTBox but trimmed for this codebase: the D (docstring), ANN
(annotation) and ARG002/ARG004/A001/A002 families are dropped because they
account for ~500 findings in a research codebase that is not docstring-first.

Also declares dependencies that package code imports but pyproject never
listed (nibabel, scipy, torchvision, batchgenerators) and promotes `progress`
and `numpy` out of the `all` extra, since auglab/utils/utils.py imports
`progress` unconditionally.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Mechanical change only, produced by:

    ruff check --fix --unsafe-fixes .
    ruff format .

Verified behavior-preserving: with torch/random/numpy all seeded, every one of
the 25 transform_params_gpu*.json configs produces byte-identical output from
AugTransformsGPU before and after this commit.

Isolated into its own commit so it can be skipped during review and added to
.git-blame-ignore-revs. In-flight branches will need a rebase.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Fixes the 26 findings left after the mechanical style pass.

The one real bug: auglab/transforms/gpu/transforms_list.py referenced
AugTransformsGPU in its __main__ demo block without importing it, so running
the module directly raised NameError. Ruff's F821 caught it.

Other genuine fixes: `raise ... from None` on the ZeroDivisionError re-raise in
image.py, lazy %-style logging instead of f-strings, loop variables no longer
shadowed by assignment, and `mp.cpu_count()` moved out of a function default in
generate_augmentations.py so it reflects the running machine.

Where a rule fought a deliberate choice, the suppression is narrow and
explains itself: nnU-Net signature parity (B008, per-file), numpy dtype
equality (E721), the shared-RNG counter (PLW0603), and spinalcordtoolbox API
parity (ARG001).

Also corrects the docstring of _choose_region_mode, which promised a
seg_mask=None check it never performed. Behavior was already correct --
_apply_region_mode short-circuits on a None mask -- so only the docstring
was wrong.

Verified: with torch/random/numpy seeded, all 25 transform_params_gpu*.json
configs still produce byte-identical output.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Addresses the "Unit-Testing" half of #34. 146 tests, CPU only, no image data
on disk, ~10s end to end -- cheap enough to gate every pull request.

- test_imports.py     every module under auglab/ imports cleanly
- test_configs.py     every shipped config parses, builds a pipeline, runs a
                      forward pass, and is reproducible under a fixed seed
- test_transforms_gpu.py  each GPU transform in isolation; classes are
                      discovered by introspection so new ones are covered
                      automatically
- test_packaging.py   builds the real wheel and checks what is inside it

Module discovery walks the filesystem rather than using pkgutil.walk_packages:
auglab has no __init__.py in transforms/, transforms/cpu/, transforms/gpu/ or
utils/, so it resolves as a PEP 420 namespace package and walk_packages reaches
only 4 of the 24 modules.

test_packaging.py immediately earned its keep -- see the package-data fix in
this commit.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Addresses the workflows, PyPI deployment and code-review parts of #34.

- lint.yml     runs `pre-commit run --all-files` on push and PR, so the hooks
               contributors run locally and the ones CI enforces cannot drift.
               Unlike the TPTBox workflow this is modelled on, it does not
               auto-commit fixes back: that breaks on fork PRs and rewrites
               contributors' branches under them.
- tests.yml    pytest on Python 3.10-3.12, plus a build job that runs
               `python -m build`, `twine check`, and asserts the wheel actually
               contains the modules and config JSONs. CPU-only torch is
               installed from the PyTorch CPU index first, otherwise pip pulls
               several GB of CUDA wheels for a suite that never touches a GPU.
- publish.yml  builds and uploads on release, authenticating with the
               PYPI_API_TOKEN secret. Refuses to publish if the release tag
               disagrees with project.version, or if the wheel is missing its
               package data. Also uploads an sdist, which auglab has never had
               on PyPI. workflow_dispatch allows a TestPyPI dry run.

CODEOWNERS routes reviewers automatically, which is what makes a
"require 1 approval" branch protection rule useful.

Branch protection itself is a repository setting and needs an admin; the exact
command is documented in CONTRIBUTING.md.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
CONTRIBUTING.md covers dev setup, the test suite, the release process, and the
repository settings only an admin can apply -- the PYPI_API_TOKEN secret and
the branch protection ruleset from #34, with a ready-to-run `gh api` command.

README gains CI/PyPI badges and a Contributing section. Its Python-versions
badge pointed at the spineps project rather than auglab.

Also pins ruff to 0.16.1 in the dev extra to match the ruff-pre-commit rev.
Ruff adds rules between releases, so an unpinned local ruff disagrees with the
one CI runs -- which is exactly how the RUF036 in spatial.py (also fixed here)
slipped past a locally clean `ruff check`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@Hendrik-code Hendrik-code self-assigned this Aug 5, 2026
@Hendrik-code Hendrik-code added documentation Improvements or additions to documentation enhancement New feature or request labels Aug 5, 2026
@Hendrik-code Hendrik-code linked an issue Aug 5, 2026 that may be closed by this pull request
5 tasks
Applied by the end-of-file-fixer and trailing-whitespace pre-commit hooks.
Whitespace only -- each file parses to an identical object, verified by
comparing json.loads() before and after.

Needed for the lint workflow to pass; without it CI is red on main from the
first push.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Hendrik-code and others added 5 commits August 5, 2026 09:26
Keeps per-experiment config JSONs and the personal configs_paul/ scratch
directory out of the repository.

Note this ignores *.json and *.yaml repo-wide. Files already tracked (the six
shipped configs, .pre-commit-config.yaml) are unaffected, but adding a genuinely
new package config now needs `git add -f`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
CI was red on `ImportError: cannot import name 'Module' from 'kornia.core'`.
kornia 0.8.3 removed the Module/Tensor re-exports from kornia.core and deleted
the kornia.utils.helpers module outright. Local dev pinned 0.7.4, so this only
appeared once CI installed the latest.

- kornia.core.Module/Tensor were plain aliases for torch.nn.Module and
  torch.Tensor (asserted with `is`), so the eight import sites now use torch
  directly and no longer depend on a kornia re-export at all.
- _extract_device_dtype moved to kornia.core.utils in 0.8.3; imported through a
  try/except so both locations work.

Verified against kornia 0.7.3, 0.7.4, 0.8.0, 0.8.1, 0.8.2 and 0.8.3 -- all 143
non-slow tests pass on every one. Behaviour is unchanged: with all RNGs seeded,
every transform_params_gpu*.json config still produces byte-identical output.

kornia is now capped at >=0.7.3,<0.9, because AugLab subclasses kornia's private
augmentation internals, and a new `kornia-compat` CI job exercises both ends of
that range -- the `test` job only ever installs the newest kornia, so it could
not have caught this.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Dynamic versioning, the setuptools counterpart of the poetry-dynamic-versioning
setup in Hendrik-code/TPTBox. project.version is gone; the released version is
whatever the git tag says, so pyproject and the tag can no longer disagree.

- tag_regex accepts the r-prefixed date tags already in use (r20260615), plus
  v-prefixed and bare. The setuptools-scm default rejects the "r" form.
- local_scheme = "no-local-version", because PyPI rejects local versions.
- fallback_version = "0.0.0" for builds outside a git checkout, chosen to be
  obviously wrong rather than plausibly right.

Verified: clean checkout on tag r20260801 builds exactly 20260801; 19 commits
past r20260615 builds 20260616.dev19; an exported tree with no .git builds
0.0.0. Wheel and sdist contents are unchanged (24 modules, configs present).

Every workflow checkout that builds or installs now uses fetch-depth: 0 -- a
shallow clone has no tags and would silently build 0.0.0.

publish.yml's old tag-vs-project.version guard is obsolete and is replaced by
two checks that catch what can still go wrong: a built version that does not
match the release tag (bad tag_regex, or missing tags), and an attempt to
publish the 0.0.0 fallback.

Also documents versioning and the kornia pin in CONTRIBUTING.md, on top of the
trimmed version from the working tree.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The previous tag_regex only matched digits and dots, so a pre-release tag like
v1.0.0rc1 did not match and silently produced the 0.0.0 fallback instead of
failing.

Now strips an optional r/v/release- prefix and lets setuptools-scm validate the
rest as PEP 440, so an unparseable tag fails loudly rather than quietly
releasing 0.0.0.

Verified by building against real tags:
  r20260801    -> 20260801      v1.2.3       -> 1.2.3
  v20260801    -> 20260801      v1.0.0rc1    -> 1.0.0rc1
  20260801     -> 20260801      v2.0.0-beta1 -> 2.0.0b1

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Replaces setuptools/setuptools-scm with the same toolchain TPTBox uses:
poetry-core as the build backend and poetry-dynamic-versioning to take the
version from the git tag. `version = "0.0.0"` in pyproject is a placeholder
substituted at build time.

Verified by building against real tags:
  r20260801    -> 20260801      v1.0.0rc1    -> 1.0.0rc1
  v20260801    -> 20260801      v2.0.0-beta1 -> 2.0.0b1  (PEP 440 normalised)
  20260801     -> 20260801      untagged     -> 20260616.dev23
  v1.2.3       -> 1.2.3

Kept pip-installable on purpose: optional dependencies are extras, not poetry
groups, so `pip install -e ".[dev]"` still works and neither CI nor
contributors need the poetry CLI or a poetry.lock.

Packaging is unchanged where it matters -- the wheel still carries 24 modules
and the config JSONs, and configs_paul/ still does not leak. auglab has no
__init__.py anywhere, so the namespace-package handling was the main risk;
poetry-core 2.4 walks it correctly and test_packaging.py asserts it.

Three things this surfaced, all handled:

- poetry-core writes only the FIRST author into wheel metadata, so listing
  Hendrik Möller under `authors` would have silently dropped him from the
  published package. He is now `maintainers`, which is emitted as Maintainer:.
- A naive format-jinja using only {{ base }} built v1.0.0rc1 as plain "1.0.0",
  which would collide with a real 1.0.0 release. stage/revision are now carried
  through.
- Between tags the version is bumped before adding .devN, so a dev build sorts
  after the release it follows rather than before it.

Unlike setuptools-scm there is no 0.0.0 fallback: building outside a git
checkout fails loudly. Published sdists are unaffected, since the concrete
version is baked into their pyproject at build time (round-trip verified).

publish.yml's tag check now compares parsed versions rather than strings, so a
normalised pre-release tag such as v2.0.0-beta1 -> 2.0.0b1 no longer false-fails.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Comment thread pyproject.toml
Comment thread pyproject.toml
Comment thread pyproject.toml
@Hendrik-code

Copy link
Copy Markdown
Collaborator Author

Btw, do you want to setup the pypi thing? Since you are technically the owner of this repo?
Plus I don't have the permission to setup branch-protection. Either give me the permission or do it yourself =)

Hendrik-code and others added 5 commits August 6, 2026 12:47
Rewrites the four test modules as unittest.TestCase subclasses. They now run
under either runner -- pytest, or unittest discovery via
`-m unittest discover -s unit_tests -t .`.

- conftest.py is replaced by helpers.py, since pytest fixtures do not apply to
  TestCase. An AugLabTestCase base class seeds the RNGs in setUp and provides
  tiny_volume()/tiny_seg(); the config-lookup helpers move across unchanged.
- @pytest.mark.parametrize is replaced by subTest. Failures still name the
  offending item -- verified by breaking one config on purpose and confirming
  the report reads SUBFAILED(config='transform_params_gpu_inoutseg.json') while
  the other 78 subtests still ran.
- pytest.importorskip and pytest.skip become importlib.util.find_spec checks
  and self.skipTest.
- test_packaging builds the wheel once per class in setUpClass rather than
  through a module-scoped fixture.

The `slow` mark stays, applied at class level, which is the form pytest honours
on TestCase subclasses -- so `-m "not slow"`, which the kornia-compat CI job
and CONTRIBUTING both rely on, keeps working.

Coverage is unchanged: 13 test methods over 142 subtests, versus 146 individual
pytest cases before.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
poetry-core writes only the FIRST entry of `authors` and only the FIRST entry
of `maintainers`; everything after that is silently dropped. With Nathan listed
first in both lists, Hendrik disappeared from the published metadata entirely --
neither Author: nor Maintainer: named him.

The two lists have to name different people:

    authors[0]     -> Author: / Author-email:   (Nathan)
    maintainers[0] -> Maintainer:               (Hendrik)

Confirmed by inspecting the built wheel's METADATA before and after. The
comment above the lists now spells out the constraint so a future reorder does
not quietly undo it.

Note the `hendrik.moeller[AT]tum.de` entry in `authors` has no effect either
way: poetry cannot parse the obfuscated address, and second entries are dropped
regardless.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Trims CODEOWNERS to a single catch-all rule, drops the TestPyPI upload step
from publish.yml, and ignores *.nii/*.nii.gz in place of the configs_paul
entry (the JSONs in that directory are still covered by the *.json rule).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@Hendrik-code
Hendrik-code marked this pull request as ready for review August 7, 2026 13:41
Copilot AI lite review requested due to automatic review settings August 7, 2026 13:41

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@Hendrik-code
Hendrik-code merged commit 71d6364 into main Aug 7, 2026
7 checks passed
@Hendrik-code
Hendrik-code deleted the hm/github-workflows branch August 7, 2026 14:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Github Workflows

3 participants