From 6c707e3875704f4d7128a68f3684fea42f87dd6f Mon Sep 17 00:00:00 2001 From: Lorenzo Pagliai <lorenzo.pagliai@seco.com> Date: Thu, 15 Dec 2022 14:54:46 +0100 Subject: [PATCH] Include integrate_into_layer.py script * The script is derived from the integrate_into_manifest file and is used to create the integration branch on layers * The script create a new branch with the modifications on the SRCREV.conf indicated as parameter and on the bitbake recipe indicated * In a future this file could be merged with the integrate_inot_manifest one --- scripts/integrate_into_layer.py | 233 ++++++++++++++++++++++++++++++++ 1 file changed, 233 insertions(+) create mode 100755 scripts/integrate_into_layer.py diff --git a/scripts/integrate_into_layer.py b/scripts/integrate_into_layer.py new file mode 100755 index 0000000..03e4e82 --- /dev/null +++ b/scripts/integrate_into_layer.py @@ -0,0 +1,233 @@ +#!/usr/bin/env python3 +import common + +import argparse +import logging +import sys +import tempfile +import re +from pathlib import Path +from furl import furl +from git import GitCommandError, Repo +from gitlab import Gitlab +from gitlab.v4.objects import Project +from lxml import etree + +def update_srcrev(srcrev, recipe_name, new_revision): + # Check if project is referenced in SRCREV.conf + # Match "...RECIPE_NAME =" + pattern = re.compile("{}[ ,\t]{{0,}}?=".format(recipe_name)) + + project_line = None + for line in srcrev.splitlines(): + if pattern.search(line): + project_line = line + break + if project_line is None: + return None + + # Get current project revision from SRCREV file + # Assuming notation: <project> = "<hash>" + old_revision = project_line.split('"')[1] + + # Update SRCREV file + srcrev = srcrev.replace(old_revision, new_revision) + return srcrev + + +def integrate_into_layer( + layer_project: Project, + layer_branch, + srcrev_file, + recipe_name, + project: Project, + merge_request, +): + gitlab = layer_project.manager.gitlab + + with tempfile.TemporaryDirectory() as layer_dir: + srcrev_filepath = Path(layer_dir) / srcrev_file + + # Construct clone url containing access token + clone_url = furl(layer_project.http_url_to_repo) + print("This is the url to clone", clone_url.url) + clone_url.username = "gitlab-ci" + clone_url.password = gitlab.private_token + + # Checkout layer + # TODO replace checkout with gitlab api access + print("Cloning layer repo: %s" % layer_project.http_url_to_repo) + try: + layer_repo = Repo.clone_from( + clone_url.url, layer_dir, branch=layer_branch + ) + except GitCommandError as e: + sys.exit("ERROR: could not clone layer repository\n" + str(e)) + except IndexError: + sys.exit("ERROR: branch '%s' not found" % layer_branch) + + # Special handling for the gitlab-ci integration + # When the branch 'merge_request.source_branch' already starts with + # integrate/gitlab-ci we add our new commit to this branch + # Otherwise (normal behaviour) a new integration branch is created + integration_branch = common.find_gitlab_ci_integration_branch( + layer_repo, merge_request.source_branch + ) + + if integration_branch is not None: + layer_repo.git.checkout( + "-b", integration_branch, "origin/{}".format(integration_branch) + ) + logging.debug("Heads: %s", layer_repo.heads) + layer_repo.heads[integration_branch].checkout() + logging.debug(layer_repo.git.log("--oneline", "-n", "5")) + print("Using existing integration branch: %s" % integration_branch) + else: + # Create integration branch (delete former one if already exists) + integration_branch = recipe_name + "/" + merge_request.source_branch + for ref in layer_repo.references: + if integration_branch == ref.name: + layer_repo.delete_head(ref) + + print("Creating integration branch: %s" % integration_branch) + layer_repo.head.set_reference( + layer_repo.create_head(integration_branch) + ) + with open('integration_branch_file', "w", encoding="utf-8") as file: + file.write(integration_branch) + # Get new project revision from merge request + new_revision = merge_request.sha + + with open(srcrev_filepath, "r", encoding="utf8") as fp: + srcrev = fp.read() + new_srcrev = update_srcrev(srcrev, recipe_name, new_revision) + # write file + if new_srcrev is None: + sys.exit( + "ERROR: project '%s' not found in layer and " + "no recipe name is specified" % project.path + ) + with open(srcrev_filepath.as_posix(), "w", encoding="utf8") as fp: + fp.write(new_srcrev) + layer_repo.index.add([srcrev_file]) + + # Make an API request to create the gitlab.user object + gitlab.auth() + + # Construct commit message and commit the change + message = "Integrate %s/%s\n%s" % ( + project.path, + merge_request.source_branch, + common.list_commits(merge_request.commits()), + ) + layer_revision = common.commit_and_push( + layer_project, + layer_repo, + message, + gitlab.user.username, + gitlab.user.email, + ) + logging.debug("New revision in layer: %s", layer_revision) + + return layer_revision + + +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 for the layer group""", + dest="token", + required=True, + ) + parser.add_argument( + "--layer-project", + help="""name of the yocto layer project""", + dest="layer_project", + required=True, + ) + parser.add_argument( + "--layer-branch", + help="""yocto layer branch to branch off from""", + dest="layer_branch", + required=True, + ) + 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( + "--recipe-name", + help="""recipe name to resolve project in 'SRCREV.conf'""", + dest="recipe_name", + default=None, + required=False, + ) + parser.add_argument( + "--project", + help="""name of the project, as specified in the layer""", + dest="project", + required=True, + ) + parser.add_argument( + "--merge-request", + help="""project merge request IID or link containing the + changes to be integrated""", + dest="merge_request", + required=True, + ) + parser.add_argument( + "--save-revision-to", + help="""path to a file where the new layer revision is stored""", + dest="revision_file", + required=False, + ) + parser.add_argument( + "-v", + "--verbose", + action="store_true", + help="""Increase verbosity.""", + ) + + args, _ = parser.parse_known_args() + if args.verbose: + logging.basicConfig(level=logging.DEBUG) + + logging.debug(args) + gitlab = Gitlab(args.gitlab_url, private_token=args.token) + layer_project = common.get_project(gitlab, args.layer_project) + + project = common.get_project(gitlab, args.project) + logging.debug("Project: %s", project.name) + logging.debug("Merge Request: %s", args.merge_request) + + gitlab = Gitlab(args.gitlab_url, private_token=args.token) + + merge_request = common.get_merge_request(project, args.merge_request) + if merge_request is None: + sys.exit("ERROR: could not get %s %s" % (project.name, args.merge_request)) + + layer_revision = integrate_into_layer( + layer_project=layer_project, + layer_branch=args.layer_branch, + srcrev_file=args.srcrev_file, + recipe_name=args.recipe_name, + project=project, + merge_request=merge_request, + ) + + if args.revision_file: + with open(args.revision_file, "w", encoding="utf-8") as file: + file.write(layer_revision) + +if __name__ == "__main__": + main() -- GitLab