Four defects in issue-view.sh, each pinned by the new hermetic suite
test-issue-view-comments.sh (mock tea + curl, sandboxed repo):
F1 tea exits 1 in any repo with extensions.worktreeconfig=true. The wrapper
now names that as a git-config condition and falls back to the API.
F2 The API fallback dumped raw issue JSON, which carries only a comment
COUNT. It now fetches /comments and renders issue + comment bodies.
F3 The tea path never passed --comments, so comment bodies were never shown
non-interactively. It now does.
F4 Every tea failure printed the REVOKED OR STALE TOKEN note. The wrapper now
relays tea's own error line and only hints at credentials when tea did.
The suite joins ci.yml and the verify-release canonical list (mirror test).
Closes #1357
138 lines
4.9 KiB
Bash
Executable File
138 lines
4.9 KiB
Bash
Executable File
#!/bin/bash
|
|
# issue-view.sh - View issue details, including comments, on GitHub or Gitea
|
|
# Usage: issue-view.sh -i <issue_number>
|
|
|
|
set -e
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
source "$SCRIPT_DIR/detect-platform.sh"
|
|
|
|
# Parse arguments
|
|
ISSUE_NUMBER=""
|
|
|
|
# get_remote_host and get_gitea_token are provided by detect-platform.sh
|
|
|
|
gitea_issue_view_api() {
|
|
local host repo token url
|
|
host=$(get_remote_host) || {
|
|
echo "Error: could not determine remote host for API fallback" >&2
|
|
return 1
|
|
}
|
|
repo=$(get_repo_info) || {
|
|
echo "Error: could not determine repo owner/name for API fallback" >&2
|
|
return 1
|
|
}
|
|
token=$(get_gitea_token "$host") || {
|
|
echo "Error: Gitea token not found for API fallback (set GITEA_TOKEN or configure ~/.git-credentials)" >&2
|
|
return 1
|
|
}
|
|
|
|
url="https://${host}/api/v1/repos/${repo}/issues/${ISSUE_NUMBER}"
|
|
local -a curl_args=(-fsS -H "User-Agent: curl/8" -H "Authorization: token ${token}")
|
|
if ! command -v python3 >/dev/null 2>&1; then
|
|
# No renderer: raw JSON is all this path can give. Comments are a
|
|
# second resource, so fetch them too rather than only the count.
|
|
curl "${curl_args[@]}" "$url"
|
|
curl "${curl_args[@]}" "${url}/comments"
|
|
return
|
|
fi
|
|
# Render issue + comments as text (#1357 F2). The old fallback dumped the
|
|
# issue JSON, which carries only a comment COUNT, so every comment body was
|
|
# invisible on this path and the wrapper could never show what
|
|
# `tea issues --comments` shows.
|
|
{
|
|
curl "${curl_args[@]}" "$url"
|
|
echo
|
|
echo "__MOSAIC_COMMENTS__"
|
|
curl "${curl_args[@]}" "${url}/comments"
|
|
} | python3 -c '
|
|
import json, sys
|
|
raw = sys.stdin.read()
|
|
issue_raw, _, comments_raw = raw.partition("__MOSAIC_COMMENTS__")
|
|
issue = json.loads(issue_raw)
|
|
comments = json.loads(comments_raw) if comments_raw.strip() else []
|
|
print("#%s %s" % (issue["number"], issue["title"]))
|
|
print("State: %s Author: %s Created: %s" % (issue["state"], issue["user"]["login"], issue["created_at"]))
|
|
labels = ", ".join(l["name"] for l in issue.get("labels") or [])
|
|
if labels:
|
|
print("Labels: " + labels)
|
|
if issue.get("milestone"):
|
|
print("Milestone: " + issue["milestone"]["title"])
|
|
print("URL: " + issue["html_url"])
|
|
print()
|
|
print(issue.get("body") or "(no body)")
|
|
if comments:
|
|
print()
|
|
print("--- Comments (%d) ---" % len(comments))
|
|
for c in comments:
|
|
print()
|
|
print("[%s at %s]" % (c["user"]["login"], c["created_at"]))
|
|
print(c.get("body") or "")
|
|
'
|
|
}
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case $1 in
|
|
-i|--issue)
|
|
ISSUE_NUMBER="$2"
|
|
shift 2
|
|
;;
|
|
-h|--help)
|
|
echo "Usage: issue-view.sh -i <issue_number>"
|
|
echo ""
|
|
echo "Options:"
|
|
echo " -i, --issue Issue number (required)"
|
|
echo ""
|
|
echo "Comments are always included (tea --comments / Gitea API /comments)."
|
|
echo " -h, --help Show this help"
|
|
exit 0
|
|
;;
|
|
*)
|
|
echo "Unknown option: $1"
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
|
|
if [[ -z "$ISSUE_NUMBER" ]]; then
|
|
echo "Error: Issue number is required (-i)"
|
|
exit 1
|
|
fi
|
|
|
|
detect_platform >/dev/null
|
|
|
|
if [[ "$PLATFORM" == "github" ]]; then
|
|
gh issue view "$ISSUE_NUMBER"
|
|
elif [[ "$PLATFORM" == "gitea" ]]; then
|
|
if command -v tea >/dev/null 2>&1; then
|
|
# --comments is what makes tea print the comment bodies (#1357 F3).
|
|
# Without it tea prompts for them interactively, which in a
|
|
# non-interactive wrapper means they are silently never shown.
|
|
tea_err=$(mktemp)
|
|
if tea issue "$ISSUE_NUMBER" $(get_gitea_repo_args) --comments 2>"$tea_err"; then
|
|
rm -f "$tea_err"
|
|
exit 0
|
|
fi
|
|
# Name the cause tea actually reported, not a guessed one (#1357 F1/F4).
|
|
# tea reads the cwd's git config before honouring --repo; a repo with
|
|
# extensions.worktreeconfig=true makes it exit 1 with a
|
|
# repositoryformatversion error. That is a git-config condition, not a
|
|
# credential one. The old path printed the REVOKED OR STALE TOKEN note
|
|
# here unconditionally, which sent readers to rotate a token that was fine.
|
|
if grep -q 'repositoryformatversion' "$tea_err"; then
|
|
echo "Warning: tea cannot read this repo's git config (extensions.worktreeconfig); not a credential problem. Using Gitea API fallback." >&2
|
|
elif grep -q 'user does not exist' "$tea_err"; then
|
|
echo "Warning: tea issue view failed, trying Gitea API fallback..." >&2
|
|
{ declare -F explain_tea_user_does_not_exist >/dev/null && explain_tea_user_does_not_exist; } || true
|
|
else
|
|
echo "Warning: tea issue view failed, trying Gitea API fallback..." >&2
|
|
fi
|
|
sed 's/^/ tea: /' "$tea_err" >&2
|
|
rm -f "$tea_err"
|
|
fi
|
|
gitea_issue_view_api
|
|
else
|
|
echo "Error: Unknown platform"
|
|
exit 1
|
|
fi
|