Skip to content

Commit 3cd93de

Browse files
committed
Check dependency declarations against the version policy
The decidable half of the `/repomatic-deps review` checklist: upper bounds, missing floors, unsorted lists, misplaced type stubs and uncommented floors are all readable from `pyproject.toml`, so they belong in `lint-deps` rather than in a skill a maintainer has to remember to run. Whether a floor is *justified* by the APIs the code calls stays where it was, since nothing settles that by parsing. Findings are warnings and never reach the exit code, keeping them clear of the shippability gate that does block a release.
1 parent 16138fd commit 3cd93de

6 files changed

Lines changed: 745 additions & 11 deletions

File tree

changelog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
- New `repomatic run <tool> --verify` reporting which files a formatter would rewrite, without touching the working tree.
1010
- `repomatic run` now resolves a tool's targets itself when given no arguments, running the invocation CI performs. A tool with no matching file is skipped instead of invoked pathless.
1111
- `lint-changelog` now warns about a released section holding no entry.
12+
- `lint-deps` now reports declarations departing from version policy: upper bounds, missing floors, unsorted lists, misplaced type stubs, uncommented floors. Warnings only, disabled with `--no-policy`.
1213
- `cancel-runs` now spares a run whose head commit carries `[changelog] Release`, so a sweep of the default branch cannot kill a release matrix.
1314
- Fix the `[tool.repomatic.workflow]` key names the `repomatic-audit` skill recommends: they are `extra-paths` and `ignore-paths`, not the snake_case attribute names.
1415

docs/repomatic.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,15 @@
137137
:undoc-members:
138138
```
139139

140+
## `repomatic.dep_policy` module
141+
142+
```{eval-rst}
143+
.. automodule:: repomatic.dep_policy
144+
:members:
145+
:show-inheritance:
146+
:undoc-members:
147+
```
148+
140149
## `repomatic.dep_report` module
141150

142151
```{eval-rst}

docs/tests.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,15 @@
135135
:undoc-members:
136136
```
137137

138+
## `tests.test_dep_policy` module
139+
140+
```{eval-rst}
141+
.. automodule:: tests.test_dep_policy
142+
:members:
143+
:show-inheritance:
144+
:undoc-members:
145+
```
146+
138147
## `tests.test_dep_report` module
139148

140149
```{eval-rst}

repomatic/cli.py

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@
9797
generate_dependency_graph,
9898
resolve_subgraph_selection,
9999
)
100+
from .dep_policy import scan_policy
100101
from .dep_sources import (
101102
LINT_DEPS_HEADER_DEFS,
102103
build_release_readiness,
@@ -1950,6 +1951,15 @@ def lint_changelog(
19501951
" pushes."
19511952
),
19521953
)
1954+
@option(
1955+
"--policy/--no-policy",
1956+
default=True,
1957+
help=(
1958+
"Also report declarations departing from the project's version"
1959+
" policy: upper bounds, missing floors, unsorted lists, misplaced"
1960+
" type stubs, uncommented floors. Never blocks a release."
1961+
),
1962+
)
19531963
@option(
19541964
"--output",
19551965
type=file_path(writable=True, resolve_path=True, allow_dash=True),
@@ -1963,6 +1973,7 @@ def lint_deps(
19631973
pyproject_path: Path,
19641974
lockfile: Path,
19651975
fatal: bool,
1976+
policy: bool,
19661977
output: Path | None,
19671978
output_format: str,
19681979
) -> None:
@@ -2014,20 +2025,26 @@ def lint_deps(
20142025
allow=config.lint_deps.allow,
20152026
)
20162027

2017-
if not findings:
2028+
policy_findings = scan_policy(pyproject_path) if policy else []
2029+
2030+
if not findings and not policy_findings:
20182031
echo("✓ Every dependency resolves from the public package index.")
20192032
ctx.exit(0)
20202033

2021-
rows = [
2022-
(
2023-
finding.package,
2024-
str(finding.kind),
2025-
finding.location,
2026-
finding.verdict,
2027-
)
2028-
for finding in findings
2029-
]
2030-
ctx.print_table(rows, LINT_DEPS_HEADER_DEFS)
2034+
if not findings:
2035+
echo("✓ Every dependency resolves from the public package index.")
2036+
2037+
if findings:
2038+
rows = [
2039+
(
2040+
finding.package,
2041+
str(finding.kind),
2042+
finding.location,
2043+
finding.verdict,
2044+
)
2045+
for finding in findings
2046+
]
2047+
ctx.print_table(rows, LINT_DEPS_HEADER_DEFS)
20312048

20322049
blocking = [finding for finding in findings if finding.blocking]
20332050
for finding in findings:
@@ -2040,6 +2057,12 @@ def lint_deps(
20402057
emit_annotation(AnnotationLevel.WARNING, finding.message)
20412058
echo(f"⚠ {finding.message}")
20422059

2060+
# Style findings render after the shippability ones and never reach the
2061+
# exit code: a release is not held for an uncommented floor.
2062+
for policy_finding in policy_findings:
2063+
emit_annotation(AnnotationLevel.WARNING, policy_finding.message)
2064+
echo(f"⚠ {policy_finding.message}")
2065+
20432066
if output:
20442067
log_output_target("dependency blockers", output)
20452068
emit_report(

0 commit comments

Comments
 (0)