fix(openclaw): verify the published npm Registry artifact #783
Workflow file for this run
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: Codex Auto Review And Approve | |
| on: | |
| pull_request_target: | |
| branches: [main] | |
| types: [opened, reopened, ready_for_review, synchronize] | |
| pull_request_review: | |
| types: [submitted] | |
| issue_comment: | |
| types: [created] | |
| permissions: | |
| contents: read | |
| issues: write | |
| pull-requests: write | |
| jobs: | |
| request-codex-review: | |
| if: github.event_name == 'pull_request_target' && !github.event.pull_request.draft | |
| concurrency: | |
| group: codex-review-request-${{ github.repository }}-${{ github.event.pull_request.number }} | |
| cancel-in-progress: true | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Ask Codex to review this PR | |
| uses: actions/github-script@v9 | |
| with: | |
| github-token: ${{ secrets.REPO_ACCESS_TOKEN }} | |
| script: | | |
| const eventPr = context.payload.pull_request; | |
| const pr = await github.rest.pulls.get({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| pull_number: eventPr.number, | |
| }).then((response) => response.data); | |
| if (pr.state !== "open" || pr.draft || pr.head.sha !== eventPr.head.sha) { | |
| core.info( | |
| `Ignoring stale review request for ${eventPr.head.sha}; live state=${pr.state}, draft=${pr.draft}, head=${pr.head.sha}.`, | |
| ); | |
| return; | |
| } | |
| const marker = `<!-- codex-auto-review:${pr.head.sha} -->`; | |
| const comments = await github.paginate(github.rest.issues.listComments, { | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: pr.number, | |
| per_page: 100, | |
| }); | |
| if (comments.some((comment) => comment.body?.includes(marker))) { | |
| core.info(`Codex review already requested for ${pr.head.sha}.`); | |
| return; | |
| } | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: pr.number, | |
| body: `@codex review\n\n${marker}`, | |
| }); | |
| approve-after-nonblocking-codex-review: | |
| # Filter before runner allocation. The approval created below emits another | |
| # pull_request_review event, and unrelated human/bot reviews must stay no-op. | |
| if: >- | |
| github.event_name == 'pull_request_review' && | |
| github.event.pull_request.base.ref == 'main' && | |
| github.event.review.user.type == 'Bot' && | |
| (contains(github.event.review.user.login, 'codex') || | |
| vars.CODEX_REVIEW_AUTHOR_PATTERN != '') | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Approve when Codex reports no blocking issues | |
| uses: actions/github-script@v9 | |
| env: | |
| CODEX_REVIEW_AUTHOR_PATTERN: ${{ vars.CODEX_REVIEW_AUTHOR_PATTERN }} | |
| with: | |
| github-token: ${{ secrets.REPO_ACCESS_TOKEN }} | |
| script: | | |
| const review = context.payload.review; | |
| const pr = context.payload.pull_request; | |
| const reviewer = review.user?.login || ""; | |
| const reviewerType = review.user?.type || ""; | |
| const authorPattern = new RegExp( | |
| process.env.CODEX_REVIEW_AUTHOR_PATTERN || | |
| "^(_chatgpt-codex-connector|codex|codex\\[bot\\]|openai-codex|openai-codex\\[bot\\]|chatgpt-codex|chatgpt-codex\\[bot\\]|chatgpt-codex-connector|chatgpt-codex-connector\\[bot\\])$", | |
| "i", | |
| ); | |
| if (!authorPattern.test(reviewer) || reviewerType !== "Bot") { | |
| core.info(`Ignoring review from ${reviewer} (${reviewerType}).`); | |
| return; | |
| } | |
| if (review.state === "APPROVED") { | |
| core.info("Codex already approved this PR."); | |
| return; | |
| } | |
| const comments = await github.paginate(github.rest.pulls.listCommentsForReview, { | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| pull_number: pr.number, | |
| review_id: review.id, | |
| per_page: 100, | |
| }); | |
| const reviewText = [review.body || "", ...comments.map((comment) => comment.body || "")] | |
| .join("\n") | |
| .trim(); | |
| const hasBlockingState = review.state === "CHANGES_REQUESTED"; | |
| const hasBlockingSeverity = /(^|[^A-Za-z0-9])(P0|P1)([^A-Za-z0-9]|$)/i.test(reviewText); | |
| if (hasBlockingState || hasBlockingSeverity) { | |
| core.info( | |
| `Codex review has blocking findings: state=${review.state}, comments=${comments.length}.`, | |
| ); | |
| return; | |
| } | |
| const livePr = await github.rest.pulls.get({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| pull_number: pr.number, | |
| }).then((response) => response.data); | |
| if (!review.commit_id || review.commit_id !== livePr.head.sha) { | |
| core.info( | |
| `Ignoring review for ${review.commit_id || "<missing>"}; live head is ${livePr.head.sha}.`, | |
| ); | |
| return; | |
| } | |
| await github.rest.pulls.createReview({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| pull_number: pr.number, | |
| commit_id: review.commit_id, | |
| event: "APPROVE", | |
| body: "Codex review completed without blocking findings. Auto-approving this PR; developers should still review and address any non-blocking suggestions before merge.", | |
| }); | |
| approve-after-clean-codex-comment: | |
| # The request job's own @codex comment also emits issue_comment. Only a | |
| # trusted Codex identity can produce a clean-review approval candidate. | |
| if: >- | |
| github.event_name == 'issue_comment' && | |
| github.event.issue.pull_request && | |
| github.event.comment.user.type == 'Bot' && | |
| (contains(github.event.comment.user.login, 'codex') || | |
| contains(github.event.comment.performed_via_github_app.slug, 'codex') || | |
| vars.CODEX_COMMENT_AUTHOR_PATTERN != '') | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Approve when Codex leaves a clean summary comment | |
| uses: actions/github-script@v9 | |
| env: | |
| CODEX_COMMENT_AUTHOR_PATTERN: ${{ vars.CODEX_COMMENT_AUTHOR_PATTERN }} | |
| with: | |
| github-token: ${{ secrets.REPO_ACCESS_TOKEN }} | |
| script: | | |
| const comment = context.payload.comment; | |
| const issue = context.payload.issue; | |
| const body = comment.body || ""; | |
| const commenter = comment.user?.login || ""; | |
| const commenterType = comment.user?.type || ""; | |
| const appSlug = comment.performed_via_github_app?.slug || ""; | |
| const authorPattern = new RegExp( | |
| process.env.CODEX_COMMENT_AUTHOR_PATTERN || | |
| "(^|[-_])codex($|[-_\\[])|chatgpt-codex|openai-codex", | |
| "i", | |
| ); | |
| if ( | |
| commenterType !== "Bot" || | |
| (!authorPattern.test(commenter) && !authorPattern.test(appSlug)) | |
| ) { | |
| core.info(`Ignoring comment from ${commenter || "unknown"} (${commenterType}).`); | |
| return; | |
| } | |
| const normalizedBody = body.replace(/\s+/g, " ").trim(); | |
| const reportsCleanReview = | |
| /Codex\s+Review\s*:\s*(?:Did(?:\s+not|n't)\s+find\s+any\s+(?:major\s+)?issues|No\s+(?:major\s+)?issues\s+found)\b/i.test( | |
| normalizedBody, | |
| ); | |
| const hasBlockingSeverity = /(^|[^A-Za-z0-9])(P0|P1)([^A-Za-z0-9]|$)/i.test( | |
| normalizedBody, | |
| ); | |
| if (!reportsCleanReview) { | |
| core.info("Codex comment does not report a clean review."); | |
| return; | |
| } | |
| if (hasBlockingSeverity) { | |
| core.info("Codex comment includes blocking severity, skipping approval."); | |
| return; | |
| } | |
| const pr = await github.rest.pulls.get({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| pull_number: issue.number, | |
| }).then((response) => response.data); | |
| if (pr.base?.ref !== "main") { | |
| core.info(`Ignoring PR targeting ${pr.base?.ref}.`); | |
| return; | |
| } | |
| const reviewedCommitMatch = body.match(/Reviewed commit[^0-9a-f]*([0-9a-f]{7,40})/i); | |
| const reviewedCommit = reviewedCommitMatch?.[1] || ""; | |
| if (!reviewedCommit || !pr.head.sha.startsWith(reviewedCommit)) { | |
| core.info( | |
| `Reviewed commit ${reviewedCommit || "<missing>"} does not match current PR head ${pr.head.sha}.`, | |
| ); | |
| return; | |
| } | |
| await github.rest.pulls.createReview({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| pull_number: pr.number, | |
| commit_id: pr.head.sha, | |
| event: "APPROVE", | |
| body: "Codex left a clean summary comment for the current head commit. Auto-approving this PR; developers should still review any follow-up comments before merge.", | |
| }); |