Compare commits

...

7 Commits

Author SHA1 Message Date
copilot-swe-agent[bot]
90407aa9f2 Fix: Fetch file content from PR head using GitHub API
The workflow was checking out the base branch but trying to read files
from the filesystem, which meant it couldn't see changes in the PR.
Now using github.rest.repos.getContent with the PR head SHA to fetch
the actual file content from the PR branch.

Co-authored-by: clydebarrow <2366188+clydebarrow@users.noreply.github.com>
2026-01-26 02:35:36 +00:00
copilot-swe-agent[bot]
a167ea8269 Fix triple-quote regex pattern for multiline strings
Co-authored-by: clydebarrow <2366188+clydebarrow@users.noreply.github.com>
2026-01-26 01:03:21 +00:00
copilot-swe-agent[bot]
354f60a9cf Address code review: add triple-quote support and improve error handling
Co-authored-by: clydebarrow <2366188+clydebarrow@users.noreply.github.com>
2026-01-26 01:01:40 +00:00
copilot-swe-agent[bot]
b8ac92c537 Improve regex and add waveshare_epaper deprecation for testing
Co-authored-by: clydebarrow <2366188+clydebarrow@users.noreply.github.com>
2026-01-26 00:59:35 +00:00
copilot-swe-agent[bot]
71390c075e Add test deprecated component for validation
Co-authored-by: clydebarrow <2366188+clydebarrow@users.noreply.github.com>
2026-01-26 00:58:09 +00:00
copilot-swe-agent[bot]
0ad82b8bbf Add deprecated component detection to auto-label workflow
Co-authored-by: clydebarrow <2366188+clydebarrow@users.noreply.github.com>
2026-01-26 00:57:23 +00:00
copilot-swe-agent[bot]
7863ac8b3a Initial plan 2026-01-26 00:53:55 +00:00
5 changed files with 132 additions and 9 deletions

View File

@@ -46,6 +46,7 @@ jobs:
const BOT_COMMENT_MARKER = '<!-- auto-label-pr-bot -->';
const CODEOWNERS_MARKER = '<!-- codeowners-request -->';
const TOO_BIG_MARKER = '<!-- too-big-request -->';
const DEPRECATED_COMPONENT_MARKER = '<!-- deprecated-component-request -->';
const MANAGED_LABELS = [
'new-component',
@@ -69,7 +70,8 @@ jobs:
'new-feature',
'breaking-change',
'developer-breaking-change',
'code-quality'
'code-quality',
'deprecated_component'
];
const DOCS_PR_PATTERNS = [
@@ -382,6 +384,75 @@ jobs:
return labels;
}
// Strategy: Deprecated component detection
async function detectDeprecatedComponents() {
const labels = new Set();
const deprecatedInfo = [];
// Compile regex once for better performance
const componentFileRegex = /^esphome\/components\/([^\/]+)\/[^/]*\.py$/;
// Get files that are modified or added in components directory
const componentFiles = changedFiles.filter(file => componentFileRegex.test(file));
if (componentFiles.length === 0) {
return { labels, deprecatedInfo };
}
// Extract unique component names using the same regex
const components = new Set();
for (const file of componentFiles) {
const match = file.match(componentFileRegex);
if (match) {
components.add(match[1]);
}
}
// Get PR head SHA to fetch files from the PR branch
const prHeadSha = context.payload.pull_request.head.sha;
// Check each component's __init__.py for DEPRECATED_COMPONENT constant
for (const component of components) {
const initFile = `esphome/components/${component}/__init__.py`;
try {
// Fetch file content from PR head using GitHub API
const { data: fileData } = await github.rest.repos.getContent({
owner,
repo,
path: initFile,
ref: prHeadSha
});
// Decode base64 content
const content = Buffer.from(fileData.content, 'base64').toString('utf8');
// Look for DEPRECATED_COMPONENT = "message" or DEPRECATED_COMPONENT = 'message'
// Support single quotes, double quotes, and triple quotes (for multiline)
const doubleQuoteMatch = content.match(/DEPRECATED_COMPONENT\s*=\s*"""([\s\S]*?)"""/s) ||
content.match(/DEPRECATED_COMPONENT\s*=\s*"((?:[^"\\]|\\.)*)"/);
const singleQuoteMatch = content.match(/DEPRECATED_COMPONENT\s*=\s*'''([\s\S]*?)'''/s) ||
content.match(/DEPRECATED_COMPONENT\s*=\s*'((?:[^'\\]|\\.)*)'/);
const deprecatedMatch = doubleQuoteMatch || singleQuoteMatch;
if (deprecatedMatch) {
labels.add('deprecated_component');
deprecatedInfo.push({
component: component,
message: deprecatedMatch[1]
});
console.log(`Found deprecated component: ${component}`);
}
} catch (error) {
// Only log if it's not a simple "file not found" error (404)
if (error.status !== 404) {
console.log(`Error reading ${initFile}:`, error.message);
}
}
}
return { labels, deprecatedInfo };
}
// Strategy: Requirements detection
async function detectRequirements(allLabels) {
const labels = new Set();
@@ -418,10 +489,26 @@ jobs:
}
// Generate review messages
function generateReviewMessages(finalLabels, originalLabelCount) {
function generateReviewMessages(finalLabels, originalLabelCount, deprecatedInfo) {
const messages = [];
const prAuthor = context.payload.pull_request.user.login;
// Deprecated component message
if (finalLabels.includes('deprecated_component') && deprecatedInfo && deprecatedInfo.length > 0) {
let message = `${DEPRECATED_COMPONENT_MARKER}\n### ⚠️ Deprecated Component\n\n`;
message += `Hey there @${prAuthor},\n`;
message += `This PR modifies one or more deprecated components. Please be aware:\n\n`;
for (const info of deprecatedInfo) {
message += `#### Component: \`${info.component}\`\n`;
message += `${info.message}\n\n`;
}
message += `Consider migrating to the recommended alternative if applicable.`;
messages.push(message);
}
// Too big message
if (finalLabels.includes('too-big')) {
const testAdditions = prFiles
@@ -468,10 +555,10 @@ jobs:
}
// Handle reviews
async function handleReviews(finalLabels, originalLabelCount) {
const reviewMessages = generateReviewMessages(finalLabels, originalLabelCount);
async function handleReviews(finalLabels, originalLabelCount, deprecatedInfo) {
const reviewMessages = generateReviewMessages(finalLabels, originalLabelCount, deprecatedInfo);
const hasReviewableLabels = finalLabels.some(label =>
['too-big', 'needs-codeowners'].includes(label)
['too-big', 'needs-codeowners', 'deprecated_component'].includes(label)
);
const { data: reviews } = await github.rest.pulls.listReviews({
@@ -580,7 +667,8 @@ jobs:
actionsLabels,
codeOwnerLabels,
testLabels,
checkboxLabels
checkboxLabels,
deprecatedResult
] = await Promise.all([
detectMergeBranch(),
detectComponentPlatforms(apiData),
@@ -592,9 +680,14 @@ jobs:
detectGitHubActionsChanges(),
detectCodeOwner(),
detectTests(),
detectPRTemplateCheckboxes()
detectPRTemplateCheckboxes(),
detectDeprecatedComponents()
]);
// Extract deprecated component info
const deprecatedLabels = deprecatedResult.labels;
const deprecatedInfo = deprecatedResult.deprecatedInfo;
// Combine all labels
const allLabels = new Set([
...branchLabels,
@@ -607,7 +700,8 @@ jobs:
...actionsLabels,
...codeOwnerLabels,
...testLabels,
...checkboxLabels
...checkboxLabels,
...deprecatedLabels
]);
// Detect requirements based on all other labels
@@ -638,7 +732,7 @@ jobs:
console.log('Computed labels:', finalLabels.join(', '));
// Handle reviews
await handleReviews(finalLabels, originalLabelCount);
await handleReviews(finalLabels, originalLabelCount, deprecatedInfo);
// Apply labels
if (finalLabels.length > 0) {

View File

@@ -0,0 +1,17 @@
# Test Deprecated Component
This is a test component to validate the `deprecated_component` label functionality in the auto-label workflow.
## Purpose
This component demonstrates how the `DEPRECATED_COMPONENT` constant should be used:
1. The component is still functional and usable
2. It's deprecated and not actively maintained
3. Users should migrate to an alternative when possible
## Usage
This is different from components that use `cv.invalid()`, which are completely unusable.
Components with `DEPRECATED_COMPONENT` are still functional but discouraged for new projects.

View File

@@ -0,0 +1,6 @@
"""Test deprecated component for validation."""
CODEOWNERS = ["@test"]
DEPRECATED_COMPONENT = "This component is deprecated and will be removed in a future release. Please use the newer_component instead."
# Component is still functional but deprecated

View File

@@ -0,0 +1,5 @@
"""Test sensor platform for deprecated component."""
import esphome.config_validation as cv
# This is a simple placeholder - component still works
CONFIG_SCHEMA = cv.Schema({})

View File

@@ -1 +1,2 @@
CODEOWNERS = ["@clydebarrow"]
DEPRECATED_COMPONENT = "The waveshare_epaper component is deprecated and no new models will be added. For new epaper displays use the epaper_spi component"