Module: tools
Branch: master
Commit: 894a49d2cb0069a98b962e5136a5d86d17230340
URL: https://source.winehq.org/git/tools.git/?a=commit;h=894a49d2cb0069a98b962e5…
Author: Jeremy White <jwhite(a)codeweavers.com>
Date: Thu Apr 28 10:13:46 2022 -0500
Do not send MR changes on apparent maintainer.
If we think that it was a maintainer and they only changed meta
information, then do not send a new copy of the MR.
Signed-off-by: Alexandre Julliard <julliard(a)winehq.org>
---
gitlab/gitlab-to-mail/gitlabtomail.py | 41 +++++++++++++++++++++++++++--------
1 file changed, 32 insertions(+), 9 deletions(-)
diff --git a/gitlab/gitlab-to-mail/gitlabtomail.py b/gitlab/gitlab-to-mail/gitlabtomail.py
index 3708679..a2f7079 100755
--- a/gitlab/gitlab-to-mail/gitlabtomail.py
+++ b/gitlab/gitlab-to-mail/gitlabtomail.py
@@ -467,7 +467,14 @@ def fixup_version(mail, version):
mail['Subject'] = re.sub(r'PATCH ([0-9])', f"PATCH v{version} \\1", subject)
-def commits_changed(new, old):
+def done_by_maintainer(author, commits):
+ for commit in commits:
+ if commit['committer_name'] != author:
+ return True
+ return False
+
+
+def commits_meta_changed(new, old):
if len(old) != len(new):
return True
for i in range(0, len(old)):
@@ -477,7 +484,7 @@ def commits_changed(new, old):
return False
-def get_changes(iid, version, versionid, old_versionid):
+def get_changes(iid, version, versionid, old_versionid, mr):
current_changes = fetch_mr_version(iid, versionid)
old_changes = fetch_mr_version(iid, old_versionid)
old_commits = []
@@ -487,12 +494,25 @@ def get_changes(iid, version, versionid, old_versionid):
version_string = f" v{version}: "
vstring_len = len(version_string)
+ # After a merge, the source branch is deleted, which will short
+ # circuit this logic. We cannot do any analysis on an MR in
+ # that condition.
+ if len(old_changes['diffs']) == 0:
+ return None
+
# We don't want to send an email notice on a rebase or other innocuous change
+ # But this gets complicated. There is a strong sense that if an
+ # author makes a change to their commit messages or description,
+ # that warrants another email. But if a maintainer adds a signed off
+ # or makes a similar small change at merge time, we *don't* want that.
+ # So we try to figure out if this is a maintainer merge, and if so
+ # we want to more aggressively 'quiet' this logic. So basically,
+ # if the messages are not changed, or only changed by a maintainer, let's be quiet.
if 'commits' in current_changes and 'commits' in old_changes:
- # If the commit messages / composition has not changed and
- if not commits_changed(current_changes['commits'], old_changes['commits']):
+ changed = commits_meta_changed(current_changes['commits'], old_changes['commits'])
+ maintainer = done_by_maintainer(mr['author']['name'], current_changes['commits'])
+ if maintainer or not changed:
if 'diffs' in current_changes and 'diffs' in old_changes:
- # The diff is unchanged, then...
if current_changes['diffs'] == old_changes['diffs']:
return None
@@ -510,7 +530,11 @@ def get_changes(iid, version, versionid, old_versionid):
return "\n" + version_string + changes
-def create_cover(mr_id, mr_iid, mr_version, versions, nr_patches, author_name, title, description):
+def create_cover(mr_id, mr_iid, mr_version, versions, nr_patches, mr):
+ author_name = f"{mr['author']['name']} (@{mr['author']['username']})"
+ title = mr['title']
+ description = mr['description']
+
mail = email.message.Message()
mail['From'] = email.utils.formataddr((author_name, settings.BRIDGE_FROM_EMAIL))
vstring = ""
@@ -523,7 +547,7 @@ def create_cover(mr_id, mr_iid, mr_version, versions, nr_patches, author_name, t
# versions array is ordered, which Arek seems to not have found
vindex = mr_version * -1
oindex = (mr_version - 1) * -1
- changes = get_changes(mr_iid, mr_version, versions[vindex]['id'], versions[oindex]['id'])
+ changes = get_changes(mr_iid, mr_version, versions[vindex]['id'], versions[oindex]['id'], mr)
if changes is None:
return None
@@ -583,8 +607,7 @@ def process_mr(mr, update_db):
return
nr_patches = len(patches)
- author = f"{mr['author']['name']} (@{mr['author']['username']})"
- cover = create_cover(mr['id'], iid, version, versions, nr_patches, author, mr['title'], mr['description'])
+ cover = create_cover(mr['id'], iid, version, versions, nr_patches, mr)
if cover is None:
log(f"MR{iid}v{version} - skipping, has no changes")
return
Module: tools
Branch: master
Commit: 4089204f69f4d08da70951986f793de58e8f27d3
URL: https://source.winehq.org/git/tools.git/?a=commit;h=4089204f69f4d08da709519…
Author: Jeremy White <jwhite(a)codeweavers.com>
Date: Thu Apr 28 07:37:04 2022 -0500
Add the ability to process select MRs and events.
This debug facility lets me test running just one
MR or event to make development simpler.
Signed-off-by: Alexandre Julliard <julliard(a)winehq.org>
---
gitlab/gitlab-to-mail/gitlabtomail.py | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
diff --git a/gitlab/gitlab-to-mail/gitlabtomail.py b/gitlab/gitlab-to-mail/gitlabtomail.py
index 74ea95e..d4d6972 100755
--- a/gitlab/gitlab-to-mail/gitlabtomail.py
+++ b/gitlab/gitlab-to-mail/gitlabtomail.py
@@ -607,8 +607,30 @@ def process_mr(mr, update_db):
db.set_last_mr_updated_at(updated_at)
+def handle_debug_requests():
+ for arg in sys.argv[2:]:
+ if arg.find("mr=") == 0:
+ print(f"Processing MR iid {arg[3:]}")
+ mr = fetch_specific_mr(int(arg[3:]))
+ process_mr(mr, False)
+ elif arg.find("event=") == 0:
+ print(f"Processing event id {arg[6:]}")
+ # I did not immediately see a way to get a specific event.
+ # This is debug code, so I'm at peace with doing this inefficiently
+ date = (datetime.datetime.now() - datetime.timedelta(days=365)).date()
+ for event in fetch_events(date):
+ if event['id'] == int(arg[6:]):
+ process_event(event)
+ else:
+ print(f"Error: unknown argument {arg}", file=sys.stderr)
+
+
def main():
+ if len(sys.argv) > 2:
+ handle_debug_requests()
+ return
+
# Process any new merge requests
last_mr_updated_at = db.get_last_mr_updated_at()
if not last_mr_updated_at:
Module: tools
Branch: master
Commit: 538a9f53d13c1ad1f503a71f9490c4a9d0e4d33d
URL: https://source.winehq.org/git/tools.git/?a=commit;h=538a9f53d13c1ad1f503a71…
Author: Jeremy White <jwhite(a)codeweavers.com>
Date: Thu Apr 28 07:15:36 2022 -0500
Refactor to create a function that handles MRs.
Signed-off-by: Alexandre Julliard <julliard(a)winehq.org>
---
gitlab/gitlab-to-mail/gitlabtomail.py | 131 ++++++++++++++++++----------------
1 file changed, 69 insertions(+), 62 deletions(-)
diff --git a/gitlab/gitlab-to-mail/gitlabtomail.py b/gitlab/gitlab-to-mail/gitlabtomail.py
index 19eafa3..c46cc98 100755
--- a/gitlab/gitlab-to-mail/gitlabtomail.py
+++ b/gitlab/gitlab-to-mail/gitlabtomail.py
@@ -536,7 +536,75 @@ def create_cover(mr_id, mr_iid, mr_version, versions, nr_patches, author_name, t
return mail
+def process_mr(mr, update_db):
+ iid = mr['iid']
+ log(f"MR{iid} updated - processing")
+
+ if mr['merged_at']:
+ log(f"MR{iid} merged - skipping")
+ return
+
+ # We jump through some hoops because the fetch_mr_patches
+ # entry point on GitLab does not allow one
+ # to specify a particular version. You can only get
+ # a nice set of patches for the most current version.
+ # Out of an abundance of caution, we make sure that the
+ # last sha1 matches a known version and then we use that.
+ updated_at = parse_gitlab_datetime(mr['updated_at'])
+ patches = split_mbox_into_messages(fetch_mr_patches(iid))
+ sha1s = [get_patch_sha1(patch) for patch in patches]
+ versions = fetch_mr_versions(iid)
+ version = get_mr_version(versions, sha1s[-1])
+ if not version:
+ # We can get an unusual case where we do not have a patch
+ # to go with the start of the versions chain. Andrew
+ # was able to do this in wine-demo MR 13, by somehow
+ # having a commit message only commit. Arek believes that
+ # this is not a sensible MR, and we should flag it as such.
+ log(f"Error: MR {iid} patch {sha1s[-1]} not in versions")
+ error = ("This merge request appears to be malformed.\n"
+ "This can be caused by a commit with no content.\n"
+ "Please check your merge request for errors.")
+
+ if not error_in_notes(iid, error):
+ post_note(iid, error)
+ if update_db:
+ db.set_last_mr_updated_at(updated_at)
+ return
+
+ date = get_mr_version_date(versions, version)
+ if db.was_mr_version_processed(iid, version):
+ log(f"MR{iid}v{version} - skipping, already processed")
+ return
+
+ nr_patches = len(patches)
+ author = f"{mr['author']['name']} (@{mr['author']['username']})"
+ cover = create_cover(mr['id'], iid, version, versions, nr_patches, author, mr['title'], mr['description'])
+ if cover is None:
+ log(f"MR{iid}v{version} - skipping, has no changes")
+ return
+
+ fixup_date(cover, date)
+ create_headers_from_mr(cover, mr)
+ send_email(cover)
+ if nr_patches <= settings.MAXIMUM_PATCHES:
+ for nr, mail in enumerate(patches):
+ fixup_patch(mail, iid, version, nr+1)
+ date = date + datetime.timedelta(seconds=1)
+ fixup_date(mail, date)
+ fixup_version(mail, version)
+ create_headers_from_mr(mail, mr)
+ mail['References'] = create_reference(mr['id'], gitlab_hostname)
+ log(f"MR{iid}v{version} - sent email")
+ send_email(mail)
+ if update_db:
+ db.mark_mr_version_processed(iid, version)
+ db.set_last_mr_updated_at(updated_at)
+
+
def main():
+
+ # Process any new merge requests
last_mr_updated_at = db.get_last_mr_updated_at()
if not last_mr_updated_at:
last_mr_updated_at = datetime.datetime.now() - datetime.timedelta(days=settings.INITIAL_BACKLOG_DAYS)
@@ -544,68 +612,7 @@ def main():
db.set_last_mr_updated_at(last_mr_updated_at)
for mr in fetch_recently_updated_mrs(last_mr_updated_at):
- iid = mr['iid']
-
- log(f"MR{iid} updated - processing")
- if mr['merged_at']:
- log(f"MR{iid} merged - skipping")
- continue
-
-
- # We jump through some hoops because the fetch_mr_patches
- # entry point on GitLab does not allow one
- # to specify a particular version. You can only get
- # a nice set of patches for the most current version.
- # Out of an abundance of caution, we make sure that the
- # last sha1 matches a known version and then we use that.
- updated_at = parse_gitlab_datetime(mr['updated_at'])
- patches = split_mbox_into_messages(fetch_mr_patches(iid))
- sha1s = [get_patch_sha1(patch) for patch in patches]
- versions = fetch_mr_versions(iid)
- version = get_mr_version(versions, sha1s[-1])
- if not version:
- # We can get an unusual case where we do not have a patch
- # to go with the start of the versions chain. Andrew
- # was able to do this in wine-demo MR 13, by somehow
- # having a commit message only commit. Arek believes that
- # this is not a sensible MR, and we should flag it as such.
- log(f"Error: MR {iid} patch {sha1s[-1]} not in versions")
- error = ("This merge request appears to be malformed.\n"
- "This can be caused by a commit with no content.\n"
- "Please check your merge request for errors.")
-
- if not error_in_notes(iid, error):
- post_note(iid, error)
- if not settings.READ_ONLY:
- db.set_last_mr_updated_at(updated_at)
- continue
-
- date = get_mr_version_date(versions, version)
- if not db.was_mr_version_processed(iid, version):
- nr_patches = len(patches)
- author = f"{mr['author']['name']} (@{mr['author']['username']})"
- cover = create_cover(mr['id'], iid, version, versions, nr_patches, author, mr['title'], mr['description'])
- if cover is None:
- log(f"MR{iid}v{version} - skipping, has no changes")
- continue
- fixup_date(cover, date)
- create_headers_from_mr(cover, mr)
- send_email(cover)
- if nr_patches <= settings.MAXIMUM_PATCHES:
- for nr, mail in enumerate(patches):
- fixup_patch(mail, iid, version, nr+1)
- date = date + datetime.timedelta(seconds=1)
- fixup_date(mail, date)
- fixup_version(mail, version)
- create_headers_from_mr(mail, mr)
- mail['References'] = create_reference(mr['id'], gitlab_hostname)
- log(f"MR{iid}v{version} - sent email")
- send_email(mail)
- if not settings.READ_ONLY:
- db.mark_mr_version_processed(iid, version)
- db.set_last_mr_updated_at(updated_at)
- else:
- log(f"MR{iid}v{version} - skipping, already processed")
+ process_mr(mr, not settings.READ_ONLY)
date = db.get_last_event_date()
if not date:
Module: tools
Branch: master
Commit: 25a13879d60b4d9c510942e13311d481c25d5b08
URL: https://source.winehq.org/git/tools.git/?a=commit;h=25a13879d60b4d9c510942e…
Author: Jeremy White <jwhite(a)codeweavers.com>
Date: Thu Apr 28 06:45:57 2022 -0500
Add a READ_ONLY flag to help with testing.
Signed-off-by: Alexandre Julliard <julliard(a)winehq.org>
---
gitlab/gitlab-to-mail/example.cfg | 4 ++++
gitlab/gitlab-to-mail/gitlabtomail.py | 19 ++++++++++++-------
gitlab/gitlab-to-mail/util.py | 2 +-
3 files changed, 17 insertions(+), 8 deletions(-)
diff --git a/gitlab/gitlab-to-mail/example.cfg b/gitlab/gitlab-to-mail/example.cfg
index 19e877c..a0fc59d 100644
--- a/gitlab/gitlab-to-mail/example.cfg
+++ b/gitlab/gitlab-to-mail/example.cfg
@@ -6,6 +6,10 @@ DATABASE= "/home/wine/example/db_example.sqlite"
# if True mails are dumped to mail_dump mbox file instead of being sent out
DEBUG = False
+# If True, no writes are done to the database. This allows a particular
+# condition to be run again and again
+#READ_ONLY = False
+
# https://docs.gitlab.com/ce/user/profile/personal_access_tokens.html with API access
GITLAB_TOKEN = "secret"
GITLAB_URL = "https://gitlab.example.com/"
diff --git a/gitlab/gitlab-to-mail/gitlabtomail.py b/gitlab/gitlab-to-mail/gitlabtomail.py
index dfe72e2..19eafa3 100755
--- a/gitlab/gitlab-to-mail/gitlabtomail.py
+++ b/gitlab/gitlab-to-mail/gitlabtomail.py
@@ -540,7 +540,8 @@ def main():
last_mr_updated_at = db.get_last_mr_updated_at()
if not last_mr_updated_at:
last_mr_updated_at = datetime.datetime.now() - datetime.timedelta(days=settings.INITIAL_BACKLOG_DAYS)
- db.set_last_mr_updated_at(last_mr_updated_at)
+ if not settings.READ_ONLY:
+ db.set_last_mr_updated_at(last_mr_updated_at)
for mr in fetch_recently_updated_mrs(last_mr_updated_at):
iid = mr['iid']
@@ -575,7 +576,8 @@ def main():
if not error_in_notes(iid, error):
post_note(iid, error)
- db.set_last_mr_updated_at(updated_at)
+ if not settings.READ_ONLY:
+ db.set_last_mr_updated_at(updated_at)
continue
date = get_mr_version_date(versions, version)
@@ -599,15 +601,17 @@ def main():
mail['References'] = create_reference(mr['id'], gitlab_hostname)
log(f"MR{iid}v{version} - sent email")
send_email(mail)
- db.mark_mr_version_processed(iid, version)
- db.set_last_mr_updated_at(updated_at)
+ if not settings.READ_ONLY:
+ db.mark_mr_version_processed(iid, version)
+ db.set_last_mr_updated_at(updated_at)
else:
log(f"MR{iid}v{version} - skipping, already processed")
date = db.get_last_event_date()
if not date:
date = (datetime.datetime.now() - datetime.timedelta(days=settings.INITIAL_BACKLOG_DAYS)).date()
- db.set_last_event_date(date)
+ if not settings.READ_ONLY:
+ db.set_last_event_date(date)
date = date - datetime.timedelta(days=1)
last_event_id = db.get_last_event_id()
@@ -624,8 +628,9 @@ def main():
log(f"sending email {mail['Subject']}")
send_email(mail)
- db.set_last_event_date(parse_gitlab_datetime(event['created_at']).date())
- db.set_last_event_id(event['id'])
+ if not settings.READ_ONLY:
+ db.set_last_event_date(parse_gitlab_datetime(event['created_at']).date())
+ db.set_last_event_id(event['id'])
if __name__ == "__main__":
diff --git a/gitlab/gitlab-to-mail/util.py b/gitlab/gitlab-to-mail/util.py
index e37ead8..aef43c6 100644
--- a/gitlab/gitlab-to-mail/util.py
+++ b/gitlab/gitlab-to-mail/util.py
@@ -31,7 +31,7 @@ class Settings:
else:
self.__dict__[s] = None
- for s in ['DEBUG']:
+ for s in ['DEBUG', 'READ_ONLY']:
if s in self.cp['settings']:
self.__dict__[s] = self.cp['settings'].getboolean(s)
else: