From a994ad364257a1d8c47978d0304ef500cc5d2c88 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Wed, 23 Jul 2025 09:28:15 +1000 Subject: [PATCH 1/6] Workflow - check all comments to find previous bot comment (#9815) --- .github/workflows/external-component-bot.yml | 26 ++++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/.github/workflows/external-component-bot.yml b/.github/workflows/external-component-bot.yml index 5f5bc703ad..29103e8eee 100644 --- a/.github/workflows/external-component-bot.yml +++ b/.github/workflows/external-component-bot.yml @@ -61,7 +61,8 @@ jobs: } async function createComment(octokit, owner, repo, prNumber, esphomeChanges, componentChanges) { - const commentMarker = ""; + const commentMarker = ""; + const legacyCommentMarker = ""; let commentBody; if (esphomeChanges.length === 1) { commentBody = generateExternalComponentInstructions(prNumber, componentChanges, owner, repo); @@ -71,14 +72,23 @@ jobs: 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 comments = await github.paginate( + github.rest.issues.listComments, + { + owner: owner, + repo: repo, + issue_number: prNumber, + per_page: 100, + } + ); - const botComment = comments.data.find(comment => - comment.body.includes(commentMarker) + 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) { From 7bfb08e60233e7ec6271400adcc05e3d4364d464 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 23 Jul 2025 11:46:14 +1200 Subject: [PATCH 2/6] [core] Match LockFreeQueue initialization order (#9813) --- esphome/core/lock_free_queue.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/core/lock_free_queue.h b/esphome/core/lock_free_queue.h index de07b0ebba..68e2825d09 100644 --- a/esphome/core/lock_free_queue.h +++ b/esphome/core/lock_free_queue.h @@ -29,7 +29,7 @@ namespace esphome { // Base lock-free queue without task notification template class LockFreeQueue { public: - LockFreeQueue() : head_(0), tail_(0), dropped_count_(0) {} + LockFreeQueue() : dropped_count_(0), head_(0), tail_(0) {} bool push(T *element) { bool was_empty; From ac7f125eb50ee8ea38b9addb7806273fd8fc0d0a Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 23 Jul 2025 13:22:54 +1200 Subject: [PATCH 3/6] [CI] Paginate codeowner comments to make sure we find it (#9817) --- .github/workflows/codeowner-review-request.yml | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/.github/workflows/codeowner-review-request.yml b/.github/workflows/codeowner-review-request.yml index 121619e049..ab3377365d 100644 --- a/.github/workflows/codeowner-review-request.yml +++ b/.github/workflows/codeowner-review-request.yml @@ -182,11 +182,14 @@ jobs: }); // Check for previous comments from this workflow to avoid duplicate pings - const { data: comments } = await github.rest.issues.listComments({ - owner, - repo, - issue_number: pr_number - }); + const comments = await github.paginate( + github.rest.issues.listComments, + { + owner, + repo, + issue_number: pr_number + } + ); const previouslyPingedUsers = new Set(); const previouslyPingedTeams = new Set(); @@ -194,7 +197,6 @@ jobs: // 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(BOT_COMMENT_MARKER) ); From d7a5db3dda6655d2e2795eca12af80012ffba2fc Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 23 Jul 2025 13:23:06 +1200 Subject: [PATCH 4/6] [CI] Paginate codeowner comments to make sure we find it (#9818) --- .github/workflows/issue-codeowner-notify.yml | 23 ++++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/.github/workflows/issue-codeowner-notify.yml b/.github/workflows/issue-codeowner-notify.yml index 27976a7952..3639d346f5 100644 --- a/.github/workflows/issue-codeowner-notify.yml +++ b/.github/workflows/issue-codeowner-notify.yml @@ -29,6 +29,9 @@ jobs: console.log(`Processing issue #${issue_number} with label: ${labelName}`); + // Hidden marker to identify bot comments from this workflow + const BOT_COMMENT_MARKER = ''; + // Extract component name from label const componentName = labelName.replace('component: ', ''); console.log(`Component: ${componentName}`); @@ -93,11 +96,14 @@ jobs: ); // Check for previous comments from this workflow to avoid duplicate pings - const { data: comments } = await github.rest.issues.listComments({ - owner, - repo, - issue_number: issue_number - }); + const comments = await github.paginate( + github.rest.issues.listComments, + { + owner, + repo, + issue_number: issue_number + } + ); const previouslyPingedUsers = new Set(); const previouslyPingedTeams = new Set(); @@ -105,9 +111,8 @@ jobs: // Look for comments from github-actions bot that contain codeowner pings for this component const workflowComments = comments.filter(comment => comment.user.type === 'Bot' && - comment.user.login === 'github-actions[bot]' && - comment.body.includes(`component: ${componentName}`) && - comment.body.includes("you've been identified as a codeowner") + comment.body.includes(BOT_COMMENT_MARKER) && + comment.body.includes(`component: ${componentName}`) ); // Extract previously mentioned users and teams from workflow comments @@ -140,7 +145,7 @@ jobs: // 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! šŸ™`; + const commentBody = `${BOT_COMMENT_MARKER}\nšŸ‘‹ 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({ From 0b9b33b81b55447ae21bf761c6ee4b8f3e5bb4a9 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 22 Jul 2025 15:42:09 -1000 Subject: [PATCH 5/6] [core] Initialize looping_components_ before setup blocking phase --- esphome/core/application.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/esphome/core/application.cpp b/esphome/core/application.cpp index 873f342277..b48f12a4cb 100644 --- a/esphome/core/application.cpp +++ b/esphome/core/application.cpp @@ -55,6 +55,9 @@ void Application::setup() { return a->get_actual_setup_priority() > b->get_actual_setup_priority(); }); + // Initialize looping_components_ early so enable_pending_loops_() works during setup + this->calculate_looping_components_(); + for (uint32_t i = 0; i < this->components_.size(); i++) { Component *component = this->components_[i]; @@ -97,7 +100,6 @@ void Application::setup() { clear_setup_priority_overrides(); this->schedule_dump_config(); - this->calculate_looping_components_(); } void Application::loop() { uint8_t new_app_state = 0; From e6961f8f243674f20095c745597c82b1da2f608e Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 22 Jul 2025 15:46:49 -1000 Subject: [PATCH 6/6] wip --- esphome/core/application.cpp | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/esphome/core/application.cpp b/esphome/core/application.cpp index b48f12a4cb..4fedd36120 100644 --- a/esphome/core/application.cpp +++ b/esphome/core/application.cpp @@ -271,24 +271,16 @@ void Application::calculate_looping_components_() { // Pre-reserve vector to avoid reallocations this->looping_components_.reserve(total_looping); - // First add all active components + // Add all components with loop override + // When called at start of setup, all components are in CONSTRUCTION state + // so none will be LOOP_DONE yet - they'll all go in the active section for (auto *obj : this->components_) { - if (obj->has_overridden_loop() && - (obj->get_component_state() & COMPONENT_STATE_MASK) != COMPONENT_STATE_LOOP_DONE) { + if (obj->has_overridden_loop()) { this->looping_components_.push_back(obj); } } this->looping_components_active_end_ = this->looping_components_.size(); - - // Then add all inactive (LOOP_DONE) components - // This handles components that called disable_loop() during setup, before this method runs - for (auto *obj : this->components_) { - if (obj->has_overridden_loop() && - (obj->get_component_state() & COMPONENT_STATE_MASK) == COMPONENT_STATE_LOOP_DONE) { - this->looping_components_.push_back(obj); - } - } } void Application::disable_component_loop_(Component *component) {