diff --git a/.github/workflows/opencode-review.yml b/.github/workflows/opencode-review.yml index 4b6a0abb..d6b4634a 100644 --- a/.github/workflows/opencode-review.yml +++ b/.github/workflows/opencode-review.yml @@ -2503,12 +2503,12 @@ jobs: if [ -z "$pr_json" ]; then return 0 fi - merge_state="$(printf '%s' "$pr_json" | jq -r '.mergeStateStatus // ""')" + merge_state="$(printf '%s' "$pr_json" | jq -r '.mergeStateStatus // ""' 2>/dev/null || echo "")" if [ "$merge_state" != "DIRTY" ] && [ "$merge_state" != "CONFLICTING" ]; then return 0 fi - base_ref="$(printf '%s' "$pr_json" | jq -r '.baseRefName // "base"')" - head_ref="$(printf '%s' "$pr_json" | jq -r '.headRefName // "head"')" + base_ref="$(printf '%s' "$pr_json" | jq -r '.baseRefName // "base"' 2>/dev/null || echo "base")" + head_ref="$(printf '%s' "$pr_json" | jq -r '.headRefName // "head"' 2>/dev/null || echo "head")" printf -v base_fetch_ref '%q' "$base_ref" printf -v base_origin_ref '%q' "origin/${base_ref}" printf -v head_push_ref '%q' "HEAD:${head_ref}" @@ -2629,10 +2629,10 @@ jobs: OPENCODE_MODEL_POOL_OUTPUT_FILE: ${{ runner.temp }}/opencode-review-model-pool.md PR_BASE_SHA: ${{ github.event.pull_request.base.sha || github.event.inputs.pr_base_sha }} PR_HEAD_SHA: ${{ github.event.pull_request.head.sha || github.event.inputs.pr_head_sha }} - APPROVAL_CHECK_WAIT_ATTEMPTS: "81" - APPROVAL_CHECK_WAIT_SLEEP_SECONDS: "30" - CHECK_LOOKUP_RETRY_ATTEMPTS: "5" - CHECK_LOOKUP_RETRY_SLEEP_SECONDS: "5" + APPROVAL_CHECK_WAIT_ATTEMPTS: "200" + APPROVAL_CHECK_WAIT_SLEEP_SECONDS: "60" + CHECK_LOOKUP_RETRY_ATTEMPTS: "10" + CHECK_LOOKUP_RETRY_SLEEP_SECONDS: "10" run: | set -euo pipefail echo "::group::OpenCode Review Approval Gate" @@ -2800,12 +2800,12 @@ jobs: if [ -z "$pr_json" ]; then return 0 fi - merge_state="$(printf '%s' "$pr_json" | jq -r '.mergeStateStatus // ""')" + merge_state="$(printf '%s' "$pr_json" | jq -r '.mergeStateStatus // ""' 2>/dev/null || echo "")" if [ "$merge_state" != "DIRTY" ] && [ "$merge_state" != "CONFLICTING" ]; then return 0 fi - base_ref="$(printf '%s' "$pr_json" | jq -r '.baseRefName // "base"')" - head_ref="$(printf '%s' "$pr_json" | jq -r '.headRefName // "head"')" + base_ref="$(printf '%s' "$pr_json" | jq -r '.baseRefName // "base"' 2>/dev/null || echo "base")" + head_ref="$(printf '%s' "$pr_json" | jq -r '.headRefName // "head"' 2>/dev/null || echo "head")" printf -v base_fetch_ref '%q' "$base_ref" printf -v base_origin_ref '%q' "origin/${base_ref}" printf -v head_push_ref '%q' "HEAD:${head_ref}" @@ -2920,9 +2920,8 @@ jobs: fi rm -f "$gh_error_file" "$review_payload_file" update_review_overview "$event" "$body" || true - printf '::error::OpenCode could not publish the pull review for head %s, so the review state was not changed.\n' "$HEAD_SHA" - echo "::endgroup::" - exit 1 + printf '::warning::OpenCode could not publish the pull review for head %s, so the review state was not changed.\n' "$HEAD_SHA" + return 0 fi rm -f "$gh_error_file" "$review_payload_file" update_review_overview "$event" "$body" @@ -3224,11 +3223,11 @@ jobs: warn_gh_publication_failure "pull review inline comments" "$gh_error_file" rm -f "$gh_error_file" if [ -s "$fallback_body_file" ]; then - update_review_overview "INLINE_COMMENT_PUBLISH_FAILED" "$(cat "$fallback_body_file")" + create_pull_review "REQUEST_CHANGES" "$(cat "$fallback_body_file")" else - update_review_overview "INLINE_COMMENT_PUBLISH_FAILED" "$body" + create_pull_review "REQUEST_CHANGES" "$body" fi - return 1 + return 0 fi rm -f "$gh_error_file" update_review_overview "$event" "$body" @@ -3265,8 +3264,8 @@ jobs: local reason local findings - summary="$(jq -r '.summary // ""' "$control_json")" - reason="$(jq -r '.reason // ""' "$control_json")" + summary="$(jq -r '.summary // ""' "$control_json" 2>/dev/null || echo "")" + reason="$(jq -r '.reason // ""' "$control_json" 2>/dev/null || echo "")" findings="$( # shellcheck disable=SC2016 jq -r ' @@ -4930,7 +4929,7 @@ jobs: gh api -X GET "repos/${GH_REPOSITORY}/issues/${PR_NUMBER}/comments" --paginate \ --jq "[.[] | select((.user.login == \"github-actions[bot]\" or .user.login == \"opencode-agent[bot]\") and (.body | contains(\"${sentinel}\")))] | sort_by(.created_at) | last // {}" )" - comment_body="$(jq -r '.body // ""' <<<"$comment_json")" + comment_body="$(jq -r '.body // ""' <<<"$comment_json" 2>/dev/null || echo "")" tmp_body="$(mktemp)" control_json="$(mktemp)" @@ -5090,8 +5089,8 @@ jobs: echo "::endgroup::" exit 0 fi - summary="$(jq -r '.summary' "$control_json")" - reason="$(jq -r '.reason' "$control_json")" + summary="$(jq -r '.summary // ""' "$control_json" 2>/dev/null || echo "")" + reason="$(jq -r '.reason // ""' "$control_json" 2>/dev/null || echo "")" body="$(printf '%s\n' \ "## Pull request overview" \ "" \ diff --git a/tests/test_opencode_agent_contract.py b/tests/test_opencode_agent_contract.py index c5dd7a9b..55750787 100644 --- a/tests/test_opencode_agent_contract.py +++ b/tests/test_opencode_agent_contract.py @@ -210,10 +210,13 @@ def test_workflow_provisions_sandbox_tool_and_reviewer_agent(): assert '"## Review outcome"' in workflow assert '"## Check outcome"' not in workflow assert "publish REQUEST_CHANGES when coverage-evidence blocker states" in workflow + assert "INLINE_COMMENT_PUBLISH_FAILED" in workflow assert 'timeout-minutes: 75' in workflow assert re.search(r"Run OpenCode PR Review model pool[\s\S]{0,240}timeout-minutes: 20", workflow) - assert 'APPROVAL_CHECK_WAIT_ATTEMPTS: "81"' in workflow - assert 'APPROVAL_CHECK_WAIT_SLEEP_SECONDS: "30"' in workflow + assert 'APPROVAL_CHECK_WAIT_ATTEMPTS: "200"' in workflow + assert 'APPROVAL_CHECK_WAIT_SLEEP_SECONDS: "60"' in workflow + assert 'CHECK_LOOKUP_RETRY_ATTEMPTS: "10"' in workflow + assert 'CHECK_LOOKUP_RETRY_SLEEP_SECONDS: "10"' in workflow assert 'OPENCODE_MODEL_CANDIDATES: "github-models/openai/gpt-5-nano"' in workflow assert 'OPENCODE_MODEL_ATTEMPTS: "1"' in workflow assert 'OPENCODE_RUN_TIMEOUT_SECONDS: "240"' in workflow diff --git a/tests/test_pr_review_merge_scheduler.py b/tests/test_pr_review_merge_scheduler.py index c6631351..d15953f3 100644 --- a/tests/test_pr_review_merge_scheduler.py +++ b/tests/test_pr_review_merge_scheduler.py @@ -2797,3 +2797,12 @@ def test_run_masks_secrets_in_args(): err_msg = str(exc_info.value) assert "ghp_abcdef1234567890abcdef1234567890abcdef" not in err_msg assert "***" in err_msg + + +def test_parse_external_head_update_reason(): + """Test extracting the external head repository from update guidance.""" + assert sched.parse_external_head_update_reason("head repo some-user/repo is external and not writable") == "some-user/repo" + assert sched.parse_external_head_update_reason("head repo external_repo is external and not writable") == "external_repo" + assert sched.parse_external_head_update_reason("head repo user-repo/123-repo is external and not writable but we try") == "user-repo/123-repo" + assert sched.parse_external_head_update_reason("something else completely") is None + assert sched.parse_external_head_update_reason("head repo is external and not writable") is None