This is an automated email from the ASF dual-hosted git repository. knaufk pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/flink-jira-bot.git
commit aa8c557fa1d1623b1c05d9c247fab2dd12a50533 Author: Konstantin Knauf <[email protected]> AuthorDate: Wed Apr 21 13:16:15 2021 +0200 [FLINK-22034] add rule for stale major+ tickets --- config.yaml | 23 ++++++++++ flink_jira_bot.py | 13 ++++++ flink_jira_rule.py | 7 +++ stale_major_or_above_rule.py | 107 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 150 insertions(+) diff --git a/config.yaml b/config.yaml index d1cb3bb..d0e7aff 100644 --- a/config.yaml +++ b/config.yaml @@ -32,3 +32,26 @@ stale_minor: done_label: "auto-closed" done_comment: 'This issue has been labeled "{warning_label}" for {warning_days} days. It is closed now. If you are still affected by this or would like to raise the priority of this ticket please re-open, removing the label "{done_label}" and raise the ticket priority accordingly.' +stale_blocker: + stale_days: 1 + warning_days: 7 + warning_label: "stale-blocker" + warning_comment: 'This Blockers is unassigned and itself and all of its Sub-Tasks have not been updated for {stale_days} days. So, it has been labeled "{warning_label}". If this ticket is a Blocker, please either assign yourself or give an update. Afterwards, please remove the label. In {warning_days} days the issue will be deprioritized.' + done_label: "auto-deprioritized-blocker" + done_comment: 'This issue has been labeled "{warning_label}" for {warning_days} days. It is deprioritized now. If this ticket is actually a Blocker, please raise the ticket priority again and assign yourself or revive the public discussion.' + +stale_critical: + stale_days: 7 + warning_days: 7 + warning_label: "stale-critical" + warning_comment: 'This critical issue is unassigned and itself and all of its Sub-Tasks have not been updated for {stale_days} days. So, it has been labeled "{warning_label}". If this ticket is indeed critical, please either assign yourself or give an update. Afterwards, please remove the label. In {warning_days} days the issue will be deprioritized.' + done_label: "auto-deprioritized-critical" + done_comment: 'This issue has been labeled "{warning_label}" for {warning_days} days. It is deprioritized now. If this ticket is actually critical, please raise the ticket priority again and assign yourself or revive the public discussion.' + +stale_major: + stale_days: 30 + warning_days: 7 + warning_label: "stale-major" + warning_comment: 'This major issue is unassigned and itself and all of its Sub-Tasks have not been updated for {stale_days} days. So, it has been labeled "{warning_label}". If this ticket is indeed "major", please either assign yourself or give an update. Afterwards, please remove the label. In {warning_days} days the issue will be deprioritized.' + done_label: "auto-deprioritized-major" + done_comment: 'This issue has been labeled "{warning_label}" for {warning_days} days. It is deprioritized now. If this ticket is actually "major", please raise the ticket priority again and assign yourself or revive the public discussion.' diff --git a/flink_jira_bot.py b/flink_jira_bot.py index 211f855..80a58e9 100644 --- a/flink_jira_bot.py +++ b/flink_jira_bot.py @@ -24,6 +24,7 @@ from argparse import ArgumentParser from pathlib import Path from stale_assigned_rule import StaleAssignedRule +from stale_major_or_above_rule import StaleMajorOrAboveRule from stale_minor_rule import StaleMinorRule @@ -63,5 +64,17 @@ if __name__ == "__main__": stale_assigned_rule = StaleAssignedRule(jira, jira_bot_config, args.dryrun) stale_minor_rule = StaleMinorRule(jira, jira_bot_config, args.dryrun) + stale_major_rule = StaleMajorOrAboveRule( + jira, jira_bot_config, args.dryrun, "Major" + ) + stale_critical_rule = StaleMajorOrAboveRule( + jira, jira_bot_config, args.dryrun, "Critical" + ) + stale_blocker_rule = StaleMajorOrAboveRule( + jira, jira_bot_config, args.dryrun, "Blocker" + ) stale_assigned_rule.run() stale_minor_rule.run() + stale_major_rule.run() + stale_critical_rule.run() + stale_blocker_rule.run() diff --git a/flink_jira_rule.py b/flink_jira_rule.py index 9960aca..566fce7 100644 --- a/flink_jira_rule.py +++ b/flink_jira_rule.py @@ -98,6 +98,13 @@ class FlinkJiraRule: else: logging.info(f"DRY_RUN (({key})): Unassigning.") + def set_priority(self, key, priority): + if not self.is_dry_run: + fields = {"priority": {"name": priority}} + self.jira_client.update_issue_field(key, fields) + else: + logging.info(f"DRY_RUN (({key})): Setting to {priority}") + @abc.abstractmethod def run(self): return diff --git a/stale_major_or_above_rule.py b/stale_major_or_above_rule.py new file mode 100644 index 0000000..de3cb17 --- /dev/null +++ b/stale_major_or_above_rule.py @@ -0,0 +1,107 @@ +################################################################################ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +################################################################################ + +import logging + +from flink_jira_rule import FlinkJiraRule + + +class StaleMajorOrAboveRule(FlinkJiraRule): + """ + Tickets major and above need an assignee, or an update within {stale_<blocker|critical|major>.stale_days}, + otherwise the priority will be reduced after a warning period of {stale_<blocker|critical|major>.warning_days} days. + An update of on of the Sub-Tasks counts as an update to the ticket. + Before this happens the assignee/reporter/watchers are notified that the ticket is about to become stale and will + be deprioritized. + The time periods before warning differ based on the priority: + """ + + def __init__(self, jira_client, config, is_dry_run, priority): + super().__init__(jira_client, config, is_dry_run) + self.stale_days = config[f"stale_{priority.lower()}"]["stale_days"].get() + self.warning_days = config[f"stale_{priority.lower()}"]["warning_days"].get() + self.warning_label = config[f"stale_{priority.lower()}"]["warning_label"].get() + self.done_label = config[f"stale_{priority.lower()}"]["done_label"].get() + self.done_comment = config[f"stale_{priority.lower()}"]["done_comment"].get() + self.warning_comment = config[f"stale_{priority.lower()}"][ + "warning_comment" + ].get() + self.priority = priority + + LOWER_PRIORITIES = {"Blocker": "Critical", "Critical": "Major", "Major": "Minor"} + + def run(self): + self.close_tickets_marked_stale() + self.mark_stale_tickets_stale() + + def close_tickets_marked_stale(self): + + tickets_marked_stale = ( + f"project=FLINK AND Priority = {self.priority} AND resolution = Unresolved AND labels in " + f'("{self.warning_label}") AND updated < startOfDay(-{self.warning_days}d)' + ) + logging.info( + f"Looking for {self.priority} tickets, which were previously marked as {self.warning_label}." + ) + issues = self.get_issues(tickets_marked_stale) + + for issue in issues: + key = issue["key"] + logging.info( + f"Found https://issues.apache.org/jira/browse/{key}. It is now deprioritized due to inactivity." + ) + + formatted_comment = self.done_comment.format( + warning_days=self.warning_days, + warning_label=self.warning_label, + done_label=self.done_label, + ) + + self.add_comment(key, formatted_comment) + self.replace_label(issue, self.warning_label, self.done_label) + self.set_priority(key, self.LOWER_PRIORITIES[self.priority]) + + def mark_stale_tickets_stale(self): + + stale_tickets = f"project=FLINK AND priority = {self.priority} AND resolution = Unresolved AND assignee is " \ + f"empty AND updated < startOfDay(-{self.stale_days}d)" + logging.info(f"Looking for {self.priority} tickets, which are stale.") + issues = self.get_issues(stale_tickets) + + for issue in issues: + key = issue["key"] + issue = self.jira_client.get_issue(key) + + if not self.has_recently_updated_subtask(key, self.stale_days): + logging.info( + f"Found https://issues.apache.org/jira/browse/{key}. It is marked stale now." + ) + formatted_comment = self.warning_comment.format( + stale_days=self.stale_days, + warning_days=self.warning_days, + warning_label=self.warning_label, + ) + + self.add_label(issue, self.warning_label) + self.add_comment(key, formatted_comment) + + else: + logging.info( + f"Found https://issues.apache.org/jira/browse/{key}, but is has recently updated Subtasks. " + f"Ignoring for now." + )
