From 1672e9490138e7f299bb6ec532c5130ddd06c2c0 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 21 May 2026 22:12:07 +0000 Subject: [PATCH] fix(ci): use jq filter in close_duplicate_issues to avoid JSON parse error The script previously called `gh api --paginate` and split the raw response on newlines before json.loads()'ing each line. This is unsafe because each page is a JSON array on a single conceptual line, and any issue `body` field containing literal newlines makes splitlines() cut the JSON apart, producing fragments that fail with `Unterminated string starting at: line 1 column ...`. Pass `--jq '.[]'` so gh emits each issue as a single compact JSON object per line (with embedded newlines escaped). The loop then parses one object per line safely. Co-authored-by: Krrish Dholakia --- .github/scripts/close_duplicate_issues.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/.github/scripts/close_duplicate_issues.py b/.github/scripts/close_duplicate_issues.py index ec522af4f88..93cee8e8cab 100755 --- a/.github/scripts/close_duplicate_issues.py +++ b/.github/scripts/close_duplicate_issues.py @@ -40,27 +40,28 @@ def gh(*args: str) -> str: def fetch_open_issues(repo: str | None) -> list[dict]: - """Fetch all open issues (excluding PRs) via gh api --paginate.""" + """Fetch all open issues (excluding PRs) via gh api --paginate. + + Uses ``--jq '.[]'`` so each issue is emitted as a single compact JSON + object per line. Splitting on newlines is unsafe on the raw array output + because issue ``body`` fields can contain literal newlines that confuse + line-based JSON parsing. + """ if repo: endpoint = ( f"repos/{repo}/issues?state=open&per_page=100&sort=created&direction=asc" ) else: endpoint = "repos/{owner}/{repo}/issues?state=open&per_page=100&sort=created&direction=asc" - cmd = ["api", "--paginate", endpoint] + cmd = ["api", "--paginate", "--jq", ".[]", endpoint] raw = gh(*cmd) - # gh --paginate concatenates JSON arrays, so we may get multiple arrays issues = [] - for line in raw.strip().splitlines(): + for line in raw.splitlines(): line = line.strip() if not line: continue - parsed = json.loads(line) - if isinstance(parsed, list): - issues.extend(parsed) - else: - issues.append(parsed) + issues.append(json.loads(line)) # Filter out pull requests (they also appear in the issues endpoint) return [i for i in issues if "pull_request" not in i]