All checks were successful
CI / test (pull_request) Successful in 3m15s
CI / release-exercise (pull_request) Successful in 12s
CI / self-guards (pull_request) Successful in 7s
CI / action-exercise (pull_request) Successful in 5s
CI / docs-sync-exercise (pull_request) Successful in 6s
Refs guard / refs-not-closing (pull_request) Successful in 6s
labels / labels (pull_request) Successful in 8s
The action's entire gather was one GraphQL query asking GitHub for its own parse of the closing keywords. Forgejo serves no GraphQL at all — /api/graphql 404s here and a forgejo-runner job arrives with GITHUB_GRAPHQL_URL empty — so there was nothing to translate it to. It is re-expressed, as #188 re-expressed its own two GraphQL sites, over two reads both backends serve plus this repo's own parser. The graph was called authoritative for including "closing keywords and sidebar links". Those halves resolve differently here: Forgejo has no sidebar-link concept, so nothing is lost there, but it DOES honour closing keywords in commit messages. A body-only port would miss a PR that closes an issue from a commit subject — exactly the contradiction this action exists to catch — so the closing set unions the body and every commit message. The hasNextPage refusal is relocated, not dropped: --paginate carries the forgejo backend's x-total-count completeness proof, and a short gather refuses rather than returning a partial verdict. lib/issue_references.sh extracts the LOCAL/CROSS classifier from issueflow-reconcile's executable. closes_references.sh's header recorded that dependency in prose; a composite action cannot source a reconciler to borrow one function, because sourcing a reconciler runs one. refs-guard.yml's github-only gate is removed in the same change. A portable action behind that gate is a guard that passes by never running. The contract test drives the boundary on BOTH backends with stubs at the transport. Mutations: body-only parse reds 4 cases, dropping --paginate reds the partial-gather case, ignoring a failed read reds 9. Refs #199
88 lines
4.2 KiB
Bash
Executable file
88 lines
4.2 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
# The composite action's executable boundary (#218). Keeping the gather here
|
|
# lets the offline contract test replace the forge and prove that failed and
|
|
# partial reads cannot accidentally produce a green verdict.
|
|
#
|
|
# THE GATHER IS REST, THROUGH THE SHIM (#199). It was a single GraphQL query
|
|
# issued through `gh`, asking GitHub for `closingIssuesReferences` — its own
|
|
# parse of the closing keywords. Forgejo serves no GraphQL surface at all:
|
|
# `/api/graphql` 404s on this instance, and a real forgejo-runner job arrives
|
|
# with GITHUB_GRAPHQL_URL set to the empty string (lib/forge.sh's header).
|
|
# There was nothing to translate it to, so it is re-expressed — exactly as
|
|
# #188 re-expressed its own two GraphQL sites — over two reads both backends
|
|
# already serve, plus a parser this repo owns.
|
|
#
|
|
# WHAT THE GRAPH GAVE THAT TWO READS MUST REPLACE. This file used to call the
|
|
# graph "authoritative because it includes both closing keywords and sidebar
|
|
# links". Those two halves resolve differently here:
|
|
#
|
|
# sidebar links Forgejo has no such concept — an issue is closed by a
|
|
# keyword, never by a manual link. Nothing is lost.
|
|
# commit messages Forgejo DOES honour closing keywords in commit messages.
|
|
# A body-only parse would miss a PR that closes an issue
|
|
# from a commit subject and let through exactly the
|
|
# contradiction this action exists to catch.
|
|
#
|
|
# Hence both reads, unioned. The commit half is not optional.
|
|
# shellcheck source=lib/forge.sh
|
|
. "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/../../lib/forge.sh"
|
|
# shellcheck source=lib/issue_references.sh
|
|
. "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/../../lib/issue_references.sh"
|
|
# shellcheck source=lib/closes_references.sh
|
|
. "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/../../lib/closes_references.sh"
|
|
|
|
# No CEREMONY_FORGE_CLIENT declaration any more (#199 removes #198's): this
|
|
# file speaks the shim, not a client. Fail CLOSED at the action boundary all
|
|
# the same — "this action cannot produce a verdict" is the ACTION's contract
|
|
# and stays a refusal, while "this check should not block the board" is the
|
|
# CALLER's decision (@codex-reviewer-andresmgsl, #198).
|
|
forge_preflight || exit 1
|
|
forge_select "" || exit 1
|
|
|
|
REPO="${REPO:-${GITHUB_REPOSITORY:-}}"
|
|
[ -n "$REPO" ] || {
|
|
echo "refs-not-closing: set REPO or GITHUB_REPOSITORY to owner/name" >&2
|
|
exit 1
|
|
}
|
|
[ -n "${PR_NUMBER:-}" ] || {
|
|
echo "refs-not-closing: pull request number is unavailable" >&2
|
|
exit 1
|
|
}
|
|
|
|
body_file="$(mktemp)"
|
|
closing_file="$(mktemp)"
|
|
trap 'rm -f "$body_file" "$closing_file"' EXIT
|
|
|
|
# A read that fails must never reach the parser: an empty body parses to an
|
|
# empty closing set, which is a PASSING verdict this action never earned.
|
|
# `set -e` covers the assignment, and the explicit checks below name which
|
|
# read failed rather than leaving the operator to guess.
|
|
if ! forge_api "repos/$REPO/pulls/$PR_NUMBER" --jq '.body // ""' >"$body_file"; then
|
|
echo "refs-not-closing: could not read PR $PR_NUMBER's body — refusing a verdict" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# --paginate carries the completeness proof: the forgejo backend walks pages
|
|
# and then compares what it collected against the server's declared
|
|
# x-total-count, refusing a short gather (#188, #4699). That IS this action's
|
|
# `hasNextPage` refusal, relocated rather than reinvented — upstream refused
|
|
# past 100 closing references rather than issue a partial verdict, and an
|
|
# incomplete commit read is the same failure wearing REST's clothes.
|
|
commits_file="$(mktemp)"
|
|
trap 'rm -f "$body_file" "$closing_file" "$commits_file"' EXIT
|
|
if ! forge_api --paginate "repos/$REPO/pulls/$PR_NUMBER/commits" \
|
|
--jq '.[].commit.message' >"$commits_file"; then
|
|
echo "refs-not-closing: could not read PR $PR_NUMBER's commits completely — refusing a partial verdict" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# The union. closes_references is line-oriented, so concatenating the body and
|
|
# every commit message and parsing once IS the union of parsing each — and it
|
|
# keeps one parse to reason about instead of two that could drift.
|
|
cat "$body_file" "$commits_file" | closes_references >"$closing_file"
|
|
|
|
mapfile -t closing_issues <"$closing_file"
|
|
bash "$GITHUB_ACTION_PATH/refs-not-closing.sh" \
|
|
"$body_file" "${closing_issues[@]}"
|