Skip to content

Commit 9e6e7e2

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

File tree

2 files changed

+187
-0
lines changed

2 files changed

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

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/ @yossiovadia
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)