From ef91d474840330e2ea7488b49e99f8f9d5f78df4 Mon Sep 17 00:00:00 2001 From: "xufeihong.xfh" Date: Sat, 21 Mar 2026 00:41:23 +0800 Subject: [PATCH 1/4] ci: auto pr assign by CODEOWNERS --- .github/CODEOWNERS | 35 +++++ .github/auto-assign-config.yml | 24 ++++ .github/workflows/auto-assign.yml | 17 +++ .github/workflows/community-pr-handler.yml | 147 +++++++++++++++++++++ 4 files changed, 223 insertions(+) create mode 100644 .github/CODEOWNERS create mode 100644 .github/auto-assign-config.yml create mode 100644 .github/workflows/auto-assign.yml create mode 100644 .github/workflows/community-pr-handler.yml diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 00000000..1db9e528 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,35 @@ +# CODEOWNERS - Auto-assign reviewers based on file paths +# Documentation: https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners + +# Default: feihongxu0824 reviews everything not covered below +* @feihongxu0824 + +# .github directory +/.github/ @Cuiyus + +# cmake, examples, scripts +/cmake/ @egolearner +/examples/ @egolearner +/scripts/ @egolearner + +# src/db, tests +/src/db/ @zhourrr +/tests/ @zhourrr + +# src/core, src/turbo +/src/core/ @richyreachy +/src/turbo/ @richyreachy + +# src/ailego +/src/ailego/ @iaojnh + +# tools +/tools/ @JalinWang + +# src/binding +/src/binding/ @Cuiyus + +# src/include, thirdparty, .gitmodules +/src/include/ @chinaux +/thirdparty/ @chinaux +.gitmodules @chinaux diff --git a/.github/auto-assign-config.yml b/.github/auto-assign-config.yml new file mode 100644 index 00000000..5f76f4a9 --- /dev/null +++ b/.github/auto-assign-config.yml @@ -0,0 +1,24 @@ +# Auto-assign configuration +# Documentation: https://github.com/kentaro-m/auto-assign-action +# +# NOTE: Reviewers are assigned via CODEOWNERS file based on file paths. +# This config only handles assignee assignment. + +# Automatically add PR author as assignee +addAssignees: author + +# Number of assignees to add (0 = add all from the list) +numberOfAssignees: 0 + +# Reviewers are handled by CODEOWNERS, not here +addReviewers: false + +# Skip draft PRs +skipDraft: true + +# Skip keywords in PR title (won't assign if title contains these) +skipKeywords: + - wip + - WIP + - draft + - DRAFT diff --git a/.github/workflows/auto-assign.yml b/.github/workflows/auto-assign.yml new file mode 100644 index 00000000..4ceb4ac4 --- /dev/null +++ b/.github/workflows/auto-assign.yml @@ -0,0 +1,17 @@ +name: Auto Assign PR + +on: + pull_request: + types: [opened, reopened, ready_for_review] + +permissions: + pull-requests: write + +jobs: + auto-assign: + runs-on: ubuntu-latest + steps: + - name: Auto Assign PR Author + uses: kentaro-m/auto-assign-action@v2.0.0 + with: + configuration-path: '.github/auto-assign-config.yml' diff --git a/.github/workflows/community-pr-handler.yml b/.github/workflows/community-pr-handler.yml new file mode 100644 index 00000000..2de59289 --- /dev/null +++ b/.github/workflows/community-pr-handler.yml @@ -0,0 +1,147 @@ +name: Community PR Handler + +on: + pull_request_target: + types: [opened, reopened, ready_for_review] + +permissions: + pull-requests: write + +jobs: + handle-community-pr: + runs-on: ubuntu-latest + if: github.event.pull_request.draft == false + steps: + - name: Check if community contributor + id: check + uses: actions/github-script@v7 + with: + script: | + const pr = context.payload.pull_request; + const prAuthor = pr.user.login; + const association = pr.author_association; + + console.log('========== PR Info =========='); + console.log(`PR Number: #${pr.number}`); + console.log(`PR Title: ${pr.title}`); + console.log(`PR Author: ${prAuthor}`); + console.log(`Author Association: ${association}`); + console.log(`Base Branch: ${pr.base.ref}`); + console.log(`Head Branch: ${pr.head.ref}`); + console.log(`Head Repo: ${pr.head.repo?.full_name || 'same repo'}`); + + // OWNER, MEMBER, COLLABORATOR are maintainers; others are community contributors + const maintainerRoles = ['OWNER', 'MEMBER', 'COLLABORATOR']; + const isCommunity = !maintainerRoles.includes(association); + + console.log('========== Decision =========='); + console.log(`Maintainer Roles: ${maintainerRoles.join(', ')}`); + console.log(`Is Community Contributor: ${isCommunity}`); + + if (!isCommunity) { + console.log('Skipping: PR author is a maintainer, no action needed.'); + } + + core.setOutput('is_community', isCommunity); + + - name: Checkout base branch for CODEOWNERS + if: steps.check.outputs.is_community == 'true' + uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.base.ref }} + sparse-checkout: .github/CODEOWNERS + sparse-checkout-cone-mode: false + + - name: Assign reviewer as assignee + if: steps.check.outputs.is_community == 'true' + uses: actions/github-script@v7 + with: + script: | + const fs = require('fs'); + const prNumber = context.payload.pull_request.number; + + console.log('========== Fetching Changed Files =========='); + // Get changed files + const { data: files } = await github.rest.pulls.listFiles({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: prNumber + }); + const changedFiles = files.map(f => f.filename); + console.log(`Total changed files: ${changedFiles.length}`); + changedFiles.forEach((f, i) => console.log(` [${i + 1}] ${f}`)); + + console.log('========== Parsing CODEOWNERS =========='); + // Parse CODEOWNERS + let codeowners = []; + try { + const content = fs.readFileSync('.github/CODEOWNERS', 'utf8'); + const lines = content.split('\n').filter(l => l.trim() && !l.trim().startsWith('#')); + console.log(`Found ${lines.length} rules in CODEOWNERS`); + for (const line of lines) { + const parts = line.trim().split(/\s+/); + if (parts.length >= 2) { + const pattern = parts[0]; + const owners = parts.slice(1).map(o => o.replace('@', '')); + codeowners.push({ pattern, owners }); + console.log(` Rule: "${pattern}" -> [${owners.join(', ')}]`); + } + } + } catch (e) { + console.log('ERROR: Could not read CODEOWNERS:', e.message); + return; + } + + console.log('========== Matching Files to Owners =========='); + // Find matching owners for changed files + const matchedOwners = new Set(); + for (const file of changedFiles) { + let matchedOwner = null; + let matchedPattern = null; + for (const rule of codeowners) { + const pattern = rule.pattern; + if (pattern === '*') { + matchedOwner = rule.owners[0]; + matchedPattern = pattern; + } else if (pattern.endsWith('/')) { + const dir = pattern.replace(/^\//, '').replace(/\/$/, ''); + if (file.startsWith(dir + '/') || file.startsWith(dir)) { + matchedOwner = rule.owners[0]; + matchedPattern = pattern; + } + } else if (file === pattern || file === pattern.replace(/^\//, '')) { + matchedOwner = rule.owners[0]; + matchedPattern = pattern; + } + } + if (matchedOwner) { + matchedOwners.add(matchedOwner); + console.log(` "${file}" -> matched "${matchedPattern}" -> @${matchedOwner}`); + } else { + console.log(` "${file}" -> NO MATCH`); + } + } + + console.log('========== Setting Assignees =========='); + // Set assignees + const assignees = Array.from(matchedOwners); + console.log(`Assignees to set: [${assignees.join(', ')}]`); + + if (assignees.length > 0) { + try { + await github.rest.issues.addAssignees({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: prNumber, + assignees: assignees + }); + console.log('SUCCESS: Assignees set successfully!'); + } catch (e) { + console.log('ERROR: Failed to set assignees:', e.message); + console.log('Error details:', JSON.stringify(e, null, 2)); + } + } else { + console.log('WARNING: No assignees to set - no matching owners found.'); + } + + console.log('========== Done =========='); From c7ec7bbbe5ec298940c547bdd3d915e75392059b Mon Sep 17 00:00:00 2001 From: "xufeihong.xfh" Date: Sat, 21 Mar 2026 00:53:32 +0800 Subject: [PATCH 2/4] fix --- .github/workflows/auto-assign.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/auto-assign.yml b/.github/workflows/auto-assign.yml index 4ceb4ac4..7cbbf60a 100644 --- a/.github/workflows/auto-assign.yml +++ b/.github/workflows/auto-assign.yml @@ -1,7 +1,7 @@ name: Auto Assign PR on: - pull_request: + pull_request_target: types: [opened, reopened, ready_for_review] permissions: @@ -11,6 +11,13 @@ jobs: auto-assign: runs-on: ubuntu-latest steps: + - name: Checkout for config + uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.base.ref }} + sparse-checkout: .github/auto-assign-config.yml + sparse-checkout-cone-mode: false + - name: Auto Assign PR Author uses: kentaro-m/auto-assign-action@v2.0.0 with: From 29615ddcab85560e35fe5415d9ef52385c44d407 Mon Sep 17 00:00:00 2001 From: feihongxu0824 Date: Sat, 21 Mar 2026 01:15:04 +0800 Subject: [PATCH 3/4] Update .github/workflows/community-pr-handler.yml Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> --- .github/workflows/community-pr-handler.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/community-pr-handler.yml b/.github/workflows/community-pr-handler.yml index 2de59289..626aab2e 100644 --- a/.github/workflows/community-pr-handler.yml +++ b/.github/workflows/community-pr-handler.yml @@ -5,6 +5,7 @@ on: types: [opened, reopened, ready_for_review] permissions: + contents: read pull-requests: write jobs: From cffacc51c39e7abd4b0f2af7d17fd132bbf8859e Mon Sep 17 00:00:00 2001 From: feihongxu0824 Date: Sat, 21 Mar 2026 01:15:25 +0800 Subject: [PATCH 4/4] Update .github/workflows/auto-assign.yml Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> --- .github/workflows/auto-assign.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/auto-assign.yml b/.github/workflows/auto-assign.yml index 7cbbf60a..bc6f14eb 100644 --- a/.github/workflows/auto-assign.yml +++ b/.github/workflows/auto-assign.yml @@ -5,6 +5,7 @@ on: types: [opened, reopened, ready_for_review] permissions: + contents: read pull-requests: write jobs: