Compare commits
3 Commits
addeaf18d2
...
fix/812-pr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fb79bff91f | ||
|
|
c3a8383330 | ||
|
|
3585cdfad6 |
@@ -23,9 +23,9 @@ FROM node:24-alpine
|
||||
|
||||
# Native toolchain required to compile node-gyp deps on musl, plus the
|
||||
# postgresql-client used by the test step's pg_isready readiness probe. `bash`
|
||||
# and `git` are baked here too — framework shell tests require both without
|
||||
# paying for per-run package installation in ci.yml.
|
||||
RUN apk add --no-cache python3 make g++ postgresql-client bash git
|
||||
# is baked here too — the sanitization step in ci.yml otherwise does a per-run
|
||||
# `apk add bash`.
|
||||
RUN apk add --no-cache python3 make g++ postgresql-client bash
|
||||
|
||||
# Pin pnpm to the repo's packageManager version via corepack.
|
||||
RUN corepack enable && corepack prepare pnpm@10.6.2 --activate
|
||||
|
||||
49
docs/scratchpads/812-pr-review-comment.md
Normal file
49
docs/scratchpads/812-pr-review-comment.md
Normal file
@@ -0,0 +1,49 @@
|
||||
# Issue #812 — durable Gitea PR review comments
|
||||
|
||||
- **Lane:** ms-812
|
||||
- **Branch:** `fix/812-pr-review-comment`
|
||||
- **Issue:** mosaicstack/stack#812
|
||||
- **Budget:** 15K working estimate; single focused shell-wrapper/test/docs change.
|
||||
|
||||
## Objective
|
||||
|
||||
Make the Gitea `comment` action in `packages/mosaic/framework/tools/git/pr-review.sh` use tea v0.11.1's supported `tea comment` command and report success only after provider read-back verifies the created comment against the intended repository, PR, and exact body.
|
||||
|
||||
## Plan
|
||||
|
||||
1. Add and commit a failing shell regression harness before production changes.
|
||||
2. Verify RED against the nonexistent `tea pr comment` fallback false-positive.
|
||||
3. Implement the minimal supported write plus ID-based provider read-back.
|
||||
4. Document that wrapper write output is not durable provenance until read-back succeeds.
|
||||
5. Run focused regression tests, touched-package tests, and repository quality gates.
|
||||
6. Run independent review, remediate findings, queue-guard, push, and open a PR. Stop at PR-open for coordinator review/merge.
|
||||
|
||||
## Progress checkpoints
|
||||
|
||||
- [x] RED regression committed and reported to mosaic-100 (`3585cdfa`)
|
||||
- [x] Initial minimal fix implemented (`c3a83833`)
|
||||
- [x] Focused and package tests green after dependency build
|
||||
- [ ] Independent review passed
|
||||
- [ ] PR opened and reported with exact head SHA
|
||||
|
||||
## Tests run
|
||||
|
||||
- RED: `bash packages/mosaic/framework/tools/git/test-pr-review-gitea-comment.sh` failed against the old path because `tea pr comment` fell back and the wrapper returned success.
|
||||
- GREEN: all `packages/mosaic/framework/tools/git/test-*.sh` harnesses passed.
|
||||
- `pnpm build` passed (23/23 tasks).
|
||||
- `pnpm --filter @mosaicstack/mosaic test` passed (67 files, 1278 tests).
|
||||
- Package and root typecheck/lint/format gates passed.
|
||||
- Codex security review found no issues.
|
||||
- Codex code review requested changes: tea v0.11.1 has no supported `tea api`; replace read-back with the existing host-scoped authenticated curl/API path when work resumes.
|
||||
|
||||
## Risks / blockers
|
||||
|
||||
- **Coordination hold:** Mos directed #812 to remain parked until in-flight #789 reaches terminal state because #789 actively uses `pr-review.sh`. Branch must be pushed but no PR opened.
|
||||
- Initial implementation's `tea api` read-back is not compatible with tea v0.11.1 and must be remediated before PR.
|
||||
- Tea output shape must provide a created comment ID in JSON; failure to parse must fail closed.
|
||||
- Read-back must not accept a pre-existing comment with the same body.
|
||||
- Existing approve/request-changes behavior must remain unchanged and covered.
|
||||
|
||||
## Final verification evidence
|
||||
|
||||
Parked pending #789 terminal. No PR opened.
|
||||
@@ -70,8 +70,6 @@ Security vulnerability review focusing on:
|
||||
~/.config/mosaic/tools/codex/codex-security-review.sh -n 42
|
||||
```
|
||||
|
||||
PR mode resolves the provider's PR diff rather than relying on the caller's checked-out branch. On Gitea, it fetches the base and `refs/pull/<number>/head` refs and diffs those explicit refs. If the refs cannot be fetched or the resulting diff is empty, the command exits nonzero before Codex runs or a review is posted.
|
||||
|
||||
### Review Against Base Branch
|
||||
|
||||
```bash
|
||||
@@ -255,7 +253,7 @@ Run the script from inside a git repository.
|
||||
|
||||
### "No changes found to review"
|
||||
|
||||
The specified non-PR mode (`--uncommitted`, `--base`, etc.) found no changes to review. PR mode instead fails closed with an actionable error when it cannot construct a non-empty provider diff; verify the PR number, remote, provider login, and ref access before retrying.
|
||||
The specified mode (--uncommitted, --base, etc.) found no changes to review.
|
||||
|
||||
### "Codex produced no output"
|
||||
|
||||
|
||||
@@ -44,47 +44,38 @@ build_diff_context() {
|
||||
diff_text=$(git show "$value" 2>/dev/null)
|
||||
;;
|
||||
pr)
|
||||
# Provider detection writes its result to stdout; suppress it so it cannot
|
||||
# be mistaken for diff content when this function is used in a substitution.
|
||||
detect_platform >/dev/null
|
||||
# For PRs, we need to fetch the PR diff
|
||||
detect_platform
|
||||
if [[ "$PLATFORM" == "github" ]]; then
|
||||
diff_text=$(gh pr diff "$value" 2>/dev/null) || {
|
||||
echo "Error: Failed to fetch the diff for PR #${value}." >&2
|
||||
return 1
|
||||
}
|
||||
diff_text=$(gh pr diff "$value" 2>/dev/null)
|
||||
elif [[ "$PLATFORM" == "gitea" ]]; then
|
||||
local pr_base base_ref pr_head_ref
|
||||
pr_base=$(tea pr list --fields index,base --output simple 2>/dev/null | awk -v pr="$value" '$1 == pr { print $2; exit }')
|
||||
if [[ -z "$pr_base" ]]; then
|
||||
echo "Error: Could not resolve the base branch for Gitea PR #${value}." >&2
|
||||
return 1
|
||||
# tea doesn't have a direct pr diff command, use git
|
||||
local pr_base
|
||||
pr_base=$(tea pr list --fields index,base --output simple 2>/dev/null | grep "^${value}" | awk '{print $2}')
|
||||
if [[ -n "$pr_base" ]]; then
|
||||
diff_text=$(git diff "${pr_base}...HEAD" 2>/dev/null)
|
||||
else
|
||||
# Fallback: fetch PR info via API
|
||||
local repo_info
|
||||
repo_info=$(get_repo_info)
|
||||
local remote_url
|
||||
remote_url=$(git remote get-url origin 2>/dev/null)
|
||||
local host
|
||||
host=$(echo "$remote_url" | sed -E 's|.*://([^/]+).*|\1|; s|.*@([^:]+).*|\1|')
|
||||
diff_text=$(curl -s "https://${host}/api/v1/repos/${repo_info}/pulls/${value}" \
|
||||
-H "Authorization: token $(tea login list --output simple 2>/dev/null | head -1 | awk '{print $2}')" \
|
||||
2>/dev/null | jq -r '.diff_url // empty')
|
||||
if [[ -n "$diff_text" && "$diff_text" != "null" ]]; then
|
||||
diff_text=$(curl -s "$diff_text" 2>/dev/null)
|
||||
else
|
||||
diff_text=$(git diff "main...HEAD" 2>/dev/null)
|
||||
fi
|
||||
fi
|
||||
|
||||
base_ref="refs/remotes/origin/${pr_base}"
|
||||
pr_head_ref="refs/remotes/origin/pr/${value}/head"
|
||||
if ! git fetch --quiet origin \
|
||||
"+refs/heads/${pr_base}:${base_ref}" \
|
||||
"+refs/pull/${value}/head:${pr_head_ref}"; then
|
||||
echo "Error: Failed to fetch the base and head refs for Gitea PR #${value}." >&2
|
||||
return 1
|
||||
fi
|
||||
diff_text=$(git diff "${base_ref}...${pr_head_ref}") || {
|
||||
echo "Error: Failed to diff the fetched refs for Gitea PR #${value}." >&2
|
||||
return 1
|
||||
}
|
||||
else
|
||||
echo "Error: Unsupported git platform while resolving PR #${value}." >&2
|
||||
return 1
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [[ "$mode" == "pr" && -z "${diff_text//[[:space:]]/}" ]]; then
|
||||
echo "Error: Unable to construct a non-empty diff for PR #${value}; verify the PR refs and provider access." >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
printf '%s\n' "$diff_text"
|
||||
echo "$diff_text"
|
||||
}
|
||||
|
||||
# Format JSON findings as markdown for PR comments
|
||||
|
||||
@@ -1,158 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Hermetic regression coverage for Gitea PR diff construction and fail-closed reviews.
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
TMP_DIR=$(mktemp -d)
|
||||
trap 'rm -rf "$TMP_DIR"' EXIT
|
||||
|
||||
fail() {
|
||||
echo "not ok - $*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
assert_contains() {
|
||||
local haystack="$1" needle="$2"
|
||||
if [[ "$haystack" != *"$needle"* ]]; then
|
||||
printf 'actual output:\n%s\n' "$haystack" >&2
|
||||
fail "expected output to contain: $needle"
|
||||
fi
|
||||
}
|
||||
|
||||
# Prevent CI-provided repository context from leaking into the fixture repositories.
|
||||
unset GIT_DIR GIT_WORK_TREE GIT_INDEX_FILE GIT_OBJECT_DIRECTORY \
|
||||
GIT_ALTERNATE_OBJECT_DIRECTORIES GIT_COMMON_DIR
|
||||
export GIT_AUTHOR_NAME="Codex Fixture"
|
||||
export GIT_AUTHOR_EMAIL="codex-fixture@example.test"
|
||||
export GIT_COMMITTER_NAME="$GIT_AUTHOR_NAME"
|
||||
export GIT_COMMITTER_EMAIL="$GIT_AUTHOR_EMAIL"
|
||||
export GITEA_LOGIN="fixture"
|
||||
export GITEA_TOKEN="fixture-token"
|
||||
export GITEA_URL="file://$TMP_DIR"
|
||||
|
||||
create_pr_fixture() {
|
||||
local fixture_root="$1" head_mode="$2"
|
||||
local origin="$fixture_root/origin.git"
|
||||
local seed="$fixture_root/seed"
|
||||
local work="$fixture_root/work"
|
||||
local base_sha head_sha
|
||||
|
||||
mkdir -p "$fixture_root"
|
||||
git init --quiet --bare "$origin"
|
||||
git init --quiet --initial-branch=release/next "$seed"
|
||||
printf 'base\n' > "$seed/pr-change.ts"
|
||||
git -C "$seed" add pr-change.ts
|
||||
git -C "$seed" commit --quiet -m "fixture base"
|
||||
base_sha=$(git -C "$seed" rev-parse HEAD)
|
||||
git -C "$seed" remote add origin "$origin"
|
||||
git -C "$seed" push --quiet origin release/next
|
||||
git --git-dir="$origin" symbolic-ref HEAD refs/heads/release/next
|
||||
|
||||
if [[ "$head_mode" == "changed" ]]; then
|
||||
git -C "$seed" switch --quiet -c feature/pr-795
|
||||
printf 'actual-pr-change\n' > "$seed/pr-change.ts"
|
||||
git -C "$seed" commit --quiet -am "fixture PR head"
|
||||
head_sha=$(git -C "$seed" rev-parse HEAD)
|
||||
git -C "$seed" push --quiet origin HEAD:refs/pull/795/head
|
||||
else
|
||||
head_sha="$base_sha"
|
||||
git --git-dir="$origin" update-ref refs/pull/795/head "$head_sha"
|
||||
fi
|
||||
|
||||
# Gitea's provider-owned PR head ref now exists in the local bare origin.
|
||||
git clone --quiet "$origin" "$work"
|
||||
printf '%s\n' "$work"
|
||||
}
|
||||
|
||||
FAKE_BIN="$TMP_DIR/bin"
|
||||
mkdir -p "$FAKE_BIN"
|
||||
cat > "$FAKE_BIN/tea" <<'STUB'
|
||||
#!/bin/bash
|
||||
if [[ "$*" == "pr list --fields index,base --output simple" ]]; then
|
||||
printf '795 release/next\n'
|
||||
exit 0
|
||||
fi
|
||||
exit 1
|
||||
STUB
|
||||
cat > "$FAKE_BIN/codex" <<'STUB'
|
||||
#!/bin/bash
|
||||
printf 'CODEX %s\n' "$*" >> "$CODEX_LOG"
|
||||
exit 99
|
||||
STUB
|
||||
chmod +x "$FAKE_BIN/tea" "$FAKE_BIN/codex"
|
||||
export PATH="$FAKE_BIN:$PATH"
|
||||
|
||||
# The valid fixture is a fresh clone on the non-main base. The PR head exists only
|
||||
# at refs/pull/795/head, so local HEAD cannot accidentally satisfy the assertion.
|
||||
if [[ "${1:-all}" != "fail-closed" ]]; then
|
||||
VALID_WORK=$(create_pr_fixture "$TMP_DIR/valid" changed)
|
||||
(
|
||||
cd "$VALID_WORK"
|
||||
# shellcheck source=common.sh
|
||||
source "$SCRIPT_DIR/common.sh"
|
||||
diff_context=$(build_diff_context pr 795)
|
||||
assert_contains "$diff_context" "actual-pr-change"
|
||||
|
||||
base_sha=$(git rev-parse refs/remotes/origin/release/next)
|
||||
head_sha=$(git rev-parse refs/remotes/origin/pr/795/head)
|
||||
local_sha=$(git rev-parse HEAD)
|
||||
[[ "$local_sha" == "$base_sha" ]] || fail "fixture clone is not on the PR base"
|
||||
[[ "$head_sha" != "$base_sha" ]] || fail "fixture PR head does not differ from its base"
|
||||
git show-ref --verify --quiet refs/remotes/origin/pr/795/head || \
|
||||
fail "fetched PR head ref is missing"
|
||||
[[ "$(git diff --name-only "${base_sha}...${head_sha}")" == "pr-change.ts" ]] || \
|
||||
fail "explicit PR refs do not contain the fixture change"
|
||||
if git show-ref --verify --quiet refs/heads/main || \
|
||||
git show-ref --verify --quiet refs/remotes/origin/main; then
|
||||
fail "fixture unexpectedly contains a main ref"
|
||||
fi
|
||||
)
|
||||
echo "ok - Gitea PR mode fetches and diffs explicit non-main base and PR head refs"
|
||||
fi
|
||||
|
||||
# Build an empty PR entirely inside another local repository. Both review wrappers
|
||||
# must emit the PR-numbered error before Codex or the stubbed post path can execute.
|
||||
if [[ "${1:-all}" != "pr-head" ]]; then
|
||||
EMPTY_WORK=$(create_pr_fixture "$TMP_DIR/empty" empty)
|
||||
SANDBOX="$TMP_DIR/sandbox"
|
||||
mkdir -p "$SANDBOX/tools/codex/schemas" "$SANDBOX/tools/git"
|
||||
cp "$SCRIPT_DIR/common.sh" \
|
||||
"$SCRIPT_DIR/codex-code-review.sh" \
|
||||
"$SCRIPT_DIR/codex-security-review.sh" \
|
||||
"$SANDBOX/tools/codex/"
|
||||
cp "$SCRIPT_DIR/schemas/code-review-schema.json" \
|
||||
"$SCRIPT_DIR/schemas/security-review-schema.json" \
|
||||
"$SANDBOX/tools/codex/schemas/"
|
||||
cp "$SCRIPT_DIR/../git/detect-platform.sh" "$SANDBOX/tools/git/"
|
||||
|
||||
cat > "$SANDBOX/tools/git/pr-review.sh" <<'STUB'
|
||||
#!/bin/bash
|
||||
printf 'POST %s\n' "$*" >> "$POST_LOG"
|
||||
STUB
|
||||
chmod +x "$SANDBOX/tools/git/pr-review.sh"
|
||||
|
||||
POST_LOG="$TMP_DIR/post.log"
|
||||
CODEX_LOG="$TMP_DIR/codex.log"
|
||||
export POST_LOG CODEX_LOG
|
||||
|
||||
for review_kind in code security; do
|
||||
: > "$POST_LOG"
|
||||
: > "$CODEX_LOG"
|
||||
review_script="$SANDBOX/tools/codex/codex-${review_kind}-review.sh"
|
||||
set +e
|
||||
(
|
||||
cd "$EMPTY_WORK"
|
||||
"$review_script" -n 795
|
||||
) >"$TMP_DIR/${review_kind}.stdout" 2>"$TMP_DIR/${review_kind}.stderr"
|
||||
review_status=$?
|
||||
set -e
|
||||
|
||||
stderr_text=$(cat "$TMP_DIR/${review_kind}.stderr")
|
||||
[[ "$review_status" -ne 0 ]] || fail "${review_kind} review returned success for an empty PR diff"
|
||||
[[ ! -s "$CODEX_LOG" ]] || fail "Codex ran for an empty ${review_kind} PR diff"
|
||||
[[ ! -s "$POST_LOG" ]] || fail "${review_kind} review auto-post ran for an empty PR diff"
|
||||
assert_contains "$stderr_text" "Error:"
|
||||
assert_contains "$stderr_text" "PR #795"
|
||||
echo "ok - empty ${review_kind} PR diff fails closed before Codex and auto-post"
|
||||
done
|
||||
fi
|
||||
9
packages/mosaic/framework/tools/git/README.md
Normal file
9
packages/mosaic/framework/tools/git/README.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# Git provider wrappers
|
||||
|
||||
These scripts provide host-aware GitHub and Gitea issue, pull-request, milestone, and CI operations.
|
||||
|
||||
## Durable review provenance
|
||||
|
||||
A successful provider write command—or a wrapper message based only on that command's exit code—is **not** durable review provenance. Review comments count as durable provenance only after the wrapper reads the created provider record back and verifies that it belongs to the intended repository and pull request and contains the exact submitted body (or verifies the provider-returned record ID).
|
||||
|
||||
`pr-review.sh` therefore fails closed when a Gitea comment cannot be written, its created comment ID cannot be identified, or provider read-back does not match. It reports comment success only after that read-back verification passes.
|
||||
@@ -101,8 +101,58 @@ elif [[ "$PLATFORM" == "gitea" ]]; then
|
||||
echo "Error: Comment required"
|
||||
exit 1
|
||||
fi
|
||||
tea pr comment "$PR_NUMBER" "$COMMENT" $(get_gitea_repo_args)
|
||||
echo "Added comment to Gitea PR #$PR_NUMBER"
|
||||
|
||||
repo=$(get_repo_slug)
|
||||
host=$(get_remote_host)
|
||||
login=$(get_gitea_login_for_host "$host")
|
||||
|
||||
comment_json=$(tea comment "$PR_NUMBER" "$COMMENT" --login "$login" --repo "$repo" --output json)
|
||||
comment_id=$(printf '%s' "$comment_json" | python3 -c '
|
||||
import json
|
||||
import sys
|
||||
|
||||
try:
|
||||
comment = json.load(sys.stdin)
|
||||
if isinstance(comment, list) and len(comment) == 1:
|
||||
comment = comment[0]
|
||||
comment_id = comment.get("id") if isinstance(comment, dict) else None
|
||||
if not isinstance(comment_id, int) or comment_id <= 0:
|
||||
raise ValueError("missing positive comment id")
|
||||
except (json.JSONDecodeError, ValueError) as error:
|
||||
print(f"Error: could not identify created Gitea comment: {error}", file=sys.stderr)
|
||||
raise SystemExit(1)
|
||||
print(comment_id)
|
||||
')
|
||||
|
||||
readback_json=$(tea api --login "$login" "/repos/$repo/issues/comments/$comment_id")
|
||||
EXPECTED_COMMENT_ID="$comment_id" EXPECTED_COMMENT_BODY="$COMMENT" EXPECTED_REPO="$repo" EXPECTED_PR_NUMBER="$PR_NUMBER" \
|
||||
python3 -c '
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
from urllib.parse import urlparse
|
||||
|
||||
try:
|
||||
comment = json.load(sys.stdin)
|
||||
expected_id = int(os.environ["EXPECTED_COMMENT_ID"])
|
||||
expected_body = os.environ["EXPECTED_COMMENT_BODY"]
|
||||
expected_repo = os.environ["EXPECTED_REPO"]
|
||||
expected_pr = os.environ["EXPECTED_PR_NUMBER"]
|
||||
issue_url = comment.get("issue_url", "") if isinstance(comment, dict) else ""
|
||||
issue_path = urlparse(issue_url).path.rstrip("/")
|
||||
expected_suffix = f"/repos/{expected_repo}/issues/{expected_pr}"
|
||||
if comment.get("id") != expected_id:
|
||||
raise ValueError("comment id mismatch")
|
||||
if comment.get("body") != expected_body:
|
||||
raise ValueError("comment body mismatch")
|
||||
if not issue_path.endswith(expected_suffix):
|
||||
raise ValueError("repository or PR mismatch")
|
||||
except (json.JSONDecodeError, KeyError, TypeError, ValueError) as error:
|
||||
print(f"Error: Gitea comment persistence verification failed: {error}", file=sys.stderr)
|
||||
raise SystemExit(1)
|
||||
' <<< "$readback_json"
|
||||
|
||||
echo "Added and verified comment on Gitea PR #$PR_NUMBER (comment ID $comment_id)"
|
||||
;;
|
||||
*)
|
||||
echo "Error: Unknown action: $ACTION"
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
#!/usr/bin/env bash
|
||||
# Regression harness for durable Gitea PR review comments (#812).
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
WORK_DIR="${MOSAIC_TEST_WORK_DIR:-$PWD/.mosaic-test-work/pr-review-gitea-comment}"
|
||||
REPO_DIR="$WORK_DIR/repo"
|
||||
BIN_DIR="$WORK_DIR/bin"
|
||||
LOG_FILE="$WORK_DIR/tea.log"
|
||||
OUTPUT_FILE="$WORK_DIR/output.log"
|
||||
|
||||
cleanup() {
|
||||
rm -rf "$WORK_DIR"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
mkdir -p "$REPO_DIR" "$BIN_DIR"
|
||||
git -C "$REPO_DIR" init -q
|
||||
git -C "$REPO_DIR" remote add origin https://git.mosaicstack.dev/mosaicstack/stack.git
|
||||
|
||||
cat > "$BIN_DIR/tea" <<'SH'
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
printf '%s\n' "$*" >> "$PR_REVIEW_TEST_LOG"
|
||||
|
||||
if [[ "$*" == "login list --output json" ]]; then
|
||||
printf '%s\n' '[{"name":"mosaicstack","url":"https://git.mosaicstack.dev"}]'
|
||||
exit 0
|
||||
fi
|
||||
|
||||
case "${PR_REVIEW_TEST_MODE:-}" in
|
||||
approve)
|
||||
[[ "$*" == "pr approve 123 --repo mosaicstack/stack --login mosaicstack" ]] || exit 90
|
||||
;;
|
||||
request-changes)
|
||||
[[ "$*" == "pr reject 123 --repo mosaicstack/stack --login mosaicstack --comment changes-required" ]] || exit 91
|
||||
;;
|
||||
legacy-fallback)
|
||||
if [[ "$*" == pr\ comment* ]]; then
|
||||
# tea v0.11.1 treats the nonexistent subcommand as `tea pr list` and exits 0.
|
||||
printf '%s\n' 'INDEX TITLE STATE'
|
||||
exit 0
|
||||
fi
|
||||
if [[ "$*" == comment* ]]; then
|
||||
printf '%s\n' 'supported comment write deliberately unavailable' >&2
|
||||
exit 23
|
||||
fi
|
||||
exit 92
|
||||
;;
|
||||
comment-success|readback-failure)
|
||||
if [[ "$*" == "comment 123 durable-body --login mosaicstack --repo mosaicstack/stack --output json" ]]; then
|
||||
printf '%s\n' '{"id":456,"body":"durable-body"}'
|
||||
exit 0
|
||||
fi
|
||||
if [[ "$*" == "api --login mosaicstack /repos/mosaicstack/stack/issues/comments/456" ]]; then
|
||||
if [[ "$PR_REVIEW_TEST_MODE" == "readback-failure" ]]; then
|
||||
printf '%s\n' '{"id":456,"body":"different-body","issue_url":"https://git.mosaicstack.dev/api/v1/repos/mosaicstack/stack/issues/123"}'
|
||||
else
|
||||
printf '%s\n' '{"id":456,"body":"durable-body","issue_url":"https://git.mosaicstack.dev/api/v1/repos/mosaicstack/stack/issues/123"}'
|
||||
fi
|
||||
exit 0
|
||||
fi
|
||||
if [[ "$*" == pr\ comment* ]]; then
|
||||
# Reproduce the old false-positive fallback if the wrapper regresses.
|
||||
printf '%s\n' 'INDEX TITLE STATE'
|
||||
exit 0
|
||||
fi
|
||||
exit 93
|
||||
;;
|
||||
write-failure)
|
||||
if [[ "$*" == "comment 123 durable-body --login mosaicstack --repo mosaicstack/stack --output json" ]]; then
|
||||
printf '%s\n' 'provider rejected comment' >&2
|
||||
exit 24
|
||||
fi
|
||||
exit 94
|
||||
;;
|
||||
*)
|
||||
exit 95
|
||||
;;
|
||||
esac
|
||||
SH
|
||||
chmod +x "$BIN_DIR/tea"
|
||||
|
||||
run_review() {
|
||||
local mode="$1" action="$2" comment="${3:-}"
|
||||
: > "$LOG_FILE"
|
||||
: > "$OUTPUT_FILE"
|
||||
(
|
||||
cd "$REPO_DIR"
|
||||
PATH="$BIN_DIR:$PATH" \
|
||||
PR_REVIEW_TEST_LOG="$LOG_FILE" \
|
||||
PR_REVIEW_TEST_MODE="$mode" \
|
||||
"$SCRIPT_DIR/pr-review.sh" -n 123 -a "$action" ${comment:+-c "$comment"}
|
||||
) > "$OUTPUT_FILE" 2>&1
|
||||
}
|
||||
|
||||
run_review approve approve
|
||||
grep -q '^pr approve 123 --repo mosaicstack/stack --login mosaicstack$' "$LOG_FILE"
|
||||
grep -q 'Approved Gitea PR #123' "$OUTPUT_FILE"
|
||||
|
||||
run_review request-changes request-changes changes-required
|
||||
grep -q '^pr reject 123 --repo mosaicstack/stack --login mosaicstack --comment changes-required$' "$LOG_FILE"
|
||||
grep -q 'Requested changes on Gitea PR #123' "$OUTPUT_FILE"
|
||||
|
||||
if run_review legacy-fallback comment durable-body; then
|
||||
echo "The old nonexistent tea pr comment fallback returned success" >&2
|
||||
cat "$OUTPUT_FILE" >&2
|
||||
exit 1
|
||||
fi
|
||||
if grep -q '^pr comment ' "$LOG_FILE"; then
|
||||
echo "Wrapper invoked unsupported tea pr comment" >&2
|
||||
exit 1
|
||||
fi
|
||||
if grep -q 'Added comment to Gitea PR' "$OUTPUT_FILE"; then
|
||||
echo "Wrapper reported success without durable persistence" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
run_review comment-success comment durable-body
|
||||
grep -q '^comment 123 durable-body --login mosaicstack --repo mosaicstack/stack --output json$' "$LOG_FILE"
|
||||
grep -q '^api --login mosaicstack /repos/mosaicstack/stack/issues/comments/456$' "$LOG_FILE"
|
||||
grep -q 'Added and verified comment on Gitea PR #123' "$OUTPUT_FILE"
|
||||
|
||||
if run_review write-failure comment durable-body; then
|
||||
echo "Expected provider write failure to return nonzero" >&2
|
||||
exit 1
|
||||
fi
|
||||
if grep -q 'Added and verified comment' "$OUTPUT_FILE"; then
|
||||
echo "Write failure reported durable success" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if run_review readback-failure comment durable-body; then
|
||||
echo "Expected mismatched provider read-back to return nonzero" >&2
|
||||
exit 1
|
||||
fi
|
||||
if grep -q 'Added and verified comment' "$OUTPUT_FILE"; then
|
||||
echo "Read-back mismatch reported durable success" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "pr-review.sh durable Gitea comment regression passed"
|
||||
@@ -24,8 +24,7 @@
|
||||
"build": "tsc",
|
||||
"lint": "eslint src",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"test": "vitest run --passWithNoTests && pnpm run test:framework-shell",
|
||||
"test:framework-shell": "bash framework/tools/codex/test-pr-diff-context.sh"
|
||||
"test": "vitest run --passWithNoTests"
|
||||
},
|
||||
"dependencies": {
|
||||
"@mosaicstack/brain": "workspace:*",
|
||||
|
||||
Reference in New Issue
Block a user