Skip to content

feat: modernize repo to use uv, pyproject.toml, and semantic-release - #281

Closed
farhan wants to merge 9 commits into
openedx:masterfrom
farhan:farhan/modernize-python-repo
Closed

farhan wants to merge 9 commits into
openedx:masterfrom
farhan:farhan/modernize-python-repo

Conversation

@farhan

@farhan farhan commented Jul 16, 2026 •

Copy link
Copy Markdown
Contributor

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 forum to uv + pyproject.toml (PEP 621/735) + src/ layout + python-semantic-release.

Part of openedx/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
  • Move the package into a src/ layout
  • Retain pylint/isort/pycodestyle/pydocstyle as on master (ruff deferred to its own epic per #506); coverage config and mypy config moved into pyproject.toml
  • 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)

Removed

Deleted files: setup.py, setup.cfg, requirements/, .coveragerc, mypy.ini, CHANGELOG.rst

Removed Makefile targets:

Target Reason
clean_tox Tox environments managed by tox-uv; manual tox cache cleaning no longer needed
compile-requirements pip-compile workflow replaced by uv + uv.lock
piptools pip-tools no longer used
test-all Superseded by tox / CI matrix
test-quality Replaced by lint target (uv run tox -e quality)
test-lint Consolidated into lint target (uv run tox -e quality)
test-codestyle Consolidated into lint target (uv run tox -e quality)
test-isort Consolidated into lint target (uv run tox -e quality)
test-mypy Replaced by mypy target (uv run tox -e mypy)
test-format Ran black --check; format checking is no longer a separate CI quality gate; the format target now auto-fixes

Versioning

