From aaf6331d3225933301e9043d8e87e1b056912d0e Mon Sep 17 00:00:00 2001
From: Tim Jaacks <tim.jaacks@garz-fricke.com>
Date: Tue, 1 Dec 2020 11:55:39 +0100
Subject: [PATCH] Move merge_into_manifest to dedicated function and add some
 documentation

---
 merge_into_manifest.py | 237 ++++++++++++++++++++++-------------------
 1 file changed, 128 insertions(+), 109 deletions(-)

diff --git a/merge_into_manifest.py b/merge_into_manifest.py
index 7f530d63..6073067c 100755
--- a/merge_into_manifest.py
+++ b/merge_into_manifest.py
@@ -12,6 +12,125 @@ from get_merge_requests import get_merge_requests
 from integrate_into_manifest import integrate_into_manifest
 
 
+def merge_into_manifest(gitlab, manifest_project, master_branch, project, commit):
+    """
+    Create a merge request on the manifest for a given merged project commit and merge
+    it immediately. Update the integration branch before, if it is not up to date with
+    the current manifest master.
+    """
+
+    # Get source merge request
+    mrs = get_merge_requests(
+        gitlab,
+        project,
+        target_branch=master_branch,
+        state="merged",
+        commit=commit,
+    )
+    if not mrs:
+        sys.exit(
+            "ERROR: could not determine source merge request for commit %s" % commit
+        )
+    source_mr = mrs[0]
+
+    # Get original branch for commit
+    original_branch = source_mr.source_branch
+    integration_branch = common.integration_branch_name(project.name, original_branch)
+    target_branch = master_branch
+
+    # Check if merge request already exists
+    mrs = get_merge_requests(
+        gitlab,
+        manifest_project,
+        source_branch=integration_branch,
+        target_branch=target_branch,
+        state="opened",
+    )
+    # If there already is a merge request, we assume that it has been opened manually
+    # and thus will be merged manually as well, so we exit with a failure here.
+    if mrs:
+        sys.exit("ERROR: There is already an open merge request:\n%s" % mrs[0].web_url)
+
+    # Check if branches exist
+    try:
+        manifest_project.branches.get(integration_branch)
+    except GitlabGetError:
+        sys.exit("ERROR: source branch '%s' does not exist." % integration_branch)
+    try:
+        manifest_project.branches.get(target_branch)
+    except GitlabGetError:
+        sys.exit("ERROR: target branch '%s' does not exist." % target_branch)
+
+    # Create new merge request
+    mr = manifest_project.mergerequests.create(
+        {
+            "source_branch": integration_branch,
+            "target_branch": target_branch,
+            "remove_source_branch": True,
+            "title": "Merge " + integration_branch,
+        }
+    )
+
+    print("Created new merge request:")
+    print(mr.web_url)
+
+    # Insert cross-links in both merge requests
+    mr.notes.create({"body": "Source merge request: %s" % source_mr.web_url})
+    source_mr.notes.create({"body": "Integration merge request: %s" % mr.web_url})
+
+    # Attempt to merge, reintegrate if necessary
+    manifest_revision = mr.sha
+    merged = False
+    while not merged:
+        merged = accept_merge_request(gitlab, manifest_project, mr)
+        if not merged:
+            # Note: if reintegration is necessary here, the source merge request was
+            # merged without running the pipeline on the latest manifest, i.e. some
+            # other project has been merged in between. Automatic rebase is not
+            # possible, though, because the altered manifest lines are part of the
+            # diff context, so we have to create a new integration commit. This
+            # possibly might result in a failing pipeline state on the master. We
+            # deliberately put up with this, as it is quite unlikely to happen and
+            # we want a completely automated process in every case.
+            manifest_revision = integrate_into_manifest(
+                gitlab=gitlab,
+                manifest_project=manifest_project,
+                integration_base=target_branch,
+                manifest_file=common.manifest_file,
+                project=project,
+                branch=original_branch,
+                commit=commit,
+            )
+
+    print("Successfully merged")
+
+    # Check if there is a running pipeline on the integration branch and cancel it.
+    # This happens when a reintegration was necessary before merging. The instant
+    # merge afterwards deletes the integration branch, so the pipeline on the branch
+    # will fail, because repo cannot checkout the code anymore. In order not to
+    # confuse people, we better cancel it. Since there is a pipeline running on the
+    # master after merging, we don't need the branch results anyway.
+    try:
+        pipelines = manifest_project.pipelines.list(
+            sha=manifest_revision,
+            ref=integration_branch,
+            retry_transient_errors=True,
+        )
+    except GitlabGetError:
+        print("WARNING: could not list pipelines for project '%s'" % project.name)
+    if pipelines:
+        pipeline = pipelines[0]
+        if pipeline.status in common.pending_states:
+            pipeline.cancel()
+            print(
+                "Cancelling running pipeline for integration branch '%s':"
+                % integration_branch
+            )
+            print(pipeline.web_url)
+
+    return manifest_revision
+
+
 def main():
     parser = argparse.ArgumentParser()
     parser.add_argument(
@@ -63,118 +182,18 @@ def main():
     project = common.get_project(gitlab, args.project)
     manifest_project = common.get_project(gitlab, args.manifest_project)
 
-    # Get source merge request
-    mrs = get_merge_requests(
-        gitlab,
-        project,
-        target_branch=args.master_branch,
-        state="merged",
+    manifest_revision = merge_into_manifest(
+        gitlab=gitlab,
+        manifest_project=manifest_project,
+        master_branch=args.master_branch,
+        project=project,
         commit=args.commit,
     )
-    if not mrs:
-        sys.exit(
-            "ERROR: could not determine source merge request for commit %s"
-            % args.commit
-        )
-    source_mr = mrs[0]
 
-    # Get original branch for commit
-    original_branch = source_mr.source_branch
-    integration_branch = common.integration_branch_name(project.name, original_branch)
-    target_branch = args.master_branch
-
-    # Check if merge request already exists
-    mrs = get_merge_requests(
-        gitlab,
-        manifest_project,
-        source_branch=integration_branch,
-        target_branch=target_branch,
-        state="opened",
-    )
-    if mrs:
-        sys.exit("ERROR: There is already an open merge request:\n%s" % mrs[0].web_url)
-
-    with gitlab:
-
-        # Check if branches exist
-        try:
-            manifest_project.branches.get(integration_branch)
-        except GitlabGetError:
-            sys.exit("ERROR: source branch '%s' does not exist." % integration_branch)
-        try:
-            manifest_project.branches.get(target_branch)
-        except GitlabGetError:
-            sys.exit("ERROR: target branch '%s' does not exist." % target_branch)
-
-        # Create new merge request
-        mr = manifest_project.mergerequests.create(
-            {
-                "source_branch": integration_branch,
-                "target_branch": target_branch,
-                "remove_source_branch": True,
-                "title": "Merge " + integration_branch,
-            }
-        )
-
-        print("Created new merge request:")
-        print(mr.web_url)
-
-        # Insert cross-links in both merge requests
-        mr.notes.create({"body": "Source merge request: %s" % source_mr.web_url})
-        source_mr.notes.create({"body": "Integration merge request: %s" % mr.web_url})
-
-        # Attempt to merge, reintegrate if necessary
-        manifest_revision = mr.sha
-        merged = False
-        while not merged:
-            merged = accept_merge_request(gitlab, manifest_project, mr)
-            if not merged:
-                # Note: if reintegration is necessary here, the source merge request was
-                # merged without running the pipeline on the latest manifest, i.e. some
-                # other project has been merged in between. Automatic rebase is not
-                # possible, though, because the altered manifest lines are part of the
-                # diff context, so we have to create a new integration commit. This
-                # possibly might result in a failing pipeline state on the master.
-                manifest_revision = integrate_into_manifest(
-                    gitlab=gitlab,
-                    manifest_project=manifest_project,
-                    integration_base=target_branch,
-                    manifest_file=common.manifest_file,
-                    project=project,
-                    branch=original_branch,
-                    commit=args.commit,
-                )
-
-        print("Successfully merged")
-
-        # Check if there is a running pipeline on the integration branch and cancel it.
-        # This happens when a reintegration was necessary before merging. The instant
-        # merge afterwards deletes the integration branch, so the pipeline on the branch
-        # will fail, because repo cannot checkout the code anymore. In order not to
-        # confuse people, we better cancel it. Since there is a pipeline running on the
-        # master after merging, we don't need the branch results anyway.
-        try:
-            pipelines = manifest_project.pipelines.list(
-                sha=manifest_revision,
-                ref=integration_branch,
-                retry_transient_errors=True,
-            )
-        except GitlabGetError:
-            print("WARNING: could not list pipelines for project '%s'" % project.name)
-        if pipelines:
-            pipeline = pipelines[0]
-            if pipeline.status in common.pending_states:
-                pipeline.cancel()
-                print(
-                    "Cancelling running pipeline for integration branch '%s':"
-                    % integration_branch
-                )
-                print(pipeline.web_url)
-
-        # Write new manifest revision to file for next pipeline stage
-        if args.revision_file:
-            with open(args.revision_file, "w") as file:
-                file.write(manifest_revision)
+    # Write new manifest revision to file for next pipeline stage
+    if args.revision_file:
+        with open(args.revision_file, "w") as file:
+            file.write(manifest_revision)
 
 
 if __name__ == "__main__":
-- 
GitLab