Skip to content

[wifi] Add clarification to note regarding disable action #2467

[wifi] Add clarification to note regarding disable action

[wifi] Add clarification to note regarding disable action #2467

Workflow file for this run

name: Auto Label PR
on:
# Runs only on pull_request_target due to having access to a App token.
# This means PRs from forks will not be able to alter this workflow to get the tokens
pull_request_target:
types: [opened, reopened, synchronize, edited]
# Allow manual triggering for recheck functionality
workflow_dispatch:
inputs:
pr_number:
description: 'Pull Request number to process'
required: true
type: number
permissions:
pull-requests: write
contents: read
env:
BOT_NAME: "esphome[bot]"
jobs:
label:
runs-on: ubuntu-latest
if: github.event.action != 'labeled' || github.event.sender.type != 'Bot'
steps:
- name: Checkout
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
- name: Generate a token
id: generate-token
uses: actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 # v2.1.4
with:
app-id: ${{ secrets.ESPHOME_GITHUB_APP_ID }}
private-key: ${{ secrets.ESPHOME_GITHUB_APP_PRIVATE_KEY }}
- name: Auto Label PR
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
env:
PR_NUMBER: ${{ github.event.inputs.pr_number || github.event.number }}
with:
github-token: ${{ steps.generate-token.outputs.token }}
script: |
const { owner, repo } = context.repo;
const pr_number = parseInt(process.env.PR_NUMBER);
console.log('Processing PR #' + pr_number);
// Get current labels
const { data: currentLabelsData } = await github.rest.issues.listLabelsOnIssue({
owner,
repo,
issue_number: pr_number
});
const currentLabels = currentLabelsData.map(label => label.name);
// Define managed labels that this workflow controls
const managedLabels = currentLabels.filter(label =>
[
'current',
'next',
'has-parent',
'wrong-base-branch',
'labeller-recheck'
].includes(label) ||
label.startsWith('component: ')
);
console.log('Current labels:', currentLabels.join(', '));
console.log('Managed labels:', managedLabels.join(', '));
const labels = new Set();
// Strategy: Branch-based labeling
let baseRef, prBody;
if (context.eventName === 'workflow_dispatch') {
// Get PR details when called via workflow_dispatch
const { data: pr } = await github.rest.pulls.get({
owner,
repo,
pull_number: pr_number
});
baseRef = pr.base.ref;
prBody = pr.body || '';
} else {
// Use context when called via pull_request_target
baseRef = context.payload.pull_request.base.ref;
prBody = context.payload.pull_request.body || '';
}
console.log('Target branch:', baseRef);
if (baseRef === 'current') {
labels.add('current');
} else if (baseRef === 'next') {
labels.add('next');
}
// Strategy: Parent PR detection
// prBody is already set above based on event type
// Look for ESPHome PR links in PR body
// Patterns to match:
// - https://github.com/esphome/esphome/pull/1234
// - esphome/esphome#1234
const esphomePrPatterns = [
/https:\/\/github\.com\/esphome\/esphome\/pull\/(\d+)/,
/esphome\/esphome#(\d+)/
];
const hasEsphomeLink = esphomePrPatterns.some(pattern =>
pattern.test(prBody)
);
console.log('PR Body contains ESPHome link:', hasEsphomeLink);
if (hasEsphomeLink) {
labels.add('has-parent');
// Extract parent PR number and fetch its labels
for (const pattern of esphomePrPatterns) {
const match = prBody.match(pattern);
if (!match) continue; // Skip to next pattern if no match
try {
// Extract PR number from the capture group
const parentPrNumber = match[1];
console.log('Found parent PR number:', parentPrNumber);
// Fetch labels from the parent PR in esphome/esphome repository
const { data: parentLabels } = await github.rest.issues.listLabelsOnIssue({
owner: 'esphome',
repo: 'esphome',
issue_number: parseInt(parentPrNumber)
});
console.log('Parent PR labels:', parentLabels.map(l => l.name).join(', '));
// Add all "component: " labels from parent PR
parentLabels.forEach(label => {
if (label.name.startsWith('component: ')) {
labels.add(label.name);
console.log('Added component label from parent:', label.name);
}
});
break; // Only process the first match
} catch (error) {
console.log('Failed to fetch parent PR labels:', error.message);
}
}
}
// Convert Set to Array
const finalLabels = Array.from(labels);
// Strategy: Wrong base branch detection
const hasParent = finalLabels.includes('has-parent');
const isCurrent = finalLabels.includes('current');
if (hasParent && isCurrent) {
finalLabels.push('wrong-base-branch');
console.log('Added wrong-base-branch label: has-parent targeting current branch');
}
console.log('Computed labels:', finalLabels.join(', '));
// Add new labels
if (finalLabels.length > 0) {
console.log(`Adding labels: ${finalLabels.join(', ')}`);
await github.rest.issues.addLabels({
owner,
repo,
issue_number: pr_number,
labels: finalLabels
});
}
// Request changes if wrong base branch is detected
if (hasParent && isCurrent) {
console.log('Requesting changes due to wrong base branch');
await github.rest.pulls.createReview({
owner,
repo,
pull_number: pr_number,
event: 'REQUEST_CHANGES',
body: 'As this is a feature matched with a PR in https://github.com/esphome/esphome, please target your PR to the `next` branch and rebase.'
});
} else {
// Check if we should dismiss any existing review from this bot
const { data: reviews } = await github.rest.pulls.listReviews({
owner,
repo,
pull_number: pr_number
});
// Find the most recent review from this bot that requested changes for wrong base branch
const botReview = reviews
.filter(review =>
review.user.login === process.env.BOT_NAME &&
review.state === 'CHANGES_REQUESTED' &&
review.body && review.body.includes('target your PR to the `next` branch')
)
.sort((a, b) => new Date(b.submitted_at) - new Date(a.submitted_at))[0];
if (botReview) {
console.log('Dismissing previous bot review as labels are now correct');
await github.rest.pulls.dismissReview({
owner,
repo,
pull_number: pr_number,
review_id: botReview.id,
message: 'Base branch has been corrected - dismissing previous review.'
});
}
}
// Remove old managed labels that are no longer needed
const labelsToRemove = managedLabels.filter(label =>
!finalLabels.includes(label)
);
for (const label of labelsToRemove) {
console.log(`Removing label: ${label}`);
try {
await github.rest.issues.removeLabel({
owner,
repo,
issue_number: pr_number,
name: label
});
} catch (error) {
console.log(`Failed to remove label ${label}:`, error.message);
}
}