mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 07:03:55 +00:00 
			
		
		
		
	[CI] New workflow to mention codeowners on issues (#9658)
This commit is contained in:
		
							
								
								
									
										119
									
								
								.github/workflows/issue-codeowner-notify.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										119
									
								
								.github/workflows/issue-codeowner-notify.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,119 @@ | ||||
| # This workflow automatically notifies codeowners when an issue is labeled with component labels. | ||||
| # It reads the CODEOWNERS file to find the maintainers for the labeled components | ||||
| # and posts a comment mentioning them to ensure they're aware of the issue. | ||||
|  | ||||
| name: Notify Issue Codeowners | ||||
|  | ||||
| on: | ||||
|   issues: | ||||
|     types: [labeled] | ||||
|  | ||||
| permissions: | ||||
|   issues: write | ||||
|   contents: read | ||||
|  | ||||
| jobs: | ||||
|   notify-codeowners: | ||||
|     name: Run | ||||
|     if: ${{ startsWith(github.event.label.name, format('component{0} ', ':')) }} | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - name: Notify codeowners for component issues | ||||
|         uses: actions/github-script@v7.0.1 | ||||
|         with: | ||||
|           script: | | ||||
|             const owner = context.repo.owner; | ||||
|             const repo = context.repo.repo; | ||||
|             const issue_number = context.payload.issue.number; | ||||
|             const labelName = context.payload.label.name; | ||||
|  | ||||
|             console.log(`Processing issue #${issue_number} with label: ${labelName}`); | ||||
|  | ||||
|             // Extract component name from label | ||||
|             const componentName = labelName.replace('component: ', ''); | ||||
|             console.log(`Component: ${componentName}`); | ||||
|  | ||||
|             try { | ||||
|               // Fetch CODEOWNERS file from root | ||||
|               const { data: codeownersFile } = await github.rest.repos.getContent({ | ||||
|                 owner, | ||||
|                 repo, | ||||
|                 path: 'CODEOWNERS' | ||||
|               }); | ||||
|               const codeownersContent = Buffer.from(codeownersFile.content, 'base64').toString('utf8'); | ||||
|  | ||||
|               // Parse CODEOWNERS file to extract component mappings | ||||
|               const codeownersLines = codeownersContent.split('\n') | ||||
|                 .map(line => line.trim()) | ||||
|                 .filter(line => line && !line.startsWith('#')); | ||||
|  | ||||
|               let componentOwners = null; | ||||
|  | ||||
|               for (const line of codeownersLines) { | ||||
|                 const parts = line.split(/\s+/); | ||||
|                 if (parts.length < 2) continue; | ||||
|  | ||||
|                 const pattern = parts[0]; | ||||
|                 const owners = parts.slice(1); | ||||
|  | ||||
|                 // Look for component patterns: esphome/components/{component}/* | ||||
|                 const componentMatch = pattern.match(/^esphome\/components\/([^\/]+)\/\*$/); | ||||
|                 if (componentMatch && componentMatch[1] === componentName) { | ||||
|                   componentOwners = owners; | ||||
|                   break; | ||||
|                 } | ||||
|               } | ||||
|  | ||||
|               if (!componentOwners) { | ||||
|                 console.log(`No codeowners found for component: ${componentName}`); | ||||
|                 return; | ||||
|               } | ||||
|  | ||||
|               console.log(`Found codeowners for '${componentName}': ${componentOwners.join(', ')}`); | ||||
|  | ||||
|               // Separate users and teams | ||||
|               const userOwners = []; | ||||
|               const teamOwners = []; | ||||
|  | ||||
|               for (const owner of componentOwners) { | ||||
|                 const cleanOwner = owner.startsWith('@') ? owner.slice(1) : owner; | ||||
|                 if (cleanOwner.includes('/')) { | ||||
|                   // Team mention (org/team-name) | ||||
|                   teamOwners.push(`@${cleanOwner}`); | ||||
|                 } else { | ||||
|                   // Individual user | ||||
|                   userOwners.push(`@${cleanOwner}`); | ||||
|                 } | ||||
|               } | ||||
|  | ||||
|               // Remove issue author from mentions to avoid self-notification | ||||
|               const issueAuthor = context.payload.issue.user.login; | ||||
|               const filteredUserOwners = userOwners.filter(mention => | ||||
|                 mention !== `@${issueAuthor}` | ||||
|               ); | ||||
|  | ||||
|               const allMentions = [...filteredUserOwners, ...teamOwners]; | ||||
|  | ||||
|               if (allMentions.length === 0) { | ||||
|                 console.log('No codeowners to notify (issue author is the only codeowner)'); | ||||
|                 return; | ||||
|               } | ||||
|  | ||||
|               // Create comment body | ||||
|               const mentionString = allMentions.join(', '); | ||||
|               const commentBody = `👋 Hey ${mentionString}!\n\nThis issue has been labeled with \`component: ${componentName}\` and you've been identified as a codeowner of this component. Please take a look when you have a chance!\n\nThanks for maintaining this component! 🙏`; | ||||
|  | ||||
|               // Post comment | ||||
|               await github.rest.issues.createComment({ | ||||
|                 owner, | ||||
|                 repo, | ||||
|                 issue_number: issue_number, | ||||
|                 body: commentBody | ||||
|               }); | ||||
|  | ||||
|               console.log(`Successfully notified codeowners: ${mentionString}`); | ||||
|  | ||||
|             } catch (error) { | ||||
|               console.log('Failed to process codeowner notifications:', error.message); | ||||
|               console.error(error); | ||||
|             } | ||||
		Reference in New Issue
	
	Block a user