mirror of
https://github.com/esphome/esphome.git
synced 2025-07-31 07:36:35 +00:00
Merge branch 'integration' into memory_api
This commit is contained in:
commit
9cd1c7a355
177
.github/workflows/auto-label-pr.yml
vendored
177
.github/workflows/auto-label-pr.yml
vendored
@ -11,49 +11,6 @@ permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
TARGET_PLATFORMS: |
|
||||
esp32
|
||||
esp8266
|
||||
rp2040
|
||||
libretiny
|
||||
bk72xx
|
||||
rtl87xx
|
||||
ln882x
|
||||
nrf52
|
||||
host
|
||||
PLATFORM_COMPONENTS: |
|
||||
alarm_control_panel
|
||||
audio_adc
|
||||
audio_dac
|
||||
binary_sensor
|
||||
button
|
||||
canbus
|
||||
climate
|
||||
cover
|
||||
datetime
|
||||
display
|
||||
event
|
||||
fan
|
||||
light
|
||||
lock
|
||||
media_player
|
||||
microphone
|
||||
number
|
||||
one_wire
|
||||
ota
|
||||
output
|
||||
packet_transport
|
||||
select
|
||||
sensor
|
||||
speaker
|
||||
stepper
|
||||
switch
|
||||
text
|
||||
text_sensor
|
||||
time
|
||||
touchscreen
|
||||
update
|
||||
valve
|
||||
SMALL_PR_THRESHOLD: 30
|
||||
MAX_LABELS: 15
|
||||
TOO_BIG_THRESHOLD: 1000
|
||||
@ -101,6 +58,9 @@ jobs:
|
||||
const { owner, repo } = context.repo;
|
||||
const pr_number = context.issue.number;
|
||||
|
||||
// Hidden marker to identify bot comments from this workflow
|
||||
const BOT_COMMENT_MARKER = '<!-- auto-label-pr-bot -->';
|
||||
|
||||
// Get current labels
|
||||
const { data: currentLabelsData } = await github.rest.issues.listLabelsOnIssue({
|
||||
owner,
|
||||
@ -143,9 +103,25 @@ jobs:
|
||||
|
||||
const labels = new Set();
|
||||
|
||||
// Fetch TARGET_PLATFORMS and PLATFORM_COMPONENTS from API
|
||||
let targetPlatforms = [];
|
||||
let platformComponents = [];
|
||||
|
||||
try {
|
||||
const response = await fetch('https://data.esphome.io/components.json');
|
||||
const componentsData = await response.json();
|
||||
|
||||
// Extract target platforms and platform components directly from API
|
||||
targetPlatforms = componentsData.target_platforms || [];
|
||||
platformComponents = componentsData.platform_components || [];
|
||||
|
||||
console.log('Target platforms from API:', targetPlatforms.length, targetPlatforms);
|
||||
console.log('Platform components from API:', platformComponents.length, platformComponents);
|
||||
} catch (error) {
|
||||
console.log('Failed to fetch components data from API:', error.message);
|
||||
}
|
||||
|
||||
// Get environment variables
|
||||
const targetPlatforms = `${{ env.TARGET_PLATFORMS }}`.split('\n').filter(p => p.trim().length > 0).map(p => p.trim());
|
||||
const platformComponents = `${{ env.PLATFORM_COMPONENTS }}`.split('\n').filter(p => p.trim().length > 0).map(p => p.trim());
|
||||
const smallPrThreshold = parseInt('${{ env.SMALL_PR_THRESHOLD }}');
|
||||
const maxLabels = parseInt('${{ env.MAX_LABELS }}');
|
||||
const tooBigThreshold = parseInt('${{ env.TOO_BIG_THRESHOLD }}');
|
||||
@ -225,6 +201,14 @@ jobs:
|
||||
|
||||
const addedFiles = prFiles.filter(file => file.status === 'added').map(file => file.filename);
|
||||
|
||||
// Calculate changes excluding root tests directory for too-big calculation
|
||||
const testChanges = prFiles
|
||||
.filter(file => file.filename.startsWith('tests/'))
|
||||
.reduce((sum, file) => sum + (file.additions || 0) + (file.deletions || 0), 0);
|
||||
|
||||
const nonTestChanges = totalChanges - testChanges;
|
||||
console.log(`Test changes: ${testChanges}, Non-test changes: ${nonTestChanges}`);
|
||||
|
||||
// Strategy: New Component detection
|
||||
for (const file of addedFiles) {
|
||||
// Check for new component files: esphome/components/{component}/__init__.py
|
||||
@ -404,16 +388,30 @@ jobs:
|
||||
|
||||
console.log('Computed labels:', finalLabels.join(', '));
|
||||
|
||||
// Check if PR is allowed to be too big
|
||||
const allowedTooBig = currentLabels.includes('mega-pr');
|
||||
// Check if PR has mega-pr label
|
||||
const isMegaPR = currentLabels.includes('mega-pr');
|
||||
|
||||
// Check if PR is too big (either too many labels or too many line changes)
|
||||
const tooManyLabels = finalLabels.length > maxLabels;
|
||||
const tooManyChanges = totalChanges > tooBigThreshold;
|
||||
const tooManyChanges = nonTestChanges > tooBigThreshold;
|
||||
|
||||
if ((tooManyLabels || tooManyChanges) && !allowedTooBig) {
|
||||
if ((tooManyLabels || tooManyChanges) && !isMegaPR) {
|
||||
const originalLength = finalLabels.length;
|
||||
console.log(`PR is too big - Labels: ${originalLength}, Changes: ${totalChanges}`);
|
||||
console.log(`PR is too big - Labels: ${originalLength}, Changes: ${totalChanges} (non-test: ${nonTestChanges})`);
|
||||
|
||||
// Get all reviews on this PR to check for existing bot reviews
|
||||
const { data: reviews } = await github.rest.pulls.listReviews({
|
||||
owner,
|
||||
repo,
|
||||
pull_number: pr_number
|
||||
});
|
||||
|
||||
// Check if there's already an active bot review requesting changes
|
||||
const existingBotReview = reviews.find(review =>
|
||||
review.user.type === 'Bot' &&
|
||||
review.state === 'CHANGES_REQUESTED' &&
|
||||
review.body && review.body.includes(BOT_COMMENT_MARKER)
|
||||
);
|
||||
|
||||
// If too big due to line changes only, keep original labels and add too-big
|
||||
// If too big due to too many labels, replace with just too-big
|
||||
@ -423,24 +421,69 @@ jobs:
|
||||
finalLabels = ['too-big'];
|
||||
}
|
||||
|
||||
// Create appropriate review message
|
||||
let reviewBody;
|
||||
if (tooManyLabels && tooManyChanges) {
|
||||
reviewBody = `This PR is too large with ${totalChanges} line changes and affects ${originalLength} different components/areas. Please consider breaking it down into smaller, focused PRs to make review easier and reduce the risk of conflicts.`;
|
||||
} else if (tooManyLabels) {
|
||||
reviewBody = `This PR affects ${originalLength} different components/areas. Please consider breaking it down into smaller, focused PRs to make review easier and reduce the risk of conflicts.`;
|
||||
} else {
|
||||
reviewBody = `This PR is too large with ${totalChanges} line changes. Please consider breaking it down into smaller, focused PRs to make review easier and reduce the risk of conflicts.`;
|
||||
}
|
||||
// Only create a new review if there isn't already an active bot review
|
||||
if (!existingBotReview) {
|
||||
// Create appropriate review message
|
||||
let reviewBody;
|
||||
if (tooManyLabels && tooManyChanges) {
|
||||
reviewBody = `${BOT_COMMENT_MARKER}\nThis PR is too large with ${nonTestChanges} line changes (excluding tests) and affects ${originalLength} different components/areas. Please consider breaking it down into smaller, focused PRs to make review easier and reduce the risk of conflicts.\n\nFor guidance on breaking down large PRs, see: https://developers.esphome.io/contributing/submitting-your-work/#but-howwww-looonnnggg`;
|
||||
} else if (tooManyLabels) {
|
||||
reviewBody = `${BOT_COMMENT_MARKER}\nThis PR affects ${originalLength} different components/areas. Please consider breaking it down into smaller, focused PRs to make review easier and reduce the risk of conflicts.\n\nFor guidance on breaking down large PRs, see: https://developers.esphome.io/contributing/submitting-your-work/#but-howwww-looonnnggg`;
|
||||
} else {
|
||||
reviewBody = `${BOT_COMMENT_MARKER}\nThis PR is too large with ${nonTestChanges} line changes (excluding tests). Please consider breaking it down into smaller, focused PRs to make review easier and reduce the risk of conflicts.\n\nFor guidance on breaking down large PRs, see: https://developers.esphome.io/contributing/submitting-your-work/#but-howwww-looonnnggg`;
|
||||
}
|
||||
|
||||
// Request changes on the PR
|
||||
await github.rest.pulls.createReview({
|
||||
owner,
|
||||
repo,
|
||||
pull_number: pr_number,
|
||||
body: reviewBody,
|
||||
event: 'REQUEST_CHANGES'
|
||||
});
|
||||
// Request changes on the PR
|
||||
await github.rest.pulls.createReview({
|
||||
owner,
|
||||
repo,
|
||||
pull_number: pr_number,
|
||||
body: reviewBody,
|
||||
event: 'REQUEST_CHANGES'
|
||||
});
|
||||
console.log('Created new "too big" review requesting changes');
|
||||
} else {
|
||||
console.log('Skipping review creation - existing bot review already requesting changes');
|
||||
}
|
||||
} else {
|
||||
// Check if PR was previously too big but is now acceptable
|
||||
const wasPreviouslyTooBig = currentLabels.includes('too-big');
|
||||
|
||||
if (wasPreviouslyTooBig || isMegaPR) {
|
||||
console.log('PR is no longer too big or has mega-pr label - dismissing bot reviews');
|
||||
|
||||
// Get all reviews on this PR to find reviews to dismiss
|
||||
const { data: reviews } = await github.rest.pulls.listReviews({
|
||||
owner,
|
||||
repo,
|
||||
pull_number: pr_number
|
||||
});
|
||||
|
||||
// Find bot reviews that requested changes
|
||||
const botReviews = reviews.filter(review =>
|
||||
review.user.type === 'Bot' &&
|
||||
review.state === 'CHANGES_REQUESTED' &&
|
||||
review.body && review.body.includes(BOT_COMMENT_MARKER)
|
||||
);
|
||||
|
||||
// Dismiss bot reviews
|
||||
for (const review of botReviews) {
|
||||
try {
|
||||
await github.rest.pulls.dismissReview({
|
||||
owner,
|
||||
repo,
|
||||
pull_number: pr_number,
|
||||
review_id: review.id,
|
||||
message: isMegaPR ?
|
||||
'Review dismissed: mega-pr label was added' :
|
||||
'Review dismissed: PR size is now acceptable'
|
||||
});
|
||||
console.log(`Dismissed review ${review.id}`);
|
||||
} catch (error) {
|
||||
console.log(`Failed to dismiss review ${review.id}:`, error.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add new labels
|
||||
|
11
.github/workflows/codeowner-review-request.yml
vendored
11
.github/workflows/codeowner-review-request.yml
vendored
@ -34,6 +34,9 @@ jobs:
|
||||
|
||||
console.log(`Processing PR #${pr_number} for codeowner review requests`);
|
||||
|
||||
// Hidden marker to identify bot comments from this workflow
|
||||
const BOT_COMMENT_MARKER = '<!-- codeowner-review-request-bot -->';
|
||||
|
||||
try {
|
||||
// Get the list of changed files in this PR
|
||||
const { data: files } = await github.rest.pulls.listFiles({
|
||||
@ -84,9 +87,9 @@ jobs:
|
||||
const allMentions = [...reviewerMentions, ...teamMentions].join(', ');
|
||||
|
||||
if (isSuccessful) {
|
||||
return `👋 Hi there! I've automatically requested reviews from codeowners based on the files changed in this PR.\n\n${allMentions} - You've been requested to review this PR as codeowner(s) of ${matchedFileCount} file(s) that were modified. Thanks for your time! 🙏`;
|
||||
return `${BOT_COMMENT_MARKER}\n👋 Hi there! I've automatically requested reviews from codeowners based on the files changed in this PR.\n\n${allMentions} - You've been requested to review this PR as codeowner(s) of ${matchedFileCount} file(s) that were modified. Thanks for your time! 🙏`;
|
||||
} else {
|
||||
return `👋 Hi there! This PR modifies ${matchedFileCount} file(s) with codeowners.\n\n${allMentions} - As codeowner(s) of the affected files, your review would be appreciated! 🙏\n\n_Note: Automatic review request may have failed, but you're still welcome to review._`;
|
||||
return `${BOT_COMMENT_MARKER}\n👋 Hi there! This PR modifies ${matchedFileCount} file(s) with codeowners.\n\n${allMentions} - As codeowner(s) of the affected files, your review would be appreciated! 🙏\n\n_Note: Automatic review request may have failed, but you're still welcome to review._`;
|
||||
}
|
||||
}
|
||||
|
||||
@ -188,11 +191,11 @@ jobs:
|
||||
const previouslyPingedUsers = new Set();
|
||||
const previouslyPingedTeams = new Set();
|
||||
|
||||
// Look for comments from github-actions bot that contain codeowner pings
|
||||
// Look for comments from github-actions bot that contain our bot marker
|
||||
const workflowComments = comments.filter(comment =>
|
||||
comment.user.type === 'Bot' &&
|
||||
comment.user.login === 'github-actions[bot]' &&
|
||||
comment.body.includes("I've automatically requested reviews from codeowners")
|
||||
comment.body.includes(BOT_COMMENT_MARKER)
|
||||
);
|
||||
|
||||
// Extract previously mentioned users and teams from workflow comments
|
||||
|
@ -203,7 +203,7 @@ message DeviceInfoResponse {
|
||||
option (id) = 10;
|
||||
option (source) = SOURCE_SERVER;
|
||||
|
||||
bool uses_password = 1;
|
||||
bool uses_password = 1 [(field_ifdef) = "USE_API_PASSWORD"];
|
||||
|
||||
// The name of the node, given by "App.set_name()"
|
||||
string name = 2;
|
||||
|
@ -1432,8 +1432,6 @@ DeviceInfoResponse APIConnection::device_info(const DeviceInfoRequest &msg) {
|
||||
DeviceInfoResponse resp{};
|
||||
#ifdef USE_API_PASSWORD
|
||||
resp.uses_password = true;
|
||||
#else
|
||||
resp.uses_password = false;
|
||||
#endif
|
||||
resp.name = App.get_name();
|
||||
resp.friendly_name = App.get_friendly_name();
|
||||
|
@ -80,7 +80,9 @@ void DeviceInfo::calculate_size(uint32_t &total_size) const {
|
||||
}
|
||||
#endif
|
||||
void DeviceInfoResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
#ifdef USE_API_PASSWORD
|
||||
buffer.encode_bool(1, this->uses_password);
|
||||
#endif
|
||||
buffer.encode_string(2, this->name);
|
||||
buffer.encode_string(3, this->mac_address);
|
||||
buffer.encode_string(4, this->esphome_version);
|
||||
@ -130,7 +132,9 @@ void DeviceInfoResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
#endif
|
||||
}
|
||||
void DeviceInfoResponse::calculate_size(uint32_t &total_size) const {
|
||||
#ifdef USE_API_PASSWORD
|
||||
ProtoSize::add_bool_field(total_size, 1, this->uses_password);
|
||||
#endif
|
||||
ProtoSize::add_string_field(total_size, 1, this->name);
|
||||
ProtoSize::add_string_field(total_size, 1, this->mac_address);
|
||||
ProtoSize::add_string_field(total_size, 1, this->esphome_version);
|
||||
|
@ -474,7 +474,9 @@ class DeviceInfoResponse : public ProtoMessage {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *message_name() const override { return "device_info_response"; }
|
||||
#endif
|
||||
#ifdef USE_API_PASSWORD
|
||||
bool uses_password{false};
|
||||
#endif
|
||||
std::string name{};
|
||||
std::string mac_address{};
|
||||
std::string esphome_version{};
|
||||
|
@ -647,10 +647,12 @@ void DeviceInfo::dump_to(std::string &out) const {
|
||||
void DeviceInfoResponse::dump_to(std::string &out) const {
|
||||
__attribute__((unused)) char buffer[64];
|
||||
out.append("DeviceInfoResponse {\n");
|
||||
#ifdef USE_API_PASSWORD
|
||||
out.append(" uses_password: ");
|
||||
out.append(YESNO(this->uses_password));
|
||||
out.append("\n");
|
||||
|
||||
#endif
|
||||
out.append(" name: ");
|
||||
out.append("'").append(this->name).append("'");
|
||||
out.append("\n");
|
||||
|
@ -12,7 +12,7 @@ platformio==6.1.18 # When updating platformio, also update /docker/Dockerfile
|
||||
esptool==4.9.0
|
||||
click==8.1.7
|
||||
esphome-dashboard==20250514.0
|
||||
aioesphomeapi==37.0.2
|
||||
aioesphomeapi==37.0.3
|
||||
zeroconf==0.147.0
|
||||
puremagic==1.30
|
||||
ruamel.yaml==0.18.14 # dashboard_import
|
||||
|
Loading…
x
Reference in New Issue
Block a user