From 25b37b9207dbb527c933885c3d08e496eba7d240 Mon Sep 17 00:00:00 2001
From: Lorenzo Pagliai <lorenzo.pagliai@seco.com>
Date: Thu, 15 Dec 2022 12:34:36 +0100
Subject: [PATCH] Insert new script for .gitlab-ci submodule integration

* The script is identical to the 'scripts/deploy_gitlab_ci_projects.py'
apart from the fact that does look for a manifest project to
integrate/merge
* In the future the two scripts may be merged to treat both cases
correctly, for now we skip this part
---
 scripts/deploy_gitlab_ci_projects.py | 307 +++++++++++++++++++++++++++
 1 file changed, 307 insertions(+)
 create mode 100755 scripts/deploy_gitlab_ci_projects.py

diff --git a/scripts/deploy_gitlab_ci_projects.py b/scripts/deploy_gitlab_ci_projects.py
new file mode 100755
index 0000000..25d5f96
--- /dev/null
+++ b/scripts/deploy_gitlab_ci_projects.py
@@ -0,0 +1,307 @@
+#!/usr/bin/env python3
+import common
+
+import argparse
+import logging
+import sys
+import os
+from gitlab import Gitlab
+from gitlab.v4.objects import Project, MergeRequest
+
+from accept_merge_request import accept_merge_request
+from create_merge_request import create_merge_request
+from get_integration_sources import get_integration_sources
+from get_merge_requests import get_merge_requests
+from update_submodule import update_submodule_and_include_ref
+from integrate_into_manifest import update_manifest, update_srcrev
+
+from ruamel.yaml import YAML
+
+
+def read_keys_from_gitlab_ci_yml(gitlab_ci_yml):
+
+    # Read values from existing file
+    yaml = YAML()
+    data = yaml.load(gitlab_ci_yml)
+    logging.debug("Yaml: %s", data)
+
+    try:
+        recipe = data["variables"]["BB_RECIPE_NAME"]
+        logging.debug("Recipe %s", recipe)
+    except KeyError:
+        recipe = None
+    return {"recipe": recipe}
+
+
+def integrate_submodule_into(
+    gitlab,
+    project_name,
+    submodule_name,
+    new_revision,
+    branch,
+    commit_and_push=True,
+    force_clone=False,
+):
+
+    gitlab_project = common.get_project(gitlab, project_name)
+
+    (
+        project_repo,
+        project_dir,
+        integration_branch_name,
+        integration_commit,
+        message,
+    ) = update_submodule_and_include_ref(
+        gitlab_project,
+        submodule_name,
+        new_revision,
+        branch,
+        commit_and_push=commit_and_push,
+        force_clone=force_clone,
+    )
+    # ======================================
+    # Store the references for creating the integration
+    # commit in the manifest later
+    # ======================================
+    ret = {
+        "project": gitlab_project,
+        "repo": project_repo,
+        "dir": project_dir,
+        "integration_branch": integration_branch_name,
+        "master_branch": branch,
+        "commit": integration_commit,
+        "message": message,
+    }
+    logging.debug(
+        "Integration branch: %s (%s)",
+        integration_branch_name,
+        integration_commit,
+    )
+    return ret
+
+
+def create_integration_merge_request(
+    project: Project,
+    integration_branch: str,
+    target_branch: str,
+    source_mr: MergeRequest = None,
+) -> MergeRequest:
+    # Create merge request
+    # This should be optional
+    mr, created = create_merge_request(project, integration_branch, target_branch)
+    if created:
+        if source_mr is not None:
+            common.crosslink_merge_requests(source_mr, mr)
+        print("Created new merge request:\n%s" % mr.web_url)
+    else:
+        print("Existing integration merge request:\n%s" % mr.web_url)
+    return mr
+
+
+def main():
+    parser = argparse.ArgumentParser()
+    parser.add_argument(
+        "--gitlab-url",
+        help="""URL to the GitLab instance""",
+        dest="gitlab_url",
+        required=True,
+    )
+    parser.add_argument(
+        "--token",
+        help="""GitLab REST API private access token""",
+        dest="token",
+        required=True,
+    )
+    parser.add_argument(
+        "--manifest-project",
+        help="""name of the manifest project""",
+        dest="manifest_project",
+        required=True,
+    )
+    parser.add_argument(
+        "--manifest-branch",
+        help="""manifest branch to integrate changes into (can be a comma-separated list)""",
+        dest="manifest_branch",
+        required=True,
+    )
+    parser.add_argument(
+        "--submodule",
+        help="""submodule to update""",
+        dest="submodule",
+        required=True,
+    )
+    parser.add_argument(
+        "--revision",
+        help="""new revision for submodule""",
+        dest="revision",
+        required=True,
+    )
+    parser.add_argument(
+        "--merge",
+        help="""if set, perform merge after integration""",
+        dest="merge",
+        action="store_true",
+        required=False,
+        default=False,
+    )
+    parser.add_argument(
+        "--project",
+        help="""gitlab-ci project path or id""",
+        dest="project",
+        default=os.environ.get("CI_PROJECT_PATH"),
+        required=False,
+    )
+    parser.add_argument(
+        "--branch",
+        help="""gitlab-ci branch that we're merging into""",
+        dest="branch",
+        default="master",
+        required=False,
+    )
+    parser.add_argument(
+        "--manifest-file",
+        help="""manifest file name (default: 'default.xml')""",
+        dest="manifest_file",
+        default=common.manifest_file,
+        required=False,
+    )
+    parser.add_argument(
+        "--srcrev-file",
+        help="""source revision file name (default: 'SRCREV.conf')""",
+        dest="srcrev_file",
+        default=common.srcrev_file,
+        required=False,
+    )
+    parser.add_argument(
+        "--group",
+        help="""group path or id to limit search scope to""",
+        dest="group",
+        required=True,
+    )
+    parser.add_argument(
+        "-v",
+        "--verbose",
+        action="store_true",
+        help="""Increase verbosity.""",
+    )
+
+    args, _ = parser.parse_known_args()
+    if args.verbose:
+        logging.basicConfig(
+            level=logging.DEBUG,
+            format="%(asctime)s %(levelname)-8s %(message)s",
+            datefmt="%H:%M:%S",
+        )
+
+    manifest_branches = args.manifest_branch.split(",")
+
+    gitlab = Gitlab(args.gitlab_url, private_token=args.token)
+    group = gitlab.groups.get(args.group)
+
+    # =======================================================
+    # Create integration branches and commits with updates
+    # submodule in all projects
+    # =======================================================
+    integration_sources = {}
+    all_integration_sources = []
+    for manifest_branch in manifest_branches:
+        print(
+            "Searching for projects in %s that are configured for automatic integration into %s:%s"
+            % (args.group, args.manifest_project, manifest_branch)
+        )
+        integration_sources[manifest_branch] = get_integration_sources(
+            args.manifest_project, manifest_branch, group
+        )
+        for s in integration_sources[manifest_branch]:
+            if s not in all_integration_sources:
+                all_integration_sources.append(s)
+
+    # Update submodule in all integration sources
+    project_integrations = []
+    for s in all_integration_sources:
+        print("Create integration commit in %s:%s" % (s["project"], s["branch"]))
+
+        integration = integrate_submodule_into(
+            gitlab, s["project"], args.submodule, args.revision, s["branch"]
+        )
+        # Store in the list if commit is set (meaning there was an update or
+        #   an exising integration branch)
+        if integration["commit"] is not None:
+            project_integrations.append(integration)
+
+    # Update submodule in all manifest branches
+    manifest_integrations = []
+    for manifest_branch in manifest_branches:
+        print(
+            "Create integration commit in %s:%s"
+            % (args.manifest_project, manifest_branch),
+        )
+        manifest_integrations.append(
+            integrate_submodule_into(
+                gitlab,
+                args.manifest_project,
+                args.submodule,
+                args.revision,
+                manifest_branch,
+                commit_and_push=False,
+                force_clone=True,
+            )
+        )
+
+    # =======================================================
+    # Create and merge merge_requests if needed
+    # =======================================================
+    if args.merge:
+        # Get source merge request (the one in the gitlab-ci repo)
+        gitlab_ci_project = common.get_project(gitlab, args.project)
+        mrs = get_merge_requests(
+            project=gitlab_ci_project,
+            target_branch=args.branch,
+            state="merged",
+            commit=args.revision,
+        )
+        if not mrs:
+            sys.exit(
+                "ERROR: could not determine source merge request for commit %s"
+                % args.revision
+            )
+        source_mr = mrs[0]
+
+        for project_integration in project_integrations:
+            logging.debug("Create MR in %s", project_integration["project"].name)
+            mr = create_integration_merge_request(
+                project_integration["project"],
+                project_integration["integration_branch"],
+                project_integration["master_branch"],
+                source_mr,
+            )
+            # Now merge
+            logging.debug("Merge %s!%s", project_integration["project"], mr.iid)
+
+            # Wait until GitLab has checked merge status
+            common.wait_until_merge_status_is_set(project_integration["project"], mr)
+
+            # Attempt to merge
+            merged, integration_commit = accept_merge_request(
+                project_integration["project"], mr, rebase=True
+            )
+            # if this has rebased the integration commit needs to be adapted:
+            project_integration["commit"] = integration_commit
+            # Save the target branch here, as the source branch gets deleted
+            # during merge
+            project_integration["integration_branch"] = mr.target_branch
+
+            if not merged:
+                sys.exit(
+                    "Integration MR could not be merged:\n"
+                    "%s\n"
+                    "This can probably be resolved by creating a new commit in "
+                    "gitlab-ci and merging it. The above MR can be closed then."
+                    % mr.web_url
+                )
+
+    if not args.merge:
+        sys.exit(0)
+
+if __name__ == "__main__":
+    main()
-- 
GitLab