fix(tools): write Gitea reviews/comments via REST POST and verify by exact created id (#865)
All checks were successful
ci/woodpecker/pr/ci Pipeline was successful
All checks were successful
ci/woodpecker/pr/ci Pipeline was successful
Replace the tea-based write + boundary/author read-back with a direct Gitea
REST POST that returns the created record's id, and verify that exact record.
BLOCKER 2 (credential ordering): resolve the acting identity, the write token,
and the read-back token from the SAME effective login. A --login override now
selects the credential used for the POST, GET /user, and the GET-by-id
read-back, so an overridden write is verified against the identity that
performed it -- not the host default. Login-name resolution is best-effort and
non-fatal (the override always wins; otherwise fall back to the host
credential), so exotic/ported hosts still resolve a token.
BLOCKER 1+3 (attribution + tautological tests): the write is now
POST /issues/{n}/comments or POST /pulls/{n}/reviews (event + body + commit_id
== PR head), parsing the provider-returned created id. Verification GETs that
exact id and checks author == acting identity and body (comments) or state +
commit_id (reviews). Keying on the created id closes the concurrency window:
a no-op create yields no id and fails closed with no list-scan fallback, and a
concurrent same-identity record has a different id. The review body travels in
the review submit, removing the separate detached comment.
Tests: the curl stub now models a real server with persistent on-disk
review/comment state -- a POST actually creates+persists a record and returns
its id, and the read-back reads that same state (no fabricated record for the
wrapper to find). Adds same-identity no-op-concurrent and author-mismatch
fail-closed cases for both comments and reviews, and >page-1 pagination
coverage for both. README "Durable review provenance" refreshed for the REST
mechanism.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -563,6 +563,54 @@ get_gitea_token() {
|
||||
return 1
|
||||
}
|
||||
|
||||
# Resolve the API token for a SPECIFIC tea login name from tea's own config
|
||||
# (the same store tea itself writes/reads for `--login <name>`). This is what
|
||||
# lets a REST write be performed AS the selected --login identity: tea keys its
|
||||
# per-login tokens by `name` in $XDG_CONFIG_HOME/tea/config.yml (default
|
||||
# ~/.config/tea/config.yml), exactly as the `tea` CLI resolves them, so a
|
||||
# --login override and its REST read-back bind to the SAME credential/identity.
|
||||
# Prints the token on success; returns non-zero (no output) if the config or a
|
||||
# matching login token cannot be found. Callers must not log the result.
|
||||
get_gitea_token_for_login() {
|
||||
local login_name="$1" config_file
|
||||
[[ -n "$login_name" ]] || return 1
|
||||
config_file="${XDG_CONFIG_HOME:-$HOME/.config}/tea/config.yml"
|
||||
[[ -f "$config_file" ]] || return 1
|
||||
|
||||
LOGIN_NAME="$login_name" python3 - "$config_file" <<'PY'
|
||||
import os
|
||||
import sys
|
||||
|
||||
try:
|
||||
import yaml
|
||||
except ImportError:
|
||||
raise SystemExit(1)
|
||||
|
||||
try:
|
||||
with open(sys.argv[1], encoding="utf-8") as handle:
|
||||
config = yaml.safe_load(handle)
|
||||
except (OSError, yaml.YAMLError):
|
||||
raise SystemExit(1)
|
||||
|
||||
wanted = os.environ["LOGIN_NAME"]
|
||||
logins = config.get("logins") if isinstance(config, dict) else None
|
||||
if not isinstance(logins, list):
|
||||
raise SystemExit(1)
|
||||
|
||||
for login in logins:
|
||||
if not isinstance(login, dict):
|
||||
continue
|
||||
if str(login.get("name") or "") == wanted:
|
||||
token = login.get("token")
|
||||
if isinstance(token, str) and token:
|
||||
print(token)
|
||||
raise SystemExit(0)
|
||||
break
|
||||
|
||||
raise SystemExit(1)
|
||||
PY
|
||||
}
|
||||
|
||||
# Resolve HTTPS basic auth credentials for a Gitea host from ~/.git-credentials.
|
||||
# Prints "username:password" for direct curl -u consumption. Callers must not log it.
|
||||
get_gitea_basic_auth() {
|
||||
|
||||
Reference in New Issue
Block a user