Skip to content

Commit cfc9dad

Browse files
committed
ci: add notify access and codeowner
Signed-off-by: bitliu <[email protected]>
1 parent a44932b commit cfc9dad

File tree

2 files changed

+191
-0
lines changed

2 files changed

+191
-0
lines changed
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
name: Owner Notification
2+
3+
on:
4+
pull_request_target:
5+
types: [assigned, opened, reopened, synchronize]
6+
7+
jobs:
8+
notify-owners:
9+
runs-on: ubuntu-latest
10+
permissions:
11+
contents: read
12+
pull-requests: write
13+
issues: write
14+
15+
steps:
16+
- name: Checkout code
17+
uses: actions/checkout@v4
18+
with:
19+
repository: ${{ github.event.pull_request.head.repo.full_name }}
20+
ref: ${{ github.event.pull_request.head.sha }}
21+
fetch-depth: 0
22+
23+
- name: Get changed files
24+
id: changed-files
25+
uses: tj-actions/changed-files@v46
26+
with:
27+
files: |
28+
**/*
29+
base_sha: ${{ github.event.pull_request.base.sha }}
30+
sha: ${{ github.event.pull_request.head.sha }}
31+
32+
- name: Find owners and notify
33+
uses: actions/github-script@v7
34+
with:
35+
script: |
36+
const fs = require('fs');
37+
const path = require('path');
38+
39+
// Get changed files
40+
const changedFiles = `${{ steps.changed-files.outputs.all_changed_files }}`.split(' ');
41+
console.log('Changed files:', changedFiles);
42+
43+
// Function to find OWNER file for a given file path
44+
function findOwnerFile(filePath) {
45+
const parts = filePath.split('/');
46+
47+
// Check first level directory FIRST (prioritize more specific owners)
48+
if (parts.length > 1) {
49+
const firstLevelDir = parts[0];
50+
const ownerPath = path.join(firstLevelDir, 'OWNER');
51+
if (fs.existsSync(ownerPath)) {
52+
const content = fs.readFileSync(ownerPath, 'utf8');
53+
const owners = content.split('\n')
54+
.filter(line => line.trim().startsWith('@'))
55+
.map(line => line.trim());
56+
if (owners.length > 0) {
57+
return { path: firstLevelDir, owners };
58+
}
59+
}
60+
}
61+
62+
// Fall back to root directory
63+
if (fs.existsSync('OWNER')) {
64+
const content = fs.readFileSync('OWNER', 'utf8');
65+
const owners = content.split('\n')
66+
.filter(line => line.trim().startsWith('@'))
67+
.map(line => line.trim());
68+
if (owners.length > 0) {
69+
return { path: '.', owners };
70+
}
71+
}
72+
73+
return null;
74+
}
75+
76+
// Collect all owners for changed files
77+
const ownerMap = new Map();
78+
79+
for (const file of changedFiles) {
80+
if (!file.trim()) continue;
81+
82+
const ownerInfo = findOwnerFile(file);
83+
if (ownerInfo) {
84+
if (!ownerMap.has(ownerInfo.path)) {
85+
ownerMap.set(ownerInfo.path, {
86+
owners: ownerInfo.owners,
87+
files: []
88+
});
89+
}
90+
ownerMap.get(ownerInfo.path).files.push(file);
91+
}
92+
}
93+
94+
if (ownerMap.size === 0) {
95+
console.log('No owners found for changed files');
96+
return;
97+
}
98+
99+
// Collect all unique owners for assignment
100+
const allOwners = new Set();
101+
for (const [, info] of ownerMap) {
102+
for (const owner of info.owners) {
103+
// Remove @ prefix for API call
104+
allOwners.add(owner.replace('@', ''));
105+
}
106+
}
107+
108+
// Assign owners to the PR
109+
if (allOwners.size > 0) {
110+
try {
111+
await github.rest.issues.addAssignees({
112+
owner: context.repo.owner,
113+
repo: context.repo.repo,
114+
issue_number: context.issue.number,
115+
assignees: Array.from(allOwners)
116+
});
117+
console.log(`Assigned PR to: ${Array.from(allOwners).join(', ')}`);
118+
} catch (error) {
119+
console.log(`Failed to assign PR: ${error.message}`);
120+
// Continue with comment creation even if assignment fails
121+
}
122+
}
123+
124+
// Create comment content
125+
let commentBody = '## 👥 vLLM Semantic Team Notification\n\n';
126+
commentBody += 'The following members have been identified for the changed files in this PR and have been automatically assigned:\n\n';
127+
128+
for (const [dirPath, info] of ownerMap) {
129+
commentBody += `### 📁 \`${dirPath === '.' ? 'Root Directory' : dirPath}\`\n`;
130+
commentBody += `**Owners:** ${info.owners.join(', ')}\n`;
131+
commentBody += `**Files changed:**\n`;
132+
for (const file of info.files) {
133+
commentBody += `- \`${file}\`\n`;
134+
}
135+
commentBody += '\n';
136+
}
137+
138+
commentBody += '---\n';
139+
commentBody += '*This comment was automatically generated based on the OWNER files in the repository.*';
140+
141+
// Get existing comments
142+
const { data: comments } = await github.rest.issues.listComments({
143+
owner: context.repo.owner,
144+
repo: context.repo.repo,
145+
issue_number: context.issue.number,
146+
});
147+
148+
// Check if we already have an owner notification comment
149+
const existingComment = comments.find(comment =>
150+
comment.user.login === 'github-actions[bot]' &&
151+
comment.body.includes('## 👥 vLLM Semantic Team Notification')
152+
);
153+
154+
if (existingComment) {
155+
// Update existing comment
156+
await github.rest.issues.updateComment({
157+
owner: context.repo.owner,
158+
repo: context.repo.repo,
159+
comment_id: existingComment.id,
160+
body: commentBody
161+
});
162+
console.log('Updated existing owner notification comment');
163+
} else {
164+
// Create new comment
165+
await github.rest.issues.createComment({
166+
owner: context.repo.owner,
167+
repo: context.repo.repo,
168+
issue_number: context.issue.number,
169+
body: commentBody
170+
});
171+
console.log('Created new owner notification comment');
172+
}

CODEOWNERS

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Root directory Owners
2+
3+
* @rootfs @Xunzhuo
4+
5+
## Subdirectory Owners
6+
7+
/src/ @rootfs @Xunzhuo @wangchen615
8+
9+
/e2e-tests/ @rootfs
10+
11+
/bench/ @yuezhu1 @Xunzhuo
12+
13+
/website/ @Xunzhuo
14+
15+
/deploy/ @rootfs @Xunzhuo
16+
17+
/config/ @rootfs @Xunzhuo
18+
19+
/candle-binding/ @rootfs @Xunzhuo

0 commit comments

Comments
 (0)