Some checks failed
CI / test (pull_request) Has been cancelled
CI / release-exercise (pull_request) Has been cancelled
CI / self-guards (pull_request) Has been cancelled
CI / action-exercise (pull_request) Has been cancelled
CI / docs-sync-exercise (pull_request) Has been cancelled
labels / labels (pull_request) Has been cancelled
Term 3 of #188. Forgejo has no GraphQL API, so these two gathers could not be translated — there is no endpoint to translate them to. A real forgejo-runner job says so from the other side: GITHUB_GRAPHQL_URL arrives set to the empty string (probe task 278). MERGED_REF_PR_RECORDS was already a body parse; GraphQL was buying pagination, nothing semantic. OPEN_PR_ISSUES used GitHub's own parse of the closing keywords, so it becomes lib/closes_references.sh — a sibling of refs_references, sharing its LOCAL/CROSS classifier so rig#112 can still never be read as local #112 (#61). Both gathers now read /pulls, which /api/v3 and /api/v1 return in the same shape (measured on both). merged_at replaces GraphQL's states: MERGED. Bodies travel base64: jq's @tsv escapes a newline to a literal backslash-n, which a line parser reads as one line and loses every declaration after the first. The accepted delta, written down rather than rediscovered: GitHub also records closing links attached through the PR development sidebar, which live in no body. This family declares links in the body, so the delta is zero here. Refs #188
79 lines
4 KiB
Bash
79 lines
4 KiB
Bash
#!/usr/bin/env bash
|
|
# lib/closes_references.sh — "which issues does this PR body close?", parsed
|
|
# here rather than asked of a forge (issue #188, term 3).
|
|
#
|
|
# Sourced, never executed: no set -e/-u — the sourcing script owns its shell
|
|
# options, as lib/version.sh and lib/forge.sh do.
|
|
#
|
|
# WHY THIS EXISTS. issueflow-reconcile asked GitHub's GraphQL API for
|
|
# `closingIssuesReferences` — GitHub's own parse of the closing keywords in
|
|
# a PR body. **Forgejo has no GraphQL API at all**, and the runner confirms
|
|
# it from the other side: a real forgejo-runner job arrives with
|
|
# GITHUB_GRAPHQL_URL set to the empty string (probe task 278, 2026-08-02).
|
|
# So that call site could not be translated to a Forgejo endpoint — there is
|
|
# nothing to translate it to. It had to be replaced by a parse this repo
|
|
# owns, over a field both forges already return:
|
|
# `GET /repos/{owner}/{repo}/pulls` carries `number` and `body` on
|
|
# /api/v3 and /api/v1 alike (measured on both).
|
|
#
|
|
# That the replacement is honest is the point. The sibling half of the same
|
|
# GraphQL query, MERGED_REF_PR_RECORDS, was ALREADY a body parse — it pulled
|
|
# `number` and `body` and ran them through refs_references. GraphQL was
|
|
# buying pagination convenience there, nothing semantic. This file makes the
|
|
# other half symmetric: one parser this repo controls and can test, for both
|
|
# link kinds, on both forges.
|
|
#
|
|
# THE ACCEPTED DELTA, stated so it is not rediscovered as a bug: GitHub also
|
|
# records closing links attached through the pull request's development
|
|
# sidebar, which live in no body and which no body parse can see. This
|
|
# family declares its links in the body — that is what BUILDER.md's PR
|
|
# template asks for — so the delta is zero in practice here. A consumer that
|
|
# links through the sidebar would see those issues go unclosed by the sweep;
|
|
# they would need to say so in the body instead.
|
|
#
|
|
# DEPENDENCY: issue_references, from issueflow-reconcile.sh — the LOCAL /
|
|
# CROSS classifier that keeps rig#112 from ever being read as local #112
|
|
# (#61). Bash resolves function calls at call time, so the order of sourcing
|
|
# does not matter; both must simply be defined before closes_references runs.
|
|
# refs_references depends on it exactly the same way.
|
|
|
|
# closes_references — PR body on stdin -> local issue numbers this body
|
|
# declares it CLOSES, sorted, unique.
|
|
#
|
|
# The keyword set is GitHub's documented one, all three verbs in all three
|
|
# tenses. Matching is case-insensitive because bodies are written by humans
|
|
# and agents both ("Closes", "closes", "CLOSES").
|
|
#
|
|
# Deliberately NOT matched: "Refs #N". That is the other relation entirely —
|
|
# refs_references owns it, and conflating them would make every referenced
|
|
# issue look closeable, which is the post-merge transition #151 had to be
|
|
# reopened by hand over.
|
|
closes_references() {
|
|
awk '
|
|
{
|
|
line = $0
|
|
lower = tolower(line)
|
|
# Every occurrence contributes, not just the first: a body that says
|
|
# "Closes #1. Closes #2." declares two, and binding to the first
|
|
# occurrence dropped the later ones — the same defect #184 fixed in
|
|
# blocked_reference_records, kept fixed here by construction.
|
|
while (match(lower, /(^|[^[:alnum:]_-])(close[sd]?|fix(e[sd])?|resolve[sd]?)[[:space:]:]+/)) {
|
|
# BOTH cursors advance together. Advancing only `lower` left the
|
|
# next match offset indexing the ORIGINAL line, so the second
|
|
# declaration on a line came back as garbage — caught by the
|
|
# "two closes on one line" case, which is why it is a case.
|
|
rest = substr(line, RSTART + RLENGTH)
|
|
line = rest
|
|
lower = tolower(rest)
|
|
if (rest ~ /^(#|([[:alnum:]_.-]+\/)?[[:alnum:]_.-]+#)[0-9]+/) {
|
|
token = rest
|
|
# Stop at the first thing that cannot be part of a reference, so
|
|
# "Closes #12, and more prose" yields #12 and not the sentence.
|
|
sub(/[^[:alnum:]_.\/#-].*/, "", token)
|
|
print token
|
|
}
|
|
}
|
|
}
|
|
' | issue_references \
|
|
| awk -F '\t' '$1 == "LOCAL" { print $2 }' | sort -nu
|
|
}
|