open-code-review-toolkit 0.2.1__tar.gz → 0.3.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. {open_code_review_toolkit-0.2.1 → open_code_review_toolkit-0.3.1}/PKG-INFO +4 -3
  2. {open_code_review_toolkit-0.2.1 → open_code_review_toolkit-0.3.1}/README.md +3 -2
  3. {open_code_review_toolkit-0.2.1 → open_code_review_toolkit-0.3.1}/pyproject.toml +7 -13
  4. {open_code_review_toolkit-0.2.1 → open_code_review_toolkit-0.3.1}/src/ocr_toolkit/_version.py +2 -2
  5. {open_code_review_toolkit-0.2.1 → open_code_review_toolkit-0.3.1}/src/ocr_toolkit/cli.py +28 -3
  6. {open_code_review_toolkit-0.2.1 → open_code_review_toolkit-0.3.1}/src/ocr_toolkit/context/manifests.py +27 -15
  7. {open_code_review_toolkit-0.2.1 → open_code_review_toolkit-0.3.1}/src/ocr_toolkit/context/repo.py +4 -2
  8. open_code_review_toolkit-0.3.1/src/ocr_toolkit/mcp_config.py +479 -0
  9. {open_code_review_toolkit-0.2.1 → open_code_review_toolkit-0.3.1}/src/ocr_toolkit/posting/gitlab.py +14 -10
  10. {open_code_review_toolkit-0.2.1 → open_code_review_toolkit-0.3.1}/src/ocr_toolkit/posting/markers.py +6 -4
  11. {open_code_review_toolkit-0.2.1 → open_code_review_toolkit-0.3.1}/src/ocr_toolkit/posting/workflow.py +6 -31
  12. {open_code_review_toolkit-0.2.1 → open_code_review_toolkit-0.3.1}/src/ocr_toolkit/preflight.py +1 -1
  13. open_code_review_toolkit-0.3.1/src/ocr_toolkit/review_runner.py +117 -0
  14. open_code_review_toolkit-0.2.1/CHANGELOG.md +0 -61
  15. open_code_review_toolkit-0.2.1/CONTRIBUTING.md +0 -14
  16. open_code_review_toolkit-0.2.1/SECURITY.md +0 -13
  17. open_code_review_toolkit-0.2.1/changelog.d/README.md +0 -3
  18. open_code_review_toolkit-0.2.1/docs/codex/AGENT_EXECUTION_PITFALLS.md +0 -44
  19. open_code_review_toolkit-0.2.1/docs/codex/TASKS_BACKLOG.md +0 -336
  20. open_code_review_toolkit-0.2.1/docs/configuration.md +0 -67
  21. open_code_review_toolkit-0.2.1/docs/development.md +0 -19
  22. open_code_review_toolkit-0.2.1/docs/engineering/project_principles.md +0 -30
  23. open_code_review_toolkit-0.2.1/docs/engineering/toolkit_strategy.md +0 -145
  24. open_code_review_toolkit-0.2.1/docs/gitlab.md +0 -31
  25. open_code_review_toolkit-0.2.1/docs/operations.md +0 -83
  26. open_code_review_toolkit-0.2.1/docs/release.md +0 -49
  27. open_code_review_toolkit-0.2.1/docs/security.md +0 -29
  28. open_code_review_toolkit-0.2.1/examples/gitlab/ocr-review.gitlab-ci.yml +0 -84
  29. open_code_review_toolkit-0.2.1/examples/gitlab/rules.json +0 -33
  30. open_code_review_toolkit-0.2.1/src/ocr_toolkit/mcp_config.py +0 -257
  31. open_code_review_toolkit-0.2.1/tests/__init__.py +0 -1
  32. open_code_review_toolkit-0.2.1/tests/support.py +0 -85
  33. open_code_review_toolkit-0.2.1/tests/test_cli.py +0 -39
  34. open_code_review_toolkit-0.2.1/tests/test_common_helpers.py +0 -391
  35. open_code_review_toolkit-0.2.1/tests/test_context_helpers.py +0 -1946
  36. open_code_review_toolkit-0.2.1/tests/test_install_local_artifact.py +0 -67
  37. open_code_review_toolkit-0.2.1/tests/test_operations_docs.py +0 -58
  38. open_code_review_toolkit-0.2.1/tests/test_posting_helpers.py +0 -1757
  39. open_code_review_toolkit-0.2.1/tests/test_project_strategy.py +0 -109
  40. open_code_review_toolkit-0.2.1/tests/test_quality_script.py +0 -12
  41. open_code_review_toolkit-0.2.1/tests/test_release_authorization.py +0 -107
  42. open_code_review_toolkit-0.2.1/tests/test_release_notes.py +0 -46
  43. open_code_review_toolkit-0.2.1/tests/test_release_process_docs.py +0 -30
  44. open_code_review_toolkit-0.2.1/tests/test_runtime_helpers.py +0 -880
  45. open_code_review_toolkit-0.2.1/tests/test_testpypi_preview.py +0 -238
  46. {open_code_review_toolkit-0.2.1 → open_code_review_toolkit-0.3.1}/.gitignore +0 -0
  47. {open_code_review_toolkit-0.2.1 → open_code_review_toolkit-0.3.1}/LICENSE +0 -0
  48. {open_code_review_toolkit-0.2.1 → open_code_review_toolkit-0.3.1}/src/ocr_toolkit/__init__.py +0 -0
  49. {open_code_review_toolkit-0.2.1 → open_code_review_toolkit-0.3.1}/src/ocr_toolkit/common/__init__.py +0 -0
  50. {open_code_review_toolkit-0.2.1 → open_code_review_toolkit-0.3.1}/src/ocr_toolkit/common/language.py +0 -0
  51. {open_code_review_toolkit-0.2.1 → open_code_review_toolkit-0.3.1}/src/ocr_toolkit/common/markdown.py +0 -0
  52. {open_code_review_toolkit-0.2.1 → open_code_review_toolkit-0.3.1}/src/ocr_toolkit/common/redaction.py +0 -0
  53. {open_code_review_toolkit-0.2.1 → open_code_review_toolkit-0.3.1}/src/ocr_toolkit/config_writer.py +0 -0
  54. {open_code_review_toolkit-0.2.1 → open_code_review_toolkit-0.3.1}/src/ocr_toolkit/configure.py +0 -0
  55. {open_code_review_toolkit-0.2.1 → open_code_review_toolkit-0.3.1}/src/ocr_toolkit/context/__init__.py +0 -0
  56. {open_code_review_toolkit-0.2.1 → open_code_review_toolkit-0.3.1}/src/ocr_toolkit/context/__main__.py +0 -0
  57. {open_code_review_toolkit-0.2.1 → open_code_review_toolkit-0.3.1}/src/ocr_toolkit/context/ansible.py +0 -0
  58. {open_code_review_toolkit-0.2.1 → open_code_review_toolkit-0.3.1}/src/ocr_toolkit/context/categorize.py +0 -0
  59. {open_code_review_toolkit-0.2.1 → open_code_review_toolkit-0.3.1}/src/ocr_toolkit/context/instructions.py +0 -0
  60. {open_code_review_toolkit-0.2.1 → open_code_review_toolkit-0.3.1}/src/ocr_toolkit/context/planner.py +0 -0
  61. {open_code_review_toolkit-0.2.1 → open_code_review_toolkit-0.3.1}/src/ocr_toolkit/context/render.py +0 -0
  62. {open_code_review_toolkit-0.2.1 → open_code_review_toolkit-0.3.1}/src/ocr_toolkit/context/settings.py +0 -0
  63. {open_code_review_toolkit-0.2.1 → open_code_review_toolkit-0.3.1}/src/ocr_toolkit/posting/__init__.py +0 -0
  64. {open_code_review_toolkit-0.2.1 → open_code_review_toolkit-0.3.1}/src/ocr_toolkit/posting/__main__.py +0 -0
  65. {open_code_review_toolkit-0.2.1 → open_code_review_toolkit-0.3.1}/src/ocr_toolkit/posting/comments.py +0 -0
  66. {open_code_review_toolkit-0.2.1 → open_code_review_toolkit-0.3.1}/src/ocr_toolkit/posting/formatting.py +0 -0
  67. {open_code_review_toolkit-0.2.1 → open_code_review_toolkit-0.3.1}/src/ocr_toolkit/posting/payloads.py +0 -0
  68. {open_code_review_toolkit-0.2.1 → open_code_review_toolkit-0.3.1}/src/ocr_toolkit/posting/result.py +0 -0
  69. {open_code_review_toolkit-0.2.1 → open_code_review_toolkit-0.3.1}/src/ocr_toolkit/posting/settings.py +0 -0
  70. {open_code_review_toolkit-0.2.1 → open_code_review_toolkit-0.3.1}/src/ocr_toolkit/posting/snapshot.py +0 -0
  71. {open_code_review_toolkit-0.2.1 → open_code_review_toolkit-0.3.1}/src/ocr_toolkit/py.typed +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: open-code-review-toolkit
