1+ name : Setup Repository Labels
2+
3+ on :
4+ workflow_dispatch : # Manual trigger
5+ push :
6+ branches : [main, master]
7+ paths :
8+ - ' .github/workflows/setup-labels.yml'
9+
10+ permissions :
11+ issues : write
12+
13+ jobs :
14+ create-labels :
15+ runs-on : ubuntu-latest
16+ steps :
17+ - name : Create all required labels
18+ uses : actions/github-script@v7
19+ with :
20+ github-token : ${{ secrets.GITHUB_TOKEN }}
21+ script : |
22+ // Define all labels with colors and descriptions
23+ const requiredLabels = [
24+ // ==================== CONTRIBUTOR LABELS ====================
25+ {
26+ name: 'org-member',
27+ color: '0E8A16',
28+ description: 'Member of the organization with admin/maintain permissions'
29+ },
30+ {
31+ name: 'first-time-contributor',
32+ color: '7057FF',
33+ description: 'First PR of an external contributor'
34+ },
35+ {
36+ name: 'repeat-contributor',
37+ color: '6F42C1',
38+ description: 'PR from an external contributor who already had PRs merged'
39+ },
40+
41+ // ==================== ISSUE TRACKING LABELS ====================
42+ {
43+ name: 'no-issue-linked',
44+ color: 'D73A4A',
45+ description: 'PR is not linked to any issue'
46+ },
47+
48+ // ==================== FILE TYPE LABELS ====================
49+ {
50+ name: 'documentation',
51+ color: '0075CA',
52+ description: 'Changes to documentation files'
53+ },
54+ {
55+ name: 'frontend',
56+ color: 'FEF2C0',
57+ description: 'Changes to frontend code'
58+ },
59+ {
60+ name: 'backend',
61+ color: 'BFD4F2',
62+ description: 'Changes to backend code'
63+ },
64+ {
65+ name: 'javascript',
66+ color: 'F1E05A',
67+ description: 'JavaScript/TypeScript code changes'
68+ },
69+ {
70+ name: 'python',
71+ color: '3572A5',
72+ description: 'Python code changes'
73+ },
74+ {
75+ name: 'configuration',
76+ color: 'EDEDED',
77+ description: 'Configuration file changes'
78+ },
79+ {
80+ name: 'github-actions',
81+ color: '2088FF',
82+ description: 'GitHub Actions workflow changes'
83+ },
84+ {
85+ name: 'dependencies',
86+ color: '0366D6',
87+ description: 'Dependency file changes'
88+ },
89+ {
90+ name: 'tests',
91+ color: 'C5DEF5',
92+ description: 'Test file changes'
93+ },
94+ {
95+ name: 'docker',
96+ color: '0DB7ED',
97+ description: 'Docker-related changes'
98+ },
99+ {
100+ name: 'ci-cd',
101+ color: '6E5494',
102+ description: 'CI/CD pipeline changes'
103+ },
104+
105+ // ==================== SIZE LABELS ====================
106+ {
107+ name: 'size/XS',
108+ color: '00FF00',
109+ description: 'Extra small PR (≤10 lines changed)'
110+ },
111+ {
112+ name: 'size/S',
113+ color: '77FF00',
114+ description: 'Small PR (11-50 lines changed)'
115+ },
116+ {
117+ name: 'size/M',
118+ color: 'FFFF00',
119+ description: 'Medium PR (51-200 lines changed)'
120+ },
121+ {
122+ name: 'size/L',
123+ color: 'FF9900',
124+ description: 'Large PR (201-500 lines changed)'
125+ },
126+ {
127+ name: 'size/XL',
128+ color: 'FF0000',
129+ description: 'Extra large PR (>500 lines changed)'
130+ }
131+ ];
132+
133+ console.log('='.repeat(60));
134+ console.log('🏷️ REPOSITORY LABEL SETUP');
135+ console.log('='.repeat(60));
136+ console.log(`Total labels to create: ${requiredLabels.length}\n`);
137+
138+ // Get existing labels with pagination
139+ const existingLabels = await github.paginate(
140+ github.rest.issues.listLabelsForRepo,
141+ {
142+ owner: context.repo.owner,
143+ repo: context.repo.repo,
144+ per_page: 100
145+ }
146+ );
147+
148+ const existingLabelNames = existingLabels.map(label => label.name);
149+
150+ let created = 0;
151+ let updated = 0;
152+ let skipped = 0;
153+ let failed = 0;
154+
155+ // Process each label
156+ for (const label of requiredLabels) {
157+ try {
158+ if (!existingLabelNames.includes(label.name)) {
159+ // Create new label
160+ await github.rest.issues.createLabel({
161+ owner: context.repo.owner,
162+ repo: context.repo.repo,
163+ name: label.name,
164+ color: label.color,
165+ description: label.description
166+ });
167+ console.log(`✅ Created: ${label.name} (#${label.color})`);
168+ created++;
169+ } else {
170+ // Update existing label (in case color/description changed)
171+ const existingLabel = existingLabels.find(l => l.name === label.name);
172+ if (existingLabel.color !== label.color || existingLabel.description !== label.description) {
173+ await github.rest.issues.updateLabel({
174+ owner: context.repo.owner,
175+ repo: context.repo.repo,
176+ name: label.name,
177+ color: label.color,
178+ description: label.description
179+ });
180+ console.log(`🔄 Updated: ${label.name} (#${label.color})`);
181+ updated++;
182+ } else {
183+ console.log(`⏭️ Skipped: ${label.name} (already exists)`);
184+ skipped++;
185+ }
186+ }
187+ } catch (error) {
188+ console.log(`❌ Failed: ${label.name} - ${error.message}`);
189+ failed++;
190+ }
191+ }
192+
193+ // Summary
194+ console.log('\n' + '='.repeat(60));
195+ console.log('📊 SUMMARY');
196+ console.log('='.repeat(60));
197+ console.log(`✅ Created: ${created}`);
198+ console.log(`🔄 Updated: ${updated}`);
199+ console.log(`⏭️ Skipped: ${skipped}`);
200+ console.log(`❌ Failed: ${failed}`);
201+ console.log('='.repeat(60));
202+
203+ // Fail the step if any labels failed to create/update
204+ if (failed > 0) {
205+ core.setFailed(`Label setup failed! ${failed} label(s) could not be created or updated.`);
206+ } else if (created > 0 || updated > 0) {
207+ console.log('\n🎉 Label setup complete! Your repository is ready.');
208+ } else {
209+ console.log('\n✨ All labels are already up to date.');
210+ }
0 commit comments