forked from heavy-duty/ceremony
Compare commits
No commits in common. "afcc95fbbd2cc864a363b3defdc533f49e63b5b6" and "17a13685a72c60f1dc1dab8b49cf34dfcac6e959" have entirely different histories.
afcc95fbbd
...
17a13685a7
5 changed files with 10 additions and 157 deletions
|
|
@ -272,7 +272,7 @@ set_required_bots() { # the PR author is recused by construction
|
||||||
# HEAD_SHA the PR's current head commit
|
# HEAD_SHA the PR's current head commit
|
||||||
# BASE_SHA the PR's base branch head (the release-shape guard's ref)
|
# BASE_SHA the PR's base branch head (the release-shape guard's ref)
|
||||||
# REQUESTED newline-separated logins with a review currently requested
|
# REQUESTED newline-separated logins with a review currently requested
|
||||||
# REVIEWS_JSON JSON array of submitted, gradeable reviews
|
# REVIEWS_JSON JSON array of submitted (non-PENDING) reviews
|
||||||
# MERGEABLE MERGEABLE | CONFLICTING | UNKNOWN (GitHub's own verdict)
|
# MERGEABLE MERGEABLE | CONFLICTING | UNKNOWN (GitHub's own verdict)
|
||||||
# CHECKS SUCCESS | FAILURE | PENDING | NONE (the check rollup)
|
# CHECKS SUCCESS | FAILURE | PENDING | NONE (the check rollup)
|
||||||
# LABELS newline-separated labels currently on the PR
|
# LABELS newline-separated labels currently on the PR
|
||||||
|
|
@ -444,25 +444,18 @@ bot_verdict() { # $1 = login → MISSING | BLOCK | APPROVE | STALE | FEEDBACK
|
||||||
if [ -z "$review" ]; then echo MISSING; return; fi
|
if [ -z "$review" ]; then echo MISSING; return; fi
|
||||||
state="$(jq -r '.state' <<<"$review")"
|
state="$(jq -r '.state' <<<"$review")"
|
||||||
commit="$(jq -r '.commit_id' <<<"$review")"
|
commit="$(jq -r '.commit_id' <<<"$review")"
|
||||||
# This case grades a submitted verdict. The ingestion allow-list answers the
|
|
||||||
# separate question of whether a row is a submitted review at all (#235).
|
|
||||||
case "$state" in
|
case "$state" in
|
||||||
CHANGES_REQUESTED | REQUEST_CHANGES)
|
CHANGES_REQUESTED)
|
||||||
# blocks at ANY head — both forges' semantic: only a newer review from
|
# blocks at ANY head — GitHub's own semantic: only a newer review
|
||||||
# the same reviewer clears it
|
# from the same reviewer clears it
|
||||||
echo BLOCK ;;
|
echo BLOCK ;;
|
||||||
APPROVED)
|
APPROVED)
|
||||||
if [ "$commit" = "$HEAD_SHA" ]; then echo APPROVE; else echo STALE; fi ;;
|
if [ "$commit" = "$HEAD_SHA" ]; then echo APPROVE; else echo STALE; fi ;;
|
||||||
COMMENTED | COMMENT)
|
|
||||||
# A comment is a non-verdict. The machine does not read bodies — if the
|
|
||||||
# comment is really an agreement, the AUTHOR says so by requesting the
|
|
||||||
# human's review.
|
|
||||||
echo FEEDBACK ;;
|
|
||||||
*)
|
*)
|
||||||
# An unknown state is not evidence that a reviewer answered. Keep the
|
# COMMENTED and anything else: a non-verdict. The machine does not
|
||||||
# round open and make the next forge vocabulary surprise visible (#235).
|
# read bodies — if the comment is really an agreement, the AUTHOR
|
||||||
log "$1: unrecognised review state $state" >&2
|
# says so by requesting the human's review.
|
||||||
echo MISSING ;;
|
echo FEEDBACK ;;
|
||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1061,15 +1054,9 @@ main() {
|
||||||
HEAD_SHA="$(jq -r '.head.sha' <<<"$PR_JSON")"
|
HEAD_SHA="$(jq -r '.head.sha' <<<"$PR_JSON")"
|
||||||
BASE_SHA="$(jq -r '.base.sha' <<<"$PR_JSON")"
|
BASE_SHA="$(jq -r '.base.sha' <<<"$PR_JSON")"
|
||||||
LABELS="$(jq -r '.labels[].name' <<<"$PR_JSON")"
|
LABELS="$(jq -r '.labels[].name' <<<"$PR_JSON")"
|
||||||
# This allow-list answers whether a row is a submitted, gradeable review;
|
# PENDING reviews are unsubmitted drafts in someone's browser — not a verdict
|
||||||
# bot_verdict separately answers what that submitted verdict says (#235).
|
|
||||||
# PENDING drafts and Forgejo REQUEST_REVIEW request rows are not reviews.
|
|
||||||
REVIEWS_JSON="$(forge_api --paginate "repos/$REPO/pulls/$n/reviews" --jq '.[]' \
|
REVIEWS_JSON="$(forge_api --paginate "repos/$REPO/pulls/$n/reviews" --jq '.[]' \
|
||||||
| jq -s '[.[] | select(.state == "APPROVED"
|
| jq -s '[.[] | select(.state != "PENDING")]')"
|
||||||
or .state == "CHANGES_REQUESTED"
|
|
||||||
or .state == "REQUEST_CHANGES"
|
|
||||||
or .state == "COMMENTED"
|
|
||||||
or .state == "COMMENT")]')"
|
|
||||||
# Read AFTER the reviews, because the raw field is not portable: Forgejo
|
# Read AFTER the reviews, because the raw field is not portable: Forgejo
|
||||||
# never clears it, so it is intersected with who still owes a verdict on
|
# never clears it, so it is intersected with who still owes a verdict on
|
||||||
# this head (#188 term 4). A no-op on GitHub, which clears it itself.
|
# this head (#188 term 4). A no-op on GitHub, which clears it itself.
|
||||||
|
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Forge release 0.6.2 consolidates upstream 0.6.1 doctrine from upstream #316, #311, #330, and #329, plus upstream 0.6.2's #336 clarification (#231).
|
|
||||||
|
|
||||||
- Upstream 0.6.3's #343 membership record and #327 gate fixes land through Forgejo-adapted reconciler changes rather than upstream bytes (#231).
|
|
||||||
|
|
||||||
- The upstream drill-record fixes at `86dc2eb`, `a72085b`, and `13ffb0d`, plus upstream 0.7.0–0.7.4, remain explicitly deferred (#231).
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Forgejo review requests no longer count as verdicts, while its blocking and comment states now grade like their GitHub equivalents (#235).
|
|
||||||
|
|
@ -17,27 +17,6 @@ resolution is standing and does not get re-decided each sync.
|
||||||
Worked example throughout: the `0.6.0` sync (#197, #198), which merged upstream
|
Worked example throughout: the `0.6.0` sync (#197, #198), which merged upstream
|
||||||
`8c3a4d1` onto `dad99dd` and took four heads to get green.
|
`8c3a4d1` onto `dad99dd` and took four heads to get green.
|
||||||
|
|
||||||
## Sync ledger
|
|
||||||
|
|
||||||
### 2026-08-24 — upstream 0.6.1–0.6.3 into forge 0.6.2
|
|
||||||
|
|
||||||
- **Consumed through:** upstream tag `upstream-0.6.3`
|
|
||||||
(`cf2892148305ab18ff234fa0a2ef28ab6fef6c0f`). The `upstream-` prefix is
|
|
||||||
mandatory in this record because bare `0.6.x` names collide with this
|
|
||||||
forge's own releases and can identify different trees.
|
|
||||||
- **Previous baseline:** upstream `0.6.0`, merge-base
|
|
||||||
`8c3a4d1dee2bdb5ac06a632a285bb65ab2615214`. This adoption ported the
|
|
||||||
upstream changes in #229 and #230 instead of merging upstream ancestry, so
|
|
||||||
`.upstream-ref` and the Git merge-base intentionally remain at that commit;
|
|
||||||
`upstream-0.6.3` is the new content baseline for the next campaign.
|
|
||||||
- **Adapted here:** the membership and gate changes were applied to the
|
|
||||||
Forgejo reconciler and its tests, and the upstream CONTRIBUTING changes
|
|
||||||
were reconciled with this forge's roster and conventions rather than copied
|
|
||||||
byte-for-byte.
|
|
||||||
- **Deferred:** upstream's drill-record bookkeeping commits `86dc2eb`,
|
|
||||||
`a72085b`, and `13ffb0d` correct drill files this forge does not mirror.
|
|
||||||
Upstream 0.7.0–0.7.4 is the next sync campaign's standing queue.
|
|
||||||
|
|
||||||
## The standing resolutions
|
## The standing resolutions
|
||||||
|
|
||||||
These recur every sync. They are decided; re-deciding them is the cost this
|
These recur every sync. They are decided; re-deciding them is the cost this
|
||||||
|
|
|
||||||
|
|
@ -172,20 +172,6 @@ $BOT2
|
||||||
$BOT3" REVIEWS_JSON='[]'
|
$BOT3" REVIEWS_JSON='[]'
|
||||||
expect "requested bots mean bots-reviewing" state:bots-reviewing "$(decide_state)"
|
expect "requested bots mean bots-reviewing" state:bots-reviewing "$(decide_state)"
|
||||||
|
|
||||||
# Forgejo materializes each live request as a REQUEST_REVIEW row. Those rows
|
|
||||||
# are not submitted verdicts (#235): they must leave all three logins
|
|
||||||
# outstanding, so an opening round stays with the panel rather than falling
|
|
||||||
# through to the builder as three comment-only answers.
|
|
||||||
REVIEWS_JSON="$(reviews \
|
|
||||||
"$(rev "$BOT1" REQUEST_REVIEW "" "" t1)" \
|
|
||||||
"$(rev "$BOT2" REQUEST_REVIEW "" "" t2)" \
|
|
||||||
"$(rev "$BOT3" REQUEST_REVIEW "" "" t3)")"
|
|
||||||
REQUESTED="$(outstanding_requests "$BOT1
|
|
||||||
$BOT2
|
|
||||||
$BOT3" 2>"$RTMP/request-round-log")"
|
|
||||||
expect "three Forgejo request rows keep the opening round with the panel" \
|
|
||||||
state:bots-reviewing "$(round_state)"
|
|
||||||
|
|
||||||
# -- a bot that never reviewed keeps the round open ---------------------------
|
# -- a bot that never reviewed keeps the round open ---------------------------
|
||||||
# With a live request that is the bots' ball; with NO request outstanding it
|
# With a live request that is the bots' ball; with NO request outstanding it
|
||||||
# is the agent's, because nothing is coming until somebody asks.
|
# is the agent's, because nothing is coming until somebody asks.
|
||||||
|
|
@ -229,25 +215,6 @@ REVIEWS_JSON="$(reviews \
|
||||||
"$(rev "$BOT3" APPROVED head1 "" t3)")"
|
"$(rev "$BOT3" APPROVED head1 "" t3)")"
|
||||||
expect "changes-requested blocks even from an old head" state:addressing "$(decide_state)"
|
expect "changes-requested blocks even from an old head" state:addressing "$(decide_state)"
|
||||||
|
|
||||||
# bot_verdict grades submitted states from both forges (#235). Each direct
|
|
||||||
# assertion names one arm so a later vocabulary regression cannot hide behind
|
|
||||||
# round_state's shared BLOCK/FEEDBACK handling.
|
|
||||||
expect "GitHub CHANGES_REQUESTED grades as a block" BLOCK \
|
|
||||||
"$(bot_verdict "$BOT1")"
|
|
||||||
REVIEWS_JSON="$(reviews "$(rev "$BOT1" REQUEST_CHANGES old1 "blockers below" t1)")"
|
|
||||||
expect "Forgejo REQUEST_CHANGES grades as a block" BLOCK \
|
|
||||||
"$(bot_verdict "$BOT1")"
|
|
||||||
REVIEWS_JSON="$(reviews "$(rev "$BOT1" COMMENT head1 "non-blocking note" t1)")"
|
|
||||||
expect "Forgejo COMMENT grades as feedback" FEEDBACK \
|
|
||||||
"$(bot_verdict "$BOT1")"
|
|
||||||
REVIEWS_JSON="$(reviews "$(rev "$BOT1" FUTURE_FORGE_STATE head1 "" t1)")"
|
|
||||||
bot_verdict "$BOT1" >"$RTMP/unknown-verdict" 2>"$RTMP/unknown-verdict-log"
|
|
||||||
expect "an unrecognised review state is conservatively missing" MISSING \
|
|
||||||
"$(cat "$RTMP/unknown-verdict")"
|
|
||||||
expect "an unrecognised review state logs the login and spelling" yes \
|
|
||||||
"$(grep -qF "$BOT1: unrecognised review state FUTURE_FORGE_STATE" \
|
|
||||||
"$RTMP/unknown-verdict-log" && echo yes || echo no)"
|
|
||||||
|
|
||||||
# -- a stale approval must not promote unreviewed code ------------------------
|
# -- a stale approval must not promote unreviewed code ------------------------
|
||||||
REVIEWS_JSON="$(reviews \
|
REVIEWS_JSON="$(reviews \
|
||||||
"$(rev "$BOT1" APPROVED old1 "" t1)" \
|
"$(rev "$BOT1" APPROVED old1 "" t1)" \
|
||||||
|
|
@ -286,24 +253,6 @@ REQUESTED="$HUMAN"
|
||||||
expect "re-requested human is needs-human again" state:needs-human "$(decide_state)"
|
expect "re-requested human is needs-human again" state:needs-human "$(decide_state)"
|
||||||
REQUESTED=""
|
REQUESTED=""
|
||||||
|
|
||||||
# Forgejo's human-block spelling carries the same meaning (#235). This is
|
|
||||||
# independently observable because only BLOCK prevents state:needs-human once
|
|
||||||
# every bot approves.
|
|
||||||
REVIEWS_JSON="$(reviews \
|
|
||||||
"$(rev "$BOT1" APPROVED head1 "" t1)" \
|
|
||||||
"$(rev "$BOT2" APPROVED head1 "" t2)" \
|
|
||||||
"$(rev "$BOT3" APPROVED head1 "" t3)" \
|
|
||||||
"$(rev "$HUMAN" REQUEST_CHANGES head1 "not yet" t4)")"
|
|
||||||
expect "Forgejo human request-changes with bots approving is addressing" \
|
|
||||||
state:addressing "$(decide_state)"
|
|
||||||
REVIEWS_JSON="$(reviews \
|
|
||||||
"$(rev "$BOT1" APPROVED head1 "" t1)" \
|
|
||||||
"$(rev "$BOT2" APPROVED head1 "" t2)" \
|
|
||||||
"$(rev "$BOT3" APPROVED head1 "" t3)" \
|
|
||||||
"$(rev "$HUMAN" APPROVED head1 "" t4)")"
|
|
||||||
expect "the same Forgejo-shaped fixture with human approval reaches needs-human" \
|
|
||||||
state:needs-human "$(decide_state)"
|
|
||||||
|
|
||||||
# -- an old human comment must not wedge the handoff (codex, #85 round 3) -----
|
# -- an old human comment must not wedge the handoff (codex, #85 round 3) -----
|
||||||
REVIEWS_JSON="$(reviews \
|
REVIEWS_JSON="$(reviews \
|
||||||
"$(rev "$HUMAN" COMMENTED old1 "early thoughts" t0)" \
|
"$(rev "$HUMAN" COMMENTED old1 "early thoughts" t0)" \
|
||||||
|
|
@ -1639,49 +1588,6 @@ expect "...with no 'reconciled.' token in the output" \
|
||||||
expect "...naming the attempt that did not happen" \
|
expect "...naming the attempt that did not happen" \
|
||||||
yes "$(grep -q 'label edit FAILED' <<<"$sf_out" && echo yes || echo no)"
|
yes "$(grep -q 'label edit FAILED' <<<"$sf_out" && echo yes || echo no)"
|
||||||
|
|
||||||
# Drive the ingestion expression through main(), independently of
|
|
||||||
# bot_verdict (#235). Capturing REVIEWS_JSON at the outstanding_requests
|
|
||||||
# boundary proves REQUEST_REVIEW never reaches the grader; the COMMENT and
|
|
||||||
# APPROVED controls prove both gradeable Forgejo states and rows generally
|
|
||||||
# survive the filter.
|
|
||||||
review_filter_probe() {
|
|
||||||
(
|
|
||||||
REPO=owner/repo
|
|
||||||
LABELS_CONF="$FIXTURE_CONF"
|
|
||||||
CEREMONY_FORGE=github
|
|
||||||
# shellcheck disable=SC2317 # reached through the forge backend, not called directly (#188)
|
|
||||||
gh() {
|
|
||||||
if [ "$1" = label ] && [ "$2" = list ]; then core_label_rows | cut -d'|' -f1; return 0; fi
|
|
||||||
if [ "$1" = pr ] && [ "$2" = list ]; then printf '%s\n' 601; return 0; fi
|
|
||||||
if [ "$1" = pr ] && [ "$2" = view ]; then
|
|
||||||
jq -n '{mergeable:"MERGEABLE",statusCheckRollup:[]}'
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
if [ "$1" = issue ] && [ "$2" = edit ]; then return 0; fi
|
|
||||||
case "$(forge_stub_path "$*")" in
|
|
||||||
*repos/owner/repo/pulls/601/reviews*)
|
|
||||||
jq -nc \
|
|
||||||
'{user:{login:"fixture-bot-one"},state:"REQUEST_REVIEW",commit_id:"",submitted_at:"2026-08-22T00:46:05Z"},
|
|
||||||
{user:{login:"fixture-bot-three"},state:"COMMENT",commit_id:"head1",submitted_at:"2026-08-22T00:46:35Z"},
|
|
||||||
{user:{login:"fixture-bot-two"},state:"APPROVED",commit_id:"head1",submitted_at:"2026-08-22T00:47:05Z"}' ;;
|
|
||||||
*/pulls/601)
|
|
||||||
jq -n '{draft:true,user:{login:"fixture-builder"},head:{sha:"head1"},base:{sha:"base1"},
|
|
||||||
labels:[{name:"state:building"}],requested_reviewers:[],
|
|
||||||
created_at:"2026-08-22T00:45:00Z"}' ;;
|
|
||||||
*) printf '[]\n' ;;
|
|
||||||
esac
|
|
||||||
}
|
|
||||||
# shellcheck disable=SC2317 # main invokes the probe override indirectly
|
|
||||||
outstanding_requests() {
|
|
||||||
printf '%s\n' "$REVIEWS_JSON" >"$RTMP/gradeable-reviews.json"
|
|
||||||
}
|
|
||||||
main >/dev/null
|
|
||||||
)
|
|
||||||
}
|
|
||||||
review_filter_probe
|
|
||||||
expect "REQUEST_REVIEW is removed before REVIEWS_JSON reaches the grader" \
|
|
||||||
COMMENT,APPROVED "$(jq -r 'map(.state) | join(",")' "$RTMP/gradeable-reviews.json")"
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# outstanding_requests — the portable "who still owes a verdict" (#188 term 4)
|
# outstanding_requests — the portable "who still owes a verdict" (#188 term 4)
|
||||||
#
|
#
|
||||||
|
|
@ -1709,15 +1615,6 @@ expect "a stale approval still owes a verdict" "$BOT3" \
|
||||||
expect "a reviewer who never reviewed still owes one" "nobody" \
|
expect "a reviewer who never reviewed still owes one" "nobody" \
|
||||||
"$(outstanding_requests "nobody")"
|
"$(outstanding_requests "nobody")"
|
||||||
|
|
||||||
REVIEWS_JSON="$(reviews \
|
|
||||||
"$(rev "$BOT1" REQUEST_REVIEW "" "" 2026-08-22T00:46:05Z)")"
|
|
||||||
expect "a Forgejo request row is not an answer and leaves the login outstanding" \
|
|
||||||
"$BOT1" "$(outstanding_requests "$BOT1" 2>"$RTMP/request-outstanding-log")"
|
|
||||||
REVIEWS_JSON="$(reviews \
|
|
||||||
"$(rev "$BOT1" APPROVED head1 "" 2026-08-01T00:00:00Z)" \
|
|
||||||
"$(rev "$BOT2" CHANGES_REQUESTED head1 "" 2026-08-01T00:00:00Z)" \
|
|
||||||
"$(rev "$BOT3" APPROVED head0 "" 2026-07-01T00:00:00Z)")"
|
|
||||||
|
|
||||||
# The Forgejo shape, end to end: the field lists all three long after every
|
# The Forgejo shape, end to end: the field lists all three long after every
|
||||||
# verdict landed. Only the stale one may survive the filter.
|
# verdict landed. Only the stale one may survive the filter.
|
||||||
expect "the never-cleared forgejo field collapses to who actually owes" \
|
expect "the never-cleared forgejo field collapses to who actually owes" \
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue