@@ -2,7 +2,12 @@ name: PR Automation
22
33on :
44 pull_request :
5- types : [opened, synchronize]
5+ types : [opened, reopened, ready_for_review, synchronize]
6+
7+ permissions :
8+ pull-requests : write
9+ issues : write
10+ contents : read
611
712jobs :
813 auto-label-and-assign :
6671 }).catch(err => {
6772 console.log('Could not assign author: ' + err.message);
6873 })
74+
75+ - name : Auto-request reviewers from JSON config
76+ uses : actions/github-script@v7
77+ with :
78+ script : |
79+ const fs = require('fs');
80+
81+ function toRegex(glob) {
82+ const escaped = glob
83+ .replace(/[.+^${}()|[\]\\]/g, '\\$&')
84+ .replace(/\*\*/g, '::DOUBLE_STAR::')
85+ .replace(/\*/g, '[^/]*')
86+ .replace(/::DOUBLE_STAR::/g, '.*');
87+ return new RegExp(`^${escaped}$`);
88+ }
89+
90+ const configPath = '.github/reviewers.json';
91+ if (!fs.existsSync(configPath)) {
92+ core.info('No .github/reviewers.json found. Skipping reviewer requests.');
93+ return;
94+ }
95+
96+ let config;
97+ try {
98+ config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
99+ } catch (e) {
100+ core.setFailed(`Invalid JSON in ${configPath}: ${e.message}`);
101+ return;
102+ }
103+
104+ const defaultReviewers = Array.isArray(config.default_reviewers) ? config.default_reviewers : [];
105+ const pathRules = Array.isArray(config.path_reviewers) ? config.path_reviewers : [];
106+
107+ const files = await github.paginate(github.rest.pulls.listFiles, {
108+ owner: context.repo.owner,
109+ repo: context.repo.repo,
110+ pull_number: context.issue.number,
111+ per_page: 100
112+ });
113+ const changed = files.map(f => f.filename);
114+
115+ const collected = new Set(defaultReviewers.filter(Boolean));
116+ for (const rule of pathRules) {
117+ if (!rule || !rule.glob || !Array.isArray(rule.reviewers)) continue;
118+ const re = toRegex(String(rule.glob));
119+ if (changed.some(f => re.test(f))) {
120+ for (const reviewer of rule.reviewers) {
121+ if (reviewer) collected.add(reviewer);
122+ }
123+ }
124+ }
125+
126+ const pr = await github.rest.pulls.get({
127+ owner: context.repo.owner,
128+ repo: context.repo.repo,
129+ pull_number: context.issue.number
130+ });
131+
132+ const author = pr.data.user?.login;
133+ const alreadyRequested = new Set((pr.data.requested_reviewers || []).map(r => r.login));
134+ const reviewers = Array.from(collected)
135+ .map(r => String(r).trim())
136+ .filter(Boolean)
137+ .filter(r => r !== author)
138+ .filter(r => !alreadyRequested.has(r))
139+ .slice(0, 15);
140+
141+ if (reviewers.length === 0) {
142+ core.info('No new reviewers to request.');
143+ return;
144+ }
145+
146+ try {
147+ await github.rest.pulls.requestReviewers({
148+ owner: context.repo.owner,
149+ repo: context.repo.repo,
150+ pull_number: context.issue.number,
151+ reviewers
152+ });
153+ core.info(`Requested reviewers: ${reviewers.join(', ')}`);
154+ } catch (e) {
155+ core.warning(`Could not request reviewers: ${e.message}`);
156+ }
0 commit comments