repomatic 6.26.0__tar.gz → 6.28.0__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.
- {repomatic-6.26.0 → repomatic-6.28.0}/PKG-INFO +2 -2
- {repomatic-6.26.0 → repomatic-6.28.0}/pyproject.toml +47 -48
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/__init__.py +2 -2
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/cli.py +196 -47
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/config.py +24 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/data/_release-engine.yaml +70 -65
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/data/autofix.yaml +39 -35
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/data/changelog.yaml +14 -14
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/data/debug.yaml +1 -1
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/data/docs.yaml +3 -3
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/data/labels.yaml +5 -5
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/data/lint.yaml +7 -7
- repomatic-6.28.0/repomatic/data/lychee.toml +15 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/data/renovate.json5 +2 -2
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/data/renovate.yaml +3 -3
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/data/skill-awesome-triage.md +2 -0
- repomatic-6.28.0/repomatic/data/skill-repomatic-changelog.md +61 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/data/skill-repomatic-ship.md +6 -4
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/data/tests.yaml +6 -3
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/data/unsubscribe.yaml +1 -1
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/github/advisories.py +1 -1
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/github/matrix.py +9 -6
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/metadata.py +53 -10
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/myst_docstrings.py +5 -1
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/test_matrix.py +29 -4
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/test_plan.py +59 -2
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/tool_runner.py +101 -14
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/uv.py +169 -10
- repomatic-6.26.0/repomatic/data/lychee.toml +0 -15
- repomatic-6.26.0/repomatic/data/skill-repomatic-changelog.md +0 -61
- {repomatic-6.26.0 → repomatic-6.28.0}/license +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/readme.md +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/__main__.py +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/binary.py +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/broken_links.py +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/cache.py +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/changelog.py +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/checksums.py +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/data/__init__.py +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/data/action-publish-pypi.yaml +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/data/agent-grunt-qa.md +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/data/agent-qa-engineer.md +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/data/agent-sphinx-docs.md +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/data/autolock.yaml +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/data/awesome_template/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/data/awesome_template/.github/ISSUE_TEMPLATE/new-link.yaml +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/data/awesome_template/.github/code-of-conduct.md +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/data/awesome_template/.github/contributing.md +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/data/awesome_template/.github/contributing.zh.md +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/data/awesome_template/.github/funding.yml +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/data/awesome_template/.github/pull_request_template.md +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/data/awesome_template/__init__.py +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/data/awesome_template/license +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/data/bumpversion.toml +3 -3
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/data/cancel-runs.yaml +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/data/codecov.yaml +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/data/labeller-content-based.yaml +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/data/labeller-file-based.yaml +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/data/labels.toml +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/data/mdformat.toml +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/data/mypy.toml +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/data/pytest.toml +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/data/release.yaml +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/data/ruff.toml +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/data/skill-av-false-positive.md +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/data/skill-babysit-ci.md +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/data/skill-benchmark-update.md +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/data/skill-brand-assets.md +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/data/skill-file-bug-report.md +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/data/skill-repomatic-audit.md +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/data/skill-repomatic-deps.md +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/data/skill-repomatic-init.md +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/data/skill-repomatic-topics.md +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/data/skill-sphinx-docs-sync.md +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/data/skill-translation-sync.md +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/data/skill-upstream-audit.md +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/data/typos.toml +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/data/yamllint.yaml +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/data/zizmor.yaml +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/deps_graph.py +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/git_ops.py +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/github/__init__.py +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/github/actions.py +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/github/dev_release.py +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/github/gh.py +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/github/issue.py +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/github/pr.py +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/github/pr_body.py +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/github/release_sync.py +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/github/releases.py +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/github/status.py +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/github/token.py +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/github/unsubscribe.py +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/github/workflow_sync.py +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/images.py +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/init_project.py +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/lint_repo.py +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/mailmap.py +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/myst_converter.py +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/py.typed +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/pypi.py +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/pyproject.py +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/registry.py +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/release_prep.py +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/renovate.py +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/rst_to_myst.py +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/sponsor.py +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/templates/__init__.py +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/templates/available-admonition.md +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/templates/broken-links-issue.md +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/templates/bump-version.md +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/templates/detect-squash-merge.md +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/templates/development-warning.md +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/templates/fix-changelog.md +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/templates/fix-typos.md +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/templates/fix-vulnerable-deps.md +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/templates/format-images.md +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/templates/format-json.md +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/templates/format-markdown.md +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/templates/format-pyproject.md +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/templates/format-python.md +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/templates/format-shell.md +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/templates/generated-footer.md +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/templates/github-releases.md +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/templates/immutable-releases.md +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/templates/pr-metadata.md.noformat +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/templates/prepare-release.md +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/templates/refresh-tip.md +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/templates/release-notes.md +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/templates/release-sync-report.md +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/templates/renovate-migration.md +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/templates/setup-guide-branch-ruleset.md +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/templates/setup-guide-dependabot.md +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/templates/setup-guide-fork-pr-approval.md +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/templates/setup-guide-pages-source.md +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/templates/setup-guide-pypi-trusted-publisher.md +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/templates/setup-guide-token.md +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/templates/setup-guide-verify.md +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/templates/setup-guide-virustotal.md +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/templates/setup-guide.md +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/templates/sync-bumpversion.md +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/templates/sync-gitignore.md +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/templates/sync-mailmap.md +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/templates/sync-repomatic.md +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/templates/sync-uv-lock.md +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/templates/unavailable-admonition.md +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/templates/unsubscribe-phase1.md +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/templates/unsubscribe-phase2.md +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/templates/update-deps-graph.md +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/templates/update-docs.md +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/templates/yanked-admonition.md +0 -0
- {repomatic-6.26.0 → repomatic-6.28.0}/repomatic/virustotal.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: repomatic
|
|
3
|
-
Version: 6.
|
|
3
|
+
Version: 6.28.0
|
|
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,renovate,repository-automation,repository-management,reusable-workflows,software-factory,sphinx-doc,typo,uv,workflow-reusable
|
|
6
6
|
Author: Kevin Deldycke
|
|
@@ -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/v6.
|
|
61
|
+
Project-URL: Download, https://github.com/kdeldycke/repomatic/releases/tag/v6.28.0
|
|
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" ]
|
|
|
5
5
|
[project]
|
|
6
6
|
# Docs: https://packaging.python.org/en/latest/guides/writing-pyproject-toml/
|
|
7
7
|
name = "repomatic"
|
|
8
|
-
version = "6.
|
|
8
|
+
version = "6.28.0"
|
|
9
9
|
description = "🏭 Automate repository maintenance, releases, and CI/CD workflows"
|
|
10
10
|
readme = "readme.md"
|
|
11
11
|
keywords = [
|
|
@@ -116,18 +116,14 @@ dependencies = [
|
|
|
116
116
|
# globbing uses iglob() with FOLLOW for symlink-aware file discovery.
|
|
117
117
|
"wcmatch>=10",
|
|
118
118
|
]
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
"Funding" = "https://github.com/sponsors/kdeldycke"
|
|
128
|
-
"Homepage" = "https://github.com/kdeldycke/repomatic"
|
|
129
|
-
"Issues" = "https://github.com/kdeldycke/repomatic/issues"
|
|
130
|
-
"Repository" = "https://github.com/kdeldycke/repomatic"
|
|
119
|
+
urls.Changelog = "https://github.com/kdeldycke/repomatic/blob/main/changelog.md"
|
|
120
|
+
urls.Documentation = "https://kdeldycke.github.io/repomatic"
|
|
121
|
+
urls.Download = "https://github.com/kdeldycke/repomatic/releases/tag/v6.28.0"
|
|
122
|
+
urls.Funding = "https://github.com/sponsors/kdeldycke"
|
|
123
|
+
urls.Homepage = "https://github.com/kdeldycke/repomatic"
|
|
124
|
+
urls.Issues = "https://github.com/kdeldycke/repomatic/issues"
|
|
125
|
+
urls.Repository = "https://github.com/kdeldycke/repomatic"
|
|
126
|
+
scripts.repomatic = "repomatic.__main__:main"
|
|
131
127
|
|
|
132
128
|
# Non-runtime development dependency groups.
|
|
133
129
|
[dependency-groups]
|
|
@@ -186,12 +182,15 @@ dependency-groups.docs = { requires-python = ">= 3.14" }
|
|
|
186
182
|
# Relative dates are fine here: uv 0.11.8+ writes a sentinel timestamp to
|
|
187
183
|
# uv.lock for relative spans, so re-resolving never produces churn.
|
|
188
184
|
exclude-newer = "1 week"
|
|
189
|
-
#
|
|
190
|
-
#
|
|
191
|
-
#
|
|
192
|
-
#
|
|
193
|
-
#
|
|
194
|
-
|
|
185
|
+
# Per-package cooldown control. A fixed date holds a package at the version
|
|
186
|
+
# available then (the day after it shipped), so `uv lock --upgrade` keeps that
|
|
187
|
+
# version instead of tracking newer releases. `sync-uv-lock` freezes new
|
|
188
|
+
# bypasses at that date and prunes each entry once its held version ages past
|
|
189
|
+
# `exclude-newer`, returning the package to the normal cooldown. repomatic is
|
|
190
|
+
# pinned to git main (no PyPI release to freeze), so it keeps a permanent
|
|
191
|
+
# "0 day" span. click-extra is held at 7.19.0 (manpages `--output-dir` flag);
|
|
192
|
+
# tomlrt is held at 1.8.2.
|
|
193
|
+
exclude-newer-package = { repomatic = "0 day", tomlrt = "2026-06-16T00:00:00Z" }
|
|
195
194
|
# Package is at root level, not in "./src/".
|
|
196
195
|
build-backend.module-root = ""
|
|
197
196
|
|
|
@@ -204,7 +203,7 @@ copyright = "Kevin Deldycke <kevin@deldycke.com> and contributors. Distributed u
|
|
|
204
203
|
file-description = "🏭 Automate repository maintenance, releases, and CI/CD workflows"
|
|
205
204
|
# Numeric only: Nuitka rejects PEP 440 .devN suffixes. Kept in sync with the
|
|
206
205
|
# package version by the dedicated [tool.bumpversion] rule that strips the suffix.
|
|
207
|
-
file-version = "6.
|
|
206
|
+
file-version = "6.28.0"
|
|
208
207
|
include-data-dir = [
|
|
209
208
|
"repomatic/data/awesome_template=repomatic/data/awesome_template",
|
|
210
209
|
]
|
|
@@ -222,7 +221,7 @@ include-package-data = [ "click_extra" ]
|
|
|
222
221
|
linux-icon = "docs/assets/icon.png"
|
|
223
222
|
macos-app-icon = "docs/assets/icon.icns"
|
|
224
223
|
product-name = "Repomatic"
|
|
225
|
-
product-version = "6.
|
|
224
|
+
product-version = "6.28.0"
|
|
226
225
|
windows-icon-from-ico = "docs/assets/icon.ico"
|
|
227
226
|
|
|
228
227
|
[tool.ruff]
|
|
@@ -284,11 +283,26 @@ default.extend-ignore-identifiers-re = [
|
|
|
284
283
|
"WARNIN",
|
|
285
284
|
]
|
|
286
285
|
|
|
287
|
-
[tool.
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
286
|
+
[tool.mypy]
|
|
287
|
+
mypy_path = "docs"
|
|
288
|
+
check_untyped_defs = true
|
|
289
|
+
warn_redundant_casts = true
|
|
290
|
+
warn_unused_ignores = true
|
|
291
|
+
warn_return_any = true
|
|
292
|
+
warn_unreachable = true
|
|
293
|
+
pretty = true
|
|
294
|
+
warn_unused_configs = true
|
|
295
|
+
overrides = [
|
|
296
|
+
{ module = [
|
|
297
|
+
"bumpversion.*",
|
|
298
|
+
"gitdb.*",
|
|
299
|
+
"py_walk.*",
|
|
300
|
+
"pydriller.*",
|
|
301
|
+
"vt",
|
|
302
|
+
], ignore_missing_imports = true },
|
|
291
303
|
]
|
|
304
|
+
|
|
305
|
+
[tool.pytest]
|
|
292
306
|
addopts = [
|
|
293
307
|
"--durations=10",
|
|
294
308
|
"--cov",
|
|
@@ -296,6 +310,10 @@ addopts = [
|
|
|
296
310
|
"--numprocesses=auto",
|
|
297
311
|
"--dist=loadgroup",
|
|
298
312
|
]
|
|
313
|
+
# https://docs.pytest.org/en/latest/customize.html#pyproject-toml
|
|
314
|
+
markers = [
|
|
315
|
+
"once: Tests that only need to run once, not across the full CI matrix.",
|
|
316
|
+
]
|
|
299
317
|
# Make sure tests that are expected to fail do not resurrect and start working all of a sudden.
|
|
300
318
|
xfail_strict = true
|
|
301
319
|
|
|
@@ -306,20 +324,20 @@ run.source = [ "repomatic" ]
|
|
|
306
324
|
report.precision = 2
|
|
307
325
|
|
|
308
326
|
[tool.bumpversion]
|
|
309
|
-
current_version = "6.
|
|
310
|
-
allow_dirty = true
|
|
311
|
-
ignore_missing_files = true
|
|
327
|
+
current_version = "6.28.0"
|
|
312
328
|
# Parse versions with an optional .devN suffix (PEP 440).
|
|
313
329
|
parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)(\\.dev(?P<dev>\\d+))?"
|
|
314
330
|
serialize = [
|
|
315
331
|
"{major}.{minor}.{patch}.dev{dev}",
|
|
316
332
|
"{major}.{minor}.{patch}",
|
|
317
333
|
]
|
|
334
|
+
ignore_missing_files = true
|
|
335
|
+
allow_dirty = true
|
|
336
|
+
parts.dev.optional_value = "release"
|
|
318
337
|
# When dev is "release" (its optional value), the .devN suffix is omitted.
|
|
319
338
|
# Bumping patch/minor/major resets dev to "0", producing X.Y.Z.dev0.
|
|
320
339
|
# Bumping dev goes from "0" to "release", stripping the suffix for release.
|
|
321
340
|
parts.dev.values = [ "0", "release" ]
|
|
322
|
-
parts.dev.optional_value = "release"
|
|
323
341
|
|
|
324
342
|
[[tool.bumpversion.files]]
|
|
325
343
|
# Update Python package version in any __init__.py file.
|
|
@@ -373,25 +391,6 @@ regex = true
|
|
|
373
391
|
search = "date-released: \\d{{4}}-\\d{{2}}-\\d{{2}}"
|
|
374
392
|
replace = "date-released: {utcnow:%Y-%m-%d}"
|
|
375
393
|
|
|
376
|
-
[tool.mypy]
|
|
377
|
-
check_untyped_defs = true
|
|
378
|
-
mypy_path = "docs"
|
|
379
|
-
warn_unused_configs = true
|
|
380
|
-
warn_redundant_casts = true
|
|
381
|
-
warn_unused_ignores = true
|
|
382
|
-
warn_return_any = true
|
|
383
|
-
warn_unreachable = true
|
|
384
|
-
pretty = true
|
|
385
|
-
overrides = [
|
|
386
|
-
{ ignore_missing_imports = true, module = [
|
|
387
|
-
"bumpversion.*",
|
|
388
|
-
"gitdb.*",
|
|
389
|
-
"py_walk.*",
|
|
390
|
-
"pydriller.*",
|
|
391
|
-
"vt",
|
|
392
|
-
] },
|
|
393
|
-
]
|
|
394
|
-
|
|
395
394
|
[tool.lychee]
|
|
396
395
|
# https://lychee.cli.rs/guides/config/
|
|
397
396
|
exclude = [
|
|
@@ -26,6 +26,7 @@ import sys
|
|
|
26
26
|
import tempfile
|
|
27
27
|
import time
|
|
28
28
|
from collections import Counter
|
|
29
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
29
30
|
from pathlib import Path
|
|
30
31
|
from urllib.request import Request, urlopen
|
|
31
32
|
|
|
@@ -39,14 +40,17 @@ from click_extra import (
|
|
|
39
40
|
EnumChoice,
|
|
40
41
|
FloatRange,
|
|
41
42
|
IntRange,
|
|
43
|
+
ParameterSource,
|
|
42
44
|
ParamType,
|
|
43
45
|
Section,
|
|
44
46
|
UsageError,
|
|
45
47
|
argument,
|
|
48
|
+
context,
|
|
46
49
|
dir_path,
|
|
47
50
|
echo,
|
|
48
51
|
file_path,
|
|
49
52
|
group,
|
|
53
|
+
jobs_option,
|
|
50
54
|
option,
|
|
51
55
|
option_group,
|
|
52
56
|
pass_context,
|
|
@@ -167,7 +171,12 @@ from .sponsor import (
|
|
|
167
171
|
is_pull_request,
|
|
168
172
|
is_sponsor,
|
|
169
173
|
)
|
|
170
|
-
from .test_plan import
|
|
174
|
+
from .test_plan import (
|
|
175
|
+
DEFAULT_TEST_PLAN,
|
|
176
|
+
CLITestCase,
|
|
177
|
+
SkippedTest,
|
|
178
|
+
parse_test_plan,
|
|
179
|
+
)
|
|
171
180
|
from .tool_runner import (
|
|
172
181
|
TOOL_REGISTRY,
|
|
173
182
|
binary_tool_context,
|
|
@@ -179,10 +188,12 @@ from .uv import (
|
|
|
179
188
|
AdvisorySource,
|
|
180
189
|
_format_upload_date,
|
|
181
190
|
build_comparison_urls,
|
|
191
|
+
collect_vulnerable_packages,
|
|
182
192
|
fetch_release_notes,
|
|
183
193
|
fix_vulnerable_deps as _fix_vulnerable_deps,
|
|
184
194
|
format_diff_table,
|
|
185
195
|
format_release_notes,
|
|
196
|
+
format_vulnerability_table,
|
|
186
197
|
sync_uv_lock as _sync_uv_lock,
|
|
187
198
|
)
|
|
188
199
|
from .virustotal import (
|
|
@@ -227,9 +238,21 @@ def prep_path(filepath: Path) -> IO:
|
|
|
227
238
|
|
|
228
239
|
For non-stdout paths, parent directories are created automatically if they don't
|
|
229
240
|
exist. This absorbs the `mkdir -p` step that workflows previously had to do.
|
|
241
|
+
|
|
242
|
+
```{note}
|
|
243
|
+
When stdout is a captured in-memory stream with no backing file descriptor
|
|
244
|
+
(Click's test runner, the Sphinx `{click:run}` directive that live-renders CLI
|
|
245
|
+
output in the docs), `fileno()` raises and we write to the stream directly. Such
|
|
246
|
+
streams are already Python text objects, so the Windows `cp1252` concern does not
|
|
247
|
+
apply: that only bites a real terminal, which always has a descriptor.
|
|
248
|
+
```
|
|
230
249
|
"""
|
|
231
250
|
if is_stdout(filepath):
|
|
232
|
-
|
|
251
|
+
try:
|
|
252
|
+
fd = sys.stdout.fileno()
|
|
253
|
+
except (OSError, ValueError):
|
|
254
|
+
return sys.stdout
|
|
255
|
+
return open(fd, "w", encoding="UTF-8", closefd=False)
|
|
233
256
|
filepath.parent.mkdir(parents=True, exist_ok=True)
|
|
234
257
|
return filepath.open("w", encoding="UTF-8")
|
|
235
258
|
|
|
@@ -749,7 +772,12 @@ def metadata(ctx, format, overwrite, output, list_keys, keys):
|
|
|
749
772
|
)
|
|
750
773
|
|
|
751
774
|
if is_stdout(output):
|
|
752
|
-
|
|
775
|
+
# The overwrite flag is moot for stdout. Warn only when the user set it
|
|
776
|
+
# explicitly: firing on the default value warns on every bare stdout run.
|
|
777
|
+
if (
|
|
778
|
+
overwrite
|
|
779
|
+
and ctx.get_parameter_source("overwrite") is not ParameterSource.DEFAULT
|
|
780
|
+
):
|
|
753
781
|
logging.warning("Ignore the --overwrite/--force/--replace option.")
|
|
754
782
|
logging.info(f"Print metadata to {sys.stdout.name}")
|
|
755
783
|
else:
|
|
@@ -766,7 +794,9 @@ def metadata(ctx, format, overwrite, output, list_keys, keys):
|
|
|
766
794
|
meta = Metadata()
|
|
767
795
|
|
|
768
796
|
# Output a warning in GitHub runners if metadata are not saved to $GITHUB_OUTPUT.
|
|
769
|
-
|
|
797
|
+
# Skip the warning for stdout: writing to stdout is a legitimate use case
|
|
798
|
+
# (human inspection, --format json piping, Sphinx docs rendering) even in CI.
|
|
799
|
+
if is_github_ci() and not is_stdout(output):
|
|
770
800
|
env_file = os.getenv("GITHUB_OUTPUT")
|
|
771
801
|
if env_file and Path(env_file) != output:
|
|
772
802
|
logging.warning(
|
|
@@ -1449,6 +1479,7 @@ def sync_mailmap(ctx, source, create_if_missing, destination_mailmap):
|
|
|
1449
1479
|
default=False,
|
|
1450
1480
|
help="Exit instantly on first failed test.",
|
|
1451
1481
|
)
|
|
1482
|
+
@jobs_option
|
|
1452
1483
|
@option(
|
|
1453
1484
|
"-T",
|
|
1454
1485
|
"--timeout",
|
|
@@ -1490,7 +1521,15 @@ def test_plan(
|
|
|
1490
1521
|
(--plan-envvar), [tool.repomatic] config, or a built-in default.
|
|
1491
1522
|
Each test invokes the command with the specified arguments and validates
|
|
1492
1523
|
the output against expected patterns.
|
|
1524
|
+
|
|
1525
|
+
Cases run in parallel by default (see --jobs): each is an independent
|
|
1526
|
+
process invocation, so they overlap well. Pass --jobs 1 for sequential
|
|
1527
|
+
execution, which lets --exit-on-error stop on the first failure.
|
|
1493
1528
|
"""
|
|
1529
|
+
# click-extra's --jobs option stores its clamped worker count on the
|
|
1530
|
+
# context; it does not drive concurrency itself, so we read and act on it.
|
|
1531
|
+
worker_count = context.get(ctx, context.JOBS, 1)
|
|
1532
|
+
|
|
1494
1533
|
# Load [tool.repomatic] config for fallback values.
|
|
1495
1534
|
config = get_tool_config(ctx)
|
|
1496
1535
|
|
|
@@ -1540,16 +1579,21 @@ def test_plan(
|
|
|
1540
1579
|
|
|
1541
1580
|
counter = Counter(total=len(test_list), skipped=0, failed=0)
|
|
1542
1581
|
|
|
1582
|
+
# Select the cases to run (respecting --select-test), keeping their 1-based
|
|
1583
|
+
# numbers for stable reporting.
|
|
1584
|
+
pending: list[tuple[int, CLITestCase]] = []
|
|
1543
1585
|
for index, test_case in enumerate(test_list):
|
|
1544
1586
|
test_number = index + 1
|
|
1545
|
-
test_name = f"#{test_number}"
|
|
1546
|
-
logging.info(f"Run test {test_name}...")
|
|
1547
|
-
|
|
1548
1587
|
if select_test and test_number not in select_test:
|
|
1549
|
-
logging.warning(f"Test {
|
|
1588
|
+
logging.warning(f"Test #{test_number} skipped by user request.")
|
|
1550
1589
|
counter["skipped"] += 1
|
|
1551
1590
|
continue
|
|
1591
|
+
pending.append((test_number, test_case))
|
|
1552
1592
|
|
|
1593
|
+
def run_case(item: tuple[int, CLITestCase]) -> tuple[int, str, CLITestCase]:
|
|
1594
|
+
"""Run one case, returning its number, outcome, and the case itself."""
|
|
1595
|
+
test_number, test_case = item
|
|
1596
|
+
logging.info(f"Run test #{test_number}...")
|
|
1553
1597
|
try:
|
|
1554
1598
|
logging.debug(f"Test case parameters: {test_case}")
|
|
1555
1599
|
test_case.run_cli_test(
|
|
@@ -1558,16 +1602,44 @@ def test_plan(
|
|
|
1558
1602
|
default_timeout=timeout,
|
|
1559
1603
|
)
|
|
1560
1604
|
except SkippedTest as ex:
|
|
1561
|
-
|
|
1562
|
-
|
|
1605
|
+
logging.warning(f"Test #{test_number} skipped: {ex}")
|
|
1606
|
+
return test_number, "skipped", test_case
|
|
1563
1607
|
except Exception as ex: # noqa: BLE001
|
|
1608
|
+
logging.error(f"Test #{test_number} failed: {ex}")
|
|
1609
|
+
return test_number, "failed", test_case
|
|
1610
|
+
return test_number, "passed", test_case
|
|
1611
|
+
|
|
1612
|
+
def tally(outcome: tuple[int, str, CLITestCase]) -> bool:
|
|
1613
|
+
"""Record an outcome in the counters and echo a failure's trace.
|
|
1614
|
+
|
|
1615
|
+
:return: `True` if the case failed.
|
|
1616
|
+
"""
|
|
1617
|
+
_, status, test_case = outcome
|
|
1618
|
+
if status == "skipped":
|
|
1619
|
+
counter["skipped"] += 1
|
|
1620
|
+
elif status == "failed":
|
|
1564
1621
|
counter["failed"] += 1
|
|
1565
|
-
logging.error(f"Test {test_name} failed: {ex}")
|
|
1566
1622
|
if show_trace_on_error and test_case.execution_trace:
|
|
1567
1623
|
echo(test_case.execution_trace)
|
|
1568
|
-
|
|
1624
|
+
return status == "failed"
|
|
1625
|
+
|
|
1626
|
+
if worker_count == 1 or len(pending) <= 1:
|
|
1627
|
+
# Sequential: --exit-on-error can short-circuit on the first failure.
|
|
1628
|
+
for item in pending:
|
|
1629
|
+
if tally(run_case(item)) and exit_on_error:
|
|
1569
1630
|
logging.debug("Don't continue testing, a failed test was found.")
|
|
1570
1631
|
ctx.exit(1)
|
|
1632
|
+
else:
|
|
1633
|
+
# Parallel: subprocess.run releases the GIL while the child runs, so
|
|
1634
|
+
# worker threads overlap each case's process spawn and execution. All
|
|
1635
|
+
# selected cases run (--exit-on-error has no effect); executor.map
|
|
1636
|
+
# yields in submission order, keeping traces and counters ordered.
|
|
1637
|
+
logging.info(
|
|
1638
|
+
f"Run {len(pending)} test cases across {worker_count} parallel workers."
|
|
1639
|
+
)
|
|
1640
|
+
with ThreadPoolExecutor(max_workers=worker_count) as executor:
|
|
1641
|
+
for outcome in executor.map(run_case, pending):
|
|
1642
|
+
tally(outcome)
|
|
1571
1643
|
|
|
1572
1644
|
if stats:
|
|
1573
1645
|
echo(
|
|
@@ -2622,9 +2694,22 @@ def verify_binary(target: str, binary_path: Path) -> None:
|
|
|
2622
2694
|
echo(f"Binary architecture verified for {target}: {binary_path}")
|
|
2623
2695
|
|
|
2624
2696
|
|
|
2697
|
+
AUDIT_HEADER_DEFS: tuple[tuple[str, str], ...] = (
|
|
2698
|
+
("Package", "package"),
|
|
2699
|
+
("Version", "version"),
|
|
2700
|
+
("Advisory", "advisory"),
|
|
2701
|
+
("Fixed", "fixed"),
|
|
2702
|
+
("Sources", "sources"),
|
|
2703
|
+
)
|
|
2704
|
+
"""Column definitions for the `repomatic audit` table."""
|
|
2705
|
+
|
|
2706
|
+
_audit_sort = SortByOption(*AUDIT_HEADER_DEFS, default="package")
|
|
2707
|
+
|
|
2708
|
+
|
|
2625
2709
|
@repomatic.command(
|
|
2626
|
-
short_help="
|
|
2627
|
-
section=
|
|
2710
|
+
short_help="Report (and optionally fix) vulnerable dependencies",
|
|
2711
|
+
section=_section_lint,
|
|
2712
|
+
params=[_audit_sort],
|
|
2628
2713
|
)
|
|
2629
2714
|
@option(
|
|
2630
2715
|
"--lockfile",
|
|
@@ -2638,58 +2723,81 @@ def verify_binary(target: str, binary_path: Path) -> None:
|
|
|
2638
2723
|
type=str,
|
|
2639
2724
|
default=lambda: os.environ.get("GITHUB_REPOSITORY", ""),
|
|
2640
2725
|
help=(
|
|
2641
|
-
"Repository in OWNER/NAME format.
|
|
2642
|
-
"
|
|
2726
|
+
"Repository in OWNER/NAME format. Enables the GitHub Advisory"
|
|
2727
|
+
" Database source. Defaults to GITHUB_REPOSITORY when set."
|
|
2728
|
+
),
|
|
2729
|
+
)
|
|
2730
|
+
@option(
|
|
2731
|
+
"--fix/--no-fix",
|
|
2732
|
+
default=False,
|
|
2733
|
+
help=(
|
|
2734
|
+
"Upgrade fixable packages and persist cooldown exemptions"
|
|
2735
|
+
" (mutates uv.lock and pyproject.toml). Default: report only."
|
|
2643
2736
|
),
|
|
2644
2737
|
)
|
|
2738
|
+
@option(
|
|
2739
|
+
"--exit-zero/--no-exit-zero",
|
|
2740
|
+
default=False,
|
|
2741
|
+
help="In report mode, exit 0 even when vulnerabilities are found.",
|
|
2742
|
+
)
|
|
2645
2743
|
@option(
|
|
2646
2744
|
"--output",
|
|
2647
2745
|
type=file_path(writable=True, resolve_path=True, allow_dash=True),
|
|
2648
2746
|
default=None,
|
|
2649
|
-
help="Write a markdown report
|
|
2747
|
+
help="Write a markdown report to this file.",
|
|
2650
2748
|
)
|
|
2651
2749
|
@output_format_option
|
|
2652
2750
|
@pass_context
|
|
2653
|
-
def
|
|
2751
|
+
def audit(
|
|
2654
2752
|
ctx: Context,
|
|
2655
2753
|
lockfile: Path,
|
|
2656
2754
|
repo: str,
|
|
2755
|
+
fix: bool,
|
|
2756
|
+
exit_zero: bool,
|
|
2657
2757
|
output: Path | None,
|
|
2658
2758
|
output_format: str,
|
|
2659
2759
|
) -> None:
|
|
2660
|
-
"""
|
|
2760
|
+
"""Scan locked dependencies for known security vulnerabilities.
|
|
2661
2761
|
|
|
2662
2762
|
\b
|
|
2663
|
-
|
|
2763
|
+
Read-only by default: queries every advisory database enabled in
|
|
2664
2764
|
[tool.repomatic] vulnerable-deps.sources (default: uv-audit and
|
|
2665
|
-
github-advisories), unions and deduplicates the results,
|
|
2666
|
-
|
|
2765
|
+
github-advisories), unions and deduplicates the results, and prints
|
|
2766
|
+
them. The table respects the global --table-format option (github,
|
|
2767
|
+
json, csv, etc.), so --table-format json yields a machine-readable
|
|
2768
|
+
report. Report mode exits 1 when any vulnerability is found (use
|
|
2769
|
+
--exit-zero to override), so it can gate CI.
|
|
2770
|
+
|
|
2771
|
+
\b
|
|
2772
|
+
With --fix, upgrades each fixable package with uv lock --upgrade-package
|
|
2773
|
+
and:
|
|
2667
2774
|
- bypasses the exclude-newer cooldown for security fixes
|
|
2668
2775
|
- persists exclude-newer-package entries in pyproject.toml
|
|
2669
2776
|
- prints a markdown report of vulnerabilities and version changes
|
|
2670
2777
|
|
|
2671
2778
|
\b
|
|
2672
2779
|
Examples:
|
|
2673
|
-
#
|
|
2674
|
-
repomatic
|
|
2780
|
+
# Report known vulnerabilities (read-only)
|
|
2781
|
+
repomatic audit
|
|
2675
2782
|
|
|
2676
2783
|
\b
|
|
2677
|
-
#
|
|
2678
|
-
repomatic
|
|
2784
|
+
# Machine-readable output
|
|
2785
|
+
repomatic --table-format json audit
|
|
2679
2786
|
|
|
2680
2787
|
\b
|
|
2681
|
-
#
|
|
2682
|
-
repomatic fix
|
|
2788
|
+
# Upgrade fixable packages (mutates uv.lock and pyproject.toml)
|
|
2789
|
+
repomatic audit --fix
|
|
2790
|
+
|
|
2791
|
+
\b
|
|
2792
|
+
# CI autofix job: write the markdown report as a step output
|
|
2793
|
+
repomatic audit --fix --repo owner/name \\
|
|
2683
2794
|
--output "$GITHUB_OUTPUT" --output-format github-actions
|
|
2684
2795
|
"""
|
|
2685
2796
|
config = get_tool_config(ctx)
|
|
2686
|
-
if not config.vulnerable_deps.sync:
|
|
2687
|
-
logging.info(
|
|
2688
|
-
"[tool.repomatic] vulnerable-deps.sync is disabled."
|
|
2689
|
-
" Skipping vulnerability scan."
|
|
2690
|
-
)
|
|
2691
|
-
ctx.exit(0)
|
|
2692
2797
|
|
|
2798
|
+
# Parse the configured advisory sources, dropping the GitHub Advisory
|
|
2799
|
+
# Database when neither --repo nor GITHUB_REPOSITORY is available to
|
|
2800
|
+
# query it.
|
|
2693
2801
|
sources: list[AdvisorySource] = []
|
|
2694
2802
|
for raw in config.vulnerable_deps.sources:
|
|
2695
2803
|
try:
|
|
@@ -2706,28 +2814,67 @@ def fix_vulnerable_deps_cmd(
|
|
|
2706
2814
|
)
|
|
2707
2815
|
sources = [s for s in sources if s is not AdvisorySource.GITHUB_ADVISORIES]
|
|
2708
2816
|
|
|
2709
|
-
|
|
2817
|
+
if fix:
|
|
2818
|
+
# vulnerable-deps.sync gates the autofix (mutation), not reporting.
|
|
2819
|
+
if not config.vulnerable_deps.sync:
|
|
2820
|
+
logging.info(
|
|
2821
|
+
"[tool.repomatic] vulnerable-deps.sync is disabled. Skipping --fix."
|
|
2822
|
+
)
|
|
2823
|
+
ctx.exit(0)
|
|
2824
|
+
|
|
2825
|
+
has_fixes, diff_table = _fix_vulnerable_deps(
|
|
2826
|
+
lockfile, repo=repo or None, sources=sources or None
|
|
2827
|
+
)
|
|
2828
|
+
if not has_fixes:
|
|
2829
|
+
echo("No fixable vulnerabilities found.")
|
|
2830
|
+
ctx.exit(0)
|
|
2831
|
+
|
|
2832
|
+
echo("Upgraded vulnerable packages.")
|
|
2833
|
+
if diff_table:
|
|
2834
|
+
echo(diff_table)
|
|
2835
|
+
if output and diff_table:
|
|
2836
|
+
# Keep the github-actions key as `diff_table`: the autofix
|
|
2837
|
+
# workflow's pr-metadata step reads steps.fix.outputs.diff_table.
|
|
2838
|
+
if output_format == "github-actions":
|
|
2839
|
+
content = format_multiline_output("diff_table", diff_table)
|
|
2840
|
+
else:
|
|
2841
|
+
content = diff_table
|
|
2842
|
+
echo(content, file=prep_path(output))
|
|
2843
|
+
ctx.exit(0)
|
|
2844
|
+
|
|
2845
|
+
# Report mode (default, read-only).
|
|
2846
|
+
vulns = collect_vulnerable_packages(
|
|
2710
2847
|
lockfile, repo=repo or None, sources=sources or None
|
|
2711
2848
|
)
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
echo("No fixable vulnerabilities found.")
|
|
2849
|
+
if not vulns:
|
|
2850
|
+
echo("No known vulnerabilities found.")
|
|
2715
2851
|
ctx.exit(0)
|
|
2716
2852
|
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2853
|
+
rows = [
|
|
2854
|
+
(
|
|
2855
|
+
v.name,
|
|
2856
|
+
v.current_version,
|
|
2857
|
+
v.advisory_id,
|
|
2858
|
+
v.fixed_version or "unknown",
|
|
2859
|
+
", ".join(sorted(s.value for s in v.sources)) or "—",
|
|
2860
|
+
)
|
|
2861
|
+
for v in vulns
|
|
2862
|
+
]
|
|
2863
|
+
ctx.print_table(AUDIT_HEADER_DEFS, rows) # type: ignore[attr-defined]
|
|
2720
2864
|
|
|
2721
|
-
if output
|
|
2865
|
+
if output:
|
|
2866
|
+
markdown = format_vulnerability_table(vulns)
|
|
2722
2867
|
if output_format == "github-actions":
|
|
2723
|
-
content = format_multiline_output("
|
|
2868
|
+
content = format_multiline_output("vuln_table", markdown)
|
|
2724
2869
|
else:
|
|
2725
|
-
content =
|
|
2870
|
+
content = markdown
|
|
2726
2871
|
echo(content, file=prep_path(output))
|
|
2727
2872
|
|
|
2873
|
+
ctx.exit(0 if exit_zero else 1)
|
|
2874
|
+
|
|
2728
2875
|
|
|
2729
2876
|
@repomatic.command(
|
|
2730
|
-
short_help="Re-lock dependencies and
|
|
2877
|
+
short_help="Re-lock dependencies and roll cooldown overrides forward",
|
|
2731
2878
|
section=_section_sync,
|
|
2732
2879
|
)
|
|
2733
2880
|
@option(
|
|
@@ -2766,8 +2913,10 @@ def sync_uv_lock_cmd(
|
|
|
2766
2913
|
|
|
2767
2914
|
\b
|
|
2768
2915
|
Wraps uv lock --upgrade and:
|
|
2769
|
-
- prunes
|
|
2770
|
-
|
|
2916
|
+
- prunes exclude-newer-package entries from pyproject.toml whose held
|
|
2917
|
+
version has aged past the exclude-newer cutoff, then freezes the
|
|
2918
|
+
survivors at their locked version (a fixed date) so the upgrade
|
|
2919
|
+
holds them instead of tracking newer releases
|
|
2771
2920
|
- prints a table of updated packages with upload dates
|
|
2772
2921
|
- optionally fetches release notes from GitHub (markdown)
|
|
2773
2922
|
|
|
@@ -284,6 +284,30 @@ class TestMatrixConfig:
|
|
|
284
284
|
Additive to the upstream default excludes.
|
|
285
285
|
"""
|
|
286
286
|
|
|
287
|
+
full_include: list[dict[str, str]] = field(
|
|
288
|
+
default_factory=list,
|
|
289
|
+
metadata={"click_extra.config_path": "full-include"},
|
|
290
|
+
)
|
|
291
|
+
"""Full-matrix-only job rows, added as standalone matrix combinations.
|
|
292
|
+
|
|
293
|
+
Each entry is a dict of GitHub Actions matrix keys fully describing one job
|
|
294
|
+
(like `{"os": "ubuntu-24.04-arm", "python-version": "3.10",
|
|
295
|
+
"click-version": "8.3.1"}`). Unlike `include`, these are appended as
|
|
296
|
+
independent rows of the full matrix, never merged into the base
|
|
297
|
+
cross-product, so a cell can't overwrite a shipped-config job that shares
|
|
298
|
+
its `os` and `python-version`. Keys left out inherit the matrix defaults
|
|
299
|
+
(the single-key `include` entries, plus `state: stable`), so a cell lists
|
|
300
|
+
only what differs from the shipped configuration.
|
|
301
|
+
|
|
302
|
+
Use this for heterogeneous coverage, like pinning each release of a
|
|
303
|
+
dependency to its own runner and Python, where carving the same shape from
|
|
304
|
+
the base cross-product with `exclude` would take many rules. Like
|
|
305
|
+
`variations` and `unstable`, it touches the full matrix only; the PR matrix
|
|
306
|
+
stays a curated reduced set. Adding any entry makes the full matrix emit as
|
|
307
|
+
a flat job list (`{"include": [...]}`), which GitHub runs verbatim with no
|
|
308
|
+
cross-product expansion.
|
|
309
|
+
"""
|
|
310
|
+
|
|
287
311
|
include: list[dict[str, str]] = field(default_factory=list)
|
|
288
312
|
"""Extra include directives applied to both full and PR test matrices.
|
|
289
313
|
|