[Dynamic] setuptools-scm with dynamic = ["version"] — master had a PyPI publish workflow (pypi-publish.yml); python-semantic-release controls the version string at release time via git tags. Repo is on 0.x, so allow_zero_version = true and major_on_zero = false are set to prevent a feat: commit from bumping 0.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:

  • Pylint/isort/pycodestyle/pydocstyle are retained exactly as on master — ruff is out of scope this cycle and lives in the companion PR feat: modernize repo to use uv, pyproject.toml, and semantic-release #280 for a future epic.
  • OIDC trusted publisher must be configured on PyPI before merging — first publish fails silently otherwise. This is an out-of-band prerequisite handled by the Axim team (Feanil): PyPI project → Publishing → Add a new publisher, pointing at openedx/forum, workflow release.yml, no environment.
  • commitlint.yml was already present on master; this PR does not change it.
  • Pay close attention to the pyproject.toml dependency groups — verify that all runtime and optional dependencies from the old requirements/*.in files are faithfully carried over, and that no transitive constraint from requirements/constraints.txt was silently dropped.
  • test-format (black --check) was removed as a quality gate; formatting is now only enforced via make format (auto-fix) rather than CI check — reviewers should verify this is acceptable for this repo.

🤖 Generated with Claude Code

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
farhan and others added 4 commits July 16, 2026 13:38
…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>
@farhan
farhan marked this pull request as draft July 16, 2026 13:17
@farhan
farhan marked this pull request as ready for review July 16, 2026 16:27
irfanuddinahmad pushed a commit to openedx/enterprise-integrated-channels that referenced this pull request Jul 17, 2026
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.
irfanuddinahmad pushed a commit to openedx/taxonomy-connector that referenced this pull request Jul 17, 2026
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.
irfanuddinahmad pushed a commit to openedx/openedx-ledger that referenced this pull request Jul 17, 2026
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.
irfanuddinahmad pushed a commit to openedx/edx-enterprise-data that referenced this pull request Jul 17, 2026
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.
irfanuddinahmad pushed a commit to openedx/edx-enterprise that referenced this pull request Jul 17, 2026
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.
Comment thread pyproject.toml Outdated
Comment thread tox.ini Outdated
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 irfanuddinahmad 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.

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>
@farhan farhan self-assigned this Jul 21, 2026
@farhan farhan moved this to 🏗 In progress in Aximprovements Team Jul 21, 2026
@farhan farhan moved this from 🏗 In progress to 👀 In review in Aximprovements Team Jul 21, 2026
irfanuddinahmad pushed a commit to openedx/edx-enterprise that referenced this pull request Jul 21, 2026
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.
irfanuddinahmad pushed a commit to openedx/edx-enterprise-data that referenced this pull request Jul 21, 2026
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.
Comment thread src/forum/api/__init__.py Outdated
)
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,

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.

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.

@farhan farhan Jul 24, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Details are in the issue: #284

Comment thread Makefile Outdated

upgrade: ## update the requirements/*.txt files with the latest packages satisfying requirements/*.in
$(MAKE) compile-requirements COMPILE_OPTS="--upgrade"
lint: ## Run linting checks

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.

Suggested change
lint: ## Run linting checks
quality: ## Run linting and other quality checks

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

fixed in this PR

Comment thread Makefile
Comment thread Makefile

requirements: clean_tox piptools ## install development environment requirements
pip-sync -q requirements/dev.txt requirements/private.*
format: ## Auto-fix formatting issues

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.

Why was this updated to only format prod files instead of all the files?

Comment thread tox.ini
DJANGO_SETTINGS_MODULE = forum.settings.test
commands =
make test-pii
code_annotations django_find_annotations --config_file .pii_annotations.yml --lint --report --coverage

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.

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fix in PR

@farhan
farhan requested a review from feanil July 22, 2026 16:48
@farhan
farhan force-pushed the farhan/modernize-python-repo branch 2 times, most recently from 470bff0 to 53a6af0 Compare July 22, 2026 17:31
@farhan
farhan force-pushed the farhan/modernize-python-repo branch from 3f87ff4 to bb4a94a Compare July 22, 2026 17:57
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>
@FuaadZam FuaadZam moved this from 👀 In review to 🏗 In progress in Aximprovements Team Jul 23, 2026
@farhan

farhan commented Jul 24, 2026

Copy link
Copy Markdown
Contributor Author

Closing on account of #283

@farhan farhan closed this Jul 24, 2026
irfanuddinahmad pushed a commit to openedx/enterprise-integrated-channels that referenced this pull request Sep 7, 2026
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.
feanil pushed a commit to openedx/taxonomy-connector that referenced this pull request Sep 29, 2026
…e) (#316)

* feat: consolidate package metadata into pyproject.toml

Replace setup.py/setup.cfg with PEP 621 [project] metadata and
setuptools-scm for git-tag-based versioning.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* feat: switch dependency management from pip-compile to uv

Replace requirements/*.in + *.txt with PEP 735 dependency-groups in
pyproject.toml and a single uv.lock. Update tox.ini to use tox-uv's
uv-venv-lock-runner, update Makefile targets, and switch CI (including
the mysql8 migrations check) to install uv and run tests via
`uv run tox`.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* feat: add semantic-release for automated PyPI publishing

Replace the manual GitHub-release-triggered publish workflow with
python-semantic-release: pushes to master with conventional commits
now automatically bump the version, tag it, and publish to PyPI.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix: correct release tag format and restore pycodestyle ignore list

semantic-release defaults to a "v{version}" tag format, but this repo's
existing release tags are bare version numbers -- without tag_format set,
semantic-release wouldn't recognize any prior release.

Also restores the ignore=E501,W503,W504 pycodestyle setting that was
dropped when setup.cfg was deleted -- without it the quality tox env
fails on pre-existing long lines that were previously suppressed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix: prevent uv sync from implicitly pulling in the dev group

Every uv sync/uv run invocation in this repo names an explicit --group,
but uv's implicit default group (named "dev") was still being synced
alongside it, silently pulling the entire dev/test/quality/ci superset
into every target. Verified with `uv sync --group ci`.

Also adds .venv/ to .gitignore alongside the existing venv/ entry.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix: publish to PyPI via OIDC instead of token

Parent issue openedx/public-engineering#506 asks for OIDC trusted-publisher
PyPI auth, not a stored token. Grant id-token: write on publish_to_pypi and
drop the explicit __token__/PYPI_UPLOAD_TOKEN credentials -- pypa/gh-action-pypi-publish
uses OIDC automatically once the permission is present and no credentials are given.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix: pin pypa/gh-action-pypi-publish to a commit SHA

@release/v1 is a floating branch ref -- xblocks-core's release just failed
with "docker: manifest unknown" because the Docker image tag it resolved to
at checkout time wasn't published on ghcr.io yet. Pin to the exact commit
backing the current v1.14.0 release instead, consistent with this repo's
own SHA-pinning rule for every other action.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix: set major_on_zero=false and allow_zero_version=true for consistency

Reviewer feedback: this should be set across the whole batch, not just
repos currently on 0.x, so no repo in this effort can ever auto-jump to
1.0.0 as an accidental side effect if it's reset to 0.x in the future.
Note this is a no-op for repos already past 1.0 -- major_on_zero only
governs the 0.x -> 1.0.0 transition, not 1.x -> 2.0.0 (there's no PSR
setting that suppresses major bumps once past 1.0; that's normal SemVer
behavior for a breaking-change commit at any version).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* refactor: adopt src-layout, matching openedx/sample-plugin

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.

* fix: pattern-gap audit (uv setup, package=true, coverage, changelog)

Same category of fixes farhan flagged on the batch's other reviewed PRs:

- ci.yml + mysql8-migrations.yml: add enable-cache/python-version to
  astral-sh/setup-uv and drop the now-redundant actions/setup-python step
- pyproject.toml: add [tool.uv] package = true
- Drop CHANGELOG.rst from the dynamic readme file list and delete it --
  python-semantic-release + GitHub Releases is the changelog of record.
  Also removes the now-stale MANIFEST.in include and the docs/changelog.rst
  page (and its toctree entry).
- Migrate .coveragerc into [tool.coverage.run] and delete the old file

Verified: uv sync (no lock drift), full django52 test run (315 passed),
quality, pii-annotations, and docs envs all pass end to end -- docs
env's apidoc + wheel build + twine check confirm the readme/coverage
config changes work correctly.

* docs: drop stale Version/Changelog checklist items from PR template

Both referenced files/processes that no longer exist post-migration:
CHANGELOG.rst was deleted (python-semantic-release + GitHub Releases is
the changelog of record now), and __version__ in taxonomy/__init__.py
is no longer manually bumped (versioning is automated by semantic-release
based on conventional commits). Neither is a manual per-PR task anymore.

* fix: pattern-gap audit round 2 (django42/52 dependency groups, uv run wrapping, dynamic readme)

Cross-checked against review comments/fixes from openedx-ledger#242,
edx-enterprise-subsidy-client#222, enterprise-access#1015, and
enterprise-subsidy#441.

- Add test-django42/test-django52 dependency-groups (each layering a
  Django version pin on top of the shared test group) and declare
  [tool.uv].conflicts between them, replacing the old single test group
  + tox deps= overlay. Update tox.ini's [testenv] to select the group by
  factor and add the previously-missing django42 job to the CI matrix --
  only django52 was actually being exercised in CI before this.
- Wrap remaining bare tool invocations in the Makefile (coverage erase,
  pytest --cov-report html, pytest (test target), diff-cover, i18n_tool,
  manage.py compilemessages) with `uv run`. Transifex's tx binary is left
  bare since it's installed via curl, not uv/pip.
- Drop the redundant dynamic readme field; keep only version as dynamic.
- Regenerate uv.lock for the new dependency groups; verified both
  py312-django42 and py312-django52 tox environments resolve and run.

* fix: correct Makefile recipe indentation (tabs, not spaces)

Make requires recipe lines to be indented with a literal tab
character. An earlier edit in this migration accidentally used
spaces for several targets, breaking 'make selfcheck' and CI.

* fix: add codecov project threshold for coverage-scope change

The uv/pyproject.toml modernization (openedx/public-engineering#506)
switched coverage measurement to [tool.coverage.run], which correctly
excludes src/taxonomy/tests/*.py from the package's own coverage
report instead of counting those (trivially self-covered) test files
toward the reported percentage.

That's a one-time drop in the reported baseline (98.91% -> 98.70%,
90 files -> 86 files) caused purely by measurement scope, not a
regression: every production file's hit/miss counts are unchanged.
Add a documented threshold so codecov/project absorbs this one-time
drop while still catching genuine future regressions, matching the
same fix applied in sibling modernization PRs (ccx-keys#190,
opaque-keys#461, openedx-chem#161).

* fix: restore CHANGELOG.rst and re-enable python-semantic-release changelog generation

A previous pass deleted CHANGELOG.rst and disabled changelog generation
(changelog: "false") in release.yml with no ticket justification -- the
changelog is genuinely useful historical documentation and semantic-release
can keep it current automatically. Restore CHANGELOG.rst from the commit
right before it was deleted, add the insertion-marker line PSR's "update"
mode looks for, wire up [tool.semantic_release.changelog] to update the
existing RST file in place, and remove changelog: "false" from release.yml.

tag_format = "{version}" was already correctly set to match this repo's
bare-version tag convention (verified against `git tag --sort=-v:refname`),
so it's left unchanged.

* docs: remove stale manual tag/PyPI-verification steps from PR template

python-semantic-release's release.yml now creates the tag, GitHub
release, and publishes to PyPI automatically on merge -- these were
no longer real manual steps for a contributor to perform.

* fix: use stable pypa publish tag and drop tox from Makefile

- pypa/gh-action-pypi-publish: revert the hash-pinned SHA back to the
  stable @release/v1 tag. A hash-pinned version of this action broke
  PyPI publishing previously, which is why the org standardized on the
  stable tag for this specific action.
- Makefile: inline the actual sphinx/pylint/pycodestyle/pydocstyle/
  isort/code_annotations commands from tox.ini's docs/quality/
  pii-annotations envs instead of shelling out to `uv run tox -e ...`,
  matching the no-tox-in-Makefile convention already used elsewhere
  (e.g. enterprise-catalog). test-all now depends on quality/pii_check/
  test directly. tox.ini itself is untouched; CI's own matrix testing
  still uses tox directly.

* fix: use int-parseable fallback_version instead of 0.0.0.dev0

"0.0.0.dev0" is the exact fallback_version value that crashed 17 tests
in a sibling repo (openedx-events) -- runtime code that parses
__version__ via tuple(map(int, __version__.split("."))) chokes on the
non-numeric "dev0" segment. No current consumer here does that (this
repo's own __version__ usage, if any, only interpolates it as a
string), but there's no reason to keep a fallback value from the exact
banned-pattern class when a plain int-parseable "0.0.0" is equally
valid and strictly safer.

* fix: pure-uv mysql8-migrations job, remove permanent codecov threshold

- mysql8-migrations.yml: replaced uv pip uninstall/install --no-binary
  (x2) with a single native `uv sync --group mysql8 --no-binary-package
  mysqlclient --no-binary-package xmlsec`. mysqlclient/xmlsec weren't
  pulled in by any existing group (confirmed: a plain `uv sync --group dev`
  installs neither), so added a new `mysql8` dependency-group for them --
  this workflow-only need is the same as master's pre-migration behavior,
  where they came in only via this same job's own pip uninstall+reinstall
  step, not via requirements/dev.txt or test.txt. Verified the sync+flag
  combo genuinely triggers source builds for both (confirmed via -v output
  and a real build attempt, blocked locally only by a missing macOS
  pkg-config/mysql-dev system dependency that the CI runner's own
  `apt-get install libxmlsec1-dev` step already covers).

- codecov.yml: removed the permanent `threshold: 1%`, per salman2013's
  question (#316 (comment))
  on whether this was meant to be temporary. It wasn't actually needed:
  this status check isn't required for merging, and `target: auto`
  compares each PR against its own base commit, so the one-time coverage-
  scope discontinuity (98.91% -> 98.70%) only ever affected this PR's own
  diff display -- once merged, 98.70% becomes the new baseline for every
  future comparison, with no lingering gap for a permanent threshold to
  paper over. Kept the explanatory comment, dropped the actual tolerance,
  since a standing 1% regression-masking allowance had no real job to do
  and only downside.

Found while auditing this repo for uv pip usage per the lessons learned on
openedx-platform#38915.

* fix: disable python-semantic-release changelog generation

Set changelog: "false" on the PSR release step and remove the
[tool.semantic_release.changelog] config / insertion marker.

Checked against openedx/XBlock's actual production release.yml (the one
repo in this effort that has cut real automated releases) -- every run
passes changelog: false and invokes `semantic-release -v version
--no-changelog`, and the repo has zero github-actions[bot] commits ever.
The auto-changelog config this migration previously added was only ever
verified via a local dry-run prototype, never against a real release.

feanil flagged the same issue on openedx/DoneXBlock#388: "I thought we
were not going to add changelogs since they can't be updated by
python-semantic-release the way we have it setup."

* fix: create GitHub release with assets attached, not after publish

This repo has immutable releases enabled, which freezes a release's assets the moment it's published. The old flow (main PSR step publishes the release, a separate publish-action step attaches assets afterward) can never work under that constraint -- it would 422 on the first real release. Matches the fix already proven and merged on openedx/sample-plugin#57 and validated end-to-end on openedx/event-tracking#434: build without publishing (vcs_release: false), then create the release with dist/* attached in one gh release create call.

* fix: trim verbose codecov.yml comment to match sibling repos

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix: pin release.yml actions to sample-plugin's gold-standard SHAs

openedx/sample-plugin's release.yml is this org's designated reference
for action pins in this migration effort. This repo's release.yml had
drifted from it on every pin:

- actions/checkout: v7.0.0 (9c091bb2) -> v7.0.1 (3d3c42e5), matching
  sample-plugin
- python-semantic-release: v10.5.3 (350c48fc) -> v10.6.2 (9a026e93)
- actions/upload-artifact: v4.6.2 (ea165f8d) -> v7.0.1 (043fb46d)
- actions/download-artifact: v4.3.0 (d3f86a10) -> v8.0.1 (3e5f45b2)
- pypa/gh-action-pypi-publish: unpinned @release/v1 -> SHA-pinned
  v1.14.2 (dc37677b)

The pypa/gh-action-pypi-publish change reverses earlier guidance on
this PR: salman2013 previously asked (review comment #3710602930) for
this action to be reverted off a SHA pin back to @release/v1 due to a
prior publish issue with the hashed version. That guidance has since
been superseded -- the same reviewer, on a sibling repo's PR in this
same migration effort, directed matching sample-plugin exactly, which
now means SHA-pinning this action like every other one in the file.

Every SHA above was independently verified against the GitHub API
(commit lookup + tag ref, resolving through the annotated tag object
where applicable) before use, per this org's history of fabricated/
swapped SHAs in this exact file.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix: restore [4.0.0] entry silently dropped from CHANGELOG.rst by merges

salman2013 asked (review comment #3710606973) why old release log
entries were being removed; this was previously addressed by restoring
CHANGELOG.rst's full history in commit e58b416. But three later
merge-from-master commits on this branch (6012451, then 1804d5d)
brought in master's tip without carrying over master's own [4.0.0] /
"chore: upgrade requirements" changelog entry -- despite there being no
textual conflict on this file (the entry existed cleanly on master's
side with no competing change on the branch side), the merge results
ended up without it, so it was silently lost again.

Confirmed via `git diff origin/master -- CHANGELOG.rst` before this
fix: the only difference between this branch and current master was
the missing [4.0.0] entry. Restored it so the file now matches master
exactly, keeping this PR's changelog history complete and consistent
with what will already be on master when this merges.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix: use v-prefixed tag_format for release tag consistency

* fix: update author to Open edX Project convention

* fix: restore __version__ dropped during pyproject.toml consolidation

4e45689 removed the hardcoded __version__ = "3.0.0" (correctly, since
version is now dynamic via setuptools-scm) but never replaced it with
the importlib.metadata equivalent, unlike sibling repos in this
migration batch. taxonomy.__version__ has been silently missing since.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix: remove dangling codecov.yml comment, not present pre-migration

Points at "see PR description" for an explanation -- fragile since
the PR description is external and mutable. Coverage config itself
stays out of scope for this migration; just dropping the comment, no
change to target/threshold behavior.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix: use secrets.GITHUB_TOKEN instead of custom org secret for release job

Matches openedx/sample-plugin's current release.yml; the custom
OPENEDX_SEMANTIC_RELEASE_GITHUB_TOKEN secret isn't needed.

* fix: restore tox delegation for docs/quality/pii_check targets

Makefile targets were fully inlining commands that tox.ini already
defines as standalone envs. pii_check delegates to tox -e
pii-annotations, matching the actual tox env name (not pii_check).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix: drop uv run from Makefile per feanil's review feedback

Makefile now assumes an already-synced/activated local env; uv run
stays in CI workflow steps only. See openedx/ccx-keys#190 (review
comment r4097501942).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix: restore full tox matrix on test-all, correct tag_format comment

test-all had drifted to depend only on `test` (a single pytest run),
making it identical to `validate` and no longer actually covering
py312-django42/py312-django52 like its own help text claims. Restore
the bare `tox` call so it runs the full envlist.

Also corrected the tag_format comment, which claimed no v-prefixed
tag existed -- v4.0.0 already does, same commit as the old bare
4.0.0 tag.

Per feanil's review on #316:
discussion_r4097760680, r4097760715.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix: remove django42 from CI matrix, restore pre-migration scope

CI stopped exercising django42 in 2026-02 (3b842b7), keeping it only in
tox.ini's envlist for local/manual use; the migration's ci.yml
regenerated the matrix from tox.ini and silently reintroduced it as a
CI job. Per this effort's policy, a migration PR must not expand CI
scope beyond what pre-existed. Coverage step already correctly targets
django52, so no other change is needed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix: include doc group in dev group; bump python-semantic-release to v10.7.0

- dev omitted {include-group = "doc"}, so `uv sync --group dev` never
  provisioned doc8/Sphinx and `make docs` failed on a fresh clone.
  Matches the pattern already used in openedx/edx-enterprise's
  pyproject.toml.
- Bump python-semantic-release action pin from v10.6.2 to v10.7.0,
  matching openedx/sample-plugin's current live pin.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

---------

Co-authored-by: Irfan Ahmad <irfan.ahmad@A006-01919.local>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants