-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Expand file tree
/
Copy pathauto-label-prs-for-source-and-content.yml
More file actions
196 lines (164 loc) · 7.55 KB
/
auto-label-prs-for-source-and-content.yml
File metadata and controls
196 lines (164 loc) · 7.55 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
name: Auto-label PRs by source and type
on:
pull_request:
types: [opened]
branches: [main]
workflow_dispatch:
inputs:
pr_number:
description: 'PR number to label'
required: true
type: string
jobs:
auto-label-source-type:
runs-on: ubuntu-latest
permissions:
pull-requests: write
contents: read
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Analyze PR and add labels
uses: actions/github-script@v7
with:
script: |
// -------------------------------------------------------
// 1. Resolve PR to process
// -------------------------------------------------------
let prToProcess;
if (context.eventName === 'pull_request') {
prToProcess = context.payload.pull_request;
} else if (context.eventName === 'workflow_dispatch') {
const prNumber = '${{ inputs.pr_number }}';
console.log(`Manual trigger for PR #${prNumber}`);
const { data: pr } = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: parseInt(prNumber)
});
prToProcess = pr;
}
const prNumber = prToProcess.number;
console.log(`Processing PR #${prNumber}`);
// -------------------------------------------------------
// 2. Fetch changed files
// -------------------------------------------------------
const { data: files } = await github.rest.pulls.listFiles({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: prNumber
});
console.log(`Found ${files.length} files changed`);
// -------------------------------------------------------
// 3. Classify each file into a documentation area
//
// Repo-relative paths look like:
// docusaurus/docs/cms/features/content-manager.md
// docusaurus/docs/cloud/getting-started/deployment.md
// docusaurus/static/img/assets/cloud/settings.png
//
// Rules (aligned with AGENTS.md branch naming):
// - Path contains /docs/cms/ → CMS
// - Path contains /docs/cloud/ → Cloud
// - Path is under static/ → neutral (shared assets)
// - Anything else → Other
// -------------------------------------------------------
let touchesCMS = false;
let touchesCloud = false;
let touchesOther = false;
let hasNewFile = false;
let totalChanges = 0;
/**
* Determine the documentation area for a file path.
* Returns 'cms', 'cloud', 'static' (neutral), or 'other'.
*/
function classifyFile(filepath) {
// Normalize: strip leading slash if any
const f = filepath.replace(/^\//, '');
// Documentation content
if (f.includes('/docs/cms/') || f.startsWith('docs/cms/')) return 'cms';
if (f.includes('/docs/cloud/') || f.startsWith('docs/cloud/')) return 'cloud';
// Shared static assets — neutral, should not influence source label
if (f.includes('/static/') || f.startsWith('static/')) return 'static';
// Snippets live under docs/ and are shared — treat as CMS by convention
if (f.includes('/docs/snippets/') || f.startsWith('docs/snippets/')) return 'cms';
// Everything else (src/, scripts, config, .github/, agents/, etc.)
return 'other';
}
for (const file of files) {
console.log(`File: ${file.filename}, status: ${file.status}, +${file.additions} -${file.deletions}`);
const area = classifyFile(file.filename);
console.log(` → area: ${area}`);
switch (area) {
case 'cms': touchesCMS = true; break;
case 'cloud': touchesCloud = true; break;
case 'static': /* neutral — does not influence source */ break;
default: touchesOther = true; break;
}
if (file.status === 'added') {
hasNewFile = true;
}
totalChanges += file.additions + file.deletions;
}
console.log(`Summary: CMS=${touchesCMS}, Cloud=${touchesCloud}, Other=${touchesOther}`);
console.log(`Has new file: ${hasNewFile}, Total changes: ${totalChanges}`);
// -------------------------------------------------------
// 4. Determine source label
//
// /cms prefix → CMS only (+ optional static)
// /cloud prefix → Cloud only (+ optional static)
// /repo prefix → anything else or mixed
// -------------------------------------------------------
let sourceLabel;
if (touchesCMS && !touchesCloud && !touchesOther) {
sourceLabel = 'source: CMS';
} else if (touchesCloud && !touchesCMS && !touchesOther) {
sourceLabel = 'source: Strapi Cloud';
} else {
sourceLabel = 'source: repo';
}
// -------------------------------------------------------
// 5. Determine PR type label
// -------------------------------------------------------
let prTypeLabel;
if (hasNewFile) {
prTypeLabel = 'pr: new content';
} else if (totalChanges > 10) {
prTypeLabel = 'pr: updated content';
} else {
prTypeLabel = 'pr: chore';
}
console.log(`Determined labels: ${sourceLabel}, ${prTypeLabel}`);
// -------------------------------------------------------
// 6. Apply labels (skip categories that already have one)
// -------------------------------------------------------
const existingLabels = prToProcess.labels.map(label => label.name);
const hasSourceLabel = existingLabels.some(label => label.startsWith('source:'));
const hasPrTypeLabel = existingLabels.some(label => label.startsWith('pr:'));
const labelsToAdd = [];
if (!hasSourceLabel) {
labelsToAdd.push(sourceLabel);
} else {
console.log(`PR #${prNumber} already has a source label, skipping`);
}
if (!hasPrTypeLabel) {
labelsToAdd.push(prTypeLabel);
} else {
console.log(`PR #${prNumber} already has a pr type label, skipping`);
}
if (labelsToAdd.length === 0) {
console.log(`No labels to add to PR #${prNumber}`);
return;
}
try {
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: prNumber,
labels: labelsToAdd
});
console.log(`Successfully added labels to PR #${prNumber}: ${labelsToAdd.join(', ')}`);
} catch (error) {
console.error(`Error adding labels to PR #${prNumber}:`, error);
core.setFailed(`Failed to add labels to PR #${prNumber}: ${error.message}`);
}