Skip to content

Commit f445fdb

Browse files
committed
Support new reference options
Closes #123 Closes #94
1 parent e6fcedc commit f445fdb

File tree

1 file changed

+49
-16
lines changed

1 file changed

+49
-16
lines changed

main.py

+49-16
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from enum import Enum
1212
import itertools
1313
import operator
14+
from collections import defaultdict
1415

1516

1617
class LineStatus(Enum):
@@ -24,7 +25,7 @@ class Issue(object):
2425
"""Basic Issue model for collecting the necessary info to send to GitHub."""
2526

2627
def __init__(self, title, labels, assignees, milestone, body, hunk, file_name,
27-
start_line, markdown_language, status, identifier):
28+
start_line, markdown_language, status, identifier, ref):
2829
self.title = title
2930
self.labels = labels
3031
self.assignees = assignees
@@ -36,6 +37,7 @@ def __init__(self, title, labels, assignees, milestone, body, hunk, file_name,
3637
self.markdown_language = markdown_language
3738
self.status = status
3839
self.identifier = identifier
40+
self.ref = ref
3941

4042

4143
class GitHubClient(object):
@@ -133,15 +135,22 @@ def create_issue(self, issue):
133135
issue_contents = formatted_issue_body + '\n\n' + url_to_line + '\n\n' + snippet
134136
else:
135137
issue_contents = url_to_line + '\n\n' + snippet
136-
# Check if the current issue already exists - if so, skip it.
137-
# The below is a simple and imperfect check based on the issue title.
138-
for existing_issue in self.existing_issues:
139-
if issue.title == existing_issue['title']:
140-
print(f'Skipping issue (already exists).')
141-
return
142138

143139
new_issue_body = {'title': title, 'body': issue_contents, 'labels': issue.labels}
144140

141+
issues_url = self.issues_url
142+
if issue.ref:
143+
if issue.ref.startswith('@'):
144+
issue.assignees.append(issue.ref.lstrip('@'))
145+
elif issue.ref.startswith('#'):
146+
issue_num = issue.ref.lstrip('#')
147+
if issue_num.isdigit():
148+
issues_url += f'/{issue_num}'
149+
elif issue.ref.startswith('!'):
150+
issue.labels.append(issue.ref.lstrip('!'))
151+
else:
152+
issue.title = f'[{issue.ref}] {issue.title}'
153+
145154
# We need to check if any assignees/milestone specified exist, otherwise issue creation will fail.
146155
valid_assignees = []
147156
if len(issue.assignees) == 0 and self.auto_assign:
@@ -163,7 +172,7 @@ def create_issue(self, issue):
163172
else:
164173
print(f'Milestone {issue.milestone} does not exist! Dropping this parameter!')
165174

166-
new_issue_request = requests.post(url=self.issues_url, headers=self.issue_headers,
175+
new_issue_request = requests.post(url=issues_url, headers=self.issue_headers,
167176
data=json.dumps(new_issue_body))
168177

169178
return new_issue_request.status_code
@@ -423,7 +432,7 @@ def parse(self, diff_file):
423432
extracted_comments = []
424433
prev_comment = None
425434
for i, comment in enumerate(comments):
426-
if i == 0 or re.search('|'.join(self.identifiers), comment.group(0)):
435+
if i == 0 or re.search('|'.join(self.identifiers), comment.group(0), re.IGNORECASE):
427436
extracted_comments.append([comment])
428437
else:
429438
if comment.start() == prev_comment.end() + 1:
@@ -493,12 +502,8 @@ def _extract_issue_if_exists(self, comment, marker, code_block):
493502
cleaned_line = self._clean_line(committed_line, marker)
494503
line_title, ref, identifier = self._get_title(cleaned_line)
495504
if line_title:
496-
if ref:
497-
issue_title = f'[{ref}] {line_title}'
498-
else:
499-
issue_title = line_title
500505
issue = Issue(
501-
title=issue_title,
506+
title=line_title,
502507
labels=['todo'],
503508
assignees=[],
504509
milestone=None,
@@ -508,7 +513,8 @@ def _extract_issue_if_exists(self, comment, marker, code_block):
508513
start_line=code_block['start_line'],
509514
markdown_language=code_block['markdown_language'],
510515
status=line_status,
511-
identifier=identifier
516+
identifier=identifier,
517+
ref=ref
512518
)
513519

514520
# Calculate the file line number that this issue references.
@@ -623,7 +629,7 @@ def _get_title(self, comment):
623629
title_pattern = re.compile(r'(?<=' + identifier + r'[\s:]).+', re.IGNORECASE)
624630
title_search = title_pattern.search(comment, re.IGNORECASE)
625631
if title_search:
626-
title = title_search.group(0).strip()
632+
title = title_search.group(0).strip(': ')
627633
break
628634
else:
629635
title_ref_pattern = re.compile(r'(?<=' + identifier + r'\().+', re.IGNORECASE)
@@ -710,13 +716,40 @@ def _should_ignore(self, file):
710716
f'Assuming this issue has been moved so skipping.')
711717
continue
712718
issues_to_process.extend(similar_issues)
719+
720+
# If a TODO with an issue number reference is updated, it will appear as an addition and deletion.
721+
# We need to ignore the deletion so it doesn't update then immediately close the issue.
722+
# First store TODOs based on their status.
723+
todos_status = defaultdict(lambda: {'added': False, 'deleted': False})
724+
725+
# Populate the status dictionary.
726+
for raw_issue in issues_to_process:
727+
if raw_issue.ref and raw_issue.ref.startswith('#'):
728+
if raw_issue.status == LineStatus.ADDED:
729+
todos_status[raw_issue.ref]['added'] = True
730+
elif raw_issue.status == LineStatus.DELETED:
731+
todos_status[raw_issue.ref]['deleted'] = True
732+
733+
# Determine which issues are both added and deleted.
734+
update_and_close_issues = set()
735+
736+
for reference, status in todos_status.items():
737+
if status['added'] and status['deleted']:
738+
update_and_close_issues.add(reference)
739+
740+
# Remove issues from issues_to_process if they are both to be updated and closed.
741+
issues_to_process = [issue for issue in issues_to_process if
742+
not (issue.ref in update_and_close_issues and issue.status == LineStatus.DELETED)]
743+
713744
# Cycle through the Issue objects and create or close a corresponding GitHub issue for each.
714745
for j, raw_issue in enumerate(issues_to_process):
715746
print(f'Processing issue {j + 1} of {len(issues_to_process)}')
716747
if raw_issue.status == LineStatus.ADDED:
717748
status_code = client.create_issue(raw_issue)
718749
if status_code == 201:
719750
print('Issue created')
751+
elif status_code == 200:
752+
print('Issue updated')
720753
else:
721754
print('Issue could not be created')
722755
elif raw_issue.status == LineStatus.DELETED and os.getenv('INPUT_CLOSE_ISSUES', 'true') == 'true':

0 commit comments

Comments
 (0)