3
- Version: 0.2.1
3
+ Version: 0.3.1
4
4
  Summary: Unofficial GitLab CI integration layer for Open Code Review
5
5
  Project-URL: Homepage, https://github.com/xeonvs/open-code-review-toolkit
6
6
  Project-URL: Repository, https://github.com/xeonvs/open-code-review-toolkit
@@ -243,7 +243,8 @@ ocr --version
243
243
  ocr-ci --help
244
244
  ```
245
245
 
246
- The current compatibility target is OCR `1.7.17`. CI should pin the release and verify its published checksum before execution.
246
+ The current compatibility target is OCR `1.8.0`. CI should pin the release and verify its published checksum before execution.
247
+ The [versioned compatibility policy](docs/compatibility.md) records tested assets and evidence and describes the conservative Dependabot-like qualification workflow for later upstream releases.
247
248
  Review output defaults to English. Set `OCR_REVIEW_LANGUAGE=Russian` to use Russian consistently in both OCR configuration and generated review context.
248
249
 
249
250
  Stable distributions are published to [PyPI](https://pypi.org/project/open-code-review-toolkit/) and mirrored as checksum-listed, provenance-attested assets in the corresponding [GitHub Release](https://github.com/xeonvs/open-code-review-toolkit/releases). Development snapshots are published only to TestPyPI.
@@ -276,7 +277,7 @@ ocr-ci preflight
276
277
  ocr-ci configure
277
278
  ocr-ci mcp-config
278
279
  ocr-ci context --output .review-context/dependencies.md
279
- # run: ocr review ... --format json
280
+ ocr-ci review --result /tmp/ocr-result.json --stderr /tmp/ocr-stderr.log -- ... --format json
280
281
  ocr-ci post --result /tmp/ocr-result.json --stderr /tmp/ocr-stderr.log
281
282
  ```
