Skip to content

ci: auto pr assign by CODEOWNERS#249

Open
feihongxu0824 wants to merge 4 commits intoalibaba:mainfrom
feihongxu0824:chore/codeowners
Open

ci: auto pr assign by CODEOWNERS#249
feihongxu0824 wants to merge 4 commits intoalibaba:mainfrom
feihongxu0824:chore/codeowners

Conversation

@feihongxu0824
Copy link
Collaborator

@feihongxu0824 feihongxu0824 commented Mar 20, 2026

Greptile Summary

This PR introduces a CODEOWNERS-based automatic reviewer/assignee system for the repository. It adds three new components: a CODEOWNERS file that maps directories to responsible maintainers, an auto-assign.yml workflow that self-assigns the PR author as an assignee, and a community-pr-handler.yml workflow that detects community (non-maintainer) PRs and assigns the relevant CODEOWNERS owners as assignees by parsing the CODEOWNERS file and matching changed files.

Key issues found:

  • Both workflows are missing contents: read permission. GitHub Actions sets any undeclared permission to none when a permissions block is present. Both auto-assign.yml and community-pr-handler.yml only declare pull-requests: write, so the actions/checkout@v4 steps will fail at runtime.
  • listFiles is not paginated in community-pr-handler.yml. The call to github.rest.pulls.listFiles fetches only the first page (default 30 files). For large PRs this will silently skip changed files, causing their CODEOWNERS owners to never be assigned.
  • The custom CODEOWNERS parser has limited glob support. It only handles a literal *, directory prefixes (ending with /), and exact file path matches. Any future CODEOWNERS rules using standard glob patterns (e.g., *.md, src/**/*.h) will silently fail to match, falling back to the default * owner instead of the correct one.

Confidence Score: 2/5

  • Not safe to merge as-is — the missing contents: read permission will cause both workflow checkout steps to fail, meaning the automation will not work at all.
  • The missing contents: read permission is a blocking bug that prevents both workflows from running. The pagination gap and limited glob parsing are logic issues that will cause silent failures in common scenarios. These are concrete, reproducible problems rather than theoretical concerns.
  • .github/workflows/community-pr-handler.yml and .github/workflows/auto-assign.yml both need the contents: read permission added.

Important Files Changed

Filename Overview
.github/workflows/community-pr-handler.yml New workflow that detects community PRs and assigns CODEOWNERS reviewers as assignees; has missing contents: read permission (breaks checkout), no pagination on listFiles (silently misses files in large PRs), and a limited CODEOWNERS glob parser that only handles *, directory prefixes, and exact paths.
.github/workflows/auto-assign.yml New workflow to auto-assign PR authors as assignees via kentaro-m/auto-assign-action; missing contents: read permission will cause the sparse checkout step to fail.
.github/CODEOWNERS New CODEOWNERS file mapping repository directories to responsible maintainers; structure is correct, though the /.github/ ownership won't be active until after this PR is merged.
.github/auto-assign-config.yml Configuration for kentaro-m/auto-assign-action; correctly sets addAssignees: author, disables reviewer assignment (handled by CODEOWNERS), and skips draft PRs.

Sequence Diagram

sequenceDiagram
    participant Fork as Fork / Community PR
    participant GH as GitHub Events
    participant AutoAssign as auto-assign.yml
    participant CommunityHandler as community-pr-handler.yml
    participant API as GitHub REST API

    Fork->>GH: PR opened / reopened / ready_for_review

    GH->>AutoAssign: pull_request_target trigger
    AutoAssign->>API: checkout base branch (sparse: auto-assign-config.yml)
    AutoAssign->>API: kentaro-m/auto-assign-action → add PR author as assignee

    GH->>CommunityHandler: pull_request_target trigger
    CommunityHandler->>API: github-script: check author_association
    alt author is OWNER / MEMBER / COLLABORATOR
        CommunityHandler-->>CommunityHandler: skip (not community)
    else community contributor
        CommunityHandler->>API: checkout base branch (sparse: CODEOWNERS)
        CommunityHandler->>API: pulls.listFiles (page 1 only ⚠️)
        CommunityHandler->>CommunityHandler: parse CODEOWNERS (limited globs ⚠️)
        CommunityHandler->>CommunityHandler: match files → owners
        CommunityHandler->>API: issues.addAssignees (CODEOWNERS owners)
    end
Loading

Last reviewed commit: "fix"

Greptile also left 5 inline comments on this PR.

Comment on lines +65 to +70
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);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 listFiles is not paginated — large PRs will silently miss files

github.rest.pulls.listFiles returns at most 30 results by default (max 100 per page, max 3000 total via pagination). For PRs touching many files, only the first page is fetched, so some changed files will be ignored and their CODEOWNERS reviewers will never be assigned.

Use github.paginate to collect all pages:

const files = await github.paginate(github.rest.pulls.listFiles, {
  owner: context.repo.owner,
  repo: context.repo.repo,
  pull_number: prNumber,
  per_page: 100,
});
const changedFiles = files.map(f => f.filename);

Comment on lines +101 to +115
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;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 CODEOWNERS pattern matching logic uses first-match but CODEOWNERS semantics require last-match

The inner loop iterates all rules and continuously overwrites matchedOwner without ever breaking. Because the CODEOWNERS file is ordered * first then specific directories, this accidentally produces correct "last match wins" behaviour today. But it also means the wildcard * sets feihongxu0824 unconditionally, even if a more specific rule is later matched and overrides it — which relies entirely on rule ordering.

More critically, the parser only handles three pattern shapes:

  • Literal *
  • Patterns ending with / (directory prefixes)
  • Exact file paths

Any future CODEOWNERS rules using standard glob patterns (e.g., *.md, docs/**, /src/*.h) will silently fail to match, causing files to fall through to feihongxu0824 (the * default) rather than their correct owner. Consider using the ignore npm package (already available in actions/github-script) or a dedicated CODEOWNERS parser to match rules correctly.

* @feihongxu0824

# .github directory
/.github/ @Cuiyus
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 /.github/ CODEOWNERS entry means this PR itself won't auto-assign @Cuiyus

@Cuiyus is listed as the owner of /.github/, but this PR is being submitted by @feihongxu0824 who is almost certainly a MEMBER/OWNER, so the community-pr-handler.yml would skip it anyway. More importantly, for future community PRs that touch .github/ files, the community-pr-handler.yml will correctly assign @Cuiyus — but native GitHub CODEOWNERS review requests are only triggered automatically if the CODEOWNERS file is in the default branch. Until this PR is merged, any in-flight fork PRs touching .github/ files won't automatically request a review from @Cuiyus via native GitHub CODEOWNERS. This is expected behaviour, just worth being aware of.

feihongxu0824 and others added 2 commits March 21, 2026 01:15
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant