repomatic 7.2.0__tar.gz → 7.3.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (163) hide show
  1. {repomatic-7.2.0 → repomatic-7.3.1}/PKG-INFO +3 -3
  2. {repomatic-7.2.0 → repomatic-7.3.1}/pyproject.toml +25 -11
  3. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/__init__.py +2 -2
  4. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/binaries_page.py +21 -2
  5. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/changelog.py +26 -7
  6. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/cli.py +13 -87
  7. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/config.py +9 -18
  8. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/data/_release-engine.yaml +56 -22
  9. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/data/agent-sphinx-docs.md +19 -21
  10. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/data/autofix.yaml +74 -62
  11. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/data/cancel-runs.yaml +2 -2
  12. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/data/changelog.yaml +22 -20
  13. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/data/debug.yaml +3 -3
  14. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/data/docs.yaml +12 -6
  15. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/data/labels.yaml +11 -11
  16. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/data/lint.yaml +18 -18
  17. repomatic-7.3.1/repomatic/data/pytest.toml +35 -0
  18. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/data/release.yaml +1 -1
  19. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/data/ruff.toml +6 -1
  20. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/data/skill-babysit-ci.md +10 -4
  21. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/data/skill-repomatic-changelog.md +4 -4
  22. repomatic-7.3.1/repomatic/data/skill-repomatic-ship.md +190 -0
  23. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/data/skill-sphinx-docs-sync.md +1 -1
  24. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/data/tests.yaml +69 -6
  25. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/data/unsubscribe.yaml +2 -2
  26. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/docs.py +17 -11
  27. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/init_project.py +14 -1
  28. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/sync_ops.py +17 -1
  29. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/templates/fix-changelog.md +1 -1
  30. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/templates/fix-vulnerable-deps.md +1 -1
  31. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/templates/sync-action-pins.md +1 -1
  32. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/templates/sync-bumpversion.md +1 -1
  33. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/templates/sync-dep-sources.md +1 -1
  34. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/templates/sync-gitignore.md +1 -1
  35. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/templates/sync-mailmap.md +1 -1
  36. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/templates/sync-repomatic.md +1 -1
  37. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/templates/sync-tool-versions.md +1 -1
  38. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/templates/sync-uv-lock.md +1 -1
  39. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/templates/sync-workflow-pins.md +1 -1
  40. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/templates/update-deps-graph.md +1 -1
  41. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/templates/update-docs.md +1 -1
  42. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/tool_runner.py +56 -17
  43. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/uv.py +62 -29
  44. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/version_sync.py +1 -1
  45. repomatic-7.2.0/repomatic/data/pytest.toml +0 -21
  46. repomatic-7.2.0/repomatic/data/skill-repomatic-ship.md +0 -153
  47. repomatic-7.2.0/repomatic/myst_converter.py +0 -292
  48. repomatic-7.2.0/repomatic/myst_docstrings.py +0 -307
  49. repomatic-7.2.0/repomatic/rst_to_myst.py +0 -156
  50. {repomatic-7.2.0 → repomatic-7.3.1}/license +0 -0
  51. {repomatic-7.2.0 → repomatic-7.3.1}/readme.md +0 -0
  52. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/__main__.py +0 -0
  53. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/binary.py +0 -0
  54. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/broken_links.py +0 -0
  55. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/cache.py +0 -0
  56. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/checksums.py +0 -0
  57. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/data/__init__.py +0 -0
  58. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/data/action-publish-pypi.yaml +0 -0
  59. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/data/agent-grunt-qa.md +0 -0
  60. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/data/agent-qa-engineer.md +0 -0
  61. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/data/autolock.yaml +0 -0
  62. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/data/awesome_template/.github/ISSUE_TEMPLATE/config.yml +0 -0
  63. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/data/awesome_template/.github/ISSUE_TEMPLATE/new-link.yaml +0 -0
  64. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/data/awesome_template/.github/code-of-conduct.md +0 -0
  65. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/data/awesome_template/.github/contributing.md +0 -0
  66. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/data/awesome_template/.github/contributing.zh.md +0 -0
  67. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/data/awesome_template/.github/funding.yml +0 -0
  68. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/data/awesome_template/.github/pull_request_template.md +0 -0
  69. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/data/awesome_template/__init__.py +0 -0
  70. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/data/awesome_template/license +0 -0
  71. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/data/bumpversion.toml +0 -0
  72. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/data/codecov.yaml +0 -0
  73. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/data/labeller-content-based.yaml +0 -0
  74. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/data/labeller-file-based.yaml +0 -0
  75. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/data/labels.toml +0 -0
  76. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/data/lychee.toml +0 -0
  77. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/data/mdformat.toml +0 -0
  78. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/data/mypy.toml +0 -0
  79. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/data/skill-av-false-positive.md +0 -0
  80. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/data/skill-awesome-triage.md +0 -0
  81. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/data/skill-benchmark-update.md +0 -0
  82. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/data/skill-brand-assets.md +0 -0
  83. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/data/skill-file-bug-report.md +0 -0
  84. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/data/skill-repomatic-audit.md +0 -0
  85. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/data/skill-repomatic-deps.md +0 -0
  86. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/data/skill-repomatic-init.md +0 -0
  87. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/data/skill-repomatic-topics.md +0 -0
  88. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/data/skill-translation-sync.md +0 -0
  89. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/data/skill-upstream-audit.md +0 -0
  90. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/data/typos.toml +0 -0
  91. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/data/uv.toml +0 -0
  92. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/data/yamllint.yaml +0 -0
  93. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/data/zizmor.yaml +0 -0
  94. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/dep_sources.py +0 -0
  95. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/deps_graph.py +0 -0
  96. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/git_ops.py +0 -0
  97. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/github/__init__.py +0 -0
  98. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/github/actions.py +0 -0
  99. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/github/dev_release.py +0 -0
  100. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/github/gh.py +0 -0
  101. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/github/issue.py +0 -0
  102. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/github/matrix.py +0 -0
  103. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/github/pr.py +0 -0
  104. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/github/pr_body.py +0 -0
  105. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/github/release_sync.py +0 -0
  106. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/github/releases.py +0 -0
  107. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/github/sponsor.py +0 -0
  108. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/github/status.py +0 -0
  109. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/github/token.py +0 -0
  110. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/github/unsubscribe.py +0 -0
  111. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/github/workflow_sync.py +0 -0
  112. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/gitignore.py +0 -0
  113. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/http.py +0 -0
  114. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/images.py +0 -0
  115. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/lint_repo.py +0 -0
  116. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/mailmap.py +0 -0
  117. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/metadata.py +0 -0
  118. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/npm.py +0 -0
  119. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/prepare_release.py +0 -0
  120. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/py.typed +0 -0
  121. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/pypi.py +0 -0
  122. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/pyproject.py +0 -0
  123. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/registry.py +0 -0
  124. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/setup_guide.py +0 -0
  125. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/templates/__init__.py +0 -0
  126. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/templates/available-admonition.md +0 -0
  127. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/templates/broken-links-issue.md +0 -0
  128. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/templates/bump-version.md +0 -0
  129. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/templates/detect-squash-merge.md +0 -0
  130. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/templates/development-warning.md +0 -0
  131. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/templates/fix-typos.md +0 -0
  132. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/templates/format-images.md +0 -0
  133. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/templates/format-json.md +0 -0
  134. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/templates/format-markdown.md +0 -0
  135. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/templates/format-pyproject.md +0 -0
  136. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/templates/format-python.md +0 -0
  137. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/templates/format-shell.md +0 -0
  138. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/templates/generated-footer.md +0 -0
  139. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/templates/github-releases.md +0 -0
  140. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/templates/immutable-releases.md +0 -0
  141. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/templates/pr-metadata.md.noformat +0 -0
  142. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/templates/prepare-release.md +0 -0
  143. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/templates/refresh-tip.md +0 -0
  144. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/templates/release-notes.md +0 -0
  145. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/templates/release-sync-report.md +0 -0
  146. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/templates/setup-guide-branch-ruleset.md +0 -0
  147. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/templates/setup-guide-dependabot.md +0 -0
  148. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/templates/setup-guide-fork-pr-approval.md +0 -0
  149. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/templates/setup-guide-notifications-pat.md +0 -0
  150. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/templates/setup-guide-pages-source.md +0 -0
  151. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/templates/setup-guide-pypi-trusted-publisher.md +0 -0
  152. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/templates/setup-guide-token.md +0 -0
  153. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/templates/setup-guide-verify.md +0 -0
  154. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/templates/setup-guide-virustotal.md +0 -0
  155. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/templates/setup-guide.md +0 -0
  156. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/templates/unavailable-admonition.md +0 -0
  157. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/templates/unsubscribe-phase1.md +0 -0
  158. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/templates/unsubscribe-phase2.md +0 -0
  159. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/templates/yanked-admonition.md +0 -0
  160. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/test_matrix.py +0 -0
  161. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/tool_runner_page.py +0 -0
  162. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/virustotal.py +0 -0
  163. {repomatic-7.2.0 → repomatic-7.3.1}/repomatic/vulnerable_deps.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: repomatic
3
- Version: 7.2.0
3
+ Version: 7.3.1
4
4
  Summary: 🏭 Automate repository maintenance, releases, and CI/CD workflows
5
5
  Keywords: changelog-formatter,dependabot-alternative,github-actions,github-labels,github-workflow,gitignore,mailmap,mypy,nuitka,python-automation,release-automation,repository-automation,repository-management,reusable-workflows,software-factory,sphinx-doc,typo,uv,workflow-reusable
6
6
  Author: Kevin Deldycke
@@ -46,7 +46,7 @@ Classifier: Typing :: Typed
46
46
  Requires-Dist: arrow>=1.3
47
47
  Requires-Dist: backports-strenum>=1 ; python_full_version < '3.11'
48
48
  Requires-Dist: boltons>=25
49
- Requires-Dist: click-extra>=8.3
49
+ Requires-Dist: click-extra>=8.5
50
50
  Requires-Dist: extra-platforms>=12.0.2
51
51
  Requires-Dist: packaging>=17
52
52
  Requires-Dist: py-walk>=0.3.1
@@ -58,7 +58,7 @@ Requires-Dist: wcmatch>=10
58
58
  Requires-Python: >=3.10
59
59
  Project-URL: Changelog, https://github.com/kdeldycke/repomatic/blob/main/changelog.md
