Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion src/codedecorum/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import sys
from dataclasses import replace
from datetime import timedelta
from importlib.metadata import version
from importlib.metadata import metadata, version
from pathlib import Path

from codedecorum.api import RuleUnit, Scope
Expand Down Expand Up @@ -39,9 +39,17 @@ def main(argv: list[str] | None = None) -> int:
metavar=("RULE", "PATH"),
)
parser.add_argument("--approval-timeout", type=_approval_timeout, metavar="DURATION")
parser.add_argument(
"--plugin-help",
action="store_true",
help="show the complete plugin setup and rule-authoring guide, then exit",
)
parser.add_argument("--version", action="version", version=version("codedecorum"))
args = parser.parse_args(argv)

if args.plugin_help:
print(_plugin_help())
return 0
if args.approval_timeout is not None and args.unlocking_with_explicit_human_approval is None:
parser.error("--approval-timeout requires --unlocking-with-explicit-human-approval")
if args.unlocking_with_explicit_human_approval is not None and args.paths:
Expand Down Expand Up @@ -125,6 +133,16 @@ def main(argv: list[str] | None = None) -> int:
return int(bool(diagnostics))


def _plugin_help() -> str:
readme = metadata("codedecorum").get_payload()
heading = "## How to write a plugin"
_, separator, remainder = readme.partition(f"{heading}\n")
if not separator:
raise RuntimeError(f"packaged README does not contain {heading!r}")
body, _, _ = remainder.partition("\n## ")
return f"{heading}\n{body.rstrip()}"


def _approval_timeout(value: str) -> timedelta:
units = {"s": 1, "m": 60, "h": 3600}
try:
Expand Down
19 changes: 19 additions & 0 deletions tests/test_contracts.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import subprocess
from collections.abc import Iterable, Mapping
from datetime import UTC, datetime, timedelta
from importlib.metadata import metadata
from pathlib import Path, PurePosixPath

import pytest
Expand Down Expand Up @@ -270,6 +271,24 @@ def test_parameterized_guidance_contract() -> None:
configure_rules((invalid,), Config())


def test_plugin_help_prints_packaged_readme_section(
capsys: pytest.CaptureFixture[str],
) -> None:
readme = metadata("codedecorum").get_payload()
expected = readme[readme.index("## How to write a plugin") :].split("\n## ", 1)[0].rstrip()

assert main(["--plugin-help"]) == 0
assert capsys.readouterr().out.rstrip() == expected

with pytest.raises(SystemExit) as exit_info:
main(["--help"])
assert exit_info.value.code == 0
help_output = capsys.readouterr().out
assert "--plugin-help" in help_output
assert "show the complete plugin setup and rule-authoring" in help_output
assert "guide, then exit" in help_output


def test_complete_plugin_pipeline(tmp_path: Path, capsys: pytest.CaptureFixture[str]) -> None:
root = tmp_path / "repo"
subprocess.run(["git", "init", "-q", "-b", "main", root], check=True)
Expand Down
Loading