feat: modernize repo to use uv, pyproject.toml, and semantic-release#281
feat: modernize repo to use uv, pyproject.toml, and semantic-release#281farhan wants to merge 9 commits into
Conversation
Split out of farhan/modernize-repo; ruff retained there for adoption in a future epic (per public-engineering#506). - Replace setup.py/setup.cfg with pyproject.toml (PEP 621 static metadata) - Switch from pip-compile to uv with PEP 735 dependency groups; commit uv.lock - Retain pylint/isort/pycodestyle/pydocstyle as on master (ruff deferred) - Update tox.ini to use tox-uv with uv-venv-lock-runner - Update CI to use astral-sh/setup-uv; SHA-pin all actions; add workflow_call trigger - Add python-semantic-release + release.yml (OIDC trusted publishing) - Add commitlint.yml to enforce conventional commit format on all future PRs Part of openedx/public-engineering#506
…warning - Run isort on all source files to fix import ordering (isort v8 is stricter than the pre-migration version; files not yet sorted) - Remove black --check from tox quality env (black was not in requirements/quality.in on master so was never reliably enforced) - Remove black from quality dependency group accordingly - Add ref.python to suppress_warnings in docs/conf.py to silence the ambiguous cross-reference warning for the `type` attribute (present in both Content and ContentSerializer); Sphinx 9.x treats this as an error with -W Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace PYPI_UPLOAD_TOKEN with id-token: write + pypa/gh-action-pypi-publish (no password input). OIDC is the org-wide standard per public-engineering#506, unconditionally applied regardless of what master used. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Repo is on 0.4.3 (0.x). Without allow_zero_version=true and major_on_zero=false, the first feat: commit after merge would bump 0.x.y → 1.0.0 unexpectedly. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Moves the importable package from forum/ to src/forum/ per the modernization standard (public-engineering#520). Updates all tooling references: setuptools find, coverage source_pkgs, tox quality/mypy envs, Makefile targets, and docs/conf.py apidoc paths. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
black was in master's ci.in but was not carried over to any dependency group in pyproject.toml, leaving the `format` target broken (uv run black fails with no such file or directory). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Moves channel_integrations/ to src/channel_integrations/, in line with the reference implementation for this modernization effort (openedx/sample-plugin) and openedx/forum#281. - pyproject.toml: add where = ["src"] to packages.find - tox.ini: prefix src/ onto pylint/pycodestyle targets in the quality env - Makefile: prefix src/ onto all 5 localization targets (extract, compile, detect_changed, pull, dummy); atlas pull's local destination path updated, remote Transifex identifier left unchanged - test_settings.py: LOCALE_PATHS root() call updated - docs/conf.py: sphinx-apidoc call updated to point at src/channel_integrations - MANIFEST.in: recursive-include path updated Verified: uv sync (no lock drift), full django42 test run (893 passed), quality and pii_check envs pass, uv build --wheel + twine check pass. The docs tox env fails both before and after this change (a pre-existing sphinx/autodoc ValueError in object_description, unrelated to src-layout and unrelated to this repo's file layout) -- confirmed by reproducing the identical failure on the pre-migration commit via git stash.
Moves taxonomy/ to src/taxonomy/, in line with the reference implementation for this modernization effort (openedx/sample-plugin) and openedx/forum#281. - pyproject.toml: add where = ["src"] to packages.find - tox.ini: prefix src/ onto pylint/pycodestyle/pydocstyle/isort targets in the quality env - Makefile: prefix src/taxonomy onto the 3 localization targets that cd into the package (compile_translations, detect_changed_source_translations, dummy_translations); compile_translations' relative ../manage.py climb updated to ../../manage.py to account for the extra nesting level - test_settings.py: LOCALE_PATHS root() call updated - docs/conf.py: sphinx-apidoc call updated to point at src/taxonomy - MANIFEST.in: recursive-include path updated Verified: uv sync (no lock drift), full django42 test run (315 passed), quality (pylint/pycodestyle/pydocstyle/isort) and pii-annotations envs pass, docs env passes end to end -- sphinx-apidoc generates real API docs from the new path, uv build --wheel + twine check pass.
Moves openedx_ledger/ to src/openedx_ledger/, in line with the reference implementation for this modernization effort (openedx/sample-plugin) and openedx/forum#281. - pyproject.toml: add where = ["src"] to packages.find - tox.ini: prefix src/ onto the quality env's pylint/pycodestyle/isort targets - Makefile: prefix src/openedx_ledger onto the standalone isort/style/lint targets and the 5 localization targets that cd into the package; extract_translations/compile_translations' relative ../manage.py climbs updated to ../../manage.py to account for the extra nesting level - test_settings.py: LOCALE_PATHS root() call updated - docs/conf.py: sphinx-apidoc call updated to point at src/openedx_ledger - MANIFEST.in: recursive-include path updated - Dockerfile needs no change: it COPYs the whole repo rather than naming the package directory directly Verified: uv build --wheel + twine check pass (direct proof the where=["src"] packaging change works). Could not run the full pytest/quality/docs tox matrix locally -- this machine has no libmysqlclient/pkg-config to build the mysqlclient C extension (a [project.dependencies] entry, so it's required for every uv sync regardless of group), same environment limitation hit previously on enterprise-access. Relying on CI for full-matrix confirmation.
Moves all three top-level packages (enterprise_data, enterprise_data_roles, enterprise_reporting) to src/, in line with the reference implementation for this modernization effort (openedx/sample-plugin) and openedx/forum#281. - pyproject.toml: add where = ["src"] to packages.find - tox.ini: prefix src/ onto the quality env's isort/pylint/pycodestyle/ pydocstyle targets, and onto the data env's pytest --ignore path and the reporting env's test-collection path (--cov's own module-name arg is left alone -- it resolves via the installed module, not a filesystem path). DJANGO_SETTINGS_MODULE=enterprise_data.settings.test is an import path, unaffected by the physical move. - Makefile: prefix src/ onto the isort target's package names - MANIFEST.in: all three recursive-include paths updated - enterprise_data/settings/test.py: this repo's in-package here()/root() path helper climbs from src/enterprise_data/settings/ back to the repo root -- fixed the climb from '../..' (2 levels, correct pre-move) to '../../..' (3 levels, correct post-move) so root() still means what its docstring says. root() has no current call sites (dead code), but fixed the definition itself for correctness -- verified by direct invocation that it now resolves to the true repo root. Verified: uv sync (no lock drift), uv build --wheel + twine check pass (direct proof the where=["src"] packaging change works across all three packages). Could not run the full tox matrix locally -- this machine's clang/zlib toolchain can't build pyminizip (a native extension in the "reporting" extra that both the "test" and "quality" dependency groups pull in), unrelated to src-layout. Relying on CI for full-matrix confirmation.
Moves all four top-level packages (enterprise, consent, integrated_channels, enterprise_learner_portal) to src/, in line with the reference implementation for this modernization effort (openedx/sample-plugin) and openedx/forum#281. Most complex repo in this batch: 4 packages, an in-package path helper with real call sites, and JS tooling (webpack, jasmine) alongside the Python side. - pyproject.toml: add where = ["src"] to packages.find - tox.ini: prefix src/ onto the pycodestyle exclude list and onto every package name in the isort/isort-check/quality env commands - Makefile: prefix src/ onto clean.static, dummy_translations, pull_translations (local destination paths only -- the Transifex/Atlas remote path identifiers are left unchanged), jshint, pylint, pycodestyle, isort, and isort-check targets - docs/conf.py: sphinx-apidoc call updated to point at src/enterprise - webpack.config.js: build context path updated to src/enterprise/static/enterprise - spec/javascripts/support/jasmine.yml: src_files glob updated to src/enterprise/static/enterprise/js/*.js - MANIFEST.in: all four recursive-include paths and the enterprise/locale symlink exclusion updated - enterprise/settings/test.py: this repo's in-package here()/root() path helper climbs from src/enterprise/settings/ back to the repo root -- fixed the climb from '../..' (2 levels) to '../../..' (3 levels), and updated its 3 real call sites (LOCALE_PATHS, REPO_ROOT, STATIC_ROOT) to prepend 'src' so they still resolve inside the package. The symlink enterprise/locale -> conf/locale is relative and moved as a unit with git mv, so it still resolves correctly post-move -- verified. Verified: uv sync (no lock drift), full py312-django52 test run (2849 passed), quality (pylint/pycodestyle/isort), isort-check, and pii_check envs pass, docs env passes end to end -- sphinx-apidoc generates real API docs from the new path, uv build --wheel + twine check pass. A pylint useless-suppression info note in integrated_channels/cornerstone/models.py is pre-existing (confirmed identical on the pre-migration commit via git stash), unrelated to this change.
CI pins the interpreter via setup-uv's python-version; the explicit basepython = python3.12 is unnecessary. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
irfanuddinahmad
left a comment
There was a problem hiding this comment.
Reviewed the modernization changes (uv/pyproject.toml + src-layout + semantic-release migration). OIDC publishing and action pinning look correct; no issues found.
Per review feedback, remove the >=61.0 minimum to stay consistent with how other modernized openedx repos declare their build-system. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Moves all four top-level packages (enterprise, consent, integrated_channels, enterprise_learner_portal) to src/, in line with the reference implementation for this modernization effort (openedx/sample-plugin) and openedx/forum#281. Most complex repo in this batch: 4 packages, an in-package path helper with real call sites, and JS tooling (webpack, jasmine) alongside the Python side. - pyproject.toml: add where = ["src"] to packages.find - tox.ini: prefix src/ onto the pycodestyle exclude list and onto every package name in the isort/isort-check/quality env commands - Makefile: prefix src/ onto clean.static, dummy_translations, pull_translations (local destination paths only -- the Transifex/Atlas remote path identifiers are left unchanged), jshint, pylint, pycodestyle, isort, and isort-check targets - docs/conf.py: sphinx-apidoc call updated to point at src/enterprise - webpack.config.js: build context path updated to src/enterprise/static/enterprise - spec/javascripts/support/jasmine.yml: src_files glob updated to src/enterprise/static/enterprise/js/*.js - MANIFEST.in: all four recursive-include paths and the enterprise/locale symlink exclusion updated - enterprise/settings/test.py: this repo's in-package here()/root() path helper climbs from src/enterprise/settings/ back to the repo root -- fixed the climb from '../..' (2 levels) to '../../..' (3 levels), and updated its 3 real call sites (LOCALE_PATHS, REPO_ROOT, STATIC_ROOT) to prepend 'src' so they still resolve inside the package. The symlink enterprise/locale -> conf/locale is relative and moved as a unit with git mv, so it still resolves correctly post-move -- verified. Verified: uv sync (no lock drift), full py312-django52 test run (2849 passed), quality (pylint/pycodestyle/isort), isort-check, and pii_check envs pass, docs env passes end to end -- sphinx-apidoc generates real API docs from the new path, uv build --wheel + twine check pass. A pylint useless-suppression info note in integrated_channels/cornerstone/models.py is pre-existing (confirmed identical on the pre-migration commit via git stash), unrelated to this change.
Moves all three top-level packages (enterprise_data, enterprise_data_roles, enterprise_reporting) to src/, in line with the reference implementation for this modernization effort (openedx/sample-plugin) and openedx/forum#281. - pyproject.toml: add where = ["src"] to packages.find - tox.ini: prefix src/ onto the quality env's isort/pylint/pycodestyle/ pydocstyle targets, and onto the data env's pytest --ignore path and the reporting env's test-collection path (--cov's own module-name arg is left alone -- it resolves via the installed module, not a filesystem path). DJANGO_SETTINGS_MODULE=enterprise_data.settings.test is an import path, unaffected by the physical move. - Makefile: prefix src/ onto the isort target's package names - MANIFEST.in: all three recursive-include paths updated - enterprise_data/settings/test.py: this repo's in-package here()/root() path helper climbs from src/enterprise_data/settings/ back to the repo root -- fixed the climb from '../..' (2 levels, correct pre-move) to '../../..' (3 levels, correct post-move) so root() still means what its docstring says. root() has no current call sites (dead code), but fixed the definition itself for correctness -- verified by direct invocation that it now resolves to the true repo root. Verified: uv sync (no lock drift), uv build --wheel + twine check pass (direct proof the where=["src"] packaging change works across all three packages). Could not run the full tox matrix locally -- this machine's clang/zlib toolchain can't build pyminizip (a native extension in the "reporting" extra that both the "test" and "quality" dependency groups pull in), unrelated to src-layout. Relying on CI for full-matrix confirmation.
| ) | ||
| from .subscriptions import create_subscription, delete_subscription, get_thread_subscriptions, get_user_subscriptions | ||
| from .threads import create_thread, delete_thread, get_course_id_by_thread, get_thread, get_user_threads, update_thread | ||
| from .users import (create_user, delete_user_posts, get_user, get_user_active_threads, get_user_course_stats, |
There was a problem hiding this comment.
A lot of imports are going from one per line to this multi-line format which seems less readable to me. Is this because of isort? is there something wrong with the config causing this? I think we prefer multi-line imports with one import per line over this version. Let's update the config so that we don't do this kind of change if we can.
|
|
||
| upgrade: ## update the requirements/*.txt files with the latest packages satisfying requirements/*.in | ||
| $(MAKE) compile-requirements COMPILE_OPTS="--upgrade" | ||
| lint: ## Run linting checks |
There was a problem hiding this comment.
| lint: ## Run linting checks | |
| quality: ## Run linting and other quality checks |
| piptools: ## install pinned version of pip-compile and pip-sync | ||
| pip install -r requirements/pip.txt | ||
| pip install -r requirements/pip-tools.txt | ||
| mypy: ## Run mypy type checks |
There was a problem hiding this comment.
Also, why was this change from test-mypy to mypy We shouldn't rename targets unless there is a good reason.
|
|
||
| requirements: clean_tox piptools ## install development environment requirements | ||
| pip-sync -q requirements/dev.txt requirements/private.* | ||
| format: ## Auto-fix formatting issues |
There was a problem hiding this comment.
Why was this updated to only format prod files instead of all the files?
| DJANGO_SETTINGS_MODULE = forum.settings.test | ||
| commands = | ||
| make test-pii | ||
| code_annotations django_find_annotations --config_file .pii_annotations.yml --lint --report --coverage |
There was a problem hiding this comment.
We sholud be calling make targets from inside tox so that we can run those targets outside of tox easily as well in a re-producable way. We sholudn't force tox for when we're trying to iterate quickly in a local dev environment.
470bff0 to
53a6af0
Compare
3f87ff4 to
bb4a94a
Compare
Addresses review feedback on openedx#281: - Restore the [isort] settings from master's setup.cfg (dropped when setup.cfg was removed) as [tool.isort] in pyproject.toml. Without it, isort fell back to defaults and collapsed imports into a horizontal grid; re-running isort restores the readable one-per-line format. - Rename the `lint` make target to `quality` for parity with master's `test-quality` and to reflect that it runs more than linting. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Important
PR implemented with the assistance of Claude Code, human-reviewed and improved before pushing to code review.
Split out of #280; ruff retained there for adoption in a future epic (per public-engineering#506).
Summary
Modernize
forumto uv + pyproject.toml (PEP 621/735) + src/ layout + python-semantic-release.Part of openedx/public-engineering#506.
setup.py/setup.cfgwithpyproject.toml(PEP 621 static metadata)uvwith PEP 735 dependency groups; commituv.locksrc/layoutpyproject.tomltox.inito usetox-uvwithuv-venv-lock-runnerastral-sh/setup-uv; SHA-pin all actions; addworkflow_calltriggerpython-semantic-release+release.yml(OIDC trusted publishing)Removed
Deleted files:
setup.py,setup.cfg,requirements/,.coveragerc,mypy.ini,CHANGELOG.rstRemoved Makefile targets:
clean_toxtox-uv; manual tox cache cleaning no longer neededcompile-requirementsuv+uv.lockpiptoolstest-alltox/ CI matrixtest-qualitylinttarget (uv run tox -e quality)test-lintlinttarget (uv run tox -e quality)test-codestylelinttarget (uv run tox -e quality)test-isortlinttarget (uv run tox -e quality)test-mypymypytarget (uv run tox -e mypy)test-formatblack --check; format checking is no longer a separate CI quality gate; theformattarget now auto-fixesVersioning
[Dynamic]
setuptools-scmwithdynamic = ["version"]— master had a PyPI publish workflow (pypi-publish.yml);python-semantic-releasecontrols the version string at release time via git tags. Repo is on0.x, soallow_zero_version = trueandmajor_on_zero = falseare set to prevent afeat:commit from bumping0.x.y → 1.0.0.Testing Notes
This PR has not been manually tested against the repo's own features. Testing relied on CI checks and local agent tooling (
make requirements,make lint,make test,python -m build). Repo-owner is encouraged to run the repo's feature tests before merging.Code reviewer notes:
openedx/forum, workflowrelease.yml, no environment.commitlint.ymlwas already present on master; this PR does not change it.pyproject.tomldependency groups — verify that all runtime and optional dependencies from the oldrequirements/*.infiles are faithfully carried over, and that no transitive constraint fromrequirements/constraints.txtwas silently dropped.test-format(black --check) was removed as a quality gate; formatting is now only enforced viamake format(auto-fix) rather than CI check — reviewers should verify this is acceptable for this repo.🤖 Generated with Claude Code