mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-30 22:53:59 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			158 lines
		
	
	
		
			6.3 KiB
		
	
	
	
		
			YAML
		
	
	
	
	
	
			
		
		
	
	
			158 lines
		
	
	
		
			6.3 KiB
		
	
	
	
		
			YAML
		
	
	
	
	
	
| name: Add External Component Comment
 | |
| 
 | |
| on:
 | |
|   pull_request_target:
 | |
|     types: [opened, synchronize]
 | |
| 
 | |
| permissions:
 | |
|   contents: read       #  Needed to fetch PR details
 | |
|   issues: write        #  Needed to create and update comments (PR comments are managed via the issues REST API)
 | |
|   pull-requests: write  # also needed?
 | |
| 
 | |
| jobs:
 | |
|   external-comment:
 | |
|     name: External component comment
 | |
|     runs-on: ubuntu-latest
 | |
|     steps:
 | |
|       - name: Add external component comment
 | |
|         uses: actions/github-script@v8.0.0
 | |
|         with:
 | |
|           github-token: ${{ secrets.GITHUB_TOKEN }}
 | |
|           script: |
 | |
|             // Generate external component usage instructions
 | |
|             function generateExternalComponentInstructions(prNumber, componentNames, owner, repo) {
 | |
|                 let source;
 | |
|                 if (owner === 'esphome' && repo === 'esphome')
 | |
|                     source = `github://pr#${prNumber}`;
 | |
|                 else
 | |
|                     source = `github://${owner}/${repo}@pull/${prNumber}/head`;
 | |
|                 return `To use the changes from this PR as an external component, add the following to your ESPHome configuration YAML file:
 | |
| 
 | |
|             \`\`\`yaml
 | |
|             external_components:
 | |
|               - source: ${source}
 | |
|                 components: [${componentNames.join(', ')}]
 | |
|                 refresh: 1h
 | |
|             \`\`\``;
 | |
|             }
 | |
| 
 | |
|             // Generate repo clone instructions
 | |
|             function generateRepoInstructions(prNumber, owner, repo, branch) {
 | |
|                 return `To use the changes in this PR:
 | |
| 
 | |
|             \`\`\`bash
 | |
|             # Clone the repository:
 | |
|             git clone https://github.com/${owner}/${repo}
 | |
|             cd ${repo}
 | |
| 
 | |
|             # Checkout the PR branch:
 | |
|             git fetch origin pull/${prNumber}/head:${branch}
 | |
|             git checkout ${branch}
 | |
| 
 | |
|             # Install the development version:
 | |
|             script/setup
 | |
| 
 | |
|             # Activate the development version:
 | |
|             source venv/bin/activate
 | |
|             \`\`\`
 | |
| 
 | |
|             Now you can run \`esphome\` as usual to test the changes in this PR.
 | |
|             `;
 | |
|             }
 | |
| 
 | |
|             async function createComment(octokit, owner, repo, prNumber, esphomeChanges, componentChanges) {
 | |
|                 const commentMarker = "<!-- This comment was generated automatically by the external-component-bot workflow. -->";
 | |
|                 const legacyCommentMarker = "<!-- This comment was generated automatically by a GitHub workflow. -->";
 | |
|                 let commentBody;
 | |
|                 if (esphomeChanges.length === 1) {
 | |
|                     commentBody = generateExternalComponentInstructions(prNumber, componentChanges, owner, repo);
 | |
|                 } else {
 | |
|                     commentBody = generateRepoInstructions(prNumber, owner, repo, context.payload.pull_request.head.ref);
 | |
|                 }
 | |
|                 commentBody += `\n\n---\n(Added by the PR bot)\n\n${commentMarker}`;
 | |
| 
 | |
|                 // Check for existing bot comment
 | |
|                 const comments = await github.paginate(
 | |
|                     github.rest.issues.listComments,
 | |
|                     {
 | |
|                         owner: owner,
 | |
|                         repo: repo,
 | |
|                         issue_number: prNumber,
 | |
|                         per_page: 100,
 | |
|                     }
 | |
|                 );
 | |
| 
 | |
|                 const sorted = comments.sort((a, b) => new Date(b.updated_at) - new Date(a.updated_at));
 | |
| 
 | |
|                 const botComment = sorted.find(comment =>
 | |
|                     (
 | |
|                       comment.body.includes(commentMarker) ||
 | |
|                       comment.body.includes(legacyCommentMarker)
 | |
|                     ) && comment.user.type === "Bot"
 | |
|                 );
 | |
| 
 | |
|                 if (botComment && botComment.body === commentBody) {
 | |
|                     // No changes in the comment, do nothing
 | |
|                     return;
 | |
|                 }
 | |
| 
 | |
|                 if (botComment) {
 | |
|                     // Update existing comment
 | |
|                     await github.rest.issues.updateComment({
 | |
|                         owner: owner,
 | |
|                         repo: repo,
 | |
|                         comment_id: botComment.id,
 | |
|                         body: commentBody,
 | |
|                     });
 | |
|                 } else {
 | |
|                     // Create new comment
 | |
|                     await github.rest.issues.createComment({
 | |
|                         owner: owner,
 | |
|                         repo: repo,
 | |
|                         issue_number: prNumber,
 | |
|                         body: commentBody,
 | |
|                     });
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             async function getEsphomeAndComponentChanges(github, owner, repo, prNumber) {
 | |
|                 const changedFiles = await github.rest.pulls.listFiles({
 | |
|                     owner: owner,
 | |
|                     repo: repo,
 | |
|                     pull_number: prNumber,
 | |
|                 });
 | |
| 
 | |
|                 const esphomeChanges = changedFiles.data
 | |
|                     .filter(file => file.filename !== "esphome/core/defines.h" && file.filename.startsWith('esphome/'))
 | |
|                     .map(file => {
 | |
|                         const match = file.filename.match(/esphome\/([^/]+)/);
 | |
|                         return match ? match[1] : null;
 | |
|                     })
 | |
|                     .filter(it => it !== null);
 | |
| 
 | |
|                 if (esphomeChanges.length === 0) {
 | |
|                     return {esphomeChanges: [], componentChanges: []};
 | |
|                 }
 | |
| 
 | |
|                 const uniqueEsphomeChanges = [...new Set(esphomeChanges)];
 | |
|                 const componentChanges = changedFiles.data
 | |
|                     .filter(file => file.filename.startsWith('esphome/components/'))
 | |
|                     .map(file => {
 | |
|                         const match = file.filename.match(/esphome\/components\/([^/]+)\//);
 | |
|                         return match ? match[1] : null;
 | |
|                     })
 | |
|                     .filter(it => it !== null);
 | |
| 
 | |
|                 return {esphomeChanges: uniqueEsphomeChanges, componentChanges: [...new Set(componentChanges)]};
 | |
|             }
 | |
| 
 | |
|             // Start of main code.
 | |
| 
 | |
|             const prNumber = context.payload.pull_request.number;
 | |
|             const {owner, repo} = context.repo;
 | |
| 
 | |
|             const {esphomeChanges, componentChanges} = await getEsphomeAndComponentChanges(github, owner, repo, prNumber);
 | |
|             if (componentChanges.length !== 0) {
 | |
|                 await createComment(github, owner, repo, prNumber, esphomeChanges, componentChanges);
 | |
|             }
 |