7a0da7932b
OSV-Scanner (Scheduled) / scan-scheduled (push) Failing after 0s
Create Release / test-gate (push) Has been cancelled
Create Release / release-gate (push) Has been cancelled
Create Release / ci-gate (push) Has been cancelled
Create Release / version-check (push) Has been cancelled
Create Release / e2e-test-gate (push) Has been cancelled
Create Release / responsive-test-gate (push) Has been cancelled
Create Release / compat-test-gate (push) Has been cancelled
Create Release / compose-integration-gate (push) Has been cancelled
Create Release / vulture-gate (push) Has been cancelled
Create Release / build (push) Has been cancelled
Create Release / provenance (push) Has been cancelled
Create Release / prerelease-docker (push) Has been cancelled
Create Release / publish-docker (push) Has been cancelled
Create Release / create-release (push) Has been cancelled
Create Release / cleanup-changelog (push) Has been cancelled
Create Release / trigger-pypi (push) Has been cancelled
Create Release / monitor-pypi (push) Has been cancelled
Create Release / Clean up orphan prerelease tags and signatures (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-form] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-metrics] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-workflow] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-core] (push) Has been cancelled
CodeQL Advanced / Analyze (javascript-typescript) (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [history-news] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [library] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [link-analytics] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-core] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-lifecycle] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [error-benchmark] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-pages] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) (push) Has been cancelled
Docker Tests (Consolidated) / Accessibility Tests (push) Has been cancelled
Docker Tests (Consolidated) / LLM Unit Tests (push) Has been cancelled
Docker Tests (Consolidated) / LLM Example Tests (push) Has been cancelled
Docker Tests (Consolidated) / Production Image Smoke Test (push) Has been cancelled
Docker Tests (Consolidated) / Infrastructure Tests (push) Has been cancelled
OSSF Scorecard / OSSF Security Scorecard Analysis (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [mobile] (push) Has been cancelled
Backwards Compatibility / Verify Encryption Constants (push) Has been cancelled
Backwards Compatibility / PyPI Version Compatibility (push) Has been cancelled
Backwards Compatibility / Database Migration Tests (push) Has been cancelled
CodeQL Advanced / Analyze (python) (push) Has been cancelled
Docker Tests (Consolidated) / detect-changes (push) Has been cancelled
Docker Tests (Consolidated) / Build Test Image (push) Has been cancelled
Docker Tests (Consolidated) / All Pytest Tests + Coverage (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [accessibility] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [api-crud] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-login] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-pages] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-register] (push) Has been cancelled
189 lines
6.0 KiB
Python
Executable File
189 lines
6.0 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Require a notes file when a file under ``advanced_search_system/`` is deleted.
|
|
|
|
Any commit that deletes a ``.py`` file under
|
|
``src/local_deep_research/advanced_search_system/`` — or renames one out of
|
|
that tree, which removes it from the tracked module — must also add or
|
|
modify a ``.md`` file under ``docs/strategies/deleted/`` in the same commit.
|
|
Rationale and template live in ``docs/strategies/deleted/README.md``.
|
|
|
|
Exempt files whose deletion does not by itself remove a component: package
|
|
``__init__.py`` aggregators. The exemption list is intentionally narrow; if
|
|
a new infra file is added (e.g. a base class in a new location), update
|
|
``EXEMPT_FILENAMES`` below. ``base_*.py`` files are NOT exempt — deleting
|
|
a base class is a significant refactor that does warrant a notes file.
|
|
"""
|
|
|
|
import subprocess
|
|
import sys
|
|
from pathlib import PurePosixPath
|
|
|
|
SCOPE_DIR = "src/local_deep_research/advanced_search_system/"
|
|
DOCS_DIR = "docs/strategies/deleted/"
|
|
EXEMPT_FILENAMES = {"__init__.py"}
|
|
|
|
# Suggested notes-file name format. PR number isn't known at commit time
|
|
# so we accept a placeholder the author fills in before pushing.
|
|
SUGGESTED_FILENAME = "pr-<number>-<short-slug>.md"
|
|
|
|
TEMPLATE = """\
|
|
# PR #<n> — <title>
|
|
|
|
Components deleted in PR #<n> (see that PR for the full pre-deletion
|
|
code — this file only summarises what was novel).
|
|
|
|
## Component: `<ClassName>`
|
|
|
|
- File deleted: `<path>` (<N> LOC at deletion).
|
|
- Reachability: <e.g. "not in factory; only referenced by its own test">.
|
|
- Closest reachable successor: `<ClassName>` (`<path>`, factory key `"<X>"`).
|
|
|
|
### Useful ideas from the pre-deletion version
|
|
|
|
- **<short name>** — <1-2 sentences on what it did, why it was
|
|
distinctive, whether it was validated>.
|
|
- **<short name>** — <...>.
|
|
|
|
### Why deletion was safe
|
|
|
|
<2-3 sentences mapping distinctive features to the successor, or
|
|
flagging at-risk items and why losing them is acceptable.>
|
|
|
|
### Recovery path
|
|
|
|
<1-2 sentences. Prefer "add a flag on the existing class" over
|
|
"restore the deleted file".>
|
|
"""
|
|
|
|
|
|
def staged_changes():
|
|
"""Yield ``(code, old_path, new_path)`` for each staged change.
|
|
|
|
For add/modify/delete, ``old_path == new_path``. For rename (R) and
|
|
copy (C), they differ; uniform unpacking lets callers use one code
|
|
path. See ``git diff --name-status`` for the format.
|
|
"""
|
|
result = subprocess.run(
|
|
["git", "diff", "--cached", "--name-status"],
|
|
capture_output=True,
|
|
text=True,
|
|
check=True,
|
|
)
|
|
for line in result.stdout.splitlines():
|
|
if not line.strip():
|
|
continue
|
|
parts = line.split("\t")
|
|
if len(parts) < 2:
|
|
continue
|
|
code = parts[0][
|
|
0
|
|
] # A / M / D / R / C (R and C carry a similarity score suffix)
|
|
if code in ("R", "C") and len(parts) >= 3:
|
|
yield code, parts[1], parts[2]
|
|
else:
|
|
yield code, parts[1], parts[1]
|
|
|
|
|
|
def _is_exempt(path: str) -> bool:
|
|
# .lower() defends against case-insensitive filesystems (macOS, Windows).
|
|
return PurePosixPath(path).name.lower() in EXEMPT_FILENAMES
|
|
|
|
|
|
def main() -> int:
|
|
deleted_files = []
|
|
doc_changes = []
|
|
|
|
for code, old_path, new_path in staged_changes():
|
|
# A rename out of SCOPE_DIR removes the component from the tracked
|
|
# tree even though the file survives elsewhere — treat as deletion.
|
|
# Renames within SCOPE_DIR are legitimate refactors; don't fire.
|
|
is_deletion_from_scope = (
|
|
code == "D" and old_path.startswith(SCOPE_DIR)
|
|
) or (
|
|
code == "R"
|
|
and old_path.startswith(SCOPE_DIR)
|
|
and not new_path.startswith(SCOPE_DIR)
|
|
)
|
|
if (
|
|
is_deletion_from_scope
|
|
and old_path.endswith(".py")
|
|
and not _is_exempt(old_path)
|
|
):
|
|
deleted_files.append(old_path)
|
|
|
|
if (
|
|
code in ("A", "M")
|
|
and new_path.startswith(DOCS_DIR)
|
|
and new_path.endswith(".md")
|
|
and PurePosixPath(new_path).name.lower() != "readme.md"
|
|
):
|
|
doc_changes.append(new_path)
|
|
|
|
if deleted_files and not doc_changes:
|
|
err = sys.stderr
|
|
print(
|
|
"\n \033[31madvanced_search_system deletion without documentation\033[0m",
|
|
file=err,
|
|
)
|
|
print(" " + "=" * 50, file=err)
|
|
print(
|
|
"\n You are deleting (or renaming out of scope) these files:",
|
|
file=err,
|
|
)
|
|
for path in deleted_files:
|
|
print(f" - {path}", file=err)
|
|
print(
|
|
f"\n To unblock this commit, add a notes file at\n"
|
|
f" {DOCS_DIR}{SUGGESTED_FILENAME}\n"
|
|
f" (or extend an existing one under {DOCS_DIR}) with the following shape:\n",
|
|
file=err,
|
|
)
|
|
for line in TEMPLATE.splitlines():
|
|
print(f" {line}" if line else "", file=err)
|
|
print(
|
|
'\n Each bullet in "Useful ideas" should answer, in 1-2',
|
|
file=err,
|
|
)
|
|
print(
|
|
" sentences:",
|
|
file=err,
|
|
)
|
|
print(
|
|
" - what the component did that was different from the successor,",
|
|
file=err,
|
|
)
|
|
print(
|
|
" - why that difference was interesting (heuristic, tuning, prompt",
|
|
file=err,
|
|
)
|
|
print(
|
|
" trick, interface gap),",
|
|
file=err,
|
|
)
|
|
print(
|
|
" - and whether it was validated or exploratory.",
|
|
file=err,
|
|
)
|
|
print(
|
|
"\n Do NOT paste verbatim prompts, docstrings, or code blocks —",
|
|
file=err,
|
|
)
|
|
print(
|
|
" git already stores them via the deletion PR/commit. Link, don't",
|
|
file=err,
|
|
)
|
|
print(
|
|
" duplicate.",
|
|
file=err,
|
|
)
|
|
print(
|
|
f"\n Full convention + rationale: {DOCS_DIR}README.md\n",
|
|
file=err,
|
|
)
|
|
return 1
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|