diff --git a/config/konflux.yaml b/config/konflux.yaml index 3076e3c..672686a 100644 --- a/config/konflux.yaml +++ b/config/konflux.yaml @@ -25,6 +25,8 @@ team_automation: issues: Feature: # collector: get_issues + rules: + - check_due_date_by_fixversion group_rules: - check_fixversion_rank Epic: diff --git a/src/rules/team/__init__.py b/src/rules/team/__init__.py index 422a80f..0bb351a 100644 --- a/src/rules/team/__init__.py +++ b/src/rules/team/__init__.py @@ -1,4 +1,5 @@ from .due_date import check_due_date +from .due_date_by_fixversion import check_due_date_by_fixversion from .fixversion_rank import check_fixversion_rank from .outcome_version import set_fix_version from .parent import check_parent_link @@ -10,6 +11,7 @@ __all__ = [ check_due_date, + check_due_date_by_fixversion, set_fix_version, check_parent_link, check_priority, diff --git a/src/rules/team/due_date_by_fixversion.py b/src/rules/team/due_date_by_fixversion.py new file mode 100644 index 0000000..ace7170 --- /dev/null +++ b/src/rules/team/due_date_by_fixversion.py @@ -0,0 +1,62 @@ +import operator as op +from time import strftime + +import celpy +import jira + +from utils.cel import issue_as_cel +from utils.jira import update + +today = strftime("%Y-%m-%d") + + +def _earliest_fixversion(issue): + fixversions = issue.fields.fixVersions + if not fixversions: + return None + fixversions = [ + fixversion for fixversion in fixversions if hasattr(fixversion, "releaseDate") + ] + if not fixversions: + return None + return sorted(fixversions, key=op.attrgetter("releaseDate"))[0] + + +def check_due_date_by_fixversion( + issue: jira.resources.Issue, context: dict, dry_run: bool, ignore: str = "" +) -> None: + if ignore: + env = celpy.Environment() + program = env.program(env.compile(ignore)) + if program.evaluate(issue_as_cel(issue)): + context["updates"].append( + f"! Ignoring {issue.key} for due date by fixVersion rule, per cel expression: {ignore}." + ) + return + + fixversion = _earliest_fixversion(issue) + if not fixversion: + return + + target_due_date = fixversion.releaseDate + target_source = fixversion + + if target_due_date is None: + raise ValueError( + "Impossible - fixversion due date cannot be None at this location" + ) + + due_date_id = issue.raw["Context"]["Field Ids"]["Due Date"] + due_date = getattr(issue.fields, due_date_id) + + if not due_date or due_date > target_due_date: + if not due_date: + message = f" * Setting Due Date to {target_due_date}, inherited from fixVersion {target_source.name}." + elif due_date > target_due_date: + message = f" * Pulling in Due Date from {due_date} to {target_due_date}, propagated from fixVersion {target_source.name}." + + context["updates"].append(message) + context["comments"].append(message) + + if not dry_run: + update(issue, {"fields": {due_date_id: target_due_date}}) diff --git a/src/rules/team/fixversion_rank.py b/src/rules/team/fixversion_rank.py index 469653a..f8d692c 100644 --- a/src/rules/team/fixversion_rank.py +++ b/src/rules/team/fixversion_rank.py @@ -91,7 +91,10 @@ class FixVersionBlock(Block): """A special-case block that gets ranked to the top""" def yield_issues(self): - yield from sorted(self.issues, key=self._earliest_fixversion_date) + """Within the fixversion block, issues get sorted by due date""" + duedate_field_id = self.issues[0].raw["Context"]["Field Ids"]["Due Date"] + duedate = lambda issue: getattr(issue.fields, duedate_field_id) + yield from sorted(self.issues, key=duedate) @property def rank(self): @@ -100,20 +103,6 @@ def rank(self): def claims(self, issue) -> bool: return self._claims(issue) - @staticmethod - def _earliest_fixversion_date(issue): - fixversions = issue.fields.fixVersions - if not fixversions: - return None - dates = [ - fixversion.releaseDate - for fixversion in fixversions - if hasattr(fixversion, "releaseDate") - ] - if not dates: - return None - return sorted(dates)[0] - @staticmethod def _claims(issue) -> bool: critical_deadline = (