282
283
 
@@ -15,7 +15,8 @@ ocr --version
15
15
  ocr-ci --help
16
16
  ```
17
17
 
18
- The current compatibility target is OCR `1.7.17`. CI should pin the release and verify its published checksum before execution.
18
+ The current compatibility target is OCR `1.8.0`. CI should pin the release and verify its published checksum before execution.
19
+ The [versioned compatibility policy](docs/compatibility.md) records tested assets and evidence and describes the conservative Dependabot-like qualification workflow for later upstream releases.
19
20
  Review output defaults to English. Set `OCR_REVIEW_LANGUAGE=Russian` to use Russian consistently in both OCR configuration and generated review context.
20
21
 
21
22
  Stable distributions are published to [PyPI](https://pypi.org/project/open-code-review-toolkit/) and mirrored as checksum-listed, provenance-attested assets in the corresponding [GitHub Release](https://github.com/xeonvs/open-code-review-toolkit/releases). Development snapshots are published only to TestPyPI.
@@ -48,7 +49,7 @@ ocr-ci preflight
48
49
  ocr-ci configure
49
50
  ocr-ci mcp-config
50
51
  ocr-ci context --output .review-context/dependencies.md
51
- # run: ocr review ... --format json
52
+ ocr-ci review --result /tmp/ocr-result.json --stderr /tmp/ocr-stderr.log -- ... --format json
52
53
  ocr-ci post --result /tmp/ocr-result.json --stderr /tmp/ocr-stderr.log
53
54
  ```
