fix(kvb-git-close-issues): drop false references found on a real PR
🧪 Test / 🧪 Lock + lint (push) Canceled after 0s
🧪 Test / 🧪 Lock + lint (push) Canceled after 0s
A read-only run of issues.py on infinito-nexus/core#1326 listed #0 and #1 from CI row names like WordPress#0 and docs#1, reported #1324 twice (plain and as infinito-nexus/core#1324 from the linked issue), and offered the pull request's own number. A bare # now needs no word character or slash before it and a number from 1, the required --repo maps the pull request's own repository to the plain form and merges the sources, and the pull request's number is skipped; the skill passes --repo and reads number. On #1326 only #1324 closing remains. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
6865ae718a
commit
32f58712e5
3 files changed
+26
-14
No files matched your search
@@ -18,7 +18,7 @@ body and the commits, a trailing number in the branch name, and the issues
|
|||||||
GitHub already links:
|
GitHub already links:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
gh pr view <pr> --json body,headRefName,commits,closingIssuesReferences | issues.py
|
gh pr view <pr> --json number,body,headRefName,commits,closingIssuesReferences | issues.py --repo <owner/name>
|
||||||
```
|
```
|
||||||
|
|
||||||
## Step 2: verify
|
## Step 2: verify
|
||||||
|
|||||||
@@ -2,9 +2,11 @@
|
|||||||
"""List the issues a pull request references, from `gh pr view --json` on stdin.
|
"""List the issues a pull request references, from `gh pr view --json` on stdin.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
stdin: JSON with body, headRefName, commits and closingIssuesReferences,
|
stdin: JSON with number, body, headRefName, commits and
|
||||||
as `gh pr view <pr> --json body,headRefName,commits,closingIssuesReferences`
|
closingIssuesReferences, as `gh pr view <pr> --json
|
||||||
prints it.
|
number,body,headRefName,commits,closingIssuesReferences` prints it.
|
||||||
|
--repo: owner/name of the pull request's repository; its own references
|
||||||
|
are reported without a repository.
|
||||||
--json: print the candidates as JSON instead of tab-separated lines.
|
--json: print the candidates as JSON instead of tab-separated lines.
|
||||||
|
|
||||||
Exit codes:
|
Exit codes:
|
||||||
@@ -19,7 +21,7 @@ import re
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
KEYWORD = r"(?:close[sd]?|fix(?:e[sd])?|resolve[sd]?)"
|
KEYWORD = r"(?:close[sd]?|fix(?:e[sd])?|resolve[sd]?)"
|
||||||
REF = r"(?:(?P<repo>[\w.-]+/[\w.-]+)#|https://github\.com/(?P<url>[\w.-]+/[\w.-]+)/issues/|#)(?P<num>\d+)"
|
REF = r"(?:(?P<repo>[\w.-]+/[\w.-]+)#|https://github\.com/(?P<url>[\w.-]+/[\w.-]+)/issues/|(?<![\w/])#)(?P<num>[1-9]\d*)"
|
||||||
PATTERN = re.compile(rf"(?P<kw>\b{KEYWORD}\b:?\s+)?{REF}", re.IGNORECASE)
|
PATTERN = re.compile(rf"(?P<kw>\b{KEYWORD}\b:?\s+)?{REF}", re.IGNORECASE)
|
||||||
BRANCH = re.compile(r"[/-](\d+)$")
|
BRANCH = re.compile(r"[/-](\d+)$")
|
||||||
|
|
||||||
@@ -40,7 +42,7 @@ def scan(text: str, source: str, found: dict) -> None:
|
|||||||
entry["sources"].append(source)
|
entry["sources"].append(source)
|
||||||
|
|
||||||
|
|
||||||
def candidates(pr: dict) -> list[dict]:
|
def candidates(pr: dict, own: str) -> list[dict]:
|
||||||
found: dict = {}
|
found: dict = {}
|
||||||
scan(pr.get("body", ""), "body", found)
|
scan(pr.get("body", ""), "body", found)
|
||||||
for commit in pr.get("commits", []):
|
for commit in pr.get("commits", []):
|
||||||
@@ -57,14 +59,20 @@ def candidates(pr: dict) -> list[dict]:
|
|||||||
entry = found.setdefault(key, {"closing": False, "sources": []})
|
entry = found.setdefault(key, {"closing": False, "sources": []})
|
||||||
entry["closing"] = True
|
entry["closing"] = True
|
||||||
entry["sources"].append("linked")
|
entry["sources"].append("linked")
|
||||||
return [
|
merged: dict = {}
|
||||||
{"repo": repo, "issue": num, **entry}
|
for (repo, num), entry in found.items():
|
||||||
for (repo, num), entry in sorted(found.items())
|
key = ("" if repo.lower() == own.lower() else repo, num)
|
||||||
]
|
if key == ("", pr.get("number")):
|
||||||
|
continue
|
||||||
|
target = merged.setdefault(key, {"closing": False, "sources": []})
|
||||||
|
target["closing"] = target["closing"] or entry["closing"]
|
||||||
|
target["sources"] += [s for s in entry["sources"] if s not in target["sources"]]
|
||||||
|
return [{"repo": repo, "issue": num, **entry} for (repo, num), entry in sorted(merged.items())]
|
||||||
|
|
||||||
|
|
||||||
def main() -> int:
|
def main() -> int:
|
||||||
parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
|
parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
|
||||||
|
parser.add_argument("--repo", required=True)
|
||||||
parser.add_argument("--json", action="store_true")
|
parser.add_argument("--json", action="store_true")
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
try:
|
try:
|
||||||
@@ -73,7 +81,7 @@ def main() -> int:
|
|||||||
fail("stdin is not the JSON of gh pr view --json")
|
fail("stdin is not the JSON of gh pr view --json")
|
||||||
if not isinstance(pr, dict):
|
if not isinstance(pr, dict):
|
||||||
fail("stdin is not a pull request object")
|
fail("stdin is not a pull request object")
|
||||||
rows = candidates(pr)
|
rows = candidates(pr, args.repo)
|
||||||
if args.json:
|
if args.json:
|
||||||
print(json.dumps(rows, indent=1))
|
print(json.dumps(rows, indent=1))
|
||||||
return 0
|
return 0
|
||||||
|
|||||||
@@ -11,7 +11,11 @@ from pathlib import Path
|
|||||||
REPO_ROOT = Path(__file__).resolve().parents[1]
|
REPO_ROOT = Path(__file__).resolve().parents[1]
|
||||||
SCRIPT = REPO_ROOT / "skills" / "kvb-git-close-issues" / "scripts" / "issues.py"
|
SCRIPT = REPO_ROOT / "skills" / "kvb-git-close-issues" / "scripts" / "issues.py"
|
||||||
PR = {
|
PR = {
|
||||||
"body": "Fixes #12 and touches #13.\nCloses: other/repo#7\nSee https://github.com/a/b/issues/9",
|
"number": 40,
|
||||||
|
"body": (
|
||||||
|
"Fixes #12 and touches #13.\nCloses: other/repo#7\nSee https://github.com/a/b/issues/9\n"
|
||||||
|
"Rows WordPress#0 and docs#1 are not issues; this is #40."
|
||||||
|
),
|
||||||
"headRefName": "fix/sys-ctl-hlth-runtime/vulnerability-engines/1324",
|
"headRefName": "fix/sys-ctl-hlth-runtime/vulnerability-engines/1324",
|
||||||
"commits": [
|
"commits": [
|
||||||
{"oid": "abcdef1234567", "messageHeadline": "fix: guard", "messageBody": "Resolved #13"},
|
{"oid": "abcdef1234567", "messageHeadline": "fix: guard", "messageBody": "Resolved #13"},
|
||||||
@@ -25,7 +29,7 @@ PR = {
|
|||||||
|
|
||||||
def _run(payload: str) -> subprocess.CompletedProcess:
|
def _run(payload: str) -> subprocess.CompletedProcess:
|
||||||
return subprocess.run(
|
return subprocess.run(
|
||||||
[sys.executable, str(SCRIPT), "--json"],
|
[sys.executable, str(SCRIPT), "--repo", "Owner/repo", "--json"],
|
||||||
input=payload,
|
input=payload,
|
||||||
check=False,
|
check=False,
|
||||||
capture_output=True,
|
capture_output=True,
|
||||||
@@ -46,7 +50,7 @@ class TestIssues(unittest.TestCase):
|
|||||||
("other/repo", 7): (True, ["body"]),
|
("other/repo", 7): (True, ["body"]),
|
||||||
("a/b", 9): (False, ["body"]),
|
("a/b", 9): (False, ["body"]),
|
||||||
("", 1324): (False, ["branch"]),
|
("", 1324): (False, ["branch"]),
|
||||||
("owner/repo", 21): (True, ["linked"]),
|
("", 21): (True, ["linked"]),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user