Skip to content
Permalink
Browse files
[FLINK-22033] add rule 2, which unassign stale assigned tickets after…
… some time
  • Loading branch information
knaufk committed Apr 16, 2021
1 parent e575ac6 commit a38d571a76c617d222e3fab5488c0ffe6d44119f
Showing 3 changed files with 121 additions and 11 deletions.
@@ -42,11 +42,13 @@ The configuration of the rules can be found in [config.yaml](config.yaml).

### Rule 1 (not implemented yet)

### Rule 2 (not implemented yet)
### Rule 2: Unassign Stale Assigned Tickets

### Rule 3
Assigned tickets without an update for {stale_assigned.stale_days} are unassigned after a warning period of {stale_assigned.warning_days}. Before this happens the assignee is notified that this is about to happen and asked for an update on the status of her contribution.

An unresolved Minor ticket without an update for {stale.minor.stale_days} is closed after a warning period of {stale.minor.warning_days} with a comment that encourages users to watch, comment and simply reopen with a higher priority if the problem insists.
### Rule 3: Close Stale Minor Tickets

An unresolved Minor ticket without an update for {stale_minor.stale_days} is closed after a warning period of {stale_minor.warning_days} with a comment that encourages users to watch, comment and simply reopen with a higher priority if the problem insists.

## About Apache Flink

@@ -16,6 +16,14 @@
# limitations under the License.
################################################################################

stale_assigned:
stale_days: 7
warning_days: 7
warning_label: "stale-assigned"
warning_comment: 'This issue is assigned but has not received an update in {stale_days} days so it has been labeled "{warning_label}". If you are still working on the issue, please give an update and remove the label. If you are no longer working on the issue, please unassign so someone else may work on it. In {warning_days} days the issue will be automatically unassigned.'
done_label: "auto-unassigned"
done_comment: 'This issue was marked "{warning_label}" and has not received an update in {warning_days} days. It is now automatically unassigned. If you are still working on it, you can assign it to yourself again. Please also give an update about the status of the work.'

stale_minor:
stale_days: 180
warning_days: 7
@@ -33,12 +33,32 @@ def __init__(self, jira_client, config, is_dry_run):
self.config = config
self.is_dry_run = is_dry_run

def get_issues(self, jql_query):
"""Queries the JIRA PI for all issues that match the given JQL Query
This method is necessary as requests tend to time out if the number of results reaches a certain number.
So, this method requests the results in multiple queries and returns a final list of all issues.
:param jql_query: the search query
:return: a list of issues matching the query
"""
limit = 200
current = 0
total = 1
issues = []
while current < total:
response = self.jira_client.jql(jql_query, limit=limit, start=current)
total = response["total"]
issues = issues + response["issues"]
current = len(issues)
logging.info(f'"{jql_query}" returned {len(issues)} issues')
return issues

def has_recently_updated_subtask(self, parent, updated_within_days):
find_subtasks_updated_within = (
f"parent = {parent} AND updated > startOfDay(-{updated_within_days}d)"
)
issues = self.jira_client.jql(find_subtasks_updated_within, limit=1)
return issues["total"] > 0
issues = self.get_issues(find_subtasks_updated_within)
return len(issues) > 0

def add_label(self, issue, label):
labels = issue["fields"]["labels"] + [label]
@@ -75,6 +95,12 @@ def close_issue(self, key):
else:
logging.info(f"DRY_RUN (({key})): Closing.")

def unassign(self, key):
if not self.is_dry_run:
self.jira_client.assign_issue(key, None)
else:
logging.info(f"DRY_RUN (({key})): Unassigning.")

@abc.abstractmethod
def run(self):
return
@@ -101,11 +127,11 @@ def close_tickets_marked_stale(self):
f'("{self.warning_label}") AND updated < startOfDay(-{self.warning_days}d)'
)
logging.info(
f"Looking for minor tickets, which were previously marked as stale: {minor_tickets_marked_stale}"
f"Looking for minor tickets, which were previously marked as {self.warning_label}."
)
issues = jira.jql(minor_tickets_marked_stale, limit=10000)
issues = self.get_issues(minor_tickets_marked_stale)

for issue in issues["issues"]:
for issue in issues:
key = issue["key"]
logging.info(
f"Found https://issues.apache.org/jira/browse/{key}. It is now closed due to inactivity."
@@ -127,12 +153,84 @@ def mark_stale_tickets_stale(self):
f"project = FLINK AND Priority = Minor AND resolution = Unresolved AND updated < "
f"startOfDay(-{self.stale_days}d)"
)
logging.info(f"Looking for minor tickets, which are stale.")
issues = self.get_issues(stale_minor_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."
)


class Rule2(FlinkJiraRule):
def __init__(self, jira_client, config, is_dry_run):
super().__init__(jira_client, config, is_dry_run)
self.stale_days = config["stale_assigned"]["stale_days"].get()
self.warning_days = config["stale_assigned"]["warning_days"].get()
self.warning_label = config["stale_assigned"]["warning_label"].get()
self.done_label = config["stale_assigned"]["done_label"].get()
self.done_comment = config["stale_assigned"]["done_comment"].get()
self.warning_comment = config["stale_assigned"]["warning_comment"].get()

def run(self):
self.unassign_tickets_marked_stale()
self.mark_stale_tickets_stale()

def unassign_tickets_marked_stale(self):

assigned_tickets_marked_stale = (
f"project=FLINK AND resolution = Unresolved AND labels in "
f'("{self.warning_label}") AND updated < startOfDay(-{self.warning_days}d)'
)
logging.info(
f"Looking for minor tickets, which are stale: {stale_minor_tickets}"
f"Looking for assigned tickets, which were previously marked as {self.warning_label}."
)
issues = self.get_issues(assigned_tickets_marked_stale)

for issue in issues:
key = issue["key"]
logging.info(
f"Found https://issues.apache.org/jira/browse/{key}. It is now unassigned 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.unassign(key)

def mark_stale_tickets_stale(self):

stale_assigned_tickets = (
f"project = FLINK AND resolution = Unresolved AND assignee is not EMPTY AND updated < "
f"startOfDay(-{self.stale_days}d)"
)
issues = self.jira_client.jql(stale_minor_tickets, limit=10000)
logging.info(f"Looking for assigned tickets, which are stale.")
issues = self.get_issues(stale_assigned_tickets)

for issue in issues["issues"]:
for issue in issues:
key = issue["key"]
issue = self.jira_client.get_issue(key)

@@ -190,5 +288,7 @@ def get_args():
password=os.environ["JIRA_PASSWORD"],
)

rule_2 = Rule2(jira, jira_bot_config, args.dryrun)
rule_3 = Rule3(jira, jira_bot_config, args.dryrun)
rule_2.run()
rule_3.run()

0 comments on commit a38d571

Please sign in to comment.