Auto Merge Codex Remediation PR #420
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Auto Merge Codex Remediation PR | |
| "on": | |
| workflow_run: | |
| workflows: ["CI"] | |
| types: [completed] | |
| jobs: | |
| auto-merge: | |
| if: github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.head_repository.full_name == github.repository && startsWith(github.event.workflow_run.head_branch, 'codex/monthly-review-issue-') && contains(fromJSON('["true","True","TRUE"]'), vars.CODEX_AUDIT_AUTO_MERGE) | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| permissions: | |
| contents: write | |
| issues: write | |
| pull-requests: write | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| with: | |
| ref: ${{ github.event.repository.default_branch }} | |
| - name: Resolve Codex remediation PR | |
| id: pr | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| set -euo pipefail | |
| mkdir -p data/output/codex_auto_merge | |
| branch="${{ github.event.workflow_run.head_branch }}" | |
| pr_payload=$(gh pr list --repo "${{ github.repository }}" --state open --head "${branch}" --json number,headRefOid,headRepository,isCrossRepository \ | |
| --jq '[.[] | select(.isCrossRepository == false and .headRepository.nameWithOwner == "${{ github.repository }}")][0] // {}') | |
| pr_number=$(python3 -c 'import json,sys; print(json.load(sys.stdin).get("number", ""))' <<<"${pr_payload}") | |
| if [ -z "${pr_number}" ]; then | |
| echo "No same-repository open PR found for ${branch}." >> "$GITHUB_STEP_SUMMARY" | |
| exit 0 | |
| fi | |
| pr_head_sha=$(python3 -c 'import json,sys; print(json.load(sys.stdin).get("headRefOid", ""))' <<<"${pr_payload}") | |
| if [ "${pr_head_sha}" != "${{ github.event.workflow_run.head_sha }}" ]; then | |
| echo "Skipping auto-merge for stale CI success on ${branch}; workflow_run head SHA does not match the current PR head." >> "$GITHUB_STEP_SUMMARY" | |
| exit 0 | |
| fi | |
| echo "pr_number=${pr_number}" >> "$GITHUB_OUTPUT" | |
| - name: Evaluate merge guard | |
| id: merge_guard | |
| if: steps.pr.outputs.pr_number != '' | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| set -euo pipefail | |
| gh pr view "${{ steps.pr.outputs.pr_number }}" --repo "${{ github.repository }}" \ | |
| --json number,isDraft,body,url,files,changedFiles,additions,deletions,reviewDecision,labels,baseRefName,headRefName,headRepositoryOwner,headRepository,isCrossRepository > data/output/codex_auto_merge/pr.json | |
| gh api --paginate --slurp \ | |
| "/repos/${{ github.repository }}/pulls/${{ steps.pr.outputs.pr_number }}/files?per_page=100" \ | |
| > data/output/codex_auto_merge/pr_files_pages.json | |
| python3 - <<'PY' | |
| import json | |
| from pathlib import Path | |
| output_dir = Path("data/output/codex_auto_merge") | |
| pr_path = output_dir / "pr.json" | |
| pr = json.loads(pr_path.read_text(encoding="utf-8")) | |
| pages = json.loads((output_dir / "pr_files_pages.json").read_text(encoding="utf-8")) | |
| files = [] | |
| for page in pages: | |
| if not isinstance(page, list): | |
| continue | |
| for item in page: | |
| if not isinstance(item, dict): | |
| continue | |
| files.append( | |
| { | |
| "path": item.get("filename", ""), | |
| "status": item.get("status", ""), | |
| "additions": item.get("additions"), | |
| "deletions": item.get("deletions"), | |
| "previous_filename": item.get("previous_filename", ""), | |
| } | |
| ) | |
| pr["files"] = files | |
| pr_path.write_text(json.dumps(pr, indent=2, sort_keys=True) + "\n", encoding="utf-8") | |
| PY | |
| python3 scripts/evaluate_codex_pr_merge.py \ | |
| --pr-json data/output/codex_auto_merge/pr.json \ | |
| --summary-file data/output/codex_auto_merge/summary.md \ | |
| --decision-file data/output/codex_auto_merge/decision.json \ | |
| --expected-base-ref "${{ github.event.repository.default_branch }}" \ | |
| --expected-head-ref "${{ github.event.workflow_run.head_branch }}" \ | |
| --expected-head-owner "${{ github.repository_owner }}" \ | |
| --expected-head-repository "${{ github.repository }}" \ | |
| --require-same-repository | |
| python3 - <<'PY' >> "$GITHUB_OUTPUT" | |
| import json | |
| from pathlib import Path | |
| decision = json.loads(Path("data/output/codex_auto_merge/decision.json").read_text(encoding="utf-8")) | |
| print(f"should_merge={str(bool(decision['should_merge'])).lower()}") | |
| print(f"reason={decision['reason']}") | |
| print(f"risk_level={decision['risk_level']}") | |
| PY | |
| - name: Append merge guard summary | |
| if: steps.pr.outputs.pr_number != '' | |
| run: cat data/output/codex_auto_merge/summary.md >> "$GITHUB_STEP_SUMMARY" | |
| - name: Comment auto-merge guard decision | |
| if: steps.pr.outputs.pr_number != '' && steps.merge_guard.outputs.should_merge != 'true' | |
| continue-on-error: true | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| set -euo pipefail | |
| python3 scripts/post_codex_auto_merge_decision_comment.py \ | |
| --repo "${{ github.repository }}" \ | |
| --pr-json data/output/codex_auto_merge/pr.json \ | |
| --decision-json data/output/codex_auto_merge/decision.json \ | |
| --output-file data/output/codex_auto_merge/guard_decision_comment.md \ | |
| --sync-labels | |
| - name: Check guarded auto-merge readiness before merge | |
| id: merge_readiness | |
| if: steps.merge_guard.outputs.should_merge == 'true' | |
| continue-on-error: true | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.CODEX_AUDIT_READINESS_TOKEN || secrets.GITHUB_TOKEN }} | |
| CODEX_AUDIT_REQUIRED_STATUS_CHECKS: ${{ vars.CODEX_AUDIT_REQUIRED_STATUS_CHECKS || 'test' }} | |
| run: | | |
| set -euo pipefail | |
| python3 scripts/check_codex_auto_merge_readiness.py \ | |
| --repo "${{ github.repository }}" \ | |
| --branch "${{ github.event.repository.default_branch }}" \ | |
| --auto-merge true \ | |
| --required-status-checks "${CODEX_AUDIT_REQUIRED_STATUS_CHECKS}" \ | |
| --summary-file data/output/codex_auto_merge/readiness.md | |
| - name: Comment merge-time readiness failure | |
| if: steps.merge_guard.outputs.should_merge == 'true' && steps.merge_readiness.outcome != 'success' | |
| continue-on-error: true | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| set -euo pipefail | |
| python3 - <<'PY' | |
| import json | |
| from pathlib import Path | |
| decision_path = Path("data/output/codex_auto_merge/decision.json") | |
| decision = json.loads(decision_path.read_text(encoding="utf-8")) | |
| risk_reasons = list(decision.get("risk_reasons") or []) | |
| risk_reasons.append("merge-time readiness check failed; see codex_auto_merge/readiness.md") | |
| metadata_errors = list(decision.get("metadata_errors") or []) | |
| metadata_errors.append("merge-time readiness check failed") | |
| decision.update( | |
| { | |
| "should_merge": False, | |
| "reason": "merge_readiness_failed", | |
| "risk_level": "high", | |
| "risk_reasons": risk_reasons, | |
| "metadata_errors": metadata_errors, | |
| } | |
| ) | |
| Path("data/output/codex_auto_merge/readiness_decision.json").write_text( | |
| json.dumps(decision, indent=2, sort_keys=True) + "\n", | |
| encoding="utf-8", | |
| ) | |
| PY | |
| python3 scripts/post_codex_auto_merge_decision_comment.py \ | |
| --repo "${{ github.repository }}" \ | |
| --pr-json data/output/codex_auto_merge/pr.json \ | |
| --decision-json data/output/codex_auto_merge/readiness_decision.json \ | |
| --output-file data/output/codex_auto_merge/readiness_guard_decision_comment.md \ | |
| --sync-labels | |
| - name: Fail on merge-time readiness failure | |
| if: steps.merge_guard.outputs.should_merge == 'true' && steps.merge_readiness.outcome != 'success' | |
| run: | | |
| echo "Guarded auto-merge readiness failed at merge time; see readiness.md and readiness_guard_decision_comment.md." >&2 | |
| exit 1 | |
| - name: Merge Codex remediation PR | |
| if: steps.merge_guard.outputs.should_merge == 'true' && steps.merge_readiness.outcome == 'success' | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: gh pr merge "${{ steps.pr.outputs.pr_number }}" --repo "${{ github.repository }}" --rebase --delete-branch --match-head-commit "${{ github.event.workflow_run.head_sha }}" | |
| - name: Upload Codex auto-merge diagnostics | |
| if: always() | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: codex-auto-merge-${{ github.run_id }} | |
| path: data/output/codex_auto_merge/ | |
| if-no-files-found: warn |