Title: feat(init): discover third-party version providers via entry points by bearomorphism · Pull Request #1971 · commitizen-tools/commitizen · GitHub
Open Graph Title: feat(init): discover third-party version providers via entry points by bearomorphism · Pull Request #1971 · commitizen-tools/commitizen
X Title: feat(init): discover third-party version providers via entry points by bearomorphism · Pull Request #1971 · commitizen-tools/commitizen
Description: Description Closes #1258. Why cz init interactively asks the user to pick a version_provider. The picker's options were hard-coded to the eight built-in providers (commitizen, cargo, composer, npm, pep621, poetry, uv, scm), even though commitizen already supports third-party providers via the commitizen.provider entry-point group at runtime (commitizen/providers/__init__.py:get_provider). The mismatch meant a user who installed e.g. commitizen-deno-provider had to either know the provider name from memory and hand-edit the config, or skip cz init entirely. The original reporter offered the implementation sketch. What changed File Change commitizen/commands/init.py _VERSION_PROVIDER_CHOICES (a tuple of questionary.Choice) is replaced by _BUILTIN_VERSION_PROVIDER_OPTIONS (data-only) and a new _construct_version_provider_choices() helper that builds the picker list at call time. _ask_version_provider() now invokes that helper. tests/commands/test_init_command.py Three new tests: built-ins-only, third-party-discovered, duplicate-third-party-deduped. docs/config/version_provider.md One-paragraph note in the "Custom Provider Plugins" section explaining that cz init now surfaces the plugin once installed. How it works Built-ins still come first, with curated descriptions. The _BUILTIN_VERSION_PROVIDER_OPTIONS tuple keeps the human-readable labels (e.g. "pep621: Get and set version from pyproject.toml:project.version field") instead of falling back to a generic suffix. Third-party providers are discovered, never loaded. Only ep.name is read; ep.load() is not called, so installing a malicious cz_* plugin can't execute its module just because the user opened cz init. Two layers of dedup, both necessary: The eight built-ins are themselves registered under [project.entry-points."commitizen.provider"] in pyproject.toml:73-81. A naive listing would yield each one twice. We seed seen_names with the built-in set and skip any entry-point name already in it. Two distributions can register a third-party provider under the same name (e.g. two unrelated forks both publishing cz_jira). After the first occurrence is added we mark the name seen and skip the rest. We deliberately don't try to merge the duplicates: commitizen.providers.get_provider() raises VersionProviderUnknown when more than one entry point matches, so the user picking the duplicated name gets a clear error instead of the picker silently choosing whichever distribution loaded first. No registry caching. Each cz init invocation re-reads the entry-point group, so a provider installed mid-shell-session is picked up immediately. Backward compatibility Users without any third-party plugin see exactly the same picker as v4.15.1 (same eight built-ins, same order, same titles). The function signature change is internal (_VERSION_PROVIDER_CHOICES was a leading-underscore module global, never exported). Checklist I have read the contributing guidelines Was generative AI tooling used to co-author this PR? Yes (please specify the tool below) Generated-by: Claude following the guidelines Code Changes Add test cases to all the changes you introduce (3 new tests, see the targeted commands below) Run uv run poe all locally to ensure this change passes linter check and tests (poe lint clean; 33/33 init tests pass) Manually test the changes (see "Steps to Test" below) Update the documentation for the changes Documentation Changes Added a paragraph about auto-discovery to docs/config/version_provider.md Run uv run poe doc locally to ensure the documentation pages render correctly Check and fix any broken links (internal or external) Expected Behavior Scenario Outcome cz init with no third-party providers installed Same eight built-in choices, same order, same descriptions as before. cz init with a third-party plugin installed (e.g. commitizen-deno-provider exposing commitizen.provider -> deno) The eight built-ins appear first, then deno: third-party version provider. Selecting it writes version_provider = "deno" to pyproject.toml. Two installed distributions both register commitizen.provider -> dup dup appears once in the picker. Selecting it surfaces the existing VersionProviderUnknown error from get_provider() so the conflict is loud, not silent. Steps to Test This Pull Request git fetch fork feat/1258-init-discover-providers git checkout fork/feat/1258-init-discover-providers # 1. Built-ins-only test (no plugins installed): all eight present, none labelled third-party. uv run pytest tests/commands/test_init_command.py::test_construct_version_provider_choices_includes_builtins -v # 2. Third-party-discovered test: a fake EntryPoint("my-third-party") is patched in. uv run pytest tests/commands/test_init_command.py::test_construct_version_provider_choices_discovers_third_party -v # 3. Duplicate-third-party-deduped test: two EntryPoint(name="duplicated") -> only one choice. uv run pytest tests/commands/test_init_command.py::test_construct_version_provider_choices_dedupes_duplicate_third_party -v # 4. Full init suite (32 pre-existing + 1 new dedup test). uv run pytest tests/commands/test_init_command.py -q # expected: 33 passed Additional Context Surfaced while triaging open issues in #1965. The implementation follows the original reporter's sketch but uses a single discover-on-call function and dedupes against the built-in list rather than building a separate registry. The duplicate-name handling was added in fixup commit 1bad9047 after a GitHub Copilot review pass flagged it as a potential UX gap.
Open Graph Description: Description Closes #1258. Why cz init interactively asks the user to pick a version_provider. The picker's options were hard-coded to the eight built-in providers (commitizen, cargo, composer, ...
X Description: Description Closes #1258. Why cz init interactively asks the user to pick a version_provider. The picker's options were hard-coded to the eight built-in providers (commitizen, cargo, compos...
Opengraph URL: https://github.com/commitizen-tools/commitizen/pull/1971
X: @github
Domain: github.com
| route-pattern | /:user_id/:repository/pull/:id/files(.:format) |
| route-controller | pull_requests |
| route-action | files |
| fetch-nonce | v2:b64a2d99-d699-808c-5de3-d85db75a2209 |
| current-catalog-service-hash | ae870bc5e265a340912cde392f23dad3671a0a881730ffdadd82f2f57d81641b |
| request-id | 9E24:38592C:F61FEC:16725E9:6A4E354A |
| html-safe-nonce | ed37ad416927b33f8843ee9af4a66167e0745948ed4b767faa088c1f04cccd76 |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiI5RTI0OjM4NTkyQzpGNjFGRUM6MTY3MjVFOTo2QTRFMzU0QSIsInZpc2l0b3JfaWQiOiIyNjIyOTI1NTEyODU5NTk2MTA2IiwicmVnaW9uX2VkZ2UiOiJpYWQiLCJyZWdpb25fcmVuZGVyIjoiaWFkIn0= |
| visitor-hmac | b8e48fd6177806a65ccc45f73aa796b05da8cc9424900882e33f9d8e906601d8 |
| hovercard-subject-tag | pull_request:3654708065 |
| github-keyboard-shortcuts | repository,pull-request-list,pull-request-conversation,pull-request-files-changed,copilot |
| google-site-verification | Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I |
| octolytics-url | https://collector.github.com/github/collect |
| analytics-location | / |
| fb:app_id | 1401488693436528 |
| apple-itunes-app | app-id=1477376905, app-argument=https://github.com/commitizen-tools/commitizen/pull/1971/files |
| twitter:image | https://avatars.githubusercontent.com/u/26526132?s=400&v=4 |
| twitter:card | summary_large_image |
| og:image | https://avatars.githubusercontent.com/u/26526132?s=400&v=4 |
| og:image:alt | Description Closes #1258. Why cz init interactively asks the user to pick a version_provider. The picker's options were hard-coded to the eight built-in providers (commitizen, cargo, composer, ... |
| og:site_name | GitHub |
| og:type | object |
| hostname | github.com |
| expected-hostname | github.com |
| None | 030096ee0db095447bfe77409d33bfac127ca7128299c58deef27c52eaa1b1f0 |
| turbo-cache-control | no-preview |
| diff-view | unified |
| go-import | github.com/commitizen-tools/commitizen git https://github.com/commitizen-tools/commitizen.git |
| octolytics-dimension-user_id | 62252524 |
| octolytics-dimension-user_login | commitizen-tools |
| octolytics-dimension-repository_id | 106127589 |
| octolytics-dimension-repository_nwo | commitizen-tools/commitizen |
| octolytics-dimension-repository_public | true |
| octolytics-dimension-repository_is_fork | false |
| octolytics-dimension-repository_network_root_id | 106127589 |
| octolytics-dimension-repository_network_root_nwo | commitizen-tools/commitizen |
| turbo-body-classes | logged-out env-production page-responsive full-width |
| disable-turbo | true |
| browser-stats-url | https://api.github.com/_private/browser/stats |
| browser-errors-url | https://api.github.com/_private/browser/errors |
| release | 42a6d378d7587a44c93aca255096cd66b7c8eb2d |
| ui-target | full |
| theme-color | #1e2327 |
| color-scheme | light dark |
Links:
Viewport: width=device-width