60
60
  Project-URL: Documentation, https://kdeldycke.github.io/repomatic
61
- Project-URL: Download, https://github.com/kdeldycke/repomatic/releases/tag/v7.2.0
61
+ Project-URL: Download, https://github.com/kdeldycke/repomatic/releases/tag/v7.3.1
62
62
  Project-URL: Funding, https://github.com/sponsors/kdeldycke
63
63
  Project-URL: Homepage, https://github.com/kdeldycke/repomatic
64
64
  Project-URL: Issues, https://github.com/kdeldycke/repomatic/issues
@@ -5,7 +5,7 @@ requires = [ "uv-build>=0.9,<0.12" ]
5
5
  [project]
6
6
  # Docs: https://packaging.python.org/en/latest/guides/writing-pyproject-toml/
7
7
  name = "repomatic"
8
- version = "7.2.0"
8
+ version = "7.3.1"
9
9
  description = "🏭 Automate repository maintenance, releases, and CI/CD workflows"
10
10
  readme = "readme.md"
11
11
  keywords = [
@@ -84,10 +84,16 @@ dependencies = [
84
84
  "backports-strenum>=1; python_version<'3.11'",
85
85
  # boltons 25.0.0 dropped Python 3.9, matching our requires-python >= 3.10.
86
86
  "boltons>=25",
87
- # click-extra 8.3 adds the `click:config` Sphinx directive and the
88
- # `schema_field_infos()` introspection API that `configuration.md` and
89
- # `show-config` are built on.
90
- "click-extra>=8.3",
87
+ # click-extra 8.5 ships the `python:render` `:mirror:` regions and extends
88
+ # `refresh-directives` to refresh them (update_mirror_blocks), with the
89
+ # fence-aware scanner that stops `update-docs` phase 4 from rewriting
90
+ # examples nested in code fences. 8.5 also carries the
91
+ # `make_schema_callable(warn_unknown=...)` unknown-key warning
92
+ # `load_repomatic_config` opts into, and the `click_extra.sphinx.
93
+ # myst_docstrings` extension `docs/conf.py` loads (absorbed from
94
+ # `repomatic.myst_docstrings`). (8.3 brought the `click:config` directive
95
+ # and `schema_field_infos()` that `configuration.md` and `show-config` use.)
96
+ "click-extra>=8.5",
91
97
  # extra-platforms 12.0.2 reimplemented is_macos() with sys.platform, avoiding
92
98
  # the platform.platform() shell-out to ``cmd /c ver`` on Windows that broke
93
99
  # under patched subprocess in tests and triggered a CPython 3.10.x bug.
@@ -122,7 +128,7 @@ dependencies = [
122
128
  ]
123
129
  urls.Changelog = "https://github.com/kdeldycke/repomatic/blob/main/changelog.md"
124
130
  urls.Documentation = "https://kdeldycke.github.io/repomatic"
125
- urls.Download = "https://github.com/kdeldycke/repomatic/releases/tag/v7.2.0"
131
+ urls.Download = "https://github.com/kdeldycke/repomatic/releases/tag/v7.3.1"
126
132
  urls.Funding = "https://github.com/sponsors/kdeldycke"
127
133
  urls.Homepage = "https://github.com/kdeldycke/repomatic"
128
134
  urls.Issues = "https://github.com/kdeldycke/repomatic/issues"
@@ -206,7 +212,7 @@ exclude-newer = "1 week"
206
212
  # `exclude-newer`, returning the package to the normal cooldown. repomatic is
207
213
  # pinned to git main (no PyPI release to freeze), so it keeps a permanent
208
214
  # "0 day" span.
209
- exclude-newer-package = { repomatic = "0 day" }
215
+ exclude-newer-package = { click-extra = "2026-07-24T00:00:00Z", repomatic = "0 day" }
210
216
  # Package is at root level, not in "./src/".
211
217
  build-backend.module-root = ""
212
218
 
@@ -219,7 +225,7 @@ copyright = "Kevin Deldycke <kevin@deldycke.com> and contributors. Distributed u
219
225
  file-description = "🏭 Automate repository maintenance, releases, and CI/CD workflows"
220
226
  # Numeric only: Nuitka rejects PEP 440 .devN suffixes. Kept in sync with the
221
227
  # package version by the dedicated [tool.bumpversion] rule that strips the suffix.
222
- file-version = "7.2.0"
228
+ file-version = "7.3.1"
223
229
  include-data-dir = [
224
230
  "repomatic/data/awesome_template=repomatic/data/awesome_template",
225
231
  ]
@@ -237,13 +243,14 @@ include-package-data = [ "click_extra" ]
237
243
  linux-icon = "docs/assets/icon.png"
238
244
  macos-app-icon = "docs/assets/icon.icns"
239
245
  product-name = "Repomatic"
240
- product-version = "7.2.0"
246
+ product-version = "7.3.1"
241
247
  windows-icon-from-ico = "docs/assets/icon.ico"
242
248
 
243
249
  [tool.ruff]
244
250
  preview = true
245
251
  fix = true
246
- unsafe-fixes = true
252
+ # Disabled for the reasons documented in the bundled repomatic/data/ruff.toml.
253
+ unsafe-fixes = false
247
254
  show-fixes = true
248
255
  # Enable reformatting of code snippets in docstrings.
249
256
  # https://docs.astral.sh/ruff/formatter/#docstring-formatting
@@ -336,11 +343,18 @@ addopts = [
336
343
  "--cov",
337
344
  "--cov-report=term",
338
345
  "--numprocesses=auto",
346
+ # Import test modules via importlib instead of prepending their root dir to
347
+ # sys.path. Avoids "import file mismatch" collection errors when an installed
348
+ # copy of the package coexists with the source tree, as in distro builds.
349
+ "--import-mode=importlib",
339
350
  ]
340
351
  # https://docs.pytest.org/en/latest/customize.html#pyproject-toml
341
352
  markers = [
342
353
  "once: Tests that only need to run once, not across the full CI matrix.",
343
354
  ]
355
+ # Restrict collection to the test suite, so pytest never descends into the
356
+ # package or an installed copy of it in dual-copy builds.
357
+ testpaths = [ "tests" ]
344
358
  # Make sure tests that are expected to fail do not resurrect and start working all of a sudden.
345
359
  xfail_strict = true
346
360
 
@@ -351,7 +365,7 @@ run.source = [ "repomatic" ]
351
365
  report.precision = 2
352
366
 
353
367
  [tool.bumpversion]
354
- current_version = "7.2.0"
368
+ current_version = "7.3.1"
355
369
  # Parse versions with an optional .devN suffix (PEP 440).
356
370
  parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)(\\.dev(?P<dev>\\d+))?"
357
371
  serialize = [
@@ -17,5 +17,5 @@
17
17
 
18
18
  from __future__ import annotations
19
19
 
20
- __version__ = "7.2.0"
21
- __git_tag_sha__ = "7336863d9f3f5faea26f887211f3f745dc963fb5"
20
+ __version__ = "7.3.1"
21
+ __git_tag_sha__ = "c81fb2e6fa884cb62ef46e69b8c8db44fa6fad15"
@@ -164,9 +164,28 @@ def _release_version(tag: str) -> Version | None:
164
164
 
165
165
 
166
166
  def _binary_assets(release: ReleaseWithAssets) -> list[ReleaseAsset]:
167
- """Return the release's compiled binaries, sorted by filename."""
167
+ """Return the release's compiled binaries, sorted by filename.
168
+
169
+ Releases also carry versionless alias copies of each binary, backing the
170
+ stable ``releases/latest/download`` URLs (see the upload step in
171
+ ``_release-engine.yaml``). An alias shares its digest with its versioned
172
+ sibling: collapse each digest group onto its longest name (the versioned
173
+ one, since the alias is that same name minus the version segment) so the
174
+ catalog lists each binary once. Assets without a recorded digest
175
+ (uploaded before GitHub started recording them, mid-2025) predate the
176
+ aliases and are kept as-is.
177
+ """
178
+ binaries = [a for a in release.assets if a.name.endswith(BINARY_ASSET_SUFFIXES)]
179
+ digest_groups: dict[str, list[ReleaseAsset]] = {}
180
+ for asset in binaries:
181
+ if asset.sha256:
182
+ digest_groups.setdefault(asset.sha256, []).append(asset)
183
+ aliases: set[str] = set()
184
+ for group in digest_groups.values():
185
+ canonical = max(group, key=lambda asset: (len(asset.name), asset.name))
186
+ aliases.update(asset.name for asset in group if asset.name != canonical.name)
168
187
  return sorted(
169
- (a for a in release.assets if a.name.endswith(BINARY_ASSET_SUFFIXES)),
188
+ (a for a in binaries if a.name not in aliases),
170
189
  key=lambda asset: asset.name,
171
190
  )
172
191
 
@@ -762,7 +762,8 @@ def warn_on_long_bullets(changelog: Changelog, threshold: int) -> None:
762
762
  """Warn about over-long bullets in the unreleased section, non-fatally.
763
763
 
764
764
  A changelog entry is a release note, not a commit message: one short
765
- sentence stating what changed (see `CLAUDE.md` § Changelog entry length).
765
+ sentence stating what changed. Canonical guideline:
766
+ https://github.com/kdeldycke/repomatic/blob/main/claude.md#changelog-entry-length
766
767
  Each unreleased bullet longer than `threshold` words emits a
767
768
  {data}`logging.WARNING` and a GitHub Actions warning annotation, without
768
769
  affecting the lint exit code.
@@ -792,7 +793,9 @@ def warn_on_long_bullets(changelog: Changelog, threshold: int) -> None:
792
793
  f"Changelog entry {index} for {version} runs {words} words, "
793
794
  f"over the {threshold}-word guideline. A changelog entry is "
794
795
  f"a release note, not a commit message: keep it to one short "
795
- f"sentence (see CLAUDE.md, Changelog entry length).",
796
+ f"sentence, per the canonical guideline "
797
+ f"https://github.com/kdeldycke/repomatic/blob/main/claude.md"
798
+ f"#changelog-entry-length",
796
799
  )
797
800
 
798
801
 
@@ -956,7 +959,16 @@ def lint_changelog_dates(
956
959
  archive_headings = Changelog(
957
960
  archive_path.read_text(encoding="UTF-8")
958
961
  ).extract_all_version_headings()
959
- all_known = set(pypi_data) | set(github_releases) | set(tag_versions)
962
+ # The changelog documents only final releases. Exclude pre-releases
963
+ # (dev/alpha/beta/rc) so a published `X.Y.Z.dev0` tag, PyPI upload, or
964
+ # GitHub prerelease is never mistaken for a missing changelog entry: an
965
+ # orphan insertion would both drop a spurious pre-release section and
966
+ # rewrite the adjacent release's comparison-URL base to point at it.
967
+ all_known = {
968
+ v
969
+ for v in (set(pypi_data) | set(github_releases) | set(tag_versions))
970
+ if not Version(v).is_prerelease
971
+ }
960
972
  orphans = all_known - changelog_headings - archive_headings
961
973
  if orphans:
962
974
  for orphan in sorted(orphans, key=Version):
@@ -1001,6 +1013,11 @@ def lint_changelog_dates(
1001
1013
 
1002
1014
  date_corrections: dict[str, str] = {}
1003
1015
  abandoned = frozenset(abandoned_versions)
1016
+ # Point maintainers at the remedy: a version that is intentionally absent
1017
+ # from the reference source is declared once in config, not re-flagged.
1018
+ abandoned_hint = (
1019
+ "list under [tool.repomatic] abandoned-versions if intentionally unpublished"
1020
+ )
1004
1021
 
1005
1022
  for version, changelog_date in releases:
1006
1023
  if use_pypi:
@@ -1015,10 +1032,10 @@ def lint_changelog_dates(
1015
1032
  f" {version}: predates PyPI (first: {first_pypi_version})"
1016
1033
  )
1017
1034
  continue
1018
- logging.warning(f"⚠ {version}: not found on PyPI")
1035
+ logging.warning(f"⚠ {version}: not found on PyPI ({abandoned_hint})")
1019
1036
  emit_annotation(
1020
1037
  AnnotationLevel.WARNING,
1021
- f"Version {version} not found on PyPI",
1038
+ f"Version {version} not found on PyPI ({abandoned_hint})",
1022
1039
  )
1023
1040
  continue
1024
1041
  ref_date = release.date
@@ -1030,10 +1047,12 @@ def lint_changelog_dates(
1030
1047
  if version in abandoned:
1031
1048
  logging.info(f" {version}: abandoned (skipped per config)")
1032
1049
  continue
1033
- logging.warning(f"⚠ {version}: not found on {source}")
1050
+ logging.warning(
1051
+ f"⚠ {version}: not found on {source} ({abandoned_hint})"
1052
+ )
1034
1053
  emit_annotation(
1035
1054
  AnnotationLevel.WARNING,
1036
- f"Version {version} not found on {source}",
1055
+ f"Version {version} not found on {source} ({abandoned_hint})",
1037
1056
  )
1038
1057
  continue
1039
1058
  ref_date = tag_date
@@ -29,7 +29,6 @@ from pathlib import Path
29
29
 
30
30
  import tomlrt
31
31
  from click_extra import (
32
- DEFAULT_JOBS,
33
32
  UNPROCESSED,
34
33
  Choice,
35
34
  ClickException,
@@ -336,13 +335,7 @@ def _require_token(module, attr):
336
335
  # names (e.g., "setup-guide" is both a config key and a subcommand). Config
337
336
  # access goes exclusively through config_schema + get_tool_config().
338
337
  @group(config_schema=Config, schema_strict=False, included_params=())
339
- # Resolve the default job count eagerly (an integer instead of the "auto"
340
- # keyword) so JobCount.convert() never emits its sequential-execution warning
341
- # for a value the user never set: on 1-CPU hosts (ubuntu-slim CI runners, docs
342
- # builders) that warning fires on every bare invocation and leaks into the
343
- # merged output captured by `{click:run}` doc renders and CliRunner tests.
344
- # show_default restores the machine-independent "auto" label in help screens.
345
- @jobs_option(default=DEFAULT_JOBS, show_default="auto")
338
+ @jobs_option()
346
339
  def repomatic():
347
340
  pass
348
341
 
@@ -734,15 +727,6 @@ init_project.help = init_project.help.format(
734
727
  )
735
728
 
736
729
 
737
- def _table_headers(header_defs: tuple[tuple[str, str], ...]) -> tuple[str, ...]:
738
- """Return the column labels from `(label, column_id)` header definitions.
739
-
740
- `ctx.print_table` takes `(table_data, headers)`; the column IDs drive
741
- `--sort-by` through the matching `SortByOption`, not the rendered header row.
742
- """
743
- return tuple(label for label, _ in header_defs)
744
-
745
-
746
730
  _metadata_sort = SortByOption(*METADATA_KEYS_HEADER_DEFS, default="key")
747
731
 
748
732
 
@@ -793,9 +777,7 @@ def metadata(ctx, format, overwrite, output, list_keys, keys):
793
777
  current_version is_python_project
794
778
  """
795
779
  if list_keys:
796
- ctx.print_table(
797
- metadata_keys_reference(), _table_headers(METADATA_KEYS_HEADER_DEFS)
798
- )
780
+ ctx.print_table(metadata_keys_reference(), METADATA_KEYS_HEADER_DEFS)
799
781
  ctx.exit(0)
800
782
 
801
783
  # Validate requested keys.
@@ -873,7 +855,7 @@ def show_config(ctx):
873
855
  (option, escape_type_for_gfm_table(ftype), default, desc)
874
856
  for option, ftype, default, desc in config_reference()
875
857
  ]
876
- ctx.print_table(rows, _table_headers(CONFIG_REFERENCE_HEADER_DEFS))
858
+ ctx.print_table(rows, CONFIG_REFERENCE_HEADER_DEFS)
877
859
 
878
860
 
879
861
  TEST_MATRIX_STATE_DISPLAY = {
@@ -924,7 +906,7 @@ def show_test_matrix(ctx, emoji, matrix_name):
924
906
  (row[0], *(TEST_MATRIX_STATE_DISPLAY.get(cell, cell) for cell in row[1:]))
925
907
  for row in rows
926
908
  )
927
- ctx.find_root().print_table(rows, headers)
909
+ ctx.print_table(rows, headers)
928
910
 
929
911
 
930
912
  @repomatic.command(
@@ -1806,69 +1788,15 @@ def update_docs() -> None:
1806
1788
  with ``{eval-rst}`` blocks.
1807
1789
  3. Run the project-specific `docs/docs_update.py` script (if present)
1808
1790
  to generate dynamic content.
1809
- 4. Refresh self-updating directive blocks (like `{matrix}` compatibility
1810
- tables) found in `docs/` pages and `readme.md`, via
1811
- `click-extra refresh-directives`.
1791
+ 4. Refresh self-updating blocks (`{matrix}` compatibility tables and
1792
+ `python:render` `:mirror:` regions) found in `docs/` pages and
1793
+ `readme.md`, via `click-extra refresh-directives`.
1812
1794
 
1813
1795
  Configuration is read from `[tool.repomatic]` in `pyproject.toml`.
1814
1796
  """
1815
1797
  _update_docs(get_tool_config())
1816
1798
 
1817
1799
 
1818
- @repomatic.command(
1819
- name="convert-to-myst",
1820
- short_help="Convert reST docstrings to MyST in Python files",
1821
- section=_section_setup,
1822
- )
1823
- @argument("directory", required=False, default=None)
1824
- def convert_to_myst(directory: str | None) -> None:
1825
- """Convert reST docstrings to MyST markdown in Python source files.
1826
-
1827
- Transforms reST markup in docstrings and `#:` comment blocks to MyST.
1828
- The companion Sphinx extension `repomatic.myst_docstrings` converts
1829
- the MyST back to reST at build time, so `sphinx.ext.autodoc` still
1830
- works.
1831
-
1832
- If DIRECTORY is not specified, auto-detects the source package directory
1833
- from the project's script entry points in `pyproject.toml`.
1834
-
1835
- Safe to re-run: already-converted MyST syntax does not match the reST
1836
- patterns, so the conversion is idempotent.
1837
- """
1838
- from .metadata import Metadata
1839
- from .myst_converter import convert_directory
1840
-
1841
- if directory:
1842
- root = Path(directory)
1843
- else:
1844
- # Auto-detect source directory from project metadata.
1845
- meta = Metadata()
1846
- source_dirs: set[str] = set()
1847
- for _cli_id, module_id, _callable_id in meta.script_entries:
1848
- source_dirs.add(module_id.split(".")[0])
1849
-
1850
- if not source_dirs:
1851
- raise ClickException(
1852
- "Cannot auto-detect source directory. "
1853
- "Specify a directory argument or add script entry points "
1854
- "to pyproject.toml."
1855
- )
1856
- if len(source_dirs) > 1:
1857
- raise ClickException(
1858
- f"Multiple source packages detected: {sorted(source_dirs)}. "
1859
- "Specify a directory argument."
1860
- )
1861
- root = Path(source_dirs.pop())
1862
-
1863
- if not root.is_dir():
1864
- raise ClickException(f"Not a directory: {root}")
1865
-
1866
- changed = convert_directory(root)
1867
- for filepath in changed:
1868
- echo(f" Converted: {filepath}")
1869
- echo(f"\n{len(changed)} file(s) converted.")
1870
-
1871
-
1872
1800
  @repomatic.command(
1873
1801
  short_help="Manage broken links issue lifecycle", section=_section_github
1874
1802
  )
@@ -2312,9 +2240,7 @@ def audit(
2312
2240
  )
2313
2241
  for v in vulns
2314
2242
  ]
2315
- ctx.print_table( # type: ignore[attr-defined]
2316
- rows, _table_headers(AUDIT_HEADER_DEFS)
2317
- )
2243
+ ctx.print_table(rows, AUDIT_HEADER_DEFS)
2318
2244
 
2319
2245
  if output:
2320
2246
  markdown = format_vulnerability_table(vulns)
@@ -2644,7 +2570,7 @@ def _print_sync_table(
2644
2570
  if show_released:
2645
2571
  row = (*row, format_released(dates.get(name, ""), reference_date))
2646
2572
  rows.append(row)
2647
- ctx.find_root().print_table(rows, headers) # type: ignore[attr-defined]
2573
+ ctx.print_table(rows, headers)
2648
2574
 
2649
2575
 
2650
2576
  def _print_held_back_table(
@@ -2671,7 +2597,7 @@ def _print_held_back_table(
2671
2597
  )
2672
2598
  for pkg in held_back
2673
2599
  ]
2674
- ctx.find_root().print_table(rows, headers) # type: ignore[attr-defined]
2600
+ ctx.print_table(rows, headers)
2675
2601
 
2676
2602
 
2677
2603
  def _print_bypass_table(ctx: Context, forecasts: list[BypassForecast]) -> None:
@@ -2687,7 +2613,7 @@ def _print_bypass_table(ctx: Context, forecasts: list[BypassForecast]) -> None:
2687
2613
  (forecast.name, forecast.held_version, forecast.expires)
2688
2614
  for forecast in forecasts
2689
2615
  ]
2690
- ctx.find_root().print_table(rows, headers) # type: ignore[attr-defined]
2616
+ ctx.print_table(rows, headers)
2691
2617
 
2692
2618
 
2693
2619
  def _emit_version_sync_report(
@@ -3571,7 +3497,7 @@ def run_cmd(
3571
3497
  (spec.name, spec.version, resolve_config_source(spec))
3572
3498
  for spec in TOOL_REGISTRY.values()
3573
3499
  ]
3574
- ctx.print_table(rows, _table_headers(TOOL_LIST_HEADER_DEFS))
3500
+ ctx.print_table(rows, TOOL_LIST_HEADER_DEFS)
3575
3501
  ctx.exit(0)
3576
3502
 
3577
3503
  if tool_name is None:
@@ -3664,7 +3590,7 @@ def show(ctx):
3664
3590
  _format_age(cfg_entry.mtime),
3665
3591
  ))
3666
3592
 
3667
- ctx.print_table(rows, _table_headers(CACHE_LIST_HEADER_DEFS))
3593
+ ctx.print_table(rows, CACHE_LIST_HEADER_DEFS)
3668
3594
  total_count = len(bin_entries) + len(http_entries) + len(cfg_entries)
3669
3595
  echo(f"\nTotal: {total_count} file(s), {format_file_size(total_size)}")
3670
3596
 
@@ -23,7 +23,6 @@ Defines the `Config` dataclass, its TOML serialization helpers, and the
23
23
 
24
24
  from __future__ import annotations
25
25
 
26
- import logging
27
26
  from dataclasses import dataclass, field
28
27
  from textwrap import dedent
29
28
 
@@ -631,8 +630,8 @@ class Config:
631
630
  exclude: list[str] = field(default_factory=list)
632
631
  """Additional components and files to exclude from repomatic operations.
633
632
 
634
- Additive to the default exclusions (`labels`, `skills`). Bare names
635
- exclude an entire component (e.g., `"workflows"`). Qualified
633
+ Additive to the default exclusions (`agents`, `labels`, `skills`). Bare
634
+ names exclude an entire component (e.g., `"workflows"`). Qualified
636
635
  `component/identifier` entries exclude a specific file within a component
637
636
  (e.g., `"workflows/debug.yaml"`, `"skills/repomatic-audit"`,
638
637
  `"labels/labeller-content-based.yaml"`).
@@ -650,9 +649,9 @@ class Config:
650
649
  include: list[str] = field(default_factory=list)
651
650
  """Components and files to force-include, overriding default exclusions.
652
651
 
653
- Use this to opt into components that are excluded by default (`labels`,
654
- `skills`). Each entry is subtracted from the effective exclude set
655
- (defaults + user `exclude`) and bypasses `RepoScope` filtering, so
652
+ Use this to opt into components that are excluded by default (`agents`,
653
+ `labels`, `skills`). Each entry is subtracted from the effective exclude
654
+ set (defaults + user `exclude`) and bypasses `RepoScope` filtering, so
656
655
  scope-restricted components (like awesome-only skills or Python-only
657
656
  `publish-pypi-action`) are included regardless of repository type.
658
657
  Qualified entries (`component/file`) implicitly select the parent
@@ -1000,18 +999,10 @@ def load_repomatic_config(
1000
999
  tool_section = pyproject_data.get("tool", {})
1001
1000
  user_config: dict[str, Any] = tool_section.get("repomatic", {})
1002
1001
 
1003
- # Warn about unknown top-level keys before loading. Collect all known
1004
- # TOML key prefixes (e.g., "test-matrix", "nuitka", "workflow") so we
1005
- # can flag unrecognized entries without crashing.
1006
- known_keys = {info.key.split(".")[0] for info in schema_field_infos(Config)}
1007
- for key in user_config:
1008
- if key.replace("_", "-") not in known_keys:
1009
- logging.warning(
1010
- "Unknown [tool.repomatic] option: %s (ignored).",
1011
- key,
1012
- )
1013
-
1014
- schema_callable = make_schema_callable(Config, strict=False)
1002
+ # The [tool.repomatic] section is schema-only (the CLI group runs with
1003
+ # included_params=()), so warn_unknown flags any key the schema does not
1004
+ # know as a typo, nested tables included.
1005
+ schema_callable = make_schema_callable(Config, strict=False, warn_unknown=True)
1015
1006
  assert schema_callable is not None
1016
1007
  config: Config = schema_callable(user_config)
1017
1008
  return config