Skip to content
This repository was archived by the owner on Sep 22, 2026. It is now read-only.

Modernize packaging + CI: setup.cfg -> pyproject.toml, legacy workflow -> uv-CI stub (and fix the red CI) - #2

Merged
thorwhalen merged 4 commits into
masterfrom
claude/rollout-modernize
Aug 4, 2026
Merged

thorwhalen merged 4 commits into
masterfrom
claude/rollout-modernize

Conversation

@thorwhalen

Copy link
Copy Markdown
Member

Modernizes udos from legacy packaging (setup.cfg + setup.py + the 2022-era
inline CI workflow) to the current ecosystem standard: pyproject.toml with
Hatchling, plus the 5-line wads uv-CI stub that reads everything from
[tool.wads.ci.*].

It also fixes the long-standing red CI on master — the cause turned out to be a
real one, not an artifact of the old workflow. Details below.

⚠️ Merging this PR publishes a release to PyPI. [tool.wads.ci.publish] is
enabled (fleet default), and this package is already on PyPI at 0.0.2.
[project].version is deliberately set to 0.0.2 so CI's automatic bump lands
on 0.0.3 rather than re-publishing behind the released version. See
"Decision for the maintainer" at the bottom before merging.

What changed

Commit What
packaging: setup.cfg/setup.py → pyproject.toml (Hatchling); legacy files deleted
ci: legacy inline workflow → uv-ci.yml@master stub
tests: one import smoke test + the missing .editorconfig
style: ruff format . (the formatter the new CI runs)

Inventory carried across from setup.cfg

Everything in the old [metadata]/[options] was accounted for:

setup.cfg pyproject.toml
name = udos [project].name
version = 0.0.2 [project].version (kept at the released version — see above)
description [project].description
long_description = file:README.md, long_description_content_type = text/markdown [project].readme = "README.md"
url [project.urls].Homepage
license = apache-2.0 [project].license = "Apache-2.0" (SPDX string, not the deprecated [project.license] table); LICENSE kept and now shipped in the wheel's dist-info/licenses/
packages = find: [tool.hatch.build.targets.wheel].packages = ["udos"]
install_requires (empty) dependencies = []
keywords (empty) filled in with real keywords

Deliberately dropped, each with a reason:

  • platforms = any, zip_safe = False — setuptools-only knobs with no effect on
    wheel builds.
  • include_package_data = True — Hatchling ships everything under the package
    directory by default; udos/ has no data files anyway.
  • root_url — read only by populate at project-creation time to derive the
    project URL, which is now written out explicitly.
  • display_name = udos — identical to the project name, which is epythet's
    default when [tool.epythet].display_name is absent, so nothing is lost.

There were no entry_points / console scripts, no package_data, no extras
and no python_requires in the old config, so nothing of that kind could be
dropped. MANIFEST.in and requirements.txt do not exist in this repo.

Added while here (the [project] block was otherwise bare on PyPI): authors,
keywords, classifiers, requires-python = ">=3.10", and
Repository/Documentation URLs. Also the standard [tool.ruff] block, without
which the repo would drift onto ruff's moving defaults and go red on unrelated
style changes.

Latent bug this surfaced: the red CI was real

The last run on master (actions/runs/17236567336) failed on Validation (3.10), and it would have kept failing after the migration.

udos is a placeholder package: udos/__init__.py contains a module docstring
and nothing else. Both the old workflow (pytest --doctest-modules -v udos) and
the new one (pytest --doctest-modules, collection driven entirely by
testpaths) therefore collect zero items, and pytest exits with code 5
("no tests collected"). run-tests-uv runs under set -e, so a non-zero exit is
a red build.

Two ways to fix it:

  1. [tool.wads.ci.testing].enabled = false — the sanctioned wads knob for repos
    with no suite.
  2. Give the repo one real, minimal test.

(2) is what this PR does, in tests/test_smoke.py. Option (1) would switch
the test step off permanently, so the day udos grows actual modules their
doctests would silently not run — exactly the failure mode that testpaths
hygiene exists to prevent. No repo in the ecosystem currently disables testing,
and an empty placeholder is a poor place to set that precedent.

Relatedly, testpaths is set to ["udos", "tests"] rather than the migration
tool's unconditional ["tests"], so package doctests are collected the moment
there are any.

No package behaviour was changed to make CI pass.

Docs build still works after deleting setup.cfg

docsrc/conf.py passes setup.cfg to epythet.config_parser.parse_config.
That function falls back to pyproject.toml when the file is missing, so the
Pages job is unaffected. Verified locally — it now resolves to
('udos', '', 'Thor Whalen', '0.0.2', 'udos'), i.e. the same project and display
name as before, plus an author it previously had no way to find.

Verification

  • Local gate (priv test-dependents udos, mirrors CI):
    baseline no-tests (0 pass / 0 fail) → final pass (1 pass / 0 fail).
    udos has no local dependents.
  • Wheel build: uv build succeeds; metadata inspected — SPDX
    License-Expression: Apache-2.0, License-File: LICENSE, all classifiers,
    URLs and keywords present, README rendered as the long description.
  • Dependencies: cross-checked with wads-deps. It flags only setuptools
    (from setup.py, now deleted) and epythet (from docsrc/conf.py, a docs
    tool). Neither is a runtime import, so dependencies stays empty — correct
    for a module that imports nothing.
  • Branch CI: https://github.com/i2mint/udos/actions/runs/30859403663 —
    success. Read Configuration ✓, Validation (3.10) ✓, Validation (3.12) ✓,
    Windows Tests ✓; Publish and Publish GitHub Pages skipped as expected on a
    non-default branch.
  • Repo audit (python -m wads.repo_audit): HIGH 2 / MEDIUM 3 / LOW 3 →
    HIGH 1 / MEDIUM 0 / LOW 3. Cleared: no-pyproject, legacy packaging files,
    legacy CI generation, no test files, missing .editorconfig. The remaining
    HIGH is the historical failed run on master, which clears when this merges.

Left undone on purpose

  • GitHub repo metadata — the homepage field is empty and topics are unset;
    the audit wants https://i2mint.github.io/udos/ and the slugified keywords.
    These are repo settings, not files, so they cannot ship in a PR, and the
    command was not run in this session. To align:
    gh repo edit i2mint/udos --homepage "https://i2mint.github.io/udos/" plus
    --add-topic for dataset data standard search acquisition sharing.
  • Agent skills (wads-skillify) — out of scope for a packaging pass, and
    premature for a package with no API yet.
  • A real test suite / docs — there is no behaviour to test or document yet.
    The smoke test exists solely so the CI test step stays enabled and meaningful.

Decision for the maintainer

udos has been a docstring-only placeholder since 2022 and has one PyPI release
(0.0.2). Merging republishes it as 0.0.3 — an empty package. That is harmless
(it holds the name), but if you would rather not spend a release on it, set
[tool.wads.ci.publish].enabled = false before merging and flip it back when the
package actually has content. Tracked in #1.

Carries every field of the old setup.cfg across:

  name, version (0.0.2 — matches the released PyPI version so CI's
  automatic bump lands on 0.0.3), description, long_description
  (README.md, text/markdown -> readme), url -> [project.urls].Homepage,
  license apache-2.0 -> SPDX `license = "Apache-2.0"` (LICENSE kept),
  packages = find: -> [tool.hatch.build.targets.wheel].packages,
  install_requires (empty) -> dependencies = [].

Deliberately dropped, with reasons:

  platforms / zip_safe        setuptools-only, no effect on wheels
  include_package_data        hatchling ships package files by default
  root_url                    read only by `populate` at creation time;
                              the derived project URL is now explicit
  display_name = udos         identical to the project name, which is
                              epythet's default, so nothing is lost

Added while here: authors, keywords, classifiers, requires-python and
Repository/Documentation urls, so the PyPI page is no longer bare.

`wads-deps` reports no runtime imports (the package is a single
docstring-only module); the two "missing" packages it flags are
setuptools (setup.py, now deleted) and epythet (docsrc/conf.py, a docs
tool), so `dependencies` stays empty.

docsrc/conf.py still passes `setup.cfg` to epythet's parse_config, which
falls back to pyproject.toml when that file is absent — verified locally:
it now resolves ('udos', '', 'Thor Whalen', '0.0.2', 'udos').

Claude-Session: https://claude.ai/code/session_01VipiLaG4xy7WctqY9w2475
`wads-migrate ci-to-uv` then `ci-to-stub`: ci.yml is now a stub calling
i2mint/wads/.github/workflows/uv-ci.yml@master, with all configuration
read from [tool.wads.ci.*] in pyproject.toml.

The old workflow's only secret references were PYPI_USERNAME/PYPI_PASSWORD
inside a `twine upload` run step. The reusable workflow publishes with
token-only auth, so PYPI_PASSWORD is passed through in the stub's
secrets: block and PYPI_USERNAME is no longer needed. No other secret
was referenced, so nothing was carried into [tool.wads.ci.env].

This workflow was the last thing that used setup.cfg/setup.py
(`python setup.py sdist`, `isee update-setup-cfg`), which the previous
commit removed. MANIFEST.in and requirements.txt do not exist here.

Claude-Session: https://claude.ai/code/session_01VipiLaG4xy7WctqY9w2475
wads CI runs `pytest --doctest-modules` with no path argument, so
collection is driven entirely by `testpaths`. udos is a placeholder
package — one module holding nothing but a docstring — so collection was
empty and pytest exited 5 ("no tests collected"), which is a non-zero
exit and therefore a red build. That is the root cause of the failing
run at actions/runs/17236567336, and it would have survived the
migration untouched.

Two ways to fix it:

  (a) set [tool.wads.ci.testing].enabled = false — the sanctioned wads
      knob for repos with no suite;
  (b) give the repo one real, minimal test.

(b) is chosen. (a) would leave the test step switched off permanently:
the day udos grows actual modules, their doctests would silently not
run, which is exactly the fleet-wide failure mode `testpaths` hygiene
exists to prevent. No repo in the ecosystem currently disables testing,
and this placeholder is a poor place to set that precedent.

testpaths is set to ["udos", "tests"] rather than the migration tool's
default ["tests"], so package doctests are collected as soon as there
are any.

Also adds the .editorconfig the wads templates ship and this repo was
missing. No package behaviour is changed.

Claude-Session: https://claude.ai/code/session_01VipiLaG4xy7WctqY9w2475
The uv CI runs `uvx ruff format .` on every job. Running it here keeps
the tree format-clean so the publish job's format-and-push-back step is
a no-op instead of generating a drive-by commit on the default branch.

Only quote normalisation in docsrc/conf.py and a trailing blank line in
udos/__init__.py. No behaviour change.

Claude-Session: https://claude.ai/code/session_01VipiLaG4xy7WctqY9w2475
@thorwhalen
thorwhalen merged commit a1d1abe into master Aug 4, 2026
12 checks passed
@thorwhalen
thorwhalen deleted the claude/rollout-modernize branch August 4, 2026 13:39
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant