Add web platform features rules#2894
Conversation
Bugs with the `web-feature` keyword are managed by the bot so that their lifetimes matches the lifetime of the related web-feature (specified in the user-story field) and are closed once the web feature is marked as implemented in Firefox. This initial change implements the following functionality: * Automatically handling renames of existing web features * Removing `web-feature` labels in the user story which don't correspond to a real web-feature. * Updating links to external resources on web-features bugs. * Reopening web-features bugs that are closed when the corresponding feature isn't marked as supported. * Closing bugs once the corresponding web-feature is marked as supported.
|
@ksy36 maybe you would be able to take a look at this? |
There was a problem hiding this comment.
Pull request overview
Adds a new WebPlatformFeatures rule to keep Bugzilla bugs tagged with web-feature aligned with the corresponding Web Platform Feature status (as sourced from BigQuery), including metadata maintenance and automatic reopen/close behavior.
Changes:
- Implemented BigQuery-driven update rules for feature renames, invalid feature ids, metadata/link updates, and auto close/reopen logic.
- Expanded the rule’s email report to show a structured “Changes” breakdown (keywords, links, user story, status/resolution).
- Added unit tests for user story parsing and for translating
BugUpdateinto Bugzilla-compatible change payloads.
Reviewed changes
Copilot reviewed 3 out of 4 changed files in this pull request and generated 6 comments.
| File | Description |
|---|---|
bugbot/rules/web_platform_features.py |
Replaces the old “add missing see_also” behavior with a full rule engine (data fetch + per-bug update plan + Bugzilla change generation). |
templates/web_platform_features.html |
Updates the report email template to display the new structured per-bug change summary. |
tests/rules/test_web_platform_features.py |
Adds tests for parsing and change-generation logic used by the new rule. |
tests/rules/__init__.py |
No functional changes (file present as part of test package structure). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| user_story_re = re.compile(r"^\s*([^\s]+)\s*:\s*(.*)") | ||
| for line in user_story.splitlines(): | ||
| key = None | ||
| value = None | ||
| m = user_story_re.match(line) | ||
| if m is not None: | ||
| maybe_key, maybe_value = m.groups() | ||
| if maybe_value: |
| add = [] | ||
| remove = [] | ||
| has_links = [current_url] + current_see_also | ||
| has_link_keys = url_keys(has_links) | ||
| expected_link_keys = url_keys(self.see_also.keys()) | ||
|
|
||
| for key, urls in expected_link_keys.items(): | ||
| for url in urls: | ||
| add_url = self.see_also[url] | ||
| if add_url and key not in has_link_keys: | ||
| add.append(url) | ||
| elif not add_url and key in has_link_keys: | ||
| remove.append(url) | ||
|
|
||
| return AddRemoveChange(add=add, remove=remove) |
| f"{feature_name} ([definition file](https://github.com/web-platform-dx/web-features/blob/main/features/{feature_name}.yml.dist))" | ||
| for feature_name in unsupported_features |
| {% if changes.keywords != None %} | ||
| {% if changes.keywords.add %} | ||
| <li> |
| <li> | ||
| <p> | ||
| Updated whiteboard from <code>{{ whiteboard }}</code> to <code>{{ changes.whiteboard }}</code> | ||
| </p> | ||
| </li> | ||
| {% endif -%} | ||
| {% if changes.user_story != None %} | ||
| <li> | ||
| <p>Updated user story from:</p> | ||
| <pre>{{ user_story }}</pre> | ||
| <p>to:</p> | ||
| <pre>{{ changes.user_story }}</pre> | ||
| </li> |
| <li> | ||
| <p> | ||
| Added see_also | ||
| {% for item in changes.see_also.add %} | ||
| <code>{{ item }}</code> | ||
| {% if not loop.last %},{% endif %} | ||
| {% endfor %} |
|
|
||
|
|
||
| @dataclass | ||
| class BugUpdate: |
There was a problem hiding this comment.
Having classes with names BugChanges, and BugUpdate seems a bit confusing.
| JOIN UNNEST(`webcompat_knowledge_base.EXTRACT_ARRAY`(bugs.user_story, "$.web-feature")) AS bug_feature | ||
| LEFT JOIN `web_features.features_latest` AS features ON features.feature = bug_feature | ||
| WHERE features.feature IS NULL | ||
| ), |
There was a problem hiding this comment.
Not sure if this is running both REPLACE and DELETE for features that are renamed (assuming previous-feature-name won't exist in web_features.features_latest?)
So it will have:
user_story_updates["web-feature"] = [REPLACE, DELETE]
and output_line will be set to new value with REPLACE, but then reset to None with DELETE here:
if change.type == UserStoryChangeType.DELETE:
output_line = None
has_updates = True
elif change.type == UserStoryChangeType.REPLACE:
assert change.new_value is not None
output_line = (key, change.new_value)
has_updates = True
Bugs with the
web-featurekeyword are managed by the bot so that their lifetimes matches the lifetime of the related web-feature (specified in the user-story field) and are closed once the web feature is marked as implemented in Firefox.This initial change implements the following functionality:
web-featurelabels in the user story which don't correspond to a real web-feature.Checklist
to-be-announcedtag added if this is worth announcing