mirror of
https://github.com/esphome/esphome.git
synced 2025-07-28 14:16:40 +00:00
[ci] Implement external component PR workflow (#9595)
Co-authored-by: clydeps <U5yx99dok9>
This commit is contained in:
parent
f7acad747f
commit
513908d8a0
146
.github/workflows/external-component-bot.yml
vendored
Normal file
146
.github/workflows/external-component-bot.yml
vendored
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
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)
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
external-comment:
|
||||||
|
name: External component comment
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Add external component comment
|
||||||
|
uses: actions/github-script@v7.0.1
|
||||||
|
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 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.rest.issues.listComments({
|
||||||
|
owner: owner,
|
||||||
|
repo: repo,
|
||||||
|
issue_number: prNumber,
|
||||||
|
});
|
||||||
|
|
||||||
|
const botComment = comments.data.find(comment =>
|
||||||
|
comment.body.includes(commentMarker)
|
||||||
|
);
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user