From c8d434efb4e61d9b9239b43081d72df56208cd9f Mon Sep 17 00:00:00 2001
From: Lorenzo Pagliai <lorenzo.pagliai@seco.com>
Date: Wed, 21 Feb 2024 15:27:48 +0100
Subject: [PATCH] [INTEGRATION][SETUP] Insert script for branch setup

The objective of the script inserted in this commit is to:
* Automate the setup of new protected main branches by creating
them within a specific group and/or in a list of projects chosen by the user.
* Automate the configuration of the variables required to enable integration via CI/CD,
i.e. INTEGRATION variable.
* Automate the creation of a manifest branch having the same name with all
variables configured correctly.
* Automate the deletion of these branches and the cleanup of the variables
if it is no longer necessary to use the branches.

The script is not integrated within the pipeline flow but to be run
on spot when a new version of Yocto is passed to support, and/or for the creation
of new branches that require development of new functionality.
---
 scripts/create_delete_integration_branch.py | 186 ++++++++++++++++++++
 1 file changed, 186 insertions(+)
 create mode 100644 scripts/create_delete_integration_branch.py

diff --git a/scripts/create_delete_integration_branch.py b/scripts/create_delete_integration_branch.py
new file mode 100644
index 0000000..e02187d
--- /dev/null
+++ b/scripts/create_delete_integration_branch.py
@@ -0,0 +1,186 @@
+import gitlab
+import argparse
+
+def create_new_branch(project, start_branch, end_branch):
+    try:
+        project.branches.create({'branch': end_branch, 'ref': start_branch})
+        project.protectedbranches.create({'name': end_branch, 'push_access_level': gitlab.MAINTAINER_ACCESS})
+        print(f"Successfully created branch '{end_branch}' from '{start_branch}' in project '{project.name}'")
+    except gitlab.exceptions.GitlabCreateError as e:
+        print(f"Failed to create branch '{end_branch}' from '{start_branch}' in project '{project.name}': {e}")
+    except Exception as e:
+        print(f"An error occurred while creating branch '{end_branch}' from '{start_branch}' in project '{project.name}': {e}")
+
+def delete_branch(project, branch_name):
+    try:
+        project.branches.delete(branch_name)
+        project.protectedbranches.get(branch_name).delete()
+        print(f"Successfully deleted branch '{branch_name}' in project '{project.name}'")
+    except gitlab.exceptions.GitlabCreateError as e:
+        print(f"Failed to delete branch '{branch_name}'' in project '{project.name}': {e}")
+    except Exception as e:
+        print(f"An error occurred while deleting branch '{branch_name}' in project '{project.name}': {e}")
+
+def update_variable(project, variable_name, action, value):
+    try:
+        variables = project.variables.list(all=True)
+        for variable in variables:
+            if variable.key == variable_name:
+                if action == 'create':
+                    variable.value += f'\n{value}'
+                elif action == 'delete':
+                    variable.value = variable.value.replace(value, '')
+                    # Remove any trailing newline characters
+                    variable.value = variable.value.rstrip('\n')
+                variable.save()
+                print(f"Successfully updated variable '{variable_name}' in project '{project.name}'")
+                break
+    except gitlab.exceptions.GitlabUpdateError as e:
+        print(f"Failed to update variable '{variable_name}' in project '{project.name}': {e}")
+    except Exception as e:
+        print(f"An error occurred while updating variable '{variable_name}' in project '{project.name}': {e}")
+
+def update_ci_variable(manifest_repo, start_branch, end_branch):
+    try:
+        # Fetch the .gitlab-ci.yml file
+        file = manifest_repo.files.get(file_path='.gitlab-ci.yml', ref=end_branch)
+        content = file.decode().decode('utf-8')
+
+        # Update the value of the MASTER_BRANCH variable
+        new_content = content.replace(f'MASTER_BRANCH: {start_branch}', f'MASTER_BRANCH: {end_branch}')
+
+        # Commit the changes perfomrm inside .gitlab-ci.yml
+        commit_data = {
+            'branch': end_branch,
+            'commit_message': f"Update MASTER_BRANCH variable in .gitlab-ci.yml to {end_branch}",
+            'actions': [
+                {
+                    'action': 'update',
+                    'file_path': '.gitlab-ci.yml',
+                    'content': new_content,
+                    'encoding': 'text'
+                }
+            ]
+        }
+        manifest_repo.commits.create(commit_data)
+        print(f"Successfully updated .gitlab-ci.yml in {manifest_repo.name} to use {end_branch}")
+    except Exception as e:
+        print(f"Error updating .gitlab-ci.yml in {manifest_repo.name}: {str(e)}")
+
+def main():
+
+    parser = argparse.ArgumentParser(description="GitLab Branch and CI/CD Variable Manager")
+    
+    parser.add_argument(
+        "--url",
+        help="URL of the GitLab instance",
+        dest="gitlab_url",
+        required=True,
+    )
+    parser.add_argument(
+        "--token",
+        help="""GitLab REST API private access token""",
+        dest="access_token",
+        required=True,
+    )
+    parser.add_argument(
+        "--group",
+        help="Name of the GitLab group where the repo that integrates into the manifest are located",
+        dest="group_name",
+        required=True,
+    )
+    parser.add_argument(
+        "--other-repos",
+        nargs='+',
+        help="Name of other GitLab repositories external to the group",
+        dest="other_repos",
+        required=True,
+    )
+    parser.add_argument(
+        "--start-branch",
+        help="Name of the starting branch",
+        dest="start_branch",
+        required=True,
+    )
+    parser.add_argument(
+        "--end-branch",
+        help="Name of the ending branch",
+        dest="end_branch",
+        required=True,
+    )
+    parser.add_argument(
+        "--manifest-repo",
+        help="Name of the manifest repository",
+        dest="manifest_repo",
+        required=True,
+    )
+    parser.add_argument(
+        "--variable",
+        help="Name of the CI/CD variable to be changed",
+        dest="variable_name",
+        required=True,
+    )
+    parser.add_argument(
+        "--action",
+        help="Action to perform: 'create' or 'delete'",
+        dest="action",
+        required=True,
+    )
+
+    args = parser.parse_args()
+
+    gl = gitlab.Gitlab(args.gitlab_url, private_token=args.access_token)
+    gl.auth()
+
+    group = gl.groups.get(args.group_name)
+
+    # Iterate over projects in the group
+    for group_project in group.projects.list(all=True, include_subgroups=True):
+        project = gl.projects.get(group_project.id)
+        print(project.name)
+        ## Layers projects first, then separately manifest repo
+        if not "manifest" in project.name:
+            if args.action == 'create':
+                # Create new branch in project
+                create_new_branch(project, args.start_branch, args.end_branch)
+            elif args.action == 'delete':
+                # Delete old branch in project
+                delete_branch(project, args.end_branch)
+            # Update CI/CD variable
+            value = f"{args.end_branch}:{args.manifest_repo}:{args.end_branch}"
+            update_variable(project, args.variable_name, args.action, value)
+
+    # Iterate over spot projects in other groups
+    for project in args.other_repos:
+        project_group_name, project_name = project.rsplit('/', 1)
+        project_group = gl.groups.get(project_group_name)
+        project_project_id = project_group.projects.list(search=project_name)[0]
+        project_repo = gl.projects.get(project_project_id.id)
+        print(project_repo.name)
+        if args.action == 'create':
+            # Create new branch in project
+            create_new_branch(project_repo, args.start_branch, args.end_branch)
+        elif args.action == 'delete':
+            # Delete old branch in project
+            delete_branch(project_repo, args.end_branch)
+        # Update CI/CD variable
+        value = f"{args.end_branch}:{args.manifest_repo}:{args.end_branch}"
+        update_variable(project_repo, args.variable_name, args.action, value)       
+
+    # Performing modifications on manifest repo
+    manifest_group_name, manifest_name = args.manifest_repo.rsplit('/', 1)
+    manifest_group = gl.groups.get(manifest_group_name)
+    manifest_project_id = manifest_group.projects.list(search=manifest_name)[0]
+    manifest_project = gl.projects.get(manifest_project_id.id)
+    
+    if args.action == 'create':
+        # Create new branch in project
+        create_new_branch(manifest_project, args.start_branch, args.end_branch)
+        # Change .gitlab-ci.yml MASTER_BRANCH reference
+        update_ci_variable(manifest_project, args.start_branch, args.end_branch)
+    elif args.action == 'delete':
+        # Delete old branch in project
+         delete_branch(manifest_project, args.end_branch)
+
+if __name__ == "__main__":
+    main()
-- 
GitLab