repomatic 7.6.0__tar.gz → 7.7.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-7.6.0 → repomatic-7.7.0}/PKG-INFO +2 -2
- {repomatic-7.6.0 → repomatic-7.7.0}/pyproject.toml +10 -6
- {repomatic-7.6.0 → repomatic-7.7.0}/pyproject.toml.orig +28 -8
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/__init__.py +2 -2
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/__main__.py +9 -8
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/awesome_toc.py +3 -3
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/binaries_page.py +36 -68
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/binary.py +119 -7
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/broken_links.py +34 -14
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/cache.py +51 -7
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/changelog.py +257 -117
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/checksums.py +11 -5
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/cli.py +124 -292
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/config.py +6 -3
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/data/_release-engine.yaml +47 -25
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/data/autofix.yaml +57 -47
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/data/cancel-runs.yaml +1 -1
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/data/changelog.yaml +14 -14
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/data/debug.yaml +2 -2
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/data/docs.yaml +6 -4
- repomatic-7.7.0/repomatic/data/labeller-content-based.yaml +31 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/data/labels.yaml +12 -10
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/data/lint.yaml +18 -14
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/data/release.yaml +15 -5
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/data/skills/awesome-triage/SKILL.md +10 -10
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/data/skills/babysit-ci/SKILL.md +10 -4
- repomatic-7.7.0/repomatic/data/skills/github-housekeeping/SKILL.md +129 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/data/skills/repomatic-audit/SKILL.md +1 -1
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/data/skills/repomatic-ship/SKILL.md +18 -2
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/data/tests.yaml +8 -5
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/data/unsubscribe.yaml +1 -1
- repomatic-7.7.0/repomatic/data/vt-trend-chart.js +52 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/dep_graph.py +14 -10
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/dep_report.py +139 -64
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/dep_sources.py +45 -57
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/docs.py +28 -16
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/git_ops.py +12 -5
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/github/actions.py +24 -1
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/github/dev_release.py +30 -7
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/github/gh.py +24 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/github/issue.py +14 -5
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/github/matrix.py +35 -6
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/github/release_sync.py +8 -27
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/github/releases.py +6 -22
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/github/sponsor.py +31 -24
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/github/token.py +26 -13
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/github/unsubscribe.py +9 -27
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/github/workflow_sync.py +126 -226
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/images.py +68 -60
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/init_project.py +189 -149
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/labels.py +39 -20
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/lint_repo.py +304 -70
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/mailmap.py +24 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/matrix_axes.py +0 -7
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/metadata.py +194 -136
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/npm.py +5 -1
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/plugin.py +16 -6
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/prepare_release.py +72 -76
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/pypi.py +94 -47
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/pyproject.py +40 -1
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/registry.py +49 -13
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/setup_guide.py +19 -8
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/sync_ops.py +205 -15
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/templates/yanked-admonition.md +2 -2
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/tool_registry.py +85 -3
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/tool_runner.py +182 -127
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/tool_runner_page.py +7 -19
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/uv.py +199 -106
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/version_sync.py +73 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/virustotal.py +9 -2
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/vulnerable_deps.py +29 -12
- repomatic-7.6.0/repomatic/data/labeller-content-based.yaml +0 -37
- {repomatic-7.6.0 → repomatic-7.7.0}/license +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/readme.md +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/bundle.py +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/compat.py +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/data/__init__.py +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/data/action-publish-pypi.yaml +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/data/agent-grunt-qa.md +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/data/agent-qa-engineer.md +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/data/agent-sphinx-docs.md +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/data/autolock.yaml +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/data/awesome_template/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/data/awesome_template/.github/ISSUE_TEMPLATE/new-link.yaml +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/data/awesome_template/.github/code-of-conduct.md +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/data/awesome_template/.github/contributing.md +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/data/awesome_template/.github/contributing.zh.md +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/data/awesome_template/.github/funding.yml +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/data/awesome_template/.github/pull_request_template.md +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/data/awesome_template/__init__.py +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/data/awesome_template/license +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/data/bumpversion.toml +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/data/codecov.yaml +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/data/labeller-file-based.yaml +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/data/labels.toml +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/data/lychee.toml +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/data/mdformat.toml +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/data/mypy.toml +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/data/pytest.toml +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/data/ruff.toml +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/data/skills/av-false-positive/SKILL.md +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/data/skills/benchmark-update/SKILL.md +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/data/skills/brand-assets/SKILL.md +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/data/skills/file-bug-report/SKILL.md +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/data/skills/repomatic-changelog/SKILL.md +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/data/skills/repomatic-deps/SKILL.md +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/data/skills/repomatic-init/SKILL.md +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/data/skills/repomatic-topics/SKILL.md +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/data/skills/sphinx-docs-sync/SKILL.md +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/data/skills/translation-sync/SKILL.md +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/data/skills/upstream-audit/SKILL.md +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/data/typos.toml +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/data/uv.toml +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/data/yamllint.yaml +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/data/zizmor.yaml +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/frontmatter.py +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/github/__init__.py +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/github/pr_body.py +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/github/status.py +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/gitignore.py +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/http.py +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/humanize.py +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/py.typed +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/templates/__init__.py +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/templates/available-admonition.md +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/templates/broken-links-issue.md +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/templates/bump-version.md +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/templates/detect-squash-merge.md +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/templates/development-warning.md +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/templates/fix-changelog.md +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/templates/fix-typos.md +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/templates/fix-vulnerable-deps.md +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/templates/format-images.md +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/templates/format-json.md +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/templates/format-markdown.md +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/templates/format-pyproject.md +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/templates/format-python.md +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/templates/format-shell.md +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/templates/generated-footer.md +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/templates/github-releases.md +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/templates/immutable-releases.md +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/templates/pr-metadata.md.noformat +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/templates/prepare-release.md.noformat +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/templates/refresh-tip.md +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/templates/release-notes.md +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/templates/release-sync-report.md +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/templates/setup-guide-branch-ruleset.md +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/templates/setup-guide-dependabot.md +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/templates/setup-guide-fork-pr-approval.md +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/templates/setup-guide-notifications-pat.md +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/templates/setup-guide-pages-source.md +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/templates/setup-guide-pypi-trusted-publisher.md +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/templates/setup-guide-sha-pinning-required.md +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/templates/setup-guide-token.md +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/templates/setup-guide-verify.md +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/templates/setup-guide-virustotal.md +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/templates/setup-guide.md +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/templates/sync-action-pins.md +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/templates/sync-bumpversion.md +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/templates/sync-dep-sources.md +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/templates/sync-gitignore.md +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/templates/sync-mailmap.md +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/templates/sync-repomatic.md +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/templates/sync-tool-versions.md +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/templates/sync-uv-lock.md +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/templates/sync-workflow-pins.md +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/templates/unavailable-admonition.md +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/templates/unsubscribe-phase1.md +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/templates/unsubscribe-phase2.md +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/templates/update-dep-graph.md +0 -0
- {repomatic-7.6.0 → repomatic-7.7.0}/repomatic/templates/update-docs.md +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: repomatic
|
|
3
|
-
Version: 7.
|
|
3
|
+
Version: 7.7.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,repository-automation,repository-management,reusable-workflows,software-factory,sphinx-doc,typo,uv,workflow-reusable
|
|
6
6
|
Author: Kevin Deldycke
|
|
@@ -59,7 +59,7 @@ Requires-Dist: wcmatch>=10
|
|
|
59
59
|
Requires-Python: >=3.10
|
|
60
60
|
Project-URL: Changelog, https://github.com/kdeldycke/repomatic/blob/main/changelog.md
|
|
61
61
|
Project-URL: Documentation, https://kdeldycke.github.io/repomatic
|
|
62
|
-
Project-URL: Download, https://github.com/kdeldycke/repomatic/releases/tag/v7.
|
|
62
|
+
Project-URL: Download, https://github.com/kdeldycke/repomatic/releases/tag/v7.7.0
|
|
63
63
|
Project-URL: Funding, https://github.com/sponsors/kdeldycke
|
|
64
64
|
Project-URL: Homepage, https://github.com/kdeldycke/repomatic
|
|
65
65
|
Project-URL: Issues, https://github.com/kdeldycke/repomatic/issues
|
|
@@ -4,7 +4,7 @@ requires = ["uv-build>=0.8"]
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "repomatic"
|
|
7
|
-
version = "7.
|
|
7
|
+
version = "7.7.0"
|
|
8
8
|
description = "🏭 Automate repository maintenance, releases, and CI/CD workflows"
|
|
9
9
|
readme = "readme.md"
|
|
10
10
|
keywords = [
|
|
@@ -92,7 +92,7 @@ email = "kevin@deldycke.com"
|
|
|
92
92
|
[project.urls]
|
|
93
93
|
Changelog = "https://github.com/kdeldycke/repomatic/blob/main/changelog.md"
|
|
94
94
|
Documentation = "https://kdeldycke.github.io/repomatic"
|
|
95
|
-
Download = "https://github.com/kdeldycke/repomatic/releases/tag/v7.
|
|
95
|
+
Download = "https://github.com/kdeldycke/repomatic/releases/tag/v7.7.0"
|
|
96
96
|
Funding = "https://github.com/sponsors/kdeldycke"
|
|
97
97
|
Homepage = "https://github.com/kdeldycke/repomatic"
|
|
98
98
|
Issues = "https://github.com/kdeldycke/repomatic/issues"
|
|
@@ -146,7 +146,7 @@ module-root = ""
|
|
|
146
146
|
[tool.nuitka]
|
|
147
147
|
copyright = "Kevin Deldycke <kevin@deldycke.com> and contributors. Distributed under GPL-2.0-or-later license."
|
|
148
148
|
file-description = "🏭 Automate repository maintenance, releases, and CI/CD workflows"
|
|
149
|
-
file-version = "7.
|
|
149
|
+
file-version = "7.7.0"
|
|
150
150
|
include-data-dir = [
|
|
151
151
|
"repomatic/data/awesome_template=repomatic/data/awesome_template",
|
|
152
152
|
"repomatic/data/skills=repomatic/data/skills",
|
|
@@ -156,7 +156,7 @@ include-package-data = ["click_extra"]
|
|
|
156
156
|
linux-icon = "docs/assets/icon.png"
|
|
157
157
|
macos-app-icon = "docs/assets/icon.icns"
|
|
158
158
|
product-name = "Repomatic"
|
|
159
|
-
product-version = "7.
|
|
159
|
+
product-version = "7.7.0"
|
|
160
160
|
windows-icon-from-ico = "docs/assets/icon.ico"
|
|
161
161
|
|
|
162
162
|
[tool.ruff]
|
|
@@ -208,6 +208,7 @@ astroid = "astroid"
|
|
|
208
208
|
Contribuer = "Contribuer"
|
|
209
209
|
oject = "oject"
|
|
210
210
|
PNGs = "PNGs"
|
|
211
|
+
quater = "quater"
|
|
211
212
|
sur = "sur"
|
|
212
213
|
|
|
213
214
|
[tool.mypy]
|
|
@@ -248,7 +249,7 @@ source = ["repomatic"]
|
|
|
248
249
|
precision = 2
|
|
249
250
|
|
|
250
251
|
[tool.bumpversion]
|
|
251
|
-
current_version = "7.
|
|
252
|
+
current_version = "7.7.0"
|
|
252
253
|
parse = '(?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)(\.dev(?P<dev>\d+))?'
|
|
253
254
|
serialize = [
|
|
254
255
|
"{major}.{minor}.{patch}.dev{dev}",
|
|
@@ -333,10 +334,13 @@ pypi-package-history = [
|
|
|
333
334
|
"gha-utils",
|
|
334
335
|
"repokit",
|
|
335
336
|
]
|
|
336
|
-
release-assets = ["repomatic-plugin.zip"]
|
|
337
|
+
release-assets = ["repomatic-claude-plugin.zip"]
|
|
337
338
|
|
|
338
339
|
[tool.repomatic.changelog]
|
|
339
340
|
archive-location = "./docs/changelog-archive.md"
|
|
340
341
|
|
|
341
342
|
[tool.repomatic.manpages]
|
|
342
343
|
script = "repomatic.cli:repomatic"
|
|
344
|
+
|
|
345
|
+
[tool.actionlint.paths."**/workflows/*"]
|
|
346
|
+
ignore = ['property "job_workflow_sha" is not defined']
|
|
@@ -12,7 +12,7 @@ requires = [ "uv-build>=0.8" ]
|
|
|
12
12
|
[project]
|
|
13
13
|
# Docs: https://packaging.python.org/en/latest/guides/writing-pyproject-toml/
|
|
14
14
|
name = "repomatic"
|
|
15
|
-
version = "7.
|
|
15
|
+
version = "7.7.0"
|
|
16
16
|
description = "🏭 Automate repository maintenance, releases, and CI/CD workflows"
|
|
17
17
|
readme = "readme.md"
|
|
18
18
|
keywords = [
|
|
@@ -145,7 +145,7 @@ dependencies = [
|
|
|
145
145
|
]
|
|
146
146
|
urls.Changelog = "https://github.com/kdeldycke/repomatic/blob/main/changelog.md"
|
|
147
147
|
urls.Documentation = "https://kdeldycke.github.io/repomatic"
|
|
148
|
-
urls.Download = "https://github.com/kdeldycke/repomatic/releases/tag/v7.
|
|
148
|
+
urls.Download = "https://github.com/kdeldycke/repomatic/releases/tag/v7.7.0"
|
|
149
149
|
urls.Funding = "https://github.com/sponsors/kdeldycke"
|
|
150
150
|
urls.Homepage = "https://github.com/kdeldycke/repomatic"
|
|
151
151
|
urls.Issues = "https://github.com/kdeldycke/repomatic/issues"
|
|
@@ -250,7 +250,7 @@ copyright = "Kevin Deldycke <kevin@deldycke.com> and contributors. Distributed u
|
|
|
250
250
|
file-description = "🏭 Automate repository maintenance, releases, and CI/CD workflows"
|
|
251
251
|
# Numeric only: Nuitka rejects PEP 440 .devN suffixes. Kept in sync with the
|
|
252
252
|
# package version by the dedicated [tool.bumpversion] rule that strips the suffix.
|
|
253
|
-
file-version = "7.
|
|
253
|
+
file-version = "7.7.0"
|
|
254
254
|
include-data-dir = [
|
|
255
255
|
"repomatic/data/awesome_template=repomatic/data/awesome_template",
|
|
256
256
|
"repomatic/data/skills=repomatic/data/skills",
|
|
@@ -269,7 +269,7 @@ include-package-data = [ "click_extra" ]
|
|
|
269
269
|
linux-icon = "docs/assets/icon.png"
|
|
270
270
|
macos-app-icon = "docs/assets/icon.icns"
|
|
271
271
|
product-name = "Repomatic"
|
|
272
|
-
product-version = "7.
|
|
272
|
+
product-version = "7.7.0"
|
|
273
273
|
windows-icon-from-ico = "docs/assets/icon.ico"
|
|
274
274
|
|
|
275
275
|
[tool.ruff]
|
|
@@ -310,8 +310,11 @@ output-prefer-rule-codes = true
|
|
|
310
310
|
[tool.typos]
|
|
311
311
|
default.extend-identifiers = { Github = "GitHub", IOS = "iOS", Javascript = "JavaScript", MacOS = "macOS", PNGs = "PNGs", PyPi = "PyPI", Typescript = "TypeScript" }
|
|
312
312
|
# False positives. "Contribuer" is the French for "Contributing", part of the
|
|
313
|
-
# translated-readme fixture in tests/test_awesome_toc.py.
|
|
314
|
-
|
|
313
|
+
# translated-readme fixture in tests/test_awesome_toc.py. "quater" is the fourth
|
|
314
|
+
# term of the Latin ordinal series numbering the repo-lint checks (bis, ter,
|
|
315
|
+
# quater), not a misspelling of "quarter": the unattended fix-typos job rewrote
|
|
316
|
+
# it once already and had to be reverted by hand.
|
|
317
|
+
default.extend-words = { astroid = "astroid", Contribuer = "Contribuer", oject = "oject", PNGs = "PNGs", quater = "quater", sur = "sur" }
|
|
315
318
|
# Skip content between <!-- typos:off --> and <!-- typos:on --> markers.
|
|
316
319
|
default.extend-ignore-re = [
|
|
317
320
|
"(?s)<!-- typos:off -->.*?<!-- typos:on -->",
|
|
@@ -395,7 +398,7 @@ run.source = [ "repomatic" ]
|
|
|
395
398
|
report.precision = 2
|
|
396
399
|
|
|
397
400
|
[tool.bumpversion]
|
|
398
|
-
current_version = "7.
|
|
401
|
+
current_version = "7.7.0"
|
|
399
402
|
# Parse versions with an optional .devN suffix (PEP 440).
|
|
400
403
|
parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)(\\.dev(?P<dev>\\d+))?"
|
|
401
404
|
serialize = [
|
|
@@ -522,4 +525,21 @@ pypi-package-history = [ "gha-utils", "repokit" ]
|
|
|
522
525
|
# attached to every GitHub release by the engine's extra-assets job. The
|
|
523
526
|
# marketplace at .claude-plugin/marketplace.json installs the plugin straight
|
|
524
527
|
# from this asset, so a release missing it breaks `/plugin install`.
|
|
525
|
-
release-assets = [ "repomatic-plugin.zip" ]
|
|
528
|
+
release-assets = [ "repomatic-claude-plugin.zip" ]
|
|
529
|
+
|
|
530
|
+
[tool.actionlint]
|
|
531
|
+
# actionlint reads no pyproject section of its own: repomatic serializes this one
|
|
532
|
+
# to a temporary YAML config and passes it via `--config-file`. `paths` keys are
|
|
533
|
+
# globs relative to the repository root, and each `ignore` entry is a regular
|
|
534
|
+
# expression matched against the error message.
|
|
535
|
+
#
|
|
536
|
+
# `github.job_workflow_sha` is a real context property, holding the commit SHA of
|
|
537
|
+
# the reusable workflow file itself, which the tool-cache keys read so they do not
|
|
538
|
+
# rotate on every caller commit. GitHub documents it only as an OIDC token claim,
|
|
539
|
+
# never on the `github` context reference page, so actionlint's schema lacks it.
|
|
540
|
+
# Drop this entry once rhysd/actionlint#671 ships in a release.
|
|
541
|
+
#
|
|
542
|
+
# The glob carries no dot on purpose: click-extra expands every dotted key of
|
|
543
|
+
# `pyproject.toml`, and one starting with a dot makes it warn on each repomatic
|
|
544
|
+
# invocation. `**` matches dot-directories, so `.github/workflows/` is covered.
|
|
545
|
+
paths."**/workflows/*".ignore = [ 'property "job_workflow_sha" is not defined' ]
|
|
@@ -35,21 +35,22 @@ from repomatic.cli import repomatic
|
|
|
35
35
|
|
|
36
36
|
|
|
37
37
|
def main():
|
|
38
|
-
"""Execute the CLI
|
|
38
|
+
"""Execute the CLI under its canonical name, whatever the entry point.
|
|
39
|
+
|
|
40
|
+
Without `prog_name`, Click derives the displayed name from the invocation:
|
|
39
41
|
|
|
40
42
|
```{code-block} shell-session
|
|
41
43
|
$ python -m repomatic --version
|
|
42
44
|
python -m repomatic, version 4.0.0
|
|
43
45
|
```
|
|
44
46
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
- plain inline package call: `python -m repomatic`,
|
|
48
|
-
- Poetry's script entry point: `repomatic = 'repomatic.__main__:main`,
|
|
49
|
-
- Nuitka's main module invocation requirement:
|
|
50
|
-
`python -m nuitka (...) repomatic/__main__.py`
|
|
47
|
+
The `main()` indirection lets three invocation styles share one entry
|
|
48
|
+
point rendering the same name:
|
|
51
49
|
|
|
52
|
-
|
|
50
|
+
- the plain module call: `python -m repomatic`,
|
|
51
|
+
- the `[project.scripts]` console script: `repomatic = "repomatic.__main__:main"`,
|
|
52
|
+
- Nuitka's main-module compilation requirement:
|
|
53
|
+
`python -m nuitka (...) repomatic/__main__.py`.
|
|
53
54
|
"""
|
|
54
55
|
repomatic(prog_name=repomatic.name)
|
|
55
56
|
|
|
@@ -235,7 +235,7 @@ def fix_awesome_toc(root: Path | None = None) -> dict[Path, list[str]]:
|
|
|
235
235
|
reference = root / REFERENCE_README
|
|
236
236
|
reference_headings: list[str] | None = None
|
|
237
237
|
if reference.is_file():
|
|
238
|
-
reference_headings = headings(reference.read_text(encoding="
|
|
238
|
+
reference_headings = headings(reference.read_text(encoding="UTF-8"))
|
|
239
239
|
else:
|
|
240
240
|
logging.warning(
|
|
241
241
|
f"No {REFERENCE_README} in {root}: translations can only be matched "
|
|
@@ -244,13 +244,13 @@ def fix_awesome_toc(root: Path | None = None) -> dict[Path, list[str]]:
|
|
|
244
244
|
|
|
245
245
|
report: dict[Path, list[str]] = {}
|
|
246
246
|
for readme in _readmes(root):
|
|
247
|
-
content = readme.read_text(encoding="
|
|
247
|
+
content = readme.read_text(encoding="UTF-8")
|
|
248
248
|
forbidden = forbidden_headings_for(content, reference_headings)
|
|
249
249
|
updated, removed = strip_toc_entries(content, forbidden)
|
|
250
250
|
if not removed:
|
|
251
251
|
logging.debug(f"No forbidden ToC entry in {readme}.")
|
|
252
252
|
continue
|
|
253
|
-
readme.write_text(updated, encoding="
|
|
253
|
+
readme.write_text(updated, encoding="UTF-8")
|
|
254
254
|
logging.info(f"Removed {len(removed)} ToC entries from {readme}.")
|
|
255
255
|
report[readme] = removed
|
|
256
256
|
return report
|
|
@@ -57,7 +57,8 @@ from pathlib import Path
|
|
|
57
57
|
|
|
58
58
|
from click_extra.blocks import replace_region
|
|
59
59
|
|
|
60
|
-
from .binary import NUITKA_BUILD_TARGETS
|
|
60
|
+
from .binary import BINARY_ASSET_SUFFIXES, NUITKA_BUILD_TARGETS
|
|
61
|
+
from .bundle import get_data_content
|
|
61
62
|
from .github.releases import parse_release_version
|
|
62
63
|
from .virustotal import VIRUSTOTAL_GUI_URL
|
|
63
64
|
|
|
@@ -68,11 +69,21 @@ if TYPE_CHECKING:
|
|
|
68
69
|
from .github.releases import ReleaseAsset, ReleaseWithAssets
|
|
69
70
|
from .virustotal import ScanRecord
|
|
70
71
|
|
|
71
|
-
|
|
72
|
-
"""
|
|
72
|
+
CHART_JS_URL = "https://cdn.jsdelivr.net/npm/chart.js@4.5.0/dist/chart.umd.min.js"
|
|
73
|
+
"""Pinned CDN artifact drawing the detections trend chart.
|
|
73
74
|
|
|
74
|
-
|
|
75
|
-
|
|
75
|
+
The one external artifact this module publishes into every downstream
|
|
76
|
+
repository's docs, so it carries a checksum beside the pin: bump the version
|
|
77
|
+
by hand together with {data}`CHART_JS_SRI`.
|
|
78
|
+
"""
|
|
79
|
+
|
|
80
|
+
CHART_JS_SRI = "sha384-XcdcwHqIPULERb2yDEM4R0XaQKU3YnDsrTmjACBZyfdVVqjh6xQ4/DCMd7XLcA6Y"
|
|
81
|
+
"""Subresource Integrity digest of {data}`CHART_JS_URL`.
|
|
82
|
+
|
|
83
|
+
The browser refuses the script if the CDN bytes stop matching. Recompute on
|
|
84
|
+
every version bump as the sha384 of the exact artifact, verified against the
|
|
85
|
+
same file inside the npm registry tarball before trusting the CDN copy:
|
|
86
|
+
`hashlib.sha384(artifact_bytes)` then base64.
|
|
76
87
|
"""
|
|
77
88
|
|
|
78
89
|
CSV_HEADERS = (
|
|
@@ -186,13 +197,14 @@ def _binary_assets(release: ReleaseWithAssets) -> list[ReleaseAsset]:
|
|
|
186
197
|
"""Return the release's compiled binaries, sorted by filename.
|
|
187
198
|
|
|
188
199
|
Releases also carry versionless alias copies of each binary, backing the
|
|
189
|
-
stable ``releases/latest/download`` URLs (
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
200
|
+
stable ``releases/latest/download`` URLs (created by
|
|
201
|
+
{func}`~repomatic.binary.pack_binary_assets`, which
|
|
202
|
+
`release-assets` runs in the release engine). An alias shares its digest
|
|
203
|
+
with its versioned sibling: collapse each digest group onto its longest
|
|
204
|
+
name (the versioned one, since {func}`~repomatic.binary.versionless_alias`
|
|
205
|
+
only ever strips the version segment) so the catalog lists each binary
|
|
206
|
+
once. Assets without a recorded digest (uploaded before GitHub started
|
|
207
|
+
recording them, mid-2025) predate the aliases and are kept as-is.
|
|
196
208
|
"""
|
|
197
209
|
binaries = [a for a in release.assets if a.name.endswith(BINARY_ASSET_SUFFIXES)]
|
|
198
210
|
digest_groups: dict[str, list[ReleaseAsset]] = {}
|
|
@@ -275,6 +287,15 @@ def render_chart_section(records: Sequence[ScanRecord]) -> str:
|
|
|
275
287
|
],
|
|
276
288
|
sort_keys=True,
|
|
277
289
|
)
|
|
290
|
+
# The chart script ships as a bundled data file so it reads (and lints) as
|
|
291
|
+
# JavaScript instead of sixty quoted Python strings. Placeholder tokens are
|
|
292
|
+
# substituted with str.replace, never str.format: the script is full of
|
|
293
|
+
# literal braces.
|
|
294
|
+
script = (
|
|
295
|
+
get_data_content("vt-trend-chart.js")
|
|
296
|
+
.replace("__VT_TREND_PAYLOAD__", payload)
|
|
297
|
+
.replace("__VT_DANGER_PCT__", str(FLAGGED_DANGER_PCT))
|
|
298
|
+
)
|
|
278
299
|
return (
|
|
279
300
|
"## VirusTotal detections\n\n"
|
|
280
301
|
"Share of antivirus engine verdicts flagging the binaries of each "
|
|
@@ -283,62 +304,9 @@ def render_chart_section(records: Sequence[ScanRecord]) -> str:
|
|
|
283
304
|
"there up.\n\n"
|
|
284
305
|
"```{raw} html\n"
|
|
285
306
|
'<div style="height: 320px;"><canvas id="vt-trend"></canvas></div>\n'
|
|
286
|
-
'<script src="
|
|
287
|
-
'
|
|
288
|
-
"<script>\n"
|
|
289
|
-
f"const VT_TREND = {payload};\n"
|
|
290
|
-
f"const VT_DANGER_PCT = {FLAGGED_DANGER_PCT};\n"
|
|
291
|
-
"const vtCss = getComputedStyle(document.documentElement);\n"
|
|
292
|
-
"const vtColor = (name, fallback) =>\n"
|
|
293
|
-
" vtCss.getPropertyValue(name).trim() || fallback;\n"
|
|
294
|
-
"const vtTint = (p) => {\n"
|
|
295
|
-
' if (p.pct === 0) { return vtColor("--sd-color-success", "#28a745"); }\n'
|
|
296
|
-
" return p.pct >= VT_DANGER_PCT\n"
|
|
297
|
-
' ? vtColor("--sd-color-danger", "#dc3545")\n'
|
|
298
|
-
' : vtColor("--sd-color-warning", "#f0b37e");\n'
|
|
299
|
-
"};\n"
|
|
300
|
-
'new Chart(document.getElementById("vt-trend"), {\n'
|
|
301
|
-
' type: "line",\n'
|
|
302
|
-
" data: {\n"
|
|
303
|
-
" datasets: [{\n"
|
|
304
|
-
" data: VT_TREND.map((p) => ({x: Date.parse(p.date), y: p.pct})),\n"
|
|
305
|
-
' borderColor: "#88888866",\n'
|
|
306
|
-
" pointBackgroundColor: VT_TREND.map(vtTint),\n"
|
|
307
|
-
" pointBorderColor: VT_TREND.map(vtTint),\n"
|
|
308
|
-
" pointRadius: 4,\n"
|
|
309
|
-
" tension: 0.2,\n"
|
|
310
|
-
" }],\n"
|
|
311
|
-
" },\n"
|
|
312
|
-
" options: {\n"
|
|
313
|
-
" maintainAspectRatio: false,\n"
|
|
314
|
-
" plugins: {\n"
|
|
315
|
-
" legend: {display: false},\n"
|
|
316
|
-
" tooltip: {callbacks: {\n"
|
|
317
|
-
" title: (items) => VT_TREND[items[0].dataIndex].tag,\n"
|
|
318
|
-
" label: (item) => {\n"
|
|
319
|
-
" const p = VT_TREND[item.dataIndex];\n"
|
|
320
|
-
' return p.flagged + " / " + p.total\n'
|
|
321
|
-
' + " verdicts flagged (" + p.pct + "%)";\n'
|
|
322
|
-
" },\n"
|
|
323
|
-
" }},\n"
|
|
324
|
-
" },\n"
|
|
325
|
-
" scales: {\n"
|
|
326
|
-
" x: {\n"
|
|
327
|
-
' type: "linear",\n'
|
|
328
|
-
" ticks: {\n"
|
|
329
|
-
" maxTicksLimit: 8,\n"
|
|
330
|
-
" callback: (value) =>\n"
|
|
331
|
-
" new Date(value).toISOString().slice(0, 10),\n"
|
|
332
|
-
" },\n"
|
|
333
|
-
" },\n"
|
|
334
|
-
" y: {\n"
|
|
335
|
-
" beginAtZero: true,\n"
|
|
336
|
-
' title: {display: true, text: "Flagged verdicts (%)"},\n'
|
|
337
|
-
" },\n"
|
|
338
|
-
" },\n"
|
|
339
|
-
" },\n"
|
|
340
|
-
"});\n"
|
|
341
|
-
"</script>\n"
|
|
307
|
+
f'<script src="{CHART_JS_URL}" integrity="{CHART_JS_SRI}" '
|
|
308
|
+
'crossorigin="anonymous"></script>\n'
|
|
309
|
+
f"<script>\n{script}</script>\n"
|
|
342
310
|
"```"
|
|
343
311
|
)
|
|
344
312
|
|
|
@@ -26,6 +26,8 @@ from __future__ import annotations
|
|
|
26
26
|
|
|
27
27
|
import hashlib
|
|
28
28
|
import logging
|
|
29
|
+
import re
|
|
30
|
+
import shutil
|
|
29
31
|
import struct
|
|
30
32
|
from pathlib import Path
|
|
31
33
|
|
|
@@ -38,6 +40,25 @@ if TYPE_CHECKING:
|
|
|
38
40
|
from typing import Final
|
|
39
41
|
|
|
40
42
|
|
|
43
|
+
BINARY_ASSET_SUFFIXES = (".bin", ".exe")
|
|
44
|
+
"""File extensions identifying compiled binaries among release assets.
|
|
45
|
+
|
|
46
|
+
The one definition of "a compiled release asset": `scan-virustotal` uploads
|
|
47
|
+
this set, the release workflow downloads it (`--pattern` flags in
|
|
48
|
+
`_release-engine.yaml`), `docs/binaries.md` lists it, and the dev-release
|
|
49
|
+
asset globs derive from it.
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
PYTHON_DIST_SUFFIXES = (".tar.gz", ".whl")
|
|
53
|
+
"""File extensions identifying Python distributions among release assets.
|
|
54
|
+
|
|
55
|
+
The counterpart of {data}`BINARY_ASSET_SUFFIXES`, and the same kind of single
|
|
56
|
+
definition: {func}`pack_binary_assets` excludes this set from the binary
|
|
57
|
+
upload list (`create-release` already attached those), and the dev-release
|
|
58
|
+
asset globs add it on top of the compiled binaries.
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
|
|
41
62
|
def compute_file_sha256(path: Path) -> str:
|
|
42
63
|
"""Compute the SHA-256 hex digest of a file.
|
|
43
64
|
|
|
@@ -112,19 +133,20 @@ NUITKA_BUILD_TARGETS = {
|
|
|
112
133
|
"min_os": "10",
|
|
113
134
|
},
|
|
114
135
|
}
|
|
115
|
-
"""
|
|
136
|
+
"""GitHub-hosted runner matrix for Nuitka builds, keyed by target name.
|
|
116
137
|
|
|
117
|
-
The key
|
|
118
|
-
|
|
138
|
+
The key doubles as the compiled binary's short target identifier: it names the
|
|
139
|
+
published release asset, so it is chosen for user-friendliness and must stay
|
|
140
|
+
stable (download URLs and `docs/binaries.md` match on it).
|
|
119
141
|
|
|
120
142
|
Values are dictionaries with the following keys:
|
|
121
143
|
|
|
122
144
|
- `os`: Operating system name, as used in [GitHub-hosted runners](https://docs.github.com/en/actions/writing-workflows/choosing-where-your-workflow-runs/choosing-the-runner-for-a-job#standard-github-hosted-runners-for-public-repositories).
|
|
123
145
|
|
|
124
146
|
```{hint}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
147
|
+
Compilation only runs on the latest supported version of each OS, for each
|
|
148
|
+
architecture. macOS and Windows do not offer their latest version on every
|
|
149
|
+
architecture.
|
|
128
150
|
```
|
|
129
151
|
|
|
130
152
|
- `platform_id`: Platform identifier, as defined by [Extra Platform](https://github.com/kdeldycke/extra-platforms).
|
|
@@ -168,6 +190,86 @@ FLAT_BUILD_TARGETS = [
|
|
|
168
190
|
"""List of build targets in a flat format, suitable for matrix inclusion."""
|
|
169
191
|
|
|
170
192
|
|
|
193
|
+
def binary_name(package: str, target: str, version: str | None = None) -> str:
|
|
194
|
+
"""Compose a compiled binary's release-asset filename.
|
|
195
|
+
|
|
196
|
+
The one definition of the naming convention:
|
|
197
|
+
``{package}-{version}-{target}.{ext}`` for the versioned upload, and with
|
|
198
|
+
no *version* the stable alias (``{package}-{target}.{ext}``) backing the
|
|
199
|
+
`releases/latest/download` URLs. The extension comes from
|
|
200
|
+
{data}`NUITKA_BUILD_TARGETS`.
|
|
201
|
+
"""
|
|
202
|
+
extension = NUITKA_BUILD_TARGETS[target]["extension"]
|
|
203
|
+
middle = f"-{version}" if version else ""
|
|
204
|
+
return f"{package}{middle}-{target}.{extension}"
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def versionless_alias(filename: str, version: str) -> str | None:
|
|
208
|
+
"""Map a versioned binary filename to its stable alias, or `None`.
|
|
209
|
+
|
|
210
|
+
Strips the `-{version}-` segment (`papaya-1.2.3-linux-arm64.bin` becomes
|
|
211
|
+
`papaya-linux-arm64.bin`). Returns `None` for filenames that carry no such
|
|
212
|
+
segment or are not compiled binaries, so callers can filter and map in one
|
|
213
|
+
pass.
|
|
214
|
+
"""
|
|
215
|
+
marked = f"-{version}-"
|
|
216
|
+
if marked not in filename or not filename.endswith(BINARY_ASSET_SUFFIXES):
|
|
217
|
+
return None
|
|
218
|
+
return filename.replace(marked, "-", 1)
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def binary_filename_re(package: str) -> re.Pattern[str]:
|
|
222
|
+
"""Match a *package* binary filename, versioned or versionless.
|
|
223
|
+
|
|
224
|
+
Captures `target` and `ext`, both alternations derived from
|
|
225
|
+
{data}`NUITKA_BUILD_TARGETS` so a new build target extends the pattern
|
|
226
|
+
without anyone editing a regex. The release freeze rewrites both spellings
|
|
227
|
+
onto the versioned form through this; `tests/test_platform_keys.py` pins
|
|
228
|
+
the pattern against every target.
|
|
229
|
+
"""
|
|
230
|
+
targets = "|".join(sorted(NUITKA_BUILD_TARGETS))
|
|
231
|
+
extensions = "|".join(
|
|
232
|
+
sorted({data["extension"] for data in NUITKA_BUILD_TARGETS.values()})
|
|
233
|
+
)
|
|
234
|
+
return re.compile(
|
|
235
|
+
rf"{re.escape(package)}(?:-[\d.]+)?-"
|
|
236
|
+
rf"(?P<target>{targets})\.(?P<ext>{extensions})"
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
def pack_binary_assets(dist_dir: Path, version: str) -> list[Path]:
|
|
241
|
+
"""Pack a release's upload list, materializing the versionless aliases.
|
|
242
|
+
|
|
243
|
+
Mirrors what the release engine's upload step needs: every file in
|
|
244
|
+
*dist_dir* except the Python distributions (`create-release` already
|
|
245
|
+
uploaded those), plus a byte-identical versionless alias copied beside
|
|
246
|
+
each versioned binary so the stable `releases/latest/download` URLs always
|
|
247
|
+
resolve. Aliases share their sibling's digest, which is what lets artifact
|
|
248
|
+
attestations verify them unchanged and the binaries catalog collapse them
|
|
249
|
+
(see `binaries_page._binary_assets`).
|
|
250
|
+
|
|
251
|
+
Idempotent: re-running overwrites the same aliases with the same bytes.
|
|
252
|
+
|
|
253
|
+
:param dist_dir: Directory holding the compiled binaries and attestation
|
|
254
|
+
bundles downloaded from the build jobs.
|
|
255
|
+
:param version: The release version whose binaries earn aliases.
|
|
256
|
+
:return: Sorted paths to upload, aliases included.
|
|
257
|
+
"""
|
|
258
|
+
uploads = {
|
|
259
|
+
path
|
|
260
|
+
for path in dist_dir.iterdir()
|
|
261
|
+
if path.is_file() and not path.name.endswith(PYTHON_DIST_SUFFIXES)
|
|
262
|
+
}
|
|
263
|
+
for path in sorted(uploads):
|
|
264
|
+
alias = versionless_alias(path.name, version)
|
|
265
|
+
if alias is None:
|
|
266
|
+
continue
|
|
267
|
+
alias_path = dist_dir / alias
|
|
268
|
+
shutil.copy2(path, alias_path)
|
|
269
|
+
uploads.add(alias_path)
|
|
270
|
+
return sorted(uploads)
|
|
271
|
+
|
|
272
|
+
|
|
171
273
|
BINARY_AFFECTING_PATHS: Final[tuple[str, ...]] = (
|
|
172
274
|
".github/workflows/_release-engine.yaml",
|
|
173
275
|
".github/workflows/release.yaml",
|
|
@@ -248,8 +350,18 @@ MACHO_FAT_MAGICS: Final[frozenset[int]] = frozenset((0xCAFEBABE, 0xCAFEBABF))
|
|
|
248
350
|
"""Big-endian magics of universal (fat) Mach-O containers, 32- and 64-bit."""
|
|
249
351
|
|
|
250
352
|
LC_VERSION_MIN_MACOSX: Final[int] = 0x24
|
|
353
|
+
"""Mach-O load command carrying the minimum macOS version (pre-10.14 SDKs)."""
|
|
354
|
+
|
|
251
355
|
LC_BUILD_VERSION: Final[int] = 0x32
|
|
356
|
+
"""Mach-O load command carrying the platform and minimum OS (10.14+ SDKs)."""
|
|
357
|
+
|
|
252
358
|
MACHO_PLATFORM_MACOS: Final[int] = 1
|
|
359
|
+
"""`platform` field value naming macOS inside an `LC_BUILD_VERSION` command."""
|
|
360
|
+
|
|
361
|
+
_PE_PROBE_BYTES: Final[int] = 65536
|
|
362
|
+
"""Upper bound read when probing for a PE header: enough to cover any
|
|
363
|
+
real-world DOS-stub offset to the COFF header without reading the whole
|
|
364
|
+
binary."""
|
|
253
365
|
|
|
254
366
|
|
|
255
367
|
def _version_key(version: str) -> tuple[int, ...]:
|
|
@@ -352,7 +464,7 @@ def _macho_info(path: Path) -> tuple[set[int], str | None]:
|
|
|
352
464
|
def _pe_machine(path: Path) -> int | None:
|
|
353
465
|
"""Return the COFF machine type of a PE executable, `None` if not PE."""
|
|
354
466
|
with path.open("rb") as stream:
|
|
355
|
-
head = stream.read(
|
|
467
|
+
head = stream.read(_PE_PROBE_BYTES)
|
|
356
468
|
if head[:2] != b"MZ" or len(head) < 0x40:
|
|
357
469
|
return None
|
|
358
470
|
pe_offset = struct.unpack_from("<I", head, 0x3C)[0]
|
|
@@ -47,6 +47,15 @@ if TYPE_CHECKING:
|
|
|
47
47
|
ISSUE_TITLE = "Broken links"
|
|
48
48
|
"""Issue title used for the combined broken links report."""
|
|
49
49
|
|
|
50
|
+
LYCHEE_BROKEN_LINKS_EXIT = 2
|
|
51
|
+
"""The one lychee exit code that reports on the links rather than on the run.
|
|
52
|
+
|
|
53
|
+
Lychee exits 0 on success, 1 on an unexpected failure, 2 when it found broken
|
|
54
|
+
links, and 3 on a config error. Only 2 is a verdict about the links; 1 and 3 say
|
|
55
|
+
the run itself did not complete, so neither "broken links found" nor "no broken
|
|
56
|
+
links" can be claimed from them.
|
|
57
|
+
"""
|
|
58
|
+
|
|
50
59
|
LYCHEE_DEFAULT_BODY = Path("./lychee/out.md")
|
|
51
60
|
"""Default output path used by the lychee-action GitHub Action."""
|
|
52
61
|
|
|
@@ -55,7 +64,7 @@ SPHINX_DEFAULT_OUTPUT = Path("./docs/_linkcheck/output.json")
|
|
|
55
64
|
|
|
56
65
|
|
|
57
66
|
# ---------------------------------------------------------------------------
|
|
58
|
-
# Sphinx linkcheck parsing, filtering, and report generation
|
|
67
|
+
# Sphinx linkcheck parsing, filtering, and report generation.
|
|
59
68
|
# ---------------------------------------------------------------------------
|
|
60
69
|
|
|
61
70
|
|
|
@@ -170,7 +179,7 @@ def generate_markdown_report(
|
|
|
170
179
|
|
|
171
180
|
|
|
172
181
|
# ---------------------------------------------------------------------------
|
|
173
|
-
# Label selection
|
|
182
|
+
# Label selection.
|
|
174
183
|
# ---------------------------------------------------------------------------
|
|
175
184
|
|
|
176
185
|
|
|
@@ -186,7 +195,7 @@ def get_label(repo_name: str) -> str:
|
|
|
186
195
|
|
|
187
196
|
|
|
188
197
|
# ---------------------------------------------------------------------------
|
|
189
|
-
# Combined broken links issue (Lychee + Sphinx linkcheck)
|
|
198
|
+
# Combined broken links issue (Lychee + Sphinx linkcheck).
|
|
190
199
|
# ---------------------------------------------------------------------------
|
|
191
200
|
|
|
192
201
|
|
|
@@ -230,7 +239,6 @@ def manage_combined_broken_links_issue(
|
|
|
230
239
|
in the Sphinx report. Auto-composed from {attr}`Metadata.repo_url
|
|
231
240
|
<repomatic.metadata.Metadata.repo_url>` and {attr}`Metadata.sha
|
|
232
241
|
<repomatic.metadata.Metadata.sha>`.
|
|
233
|
-
:raises ValueError: If lychee exit code is not 0, 2, or `None`.
|
|
234
242
|
:raises ValueError: If `repo_name` cannot be determined.
|
|
235
243
|
"""
|
|
236
244
|
# Shared Metadata instance for all CI context lookups.
|
|
@@ -270,20 +278,27 @@ def manage_combined_broken_links_issue(
|
|
|
270
278
|
sphinx_source_url = f"{md.repo_url}/blob/{md.sha}/docs"
|
|
271
279
|
logging.info(f"Auto-composed source URL: {sphinx_source_url}")
|
|
272
280
|
|
|
273
|
-
# Interpret lychee exit code.
|
|
274
|
-
#
|
|
275
|
-
#
|
|
276
|
-
# produced an output file with actual content. A non-zero exit code
|
|
277
|
-
# without output (e.g., config error, transient failure) should not
|
|
278
|
-
# create an issue that misleadingly says "No broken links found."
|
|
281
|
+
# Interpret lychee's exit code. Only LYCHEE_BROKEN_LINKS_EXIT reports on the
|
|
282
|
+
# links themselves; every other non-zero code reports that the run failed,
|
|
283
|
+
# and says nothing either way about the links.
|
|
279
284
|
lychee_has_broken = False
|
|
285
|
+
lychee_failed = False
|
|
280
286
|
if lychee_exit_code is not None:
|
|
281
|
-
if lychee_exit_code
|
|
282
|
-
|
|
287
|
+
if lychee_exit_code == LYCHEE_BROKEN_LINKS_EXIT:
|
|
288
|
+
# An output file is required: a broken-links verdict with nothing to
|
|
289
|
+
# show would file an issue whose body claims the links are fine.
|
|
290
|
+
if lychee_body_file is not None:
|
|
291
|
+
lychee_has_broken = True
|
|
292
|
+
else:
|
|
293
|
+
logging.warning(
|
|
294
|
+
f"Lychee exit code {lychee_exit_code} but no output file found. "
|
|
295
|
+
"Skipping broken links report."
|
|
296
|
+
)
|
|
283
297
|
elif lychee_exit_code != 0:
|
|
298
|
+
lychee_failed = True
|
|
284
299
|
logging.warning(
|
|
285
|
-
f"Lychee exit code {lychee_exit_code}
|
|
286
|
-
"
|
|
300
|
+
f"Lychee failed with exit code {lychee_exit_code}, which reports a"
|
|
301
|
+
" run failure rather than broken links."
|
|
287
302
|
)
|
|
288
303
|
logging.info(
|
|
289
304
|
f"Lychee exit code {lychee_exit_code}: "
|
|
@@ -314,6 +329,11 @@ def manage_combined_broken_links_issue(
|
|
|
314
329
|
lychee_content = sanitize_markdown_mentions(
|
|
315
330
|
lychee_body_file.read_text(encoding="UTF-8").strip(),
|
|
316
331
|
)
|
|
332
|
+
elif lychee_failed:
|
|
333
|
+
lychee_content = (
|
|
334
|
+
f"Lychee did not complete (exit code {lychee_exit_code}), so the"
|
|
335
|
+
" links it covers were not checked this run."
|
|
336
|
+
)
|
|
317
337
|
else:
|
|
318
338
|
lychee_content = "No broken links found."
|
|
319
339
|
lychee_section = f"## Lychee\n\n{lychee_content}"
|