diff --git a/.github/workflows/opencode-review.yml b/.github/workflows/opencode-review.yml index eb3e03117..36b65d508 100644 --- a/.github/workflows/opencode-review.yml +++ b/.github/workflows/opencode-review.yml @@ -6028,10 +6028,10 @@ jobs: - name: Run merge scheduler after approval continue-on-error: true env: - GH_TOKEN: ${{ (github.event_name == 'pull_request_target' || github.event.inputs.target_repository == '' || github.event.inputs.target_repository == github.repository) && github.token || secrets.PR_REVIEW_MERGE_TOKEN || secrets.OPENCODE_APPROVE_TOKEN || steps.opencode_app_token.outputs.token }} + GH_TOKEN: ${{ secrets.PR_REVIEW_MERGE_TOKEN || secrets.OPENCODE_APPROVE_TOKEN || steps.opencode_app_token.outputs.token || github.token }} SCHEDULER_ACTIONS_TOKEN: ${{ github.token }} SCHEDULER_READ_TOKEN: ${{ (github.event_name == 'pull_request_target' || github.event.inputs.target_repository == '' || github.event.inputs.target_repository == github.repository) && github.token || secrets.PR_REVIEW_MERGE_TOKEN || secrets.OPENCODE_APPROVE_TOKEN || steps.opencode_app_token.outputs.token }} - SCHEDULER_MUTATION_TOKEN_SOURCE: ${{ (github.event_name == 'pull_request_target' || github.event.inputs.target_repository == '' || github.event.inputs.target_repository == github.repository) && 'github-token' || secrets.PR_REVIEW_MERGE_TOKEN != '' && 'PR_REVIEW_MERGE_TOKEN' || secrets.OPENCODE_APPROVE_TOKEN != '' && 'OPENCODE_APPROVE_TOKEN' || steps.opencode_app_token.outputs.available == 'true' && 'opencode-app' || 'missing' }} + SCHEDULER_MUTATION_TOKEN_SOURCE: ${{ secrets.PR_REVIEW_MERGE_TOKEN != '' && 'PR_REVIEW_MERGE_TOKEN' || secrets.OPENCODE_APPROVE_TOKEN != '' && 'OPENCODE_APPROVE_TOKEN' || steps.opencode_app_token.outputs.available == 'true' && 'opencode-app' || 'github-token' }} GH_REPOSITORY: ${{ github.event.pull_request.base.repo.full_name || github.event.inputs.target_repository || github.repository }} PR_BASE_REF: ${{ github.event.pull_request.base.ref || github.event.inputs.pr_base_ref || '' }} PR_NUMBER: ${{ github.event.pull_request.number || github.event.inputs.pr_number || '' }} diff --git a/scripts/ci/test_strix_quick_gate.sh b/scripts/ci/test_strix_quick_gate.sh index d8fcd2d3e..54a68e979 100755 --- a/scripts/ci/test_strix_quick_gate.sh +++ b/scripts/ci/test_strix_quick_gate.sh @@ -715,10 +715,11 @@ assert_opencode_review_uses_codegraph_and_gpt5_fallback() { assert_file_contains "$workflow_file" "not falling back to npm" "coverage evidence logs package-runner activation failures instead of silently using npm" assert_file_contains "$workflow_file" "Run merge scheduler after approval" "opencode approval runs the merge scheduler after current-head review publication" assert_file_contains "$workflow_file" "python3 scripts/ci/pr_review_merge_scheduler.py" "opencode approval directly executes the trusted central merge scheduler when required workflows are not repo-local dispatch targets" - assert_file_contains "$workflow_file" "pull-requests: write" "opencode approval can use github-actions[bot] for same-repository mechanical merge/update follow-up" + assert_file_contains "$workflow_file" "pull-requests: write" "opencode approval has pull-request mutation permission for merge/update follow-up" assert_file_contains "$workflow_file" 'SCHEDULER_ACTIONS_TOKEN: ${{ github.token }}' "opencode scheduler follow-up gives workflow-control calls the GitHub Actions token" assert_file_contains "$workflow_file" 'SCHEDULER_READ_TOKEN: ${{ (github.event_name == '\''pull_request_target'\'' || github.event.inputs.target_repository == '\'''\'' || github.event.inputs.target_repository == github.repository) && github.token || secrets.PR_REVIEW_MERGE_TOKEN || secrets.OPENCODE_APPROVE_TOKEN || steps.opencode_app_token.outputs.token }}' "opencode scheduler follow-up reads cross-repository PR state with target-capable credentials" - assert_file_contains "$workflow_file" "&& 'github-token' || secrets.PR_REVIEW_MERGE_TOKEN" "opencode scheduler follow-up prefers github-actions[bot] for same-repository mechanical mutations" + assert_file_contains "$workflow_file" 'GH_TOKEN: ${{ secrets.PR_REVIEW_MERGE_TOKEN || secrets.OPENCODE_APPROVE_TOKEN || steps.opencode_app_token.outputs.token || github.token }}' "opencode scheduler follow-up escalates merge mutations before falling back to github-actions token" + assert_file_contains "$workflow_file" "steps.opencode_app_token.outputs.available == 'true' && 'opencode-app' || 'github-token'" "opencode scheduler follow-up labels the actual escalating mutation credential" assert_file_not_contains "$workflow_file" "gh workflow run pr-review-merge-scheduler.yml" "opencode approval must not rely on repo-local workflow dispatch for organization required workflows" assert_file_contains "$workflow_file" "gh api \"repos/\${GH_REPOSITORY}\" --jq '.default_branch // empty'" "opencode scheduler dispatch uses the target repository default branch" assert_file_contains "$workflow_file" 'base_branch="${PR_BASE_REF:-${default_branch:-main}}"' "opencode scheduler follow-up derives the target base branch instead of hard-coding main" diff --git a/tests/test_opencode_agent_contract.py b/tests/test_opencode_agent_contract.py index 34815f8b4..af933f885 100644 --- a/tests/test_opencode_agent_contract.py +++ b/tests/test_opencode_agent_contract.py @@ -683,7 +683,11 @@ def test_opencode_runs_merge_scheduler_after_review_without_repo_local_dispatch( assert "python3 scripts/ci/pr_review_merge_scheduler.py" in workflow assert "gh workflow run pr-review-merge-scheduler.yml" not in workflow assert "github.event_name == 'pull_request_target'" in workflow - assert "&& github.token || secrets.PR_REVIEW_MERGE_TOKEN || secrets.OPENCODE_APPROVE_TOKEN || steps.opencode_app_token.outputs.token" in workflow + assert ( + "GH_TOKEN: ${{ secrets.PR_REVIEW_MERGE_TOKEN || " + "secrets.OPENCODE_APPROVE_TOKEN || steps.opencode_app_token.outputs.token || " + "github.token }}" + ) in workflow assert "SCHEDULER_ACTIONS_TOKEN: ${{ github.token }}" in workflow assert ( "SCHEDULER_READ_TOKEN: ${{ (github.event_name == 'pull_request_target' || " @@ -692,7 +696,12 @@ def test_opencode_runs_merge_scheduler_after_review_without_repo_local_dispatch( "secrets.PR_REVIEW_MERGE_TOKEN || secrets.OPENCODE_APPROVE_TOKEN || " "steps.opencode_app_token.outputs.token }}" ) in workflow - assert "&& 'github-token' || secrets.PR_REVIEW_MERGE_TOKEN" in workflow + assert ( + "SCHEDULER_MUTATION_TOKEN_SOURCE: ${{ secrets.PR_REVIEW_MERGE_TOKEN != '' && " + "'PR_REVIEW_MERGE_TOKEN' || secrets.OPENCODE_APPROVE_TOKEN != '' && " + "'OPENCODE_APPROVE_TOKEN' || steps.opencode_app_token.outputs.available == 'true' && " + "'opencode-app' || 'github-token' }}" + ) in workflow assert "--no-trigger-reviews" in workflow assert "--enable-auto-merge" in workflow assert "--no-update-branches" in workflow