54
55
 
@@ -38,6 +38,7 @@ ocr-ci = "ocr_toolkit.cli:main"
38
38
 
39
39
  [dependency-groups]
40
40
  dev = [
41
+ "bandit==1.9.4",
41
42
  "build>=1.2.2",
42
43
  "coverage[toml]>=7.6",
43
44
  "hatch-vcs>=0.4",
@@ -60,19 +61,7 @@ version-file = "src/ocr_toolkit/_version.py"
60
61
  packages = ["src/ocr_toolkit"]
61
62
 
62
63
  [tool.hatch.build.targets.sdist]
63
- include = [
64
- "/src",
65
- "/tests",
66
- "/examples",
67
- "/docs",
68
- "/changelog.d",
69
- "/README.md",
70
- "/CONTRIBUTING.md",
71
- "/SECURITY.md",
72
- "/LICENSE",
73
- "/CHANGELOG.md",
74
- "/pyproject.toml",
75
- ]
64
+ only-include = ["src", "README.md", "LICENSE", "pyproject.toml"]
76
65
 
77
66
  [tool.hatch.version.raw-options]
78
67
  fallback_version = "0.3.0.dev0"
@@ -101,6 +90,11 @@ exclude = ["src/ocr_toolkit/_version.py"]
101
90
  warn_unreachable = false
102
91
  disable_error_code = ["arg-type", "assignment", "import-not-found", "index", "name-defined", "no-any-return", "no-redef", "union-attr", "unused-ignore", "var-annotated"]
103
92
 
93
+ [tool.bandit]
94
+ exclude_dirs = ["tests"]
95
+ severity = "medium"
96
+ confidence = "medium"
97
+
104
98
  [tool.pytest.ini_options]
105
99
  addopts = "--strict-config --strict-markers"
106
100
  testpaths = ["tests"]
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '0.2.1'
22
- __version_tuple__ = version_tuple = (0, 2, 1)
21
+ __version__ = version = '0.3.1'
22
+ __version_tuple__ = version_tuple = (0, 3, 1)
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -3,9 +3,11 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import argparse
6
+ import sys
6
7
  from collections.abc import Sequence
8
+ from pathlib import Path
7
9
 
8
- from ocr_toolkit import configure, mcp_config, preflight
10
+ from ocr_toolkit import configure, mcp_config, preflight, review_runner
9
11
  from ocr_toolkit.context import render as context_render
10
12
  from ocr_toolkit.posting.workflow import main as posting_main
11
13
 
@@ -21,6 +23,14 @@ def build_parser() -> argparse.ArgumentParser:
21
23
  subparsers.add_parser("preflight", help="Validate OCR, GitLab, and LLM access.")
22
24
  subparsers.add_parser("configure", help="Write the OCR runtime configuration.")
23
25
  subparsers.add_parser("mcp-config", help="Write OCR MCP server configuration.")
26
+ review_parser = subparsers.add_parser(
27
+ "review", help="Run OCR with private artifacts and safe failure diagnostics."
28
+ )
29
+ review_parser.add_argument("--result", required=True, help="OCR JSON output path.")
30
+ review_parser.add_argument("--stderr", required=True, help="Full OCR stderr artifact path.")
31
+ review_parser.add_argument(
32
+ "ocr_args", nargs=argparse.REMAINDER, help="OCR review arguments after --."
33
+ )
24
34
 
25
35
  context_parser = subparsers.add_parser(
26
36
  "context", help="Generate bounded repository review context."
@@ -33,9 +43,17 @@ def build_parser() -> argparse.ArgumentParser:
33
43
 
34
44
  post_parser = subparsers.add_parser("post", help="Publish an OCR result artifact to GitLab.")
35
45
  post_parser.add_argument(
36
- "--result", default="/tmp/ocr-result.json", help="OCR JSON result path."
46
+ # The public GitLab example writes this fixed path inside an isolated CI job.
47
+ "--result",
48
+ default="/tmp/ocr-result.json", # nosec B108
49
+ help="OCR JSON result path.",
50
+ )
51
+ post_parser.add_argument(
52
+ # The file contains only the current job's OCR stderr and is never shared.
53
+ "--stderr",
54
+ default="/tmp/ocr-stderr.log", # nosec B108
55
+ help="OCR stderr log path.",
37
56
  )
38
- post_parser.add_argument("--stderr", default="/tmp/ocr-stderr.log", help="OCR stderr log path.")
39
57
  return parser
40
58
 
41
59
 
@@ -49,6 +67,13 @@ def main(argv: Sequence[str] | None = None) -> int:
49
67
  return configure.main()
50
68
  if args.command == "mcp-config":
51
69
  return mcp_config.configure_mcp_servers()
70
+ if args.command == "review":
71
+ ocr_args = args.ocr_args[1:] if args.ocr_args[:1] == ["--"] else args.ocr_args
72
+ try:
73
+ return review_runner.run_review(Path(args.result), Path(args.stderr), ocr_args)
74
+ except review_runner.ReviewRunnerError as exc:
75
+ print(f"Cannot run Open Code Review: {exc}", file=sys.stderr)
76
+ return 2
52
77
  if args.command == "context":
53
78
  return context_render.main(["--output", args.output])
54
79
  if args.command == "post":
@@ -196,9 +196,9 @@ def parse_pyproject(path: Path) -> dict[str, Any]:
196
196
 
197
197
  try:
198
198
  try:
199
- import tomllib
199
+ import tomllib # type: ignore[import-untyped]
200
200
  except ModuleNotFoundError:
201
- import tomli as tomllib # type: ignore[import-not-found]
201
+ import tomli as tomllib
202
202
 
203
203
  data = tomllib.loads(text)
204
204
  except ModuleNotFoundError:
@@ -209,9 +209,12 @@ def parse_pyproject(path: Path) -> dict[str, Any]:
209
209
  if not isinstance(data, dict):
210
210
  return {"present": True, "parse_error": JSON_OBJECT_PARSE_ERROR}
211
211
 
212
- project = data.get("project") if isinstance(data.get("project"), dict) else {}
213
- tool = data.get("tool") if isinstance(data.get("tool"), dict) else {}
214
- poetry = tool.get("poetry") if isinstance(tool.get("poetry"), dict) else {}
212
+ raw_project = data.get("project")
213
+ raw_tool = data.get("tool")
214
+ project: dict[Any, Any] = raw_project if isinstance(raw_project, dict) else {}
215
+ tool: dict[Any, Any] = raw_tool if isinstance(raw_tool, dict) else {}
216
+ raw_poetry = tool.get("poetry")
217
+ poetry: dict[Any, Any] = raw_poetry if isinstance(raw_poetry, dict) else {}
215
218
 
216
219
  dependencies: list[str] = []
217
220
  if isinstance(project.get("dependencies"), list):
@@ -233,12 +236,14 @@ def parse_pyproject(path: Path) -> dict[str, Any]:
233
236
  f"group.{group_name}: {redact_url_userinfo(str(dep))}" for dep in group_deps
234
237
  )
235
238
 
236
- poetry_deps = poetry.get("dependencies") if isinstance(poetry.get("dependencies"), dict) else {}
239
+ raw_poetry_deps = poetry.get("dependencies")
240
+ poetry_deps: dict[Any, Any] = raw_poetry_deps if isinstance(raw_poetry_deps, dict) else {}
237
241
  for name, version in poetry_deps.items():
238
242
  if str(name).lower() != "python":
239
243
  dependencies.append(f"{name}: {redact_url_userinfo(str(version))}")
240
244
 
241
- poetry_groups = poetry.get("group") if isinstance(poetry.get("group"), dict) else {}
245
+ raw_poetry_groups = poetry.get("group")
246
+ poetry_groups: dict[Any, Any] = raw_poetry_groups if isinstance(raw_poetry_groups, dict) else {}
242
247
  for group_name, group_value in poetry_groups.items():
243
248
  if not isinstance(group_value, dict):
244
249
  continue
@@ -354,10 +359,14 @@ def parse_composer_json(path: Path) -> dict[str, Any]:
354
359
  if not isinstance(data, dict):
355
360
  return {"present": True, "parse_error": JSON_OBJECT_PARSE_ERROR}
356
361
 
357
- require = data.get("require") if isinstance(data.get("require"), dict) else {}
358
- require_dev = data.get("require-dev") if isinstance(data.get("require-dev"), dict) else {}
359
- config = data.get("config") if isinstance(data.get("config"), dict) else {}
360
- platform = config.get("platform") if isinstance(config.get("platform"), dict) else {}
362
+ raw_require = data.get("require")
363
+ raw_require_dev = data.get("require-dev")
364
+ raw_config = data.get("config")
365
+ require: dict[Any, Any] = raw_require if isinstance(raw_require, dict) else {}
366
+ require_dev: dict[Any, Any] = raw_require_dev if isinstance(raw_require_dev, dict) else {}
367
+ config: dict[Any, Any] = raw_config if isinstance(raw_config, dict) else {}
368
+ raw_platform = config.get("platform")
369
+ platform: dict[Any, Any] = raw_platform if isinstance(raw_platform, dict) else {}
361
370
 
362
371
  platform_items, platform_omitted = limited_manifest_items(
363
372
  [f"{name}: {version}" for name, version in platform.items()]
@@ -433,10 +442,13 @@ def parse_package_json(path: Path) -> dict[str, Any]:
433
442
  if not isinstance(data, dict):
434
443
  return {"present": True, "parse_error": JSON_OBJECT_PARSE_ERROR}
435
444
 
436
- engines = data.get("engines") if isinstance(data.get("engines"), dict) else {}
437
- dependencies = data.get("dependencies") if isinstance(data.get("dependencies"), dict) else {}
438
- dev_dependencies = (
439
- data.get("devDependencies") if isinstance(data.get("devDependencies"), dict) else {}
445
+ raw_engines = data.get("engines")
446
+ raw_dependencies = data.get("dependencies")
447
+ raw_dev_dependencies = data.get("devDependencies")
448
+ engines: dict[Any, Any] = raw_engines if isinstance(raw_engines, dict) else {}
449
+ dependencies: dict[Any, Any] = raw_dependencies if isinstance(raw_dependencies, dict) else {}
450
+ dev_dependencies: dict[Any, Any] = (
451
+ raw_dev_dependencies if isinstance(raw_dev_dependencies, dict) else {}
440
452
  )
441
453
 
442
454
  engines_items, engines_omitted = limited_manifest_items(
@@ -162,11 +162,13 @@ def resolve_output_path(value: str) -> Path | None:
162
162
  requested = Path(value)
163
163
  logical_output = requested if requested.is_absolute() else root_lexical / requested
164
164
  logical_output = logical_output.absolute()
165
- temp_lexical = Path(tempfile.gettempdir()).absolute()
165
+ # tempfile supplies the platform temp root; containment and symlink checks follow.
166
+ temp_lexical = Path(tempfile.gettempdir()).absolute() # nosec B108
166
167
  allowed_roots = [(root_lexical, root), (root, root)]
167
168
  temp_resolved = temp_lexical.resolve()
168
169
  allowed_roots.extend([(temp_lexical, temp_resolved), (temp_resolved, temp_resolved)])
169
- tmp_path = Path("/tmp")
170
+ # POSIX /tmp may be a distinct lexical alias (for example, /private/tmp).
171
+ tmp_path = Path("/tmp") # nosec B108
170
172
  if tmp_path.exists():
171
173
  tmp_lexical = tmp_path.absolute()
172
174
  tmp_resolved = tmp_path.resolve()