diff --git a/.github/workflows/auto-label-pr.yml b/.github/workflows/auto-label-pr.yml index d32d8e01c2..241b64f55a 100644 --- a/.github/workflows/auto-label-pr.yml +++ b/.github/workflows/auto-label-pr.yml @@ -46,6 +46,7 @@ jobs: const BOT_COMMENT_MARKER = ''; const CODEOWNERS_MARKER = ''; const TOO_BIG_MARKER = ''; + const DEPRECATED_COMPONENT_MARKER = ''; 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,55 @@ jobs: return labels; } + // Strategy: Deprecated component detection + async function detectDeprecatedComponents() { + const labels = new Set(); + const deprecatedInfo = []; + + // Get files that are modified or added in components directory + const componentFiles = changedFiles.filter(file => + file.match(/^esphome\/components\/([^\/]+)\/.+\.py$/) + ); + + if (componentFiles.length === 0) { + return { labels, deprecatedInfo }; + } + + // Extract unique component names + const components = new Set(); + for (const file of componentFiles) { + const match = file.match(/^esphome\/components\/([^\/]+)\//); + if (match) { + components.add(match[1]); + } + } + + // Check each component's __init__.py for DEPRECATED_COMPONENT constant + for (const component of components) { + const initFile = `esphome/components/${component}/__init__.py`; + try { + const content = fs.readFileSync(initFile, 'utf8'); + + // Look for DEPRECATED_COMPONENT = "message" or DEPRECATED_COMPONENT = 'message' + const deprecatedMatch = content.match(/DEPRECATED_COMPONENT\s*=\s*["'](.+?)["']/s); + + if (deprecatedMatch) { + labels.add('deprecated_component'); + deprecatedInfo.push({ + component: component, + message: deprecatedMatch[1] + }); + console.log(`Found deprecated component: ${component}`); + } + } catch (error) { + // File doesn't exist or can't be read - not an error, just skip + console.log(`Could not read ${initFile}:`, error.message); + } + } + + return { labels, deprecatedInfo }; + } + // Strategy: Requirements detection async function detectRequirements(allLabels) { const labels = new Set(); @@ -418,10 +469,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 +535,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 +647,8 @@ jobs: actionsLabels, codeOwnerLabels, testLabels, - checkboxLabels + checkboxLabels, + deprecatedResult ] = await Promise.all([ detectMergeBranch(), detectComponentPlatforms(apiData), @@ -592,9 +660,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 +680,8 @@ jobs: ...actionsLabels, ...codeOwnerLabels, ...testLabels, - ...checkboxLabels + ...checkboxLabels, + ...deprecatedLabels ]); // Detect requirements based on all other labels @@ -638,7 +712,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) {