build(deps-dev): bump typescript from 6.0.3 to 7.0.2 in the typescript group across 1 directory #2551
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: Cursor Auto Review | |
| on: | |
| pull_request: | |
| types: [opened, synchronize, reopened] | |
| jobs: | |
| check_membership: | |
| if: ${{ !github.event.pull_request.head.repo.fork }} | |
| runs-on: ubuntu-latest | |
| outputs: | |
| is_member: ${{ steps.membership.outputs.is_member }} | |
| steps: | |
| - name: Verify trusted launcher | |
| uses: actions/github-script@v9 | |
| id: membership | |
| with: | |
| github-token: ${{ secrets.DAX_PAT }} | |
| script: | | |
| const prAuthor = context.payload.pull_request.user.login; | |
| // For human-authored PRs the PR author IS the launcher | |
| // — check team membership against them. For Cursor-app | |
| // PRs (`cursor[bot]`) the PR author is the bot, which | |
| // is never a team member, so we resolve a human identity | |
| // from the head commit instead. Prefer the committer | |
| // (who pushed); Cursor app commits often keep | |
| // `cursor[bot]` as committer, so fall back to the | |
| // commit author. Same-repo branches are already | |
| // required (the `head.repo.fork` check on this job), | |
| // so a human committer can only have been a user with | |
| // push access to this repo. | |
| let userToCheck = prAuthor; | |
| if (prAuthor === 'cursor[bot]') { | |
| const { data: headCommit } = await github.rest.repos.getCommit({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| ref: context.payload.pull_request.head.sha, | |
| }); | |
| const candidates = [ | |
| { label: 'committer', user: headCommit.committer }, | |
| { label: 'author', user: headCommit.author }, | |
| ]; | |
| const resolved = candidates.find( | |
| ({ user }) => user?.login && user.type !== 'Bot' | |
| ); | |
| if (!resolved) { | |
| console.log(`⏭️ ${prAuthor} PR has no resolvable human committer/author — skipping auto-review`); | |
| core.setOutput('is_member', 'false'); | |
| return; | |
| } | |
| console.log(`Resolved ${prAuthor} launcher to ${resolved.label}: ${resolved.user.login}`); | |
| userToCheck = resolved.user.login; | |
| } | |
| try { | |
| const { data } = await github.rest.teams.getMembershipForUserInOrg({ | |
| org: 'duckduckgo', | |
| team_slug: 'core', | |
| username: userToCheck | |
| }); | |
| if (data.state === 'active') { | |
| console.log(`✅ ${userToCheck} is an active member of duckduckgo/core`); | |
| core.setOutput('is_member', 'true'); | |
| } else { | |
| console.log(`⏭️ ${userToCheck} is not an active member — skipping auto-review`); | |
| core.setOutput('is_member', 'false'); | |
| } | |
| } catch (error) { | |
| if (error.status === 404) { | |
| console.log(`⏭️ ${userToCheck} is not a member of duckduckgo/core — skipping auto-review`); | |
| } else { | |
| console.log(`⚠️ Could not verify ${userToCheck}: ${error.message} (status: ${error.status})`); | |
| } | |
| core.setOutput('is_member', 'false'); | |
| } | |
| cursor_auto_review: | |
| needs: check_membership | |
| if: needs.check_membership.outputs.is_member == 'true' | |
| runs-on: ubuntu-latest | |
| concurrency: cursor-review-${{ github.event.pull_request.number }} | |
| steps: | |
| - name: Dismiss stale Dax approval on new push | |
| if: github.event.action == 'synchronize' | |
| uses: actions/github-script@v9 | |
| with: | |
| github-token: ${{ secrets.DAX_PAT }} | |
| script: | | |
| const reviews = await github.paginate(github.rest.pulls.listReviews, { | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| pull_number: context.payload.pull_request.number | |
| }); | |
| for (const review of reviews) { | |
| if (review.state === 'APPROVED' && review.user.login === 'daxtheduck') { | |
| await github.rest.pulls.dismissReview({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| pull_number: context.payload.pull_request.number, | |
| review_id: review.id, | |
| message: 'Dismissing stale approval — new commits pushed, awaiting Cursor re-review.' | |
| }); | |
| console.log(`Dismissed stale approval (review ${review.id})`); | |
| } | |
| } | |
| - name: Checkout base branch | |
| uses: actions/checkout@v7 | |
| with: | |
| ref: ${{ github.event.pull_request.base.ref }} | |
| sparse-checkout: | | |
| .github/REQUIRED_TEAMS | |
| .github/scripts | |
| sparse-checkout-cone-mode: false | |
| - name: Debounce delay | |
| run: sleep 5 | |
| - name: Wait for Cursor Bugbot | |
| uses: fountainhead/action-wait-for-check@v1.2.0 | |
| id: wait-for-cursor | |
| with: | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| checkName: Cursor Bugbot | |
| ref: ${{ github.event.pull_request.head.sha }} | |
| timeoutSeconds: 600 | |
| - name: Check Cursor Bugbot risk assessment | |
| if: steps.wait-for-cursor.outputs.conclusion == 'success' | |
| uses: actions/github-script@v9 | |
| id: risk_check | |
| with: | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| script: | | |
| const { findRiskLevel } = await import(`${process.cwd()}/.github/scripts/review-helpers.mjs`); | |
| const riskLevel = await findRiskLevel(github, { | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| prNumber: context.payload.pull_request.number | |
| }); | |
| console.log(`Cursor Bugbot risk level: ${riskLevel ?? 'not found'}`); | |
| core.setOutput('risk_level', riskLevel ?? 'unknown'); | |
| core.setOutput('is_low_risk', riskLevel?.toLowerCase() === 'low'); | |
| - name: Check for existing Dax approval | |
| uses: actions/github-script@v9 | |
| id: check_reviews | |
| with: | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| script: | | |
| const { DAX_USERNAME } = await import(`${process.cwd()}/.github/scripts/review-helpers.mjs`); | |
| const reviews = await github.paginate(github.rest.pulls.listReviews, { | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| pull_number: context.payload.pull_request.number | |
| }); | |
| const has = reviews.some(r => r.state === 'APPROVED' && r.user.login === DAX_USERNAME); | |
| console.log(`Found approved review from ${DAX_USERNAME}: ${has}`); | |
| core.setOutput('has_approved_review', has); | |
| - name: Auto approve | |
| if: | | |
| steps.wait-for-cursor.outputs.conclusion == 'success' && | |
| steps.risk_check.outputs.is_low_risk == 'true' && | |
| steps.check_reviews.outputs.has_approved_review == 'false' | |
| run: gh pr review --approve "$PR_URL" | |
| env: | |
| PR_URL: ${{ github.event.pull_request.html_url }} | |
| GITHUB_TOKEN: ${{ secrets.DAX_PAT }} | |
| - name: Find previous review comment | |
| if: | | |
| steps.wait-for-cursor.outputs.conclusion != 'success' || | |
| steps.risk_check.outputs.is_low_risk != 'true' | |
| uses: peter-evans/find-comment@v4 | |
| id: find_comment | |
| with: | |
| issue-number: ${{ github.event.pull_request.number }} | |
| comment-author: 'github-actions[bot]' | |
| body-includes: 'requires a manual review' | |
| direction: last | |
| - name: Post manual review required comment | |
| if: | | |
| (steps.wait-for-cursor.outputs.conclusion != 'success' || | |
| steps.risk_check.outputs.is_low_risk != 'true') && | |
| steps.find_comment.outputs.comment-id == '' | |
| uses: actions/github-script@v9 | |
| env: | |
| CURSOR_CONCLUSION: ${{ steps.wait-for-cursor.outputs.conclusion }} | |
| RISK_LEVEL: ${{ steps.risk_check.outputs.risk_level }} | |
| with: | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| script: | | |
| const { loadRequiredTeams, formatTeamList } = await import(`${process.cwd()}/.github/scripts/review-helpers.mjs`); | |
| const teams = loadRequiredTeams(); | |
| const teamList = formatTeamList(teams); | |
| const cursorPassed = process.env.CURSOR_CONCLUSION === 'success'; | |
| const riskLevel = process.env.RISK_LEVEL; | |
| const reason = cursorPassed | |
| ? `Cursor assessed this PR as **${riskLevel} Risk** (only Low Risk is auto-approved).` | |
| : 'Cursor review was not successful.'; | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: context.payload.pull_request.number, | |
| body: `⚠️ ${reason}\n\nThis PR requires a manual review and approval from a member of one of the following teams:\n\n${teamList}` | |
| }); |