aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-ximport_issues.py125
1 files changed, 107 insertions, 18 deletions
diff --git a/import_issues.py b/import_issues.py
index 245cb55..1c27f80 100755
--- a/import_issues.py
+++ b/import_issues.py
@@ -27,16 +27,17 @@
# Tickets are created in sr.ht via SMTP, so a working mail setup is required.
# Surely using Sourcehut's API would be better.
#
-# There are a few of big caveats:
+# There are a few caveats:
#
# 1. If all issue IDs from 1 to the max ID are available in your export, and the
# tracker you import into is a new tracker, then your Gitlab and Sourcehut issue
-# IDs will match up one-to-one. If not, then all bets are off. The ticket
-# descriptions in Sourcehut will indicate the original Gitlab ID, but no
-# rewriting of text is done, so issue references will be incorrect. By default,
-# the script tries to avoid this by checking that all IDs from 1 to the number
-# of issues are in the export, and aborting if not. To override this, pass
-# --allow-missing-issues.
+# IDs will match up one-to-one, and mentions of one ticket from another will
+# work. If not, you need to decide how you want to handle this. You can choose
+# to create empty Sourcehut tickets for the missing Gitlab issues so that IDs
+# still match, by passing --create-missing-issues. Blank issues will be created
+# then closed. Alternatively, you can pass --skip-missing-issues to not create
+# any extra Sourcehut tickets, but IDs will not line up. If one of these issues
+# is needed, this program will tell you.
#
# 2. Because emails are used to create tickets, we also assume that emails are
# processed in the order that they are sent, so that tickets don't get created
@@ -253,6 +254,45 @@ def open_ticket(
return issue_count
+def file_missing_ticket(
+ *,
+ smtp,
+ smtp_delay: float,
+ mode: str,
+ srht_owner: str,
+ srht_tracker: str,
+ frm: str,
+ issue_id: int,
+) -> int:
+ global issue_count
+
+ do_mail(
+ smtp=smtp,
+ smtp_delay=smtp_delay,
+ mode=mode,
+ frm=frm,
+ to=f"~{srht_owner}/{srht_tracker}@todo.sr.ht",
+ subject="Missing issue",
+ body=f"Issue {issue_id} is not known.",
+ )
+
+ issue_count += 1
+
+ # TODO Send these emails at the end, so that there isn't such a need for the
+ # previous issue to be processed promptly.
+ close_ticket(
+ smtp=smtp,
+ smtp_delay=smtp_delay,
+ mode=mode,
+ srht_owner=srht_owner,
+ srht_tracker=srht_tracker,
+ frm=frm,
+ issue_id=issue_count,
+ closed_at=None,
+ is_closed=False, # Save one line of text.
+ )
+
+
def send_comment(
*,
smtp,
@@ -336,7 +376,8 @@ def run(
frm: str,
export_dir_path: Path,
gitlab_project_url: str,
- allow_missing_issues: bool,
+ skip_missing_issues: bool,
+ create_missing_issues: bool,
):
milestone_jsons = []
with open(export_dir_path / 'milestones.ndjson') as milestones_file:
@@ -353,10 +394,21 @@ def run(
issue_jsons.append(json.loads(line))
issue_jsons.sort(key=lambda x: x['iid'])
- if not allow_missing_issues:
- assert [x['iid'] for x in issue_jsons] == list(range(1, len(issue_jsons) + 1)), \
- f"Don't have all issues from 1 to {len(issue_jsons)}, cannot proceed."
+ max_issue_id = max(x['iid'] for x in issue_jsons)
+ present_issue_id_set = {x['iid'] for x in issue_jsons}
+ missing_issue_ids = set(range(1, max_issue_id + 1)) - present_issue_id_set
+ if missing_issue_ids and not (skip_missing_issues or create_missing_issues):
+ raise RuntimeError(
+ f"Don't have all issues from 1 to {max_issue_id}, please pass "
+ f"--create-missing-issues or --skip-missing-issues to proceed."
+ )
+
+ issues_by_id = {}
+ for issue_json in issue_jsons:
+ issues_by_id[issue_json['iid']] = issue_json
+
+ # Need to sort notes by date, they seem to come unsorted.
for issue_json in issue_jsons:
issue_json['notes'].sort(key=lambda x: x['created_at'])
@@ -364,8 +416,31 @@ def run(
issue_id_map: Dict[int, int] = {}
- for issue_json in issue_jsons:
- gitlab_issue_id = issue_json['iid']
+ # While we're creating tickets, we can't just loop over the sorted
+ # issue_jsons. We have to loop over potential issue IDs and handle any that
+ # are missing as well.
+ for gitlab_issue_id in range(1, max_issue_id + 1):
+ if gitlab_issue_id not in issues_by_id:
+ if create_missing_issues:
+ file_missing_ticket(
+ smtp=smtp,
+ smtp_delay=smtp_delay,
+ mode=mode,
+ srht_owner=srht_owner,
+ srht_tracker=srht_tracker,
+ frm=frm,
+ issue_id=gitlab_issue_id,
+ )
+ elif not skip_missing_issues:
+ raise RuntimeError(
+ f"Internal error, don't know what to do with missing "
+ f"issue ID {gitlab_issue_id}."
+ )
+
+ continue
+
+ issue_json = issues_by_id[gitlab_issue_id]
+
author_id = issue_json['author_id']
created_by: Optional[str]
if USERS is None:
@@ -393,10 +468,12 @@ def run(
gitlab_ticket_url=f"{gitlab_project_url}/-/issues/{gitlab_issue_id}",
)
- if not allow_missing_issues:
+ if not skip_missing_issues:
assert srht_issue_id == gitlab_issue_id, \
f"Internal error, srht_issue_id {srht_issue_id} != " \
- f"gitlab_issue_id {gitlab_issue_id}."
+ f"gitlab_issue_id {gitlab_issue_id} " \
+ f"(skip_missing_issues={skip_missing_issues}, " \
+ f"create_missing_issues={create_missing_issues})."
issue_id_map[gitlab_issue_id] = srht_issue_id
@@ -530,9 +607,15 @@ def main():
)
parser.add_argument(
- '--allow-missing-issues',
+ '--skip-missing-issues',
action='store_true',
- help="Don't abort if there are missing issue IDs in the export.",
+ help="Skip missing Gitlab issue IDs; GL and sr.ht IDs will not match.",
+ )
+
+ parser.add_argument(
+ '--create-missing-issues',
+ action='store_true',
+ help="Create missing GL issues in sr.ht to make issue IDs match.",
)
parser.add_argument(
@@ -551,6 +634,11 @@ def main():
mode = args['mode']
frm = args['from']
+ skip_missing_issues = args['skip_missing_issues']
+ create_missing_issues = args['create_missing_issues']
+ assert not (skip_missing_issues and create_missing_issues), \
+ f"Can accept at most one of --skip-missing-issues and --create-missing-issues."
+
if mode == 'print':
smtp = None
elif mode == 'send':
@@ -584,7 +672,8 @@ def main():
frm=frm,
export_dir_path=export_dir_path,
gitlab_project_url=args['gitlab_project_url'].rstrip('/'),
- allow_missing_issues=args['allow_missing_issues'],
+ skip_missing_issues=args['skip_missing_issues'],
+ create_missing_issues=args['create_missing_issues'],
)
if mode == 'send':