feat(framework/tools): orchestration helpers — lane-brief.sh + ci-wait.sh (#546)
Two additive orchestration tools distilled from forensic analysis of a live U-Connect delivery session, both adopted by the live orchestrator before this contribution. lane-brief.sh (git/): one call returns the CURRENT open issue set for a repo lane (milestone/label) from Gitea, classified for dispatch. Defeats stale worker self-report (workers brief from static notes and report already-CLOSED issues as "todo"). Closed excluded by definition; partitions by PR-linkage (reliable) not assignee/dependency (empty in this fleet). Login resolution: -L > $GITEA_LOGIN > owner inference > detect-platform.sh fallback. ci-wait.sh (woodpecker/): blocks until pipeline(s) reach terminal state, wrapping pipeline-status.sh (resolves repo->id, instance-aware). Replaces hand-rolled `curl .../repos/1/pipelines/$n` loops that hardcode repo id 1. Intended as a Monitor command + long (>=1500s) timed fallback, not a tight poll. Exit 0=all success / 1=terminal non-success / 2=usage / 3=timeout. Tested live vs usc/uconnect. README updated. No version bump (separate release PR per convention). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Kt2D8TsnDwhtzEAPijsNmR
This commit is contained in:
98
packages/mosaic/framework/tools/git/lane-brief.sh
Executable file
98
packages/mosaic/framework/tools/git/lane-brief.sh
Executable file
@@ -0,0 +1,98 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# lane-brief.sh — live dispatch brief for a repo "lane" (milestone/label), straight
|
||||
# from current Gitea state. Defeats stale worker self-report: workers brief from
|
||||
# static notes and routinely report issues "todo" that are already CLOSED, forcing
|
||||
# the orchestrator to re-verify each one before dispatch. This returns the CURRENT
|
||||
# open set, classified for dispatch, in one call.
|
||||
#
|
||||
# Usage:
|
||||
# lane-brief.sh -r <owner/repo> [-m <milestone>] [-l <label>] [-L <login>] [-n <limit>]
|
||||
# lane-brief.sh -r usc/uconnect -m "M2M Part Search (0.0.45)"
|
||||
# lane-brief.sh -r usc/uconnect -l domain/6-security
|
||||
#
|
||||
# Reliable signals (closed issues are excluded by definition — that's the point):
|
||||
# - open-vs-closed : authoritative; this is the stale-intake failure mode.
|
||||
# - PR-linkage : an open PR referencing the issue = work underway.
|
||||
# Assignees/dependencies are intentionally NOT trusted as "available" signals —
|
||||
# fleets that track work-state out-of-band (tmux board, issue text) leave them
|
||||
# empty in Gitea. Output therefore partitions by PR presence and the OPEN-NO-PR set
|
||||
# is "dispatch candidates to cross-check against the live fleet", not a blind list.
|
||||
#
|
||||
# Login resolution order: -L flag > $GITEA_LOGIN > owner inference (usc->usc,
|
||||
# mosaicstack/mosaic->mosaicstack) > detect-platform.sh default-login fallback.
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
# shellcheck source=/dev/null
|
||||
source "$SCRIPT_DIR/detect-platform.sh"
|
||||
|
||||
REPO="" MILESTONE="" LABEL="" LOGIN="" LIMIT=100
|
||||
while getopts "r:m:l:L:n:h" opt; do
|
||||
case "$opt" in
|
||||
r) REPO="$OPTARG" ;;
|
||||
m) MILESTONE="$OPTARG" ;;
|
||||
l) LABEL="$OPTARG" ;;
|
||||
L) LOGIN="$OPTARG" ;;
|
||||
n) LIMIT="$OPTARG" ;;
|
||||
h) grep '^#' "$0" | sed 's/^# \?//'; exit 0 ;;
|
||||
*) echo "see -h" >&2; exit 2 ;;
|
||||
esac
|
||||
done
|
||||
[[ -n "$REPO" ]] || { echo "FATAL: -r <owner/repo> required" >&2; exit 2; }
|
||||
|
||||
# Resolve login: explicit -L, then $GITEA_LOGIN, then owner inference, then the
|
||||
# shared default-login resolver. Owner inference comes before the shared fallback
|
||||
# because the latter is not owner-aware (picks the default tea login), which is
|
||||
# wrong for cross-instance lanes.
|
||||
if [[ -z "$LOGIN" ]]; then
|
||||
if [[ -n "${GITEA_LOGIN:-}" ]]; then
|
||||
LOGIN="$GITEA_LOGIN"
|
||||
else
|
||||
case "${REPO%%/*}" in
|
||||
usc|USC) LOGIN=usc ;;
|
||||
mosaicstack|mosaic) LOGIN=mosaicstack ;;
|
||||
*) LOGIN="$(get_gitea_login_for_repo_override 2>/dev/null || true)" ;;
|
||||
esac
|
||||
fi
|
||||
fi
|
||||
[[ -n "$LOGIN" ]] || { echo "FATAL: could not resolve a Gitea login for $REPO (pass -L or set GITEA_LOGIN)" >&2; exit 2; }
|
||||
|
||||
command -v tea >/dev/null || { echo "FATAL: tea not found" >&2; exit 1; }
|
||||
command -v jq >/dev/null || { echo "FATAL: jq not found" >&2; exit 1; }
|
||||
|
||||
ISSUES_JSON="$(tea issues list --repo "$REPO" --login "$LOGIN" --state open --limit "$LIMIT" \
|
||||
--fields index,title,assignees,milestone,labels --output json 2>/dev/null)" || {
|
||||
echo "FATAL: tea issues list failed for $REPO (login=$LOGIN)" >&2; exit 1; }
|
||||
|
||||
# Open PRs, to cross-ref which issues already have work in flight.
|
||||
PRS_TSV="$(tea pulls list --repo "$REPO" --login "$LOGIN" --state open \
|
||||
--fields index,title,head --output tsv 2>/dev/null || true)"
|
||||
PR_ISSUE_REFS="$(printf '%s\n' "$PRS_TSV" | grep -oE '#[0-9]+|[/-][0-9]{3,}' | grep -oE '[0-9]+' | sort -u || true)"
|
||||
|
||||
ts="$(date -u '+%Y-%m-%d %H:%MZ' 2>/dev/null || echo '?')"
|
||||
filt="$REPO"; [[ -n "$MILESTONE" ]] && filt="$filt · milestone:'$MILESTONE'"; [[ -n "$LABEL" ]] && filt="$filt · label:'$LABEL'"
|
||||
echo "LANE BRIEF — $filt · $ts (login=$LOGIN)"
|
||||
echo "(open issues only; closed are excluded by definition — that's the point)"
|
||||
echo
|
||||
|
||||
printf '%s' "$ISSUES_JSON" | jq -r --arg ms "$MILESTONE" --arg lb "$LABEL" --arg prs "$PR_ISSUE_REFS" '
|
||||
($prs | split("\n") | map(select(length>0))) as $prrefs
|
||||
| map(
|
||||
select( ($ms=="" or .milestone==$ms)
|
||||
and ($lb=="" or ((.labels//"") | contains($lb))) )
|
||||
| . + { assigned: ((.assignees//"")|length>0),
|
||||
haspr: (.index as $ix | ($prrefs | index($ix)) != null) }
|
||||
)
|
||||
| (map(select(.haspr|not))) as $candidates
|
||||
| (map(select(.haspr))) as $inflight
|
||||
| "DISPATCH CANDIDATES (open · no open PR) — \($candidates|length) [cross-check vs live fleet]:",
|
||||
( $candidates[] | " #\(.index) \(.title[0:90])\(if .assigned then " (gitea-assignee set)" else "" end)" ),
|
||||
"",
|
||||
"WORK UNDERWAY (open · PR in flight) — \($inflight|length):",
|
||||
( $inflight[] | " #\(.index) \(.title[0:80]) [PR open]" )
|
||||
'
|
||||
echo
|
||||
echo "Closed issues are excluded — do NOT take a worker's self-reported 'todo' on faith."
|
||||
echo "Candidates = open + no PR; confirm against the live fleet before dispatch"
|
||||
echo "(fleets that don't self-assign in Gitea leave 'unassigned' meaningless)."
|
||||
Reference in New Issue
Block a user