1+ name : Label Merged PRs
2+
3+ on :
4+ pull_request :
5+ types : [closed]
6+
7+ jobs :
8+ label-pr :
9+ if : github.event.pull_request.merged == true
10+ runs-on : ubuntu-latest
11+ permissions :
12+ pull-requests : write
13+
14+ steps :
15+ - name : Checkout repository
16+ uses : actions/checkout@v4
17+
18+ - name : Check if author is in tier list
19+ id : check-author
20+ run : |
21+ USERS=(
22+ "TyDunn"
23+ "jpoly1219"
24+ "bdougie"
25+ "uinstinct"
26+ "sestinj"
27+ "tingwai"
28+ "tomasz-stefaniak"
29+ "RomneyDa"
30+ "Patrick-Erichsen"
31+ )
32+
33+ PR_AUTHOR="${{ github.event.pull_request.user.login }}"
34+ SHOULD_LABEL="false"
35+
36+ for user in "${USERS[@]}"; do
37+ if [[ "$PR_AUTHOR" == "$user" ]]; then
38+ SHOULD_LABEL="true"
39+ break
40+ fi
41+ done
42+
43+ echo "should_label=$SHOULD_LABEL" >> $GITHUB_OUTPUT
44+
45+ - name : Determine and apply tier label
46+ if : steps.check-author.outputs.should_label == 'true'
47+ uses : actions/github-script@v7
48+ with :
49+ script : |
50+ const pr = context.payload.pull_request;
51+
52+ // Check if PR already has a tier label
53+ const existingLabels = pr.labels.map(l => l.name);
54+ if (existingLabels.some(label => label.match(/^tier [1-3]$/))) {
55+ console.log('PR already has a tier label, skipping');
56+ return;
57+ }
58+
59+ // Function to extract conventional commit prefix
60+ function getConventionalPrefix(title) {
61+ const match = title.match(/^([a-z]+)(\s|$|:)/);
62+ return match ? match[1] : '';
63+ }
64+
65+ // Function to determine tier
66+ function determineTier(pr) {
67+ const additions = pr.additions;
68+ const deletions = pr.deletions;
69+ const changedFiles = pr.changed_files;
70+ const title = pr.title;
71+ const body = pr.body || '';
72+
73+ const prefix = getConventionalPrefix(title);
74+
75+ // If no conventional commit prefix, skip
76+ if (!prefix) {
77+ console.log('No conventional commit format, skipping');
78+ return null;
79+ }
80+
81+ // Tier 4 (unlabeled): fix, test, docs, style, refactor, perf, build, ci, revert, improve
82+ const tier4Prefixes = ['fix', 'test', 'docs', 'style', 'refactor', 'perf', 'build', 'ci', 'revert', 'chore', 'improve'];
83+ if (tier4Prefixes.includes(prefix)) {
84+ console.log('Tier 4 prefix detected, will remain unlabeled');
85+ return null;
86+ }
87+
88+ const totalChanges = additions + deletions;
89+
90+ // Tier 1: Major features (1000+ lines changed, or 20+ files, or feat with large scope)
91+ if (totalChanges > 1000 || changedFiles > 20 ||
92+ (prefix === 'feat' && totalChanges > 800) ||
93+ title.match(/(major|milestone|launch)/)) {
94+ return 'tier 1';
95+ }
96+
97+ // Tier 2: Important features (500+ lines changed, or 10+ files, or standard feat)
98+ if (totalChanges > 500 || changedFiles > 10 || prefix === 'feat') {
99+ return 'tier 2';
100+ }
101+
102+ // Tier 3: Smaller improvements
103+ return 'tier 3';
104+ }
105+
106+ const tier = determineTier(pr);
107+
108+ if (tier) {
109+ console.log(`Assigning ${tier} label to PR #${pr.number}`);
110+ console.log(`Title: ${pr.title}`);
111+ console.log(`Changes: +${pr.additions} -${pr.deletions} (${pr.changed_files} files)`);
112+
113+ await github.rest.issues.addLabels({
114+ owner: context.repo.owner,
115+ repo: context.repo.repo,
116+ issue_number: pr.number,
117+ labels: [tier]
118+ });
119+
120+ console.log(`✓ Label '${tier}' added successfully`);
121+ }
0 commit comments