Superflore Tooling Modernization - #329
Conversation
|
@wentasah Please let me know what you think of these changes? It shouldn't affect the Nix generator, but it may affect how you install it. My hope is to release a new package version to PyPi so we can have formally tested releases. |
|
@cottsay I would love any feedback you have on the tooling and CI tools selected. Using ruff and mypy seems like the way to go, but I am open to any alternatives or additions you suggest. |
|
@tfoote Is there anything else that I should add or do to work towards publishing a new version (v0.4.0) to PyPI? I have published packages both manually as well as with GitHub Actions using the "Trusted Publishing" mechanism. What would you recommend? |
|
@robwoolley It works for me. I use two small patches, which I haven't sent you yet. It was necessary to resolve conflicts caused by reformatting the code, but it was easy. Modernization required minor changes in packaging on my side. I verified that with these changes, it generates exactly the same output as before. The only thing that I noticed are the following warnings: Maybe, these can be addressed somehow in the update. |
Thanks for the feedback! I have pushed another commit which should address the problem you saw. |
|
We've historically done it manually, but setting up a trusted automated publishing would be even better. I'd suggest talking to the infrastructure pmc about it. Cc @cottsay |
3670fee to
61ea3c8
Compare
Includes a duplicate entry for wrynose and formatting changes. Signed-off-by: Rob Woolley <rob.woolley@windriver.com>
Replace setup.py and requirements.txt with PEP 621 metadata in pyproject.toml. Versioning moves from a hand-rolled git-describe function to setuptools-scm, and runtime deps are separated from the existing test/dev toolchain via optional-dependencies. CI and the README install instructions are updated to match. Assisted-by: Claude:claude-opus-5 AI-Generated: Uses Claude (claude-opus-5) Signed-off-by: Rob Woolley <rob.woolley@windriver.com>
Swap pynose for pytest in CI and the test dependency group; the existing unittest.TestCase-based tests needed no code changes to run under pytest. The nose --exclude/--ignore-files flags that were silently skipping tests from the CI command line are replaced with in-file @unittest.skip markers carrying the original reasons, so skipped coverage is visible in test output instead of hidden in a CI YAML line. Assisted-by: Claude:claude-opus-5 AI-Generated: Uses Claude (claude-opus-5) Signed-off-by: Rob Woolley <rob.woolley@windriver.com>
Replace flake8, flake8-import-order, and pep8 with Ruff for linting, import sorting, and formatting; add its config to pyproject.toml and swap the CI/dev dependencies and workflow steps accordingly. The line-length limit moves from flake8's unconfigured default (79, only ever enforced against superflore/, never tests/) to Ruff's 88 applied to both. Delete tests/test_code_format.py: running the linter a second time as a pytest test duplicated the new CI lint step and carried dead Python-2-compat branches. Fix issues Ruff surfaced when it covered tests/ for the first time: tests/test_nix.py had two methods both named test_escape_quote, so the second silently shadowed the first under both nose and pytest; deduplicating the names exposed that the shadowed test's expected value was stale, now corrected to match NixLicense's actual (space -to-dash) escaping behavior. A handful of unused local variables in assertRaises blocks are also removed. Ruff's import sorting is isort-compatible but not identical to flake8-import-order's "google" style, so this also reflows most files' import blocks; `ruff format` was run once across superflore/ and tests/ for consistent style, configured to keep single-quote strings to minimize diff noise against the existing convention. Assisted-by: Claude:claude-opus-5 AI-Generated: Uses Claude (claude-opus-5) Signed-off-by: Rob Woolley <rob.woolley@windriver.com>
Wire up .pre-commit-config.yaml running the same ruff-check/ruff-format hooks as CI, add pre-commit to the dev dependency group, and document the one-time `pre-commit install` setup step in the README. Assisted-by: Claude:claude-opus-5 AI-Generated: Uses Claude (claude-opus-5) Signed-off-by: Rob Woolley <rob.woolley@windriver.com>
Enable actions/setup-python's pip cache, keyed on pyproject.toml, so the 10-way OS/Python matrix stops reinstalling every dependency from scratch on each run. The pytest/ruff invocations were already updated to their final form in the earlier commits so CI stayed green at every step. Assisted-by: Claude:claude-opus-5 AI-Generated: Uses Claude (claude-opus-5) Signed-off-by: Rob Woolley <rob.woolley@windriver.com>
Add mypy to the dev dependency group and a report-only CI step (continue-on-error, so it never blocks a merge). Coverage is scoped via [tool.mypy].files to the handful of modules that already carry type annotations, with follow_imports=silent so errors from unlisted modules pulled in transitively don't leak into the report. Expand the files list opportunistically as modules gain annotations, rather than as a dedicated typing sweep -- the codebase has almost no existing type coverage. Assisted-by: Claude:claude-opus-5 AI-Generated: Uses Claude (claude-opus-5) Signed-off-by: Rob Woolley <rob.woolley@windriver.com>
actions/checkout@v4 defaults to a shallow, tag-less clone. With no tags reachable, setuptools-scm falls back to its default base version (0.1.devN+g<hash>), which fails test_get_superflore_version's sanity check that the version isn't lower than expected. Reproduced locally: a --depth 1 clone reads back '0.1.dev1+g<hash>' and fails the test; a full clone correctly resolves to '0.3.4.devNN+g<hash>' and passes. Assisted-by: Claude:claude-opus-5 AI-Generated: Uses Claude (claude-opus-5) Signed-off-by: Rob Woolley <rob.woolley@windriver.com>
Replace the "License :: OSI Approved" classifiers with a PEP 639 SPDX license expression. The project is Apache-2.0 overall, but two files (nix_expression.py, yocto_recipe.py) carry their own standalone MIT headers, so the correct expression is "Apache-2.0 AND MIT" (not "OR" -- it's not a licensee's choice, different files are under different licenses). Bumps the setuptools build requirement to >=77, which is what added SPDX license-expression support. Assisted-by: Claude:claude-opus-5 AI-Generated: Uses Claude (claude-opus-5) Signed-off-by: Rob Woolley <rob.woolley@windriver.com>
3978138 to
87f47ed
Compare
I used Claude to assist with modernizing the tooling for Superflore. The details are listed below. The commits have been manually reviewed for accuracy.
This pull request is being shared to gather feedback from downstream users of superflore and to perform additional testing on GitHub.
Goal: modernize packaging, linting, and testing tooling with no change to
runtime behavior. Milestones are ordered must-do → recommended → nice-to-have
(see the accompanying recommendations for the full rationale and tradeoffs).
Flag-only items (
stdeb.cfg,setup_superflore.sh) are intentionallyexcluded — they need an owner decision before any milestone touches them.
Each milestone should land as its own PR so CI validates it independently.
Must-do
M1 — Migrate packaging to
pyproject.tomlDepends on: nothing (do first).
Tasks:
pyproject.tomlwith[project]metadata (name, description, authors,license, classifiers,
python_requires,console_scriptsentry points)ported from setup.py.
setuptools-scmfor versioning, replacing the customappend_local_version_label()git-describe logic. Configure via[tool.setuptools_scm]; confirm the resulting version string is stillPEP 440-compliant and acceptable to anything that parses it downstream
(release scripts, changelog tooling).
xmltodict,termcolor,gitpython,requests,docker,pyyaml,pygithub,rosinstall_generator,rosdistro,rosdep,catkin_pkg,rospkg,setuptools) →[project.dependencies].pynose,flake8,flake8-import-order,pep8)→ a
dev/testgroup under[project.optional-dependencies]or[dependency-groups]. These get replaced in M3/M2 but should move as-ishere so this milestone stays packaging-only.
setup.pyoncepyproject.tomlfully covers it, unlesssetuptools-scm or the build backend needs a minimal shim — confirm with a
clean
pip install .andpython -m build.requirements.txtis retired (replaced bypip install -e ".[dev]") or kept as a pinned/lock-style install path.Acceptance criteria:
pip install .andpip install -e ".[dev]"both work from a clean venv.python -m buildproduces a valid sdist + wheel with correct versionstring (test against an actual git tag and against a dirty/untagged tree).
--help.M2 — Migrate test suite from nose to pytest
Depends on: M1 (so the
dev/testdependency group exists to addpytestto and removepynosefrom).Tasks:
pytestto the test dependency group; removepynose.python -m 'nose' --exclude ... --ignore-files ...(ci.yaml) with
pytest.(
test_pull,test_run,test_logger_output,test_failed_to_create,test_generate_installers,test_ebuild.py,test_docker.py) intoin-file
pytest.mark.skip(reason=...)/pytest.mark.xfail(reason=...)markers, so each skip carries its reason next to the test instead of in
CI YAML.
(the current
tests/__init__.py-based package layout is compatible;no restructuring expected).
test_flake8/test_pep8intests/test_code_format.py yet — that's M3.
Acceptance criteria:
pytestrun locally and in CI produces the same pass/fail set as the oldnose invocation (module the previously-excluded tests, which now show as
explicit skips/xfails instead of being invisible).
--exclude/--ignore-filesentry has a corresponding marker with a stated reason.
Recommended
M3 — Consolidate linting/formatting onto Ruff
Depends on: M1 (dependency groups), M2 (pytest already in place, since
this milestone removes the last nose-adjacent test-as-lint-runner file).
Tasks:
ruffto the dev dependency group; removeflake8,flake8-import-order, andpep8(the deprecated pycodestyle package name).[tool.ruff]config topyproject.tomlcovering lint rules and importsorting, replacing the
--import-order-style=googleflag that's currentlypassed at the CLI in two places
(ci.yaml and
tests/test_code_format.py).
ruff checkandruff formatonce across the codebase and commit theresulting reformat as its own commit, separate from any config changes, so
the diff is reviewable.
test_flake8andtest_pep8fromtests/test_code_format.py — linting as a
pytest test duplicates the CI lint step and drags in the dead
sys.version_info < (3, 4)Python-2-compat branches. If the file ends upempty, delete it.
google-style expectations —Ruff's isort-compatible ordering isn't byte-identical to
flake8-import-order's
googlestyle, so this is the one step in the wholespec with a real (if small) risk of unwanted churn.
Acceptance criteria:
ruff check superflore testsandruff format --check superflore testsboth pass clean in CI.
flake8,flake8-import-order, orpep8remain independency metadata, CI, or test files.
M4 — Add pre-commit configuration
Depends on: M3 (Ruff config must exist to wire in).
Tasks:
.pre-commit-config.yamlrunningruff check --fixandruff format(using the
ruff-pre-commithook repo) against the same config as CI.pre-commit installsetup step inREADME.md (contributor-facing section).
Acceptance criteria:
pre-commit run --all-filespasses clean.main/masterrefs).M5 — Update CI workflow
Depends on: M2, M3 (pytest and Ruff must already be the tools in use).
Tasks:
ci.yaml with
pytestandruff check/ruff format --check.actions/setup-python'scache: pip, keyed offpyproject.toml) so the 10-way matrix (2 OS × 5 Python versions) isn'treinstalling everything from scratch on every run.
to Python tooling.
Acceptance criteria:
Nice-to-have
M6 — Introduce type checking (incremental)
Depends on: M1–M5 complete (this is additive polish, not a prerequisite
for anything above).
Tasks:
mypy(orpyright) to the dev dependency group.fail the build) against the existing ~5 files that already touch
typing, with the rest of the codebase excluded via config.rather than as a dedicated sweep — the codebase has almost no existing
type coverage, so a full pass isn't worth front-loading.
Acceptance criteria:
is explicit rather than implicit.