Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • seco-ne/yocto/infrastructure/gitlab-ci
1 result
Show changes
File moved
...@@ -239,6 +239,7 @@ def clone_project(project: Project, into, branch=None): ...@@ -239,6 +239,7 @@ def clone_project(project: Project, into, branch=None):
def get_repository_file_raw(project: Project, filename, ref=None): def get_repository_file_raw(project: Project, filename, ref=None):
# TODO tree objects are not supported # TODO tree objects are not supported
fileobj = get_repository_file_obj(project, filename, ref) fileobj = get_repository_file_obj(project, filename, ref)
logging.debug("Read file '%s' from '%s' at ref %s", filename, project.name, ref)
return project.repository_raw_blob( return project.repository_raw_blob(
fileobj["id"], retry_transient_errors=True fileobj["id"], retry_transient_errors=True
).decode() ).decode()
...@@ -253,7 +254,7 @@ def get_repository_file_obj(project: Project, filename, ref=None): ...@@ -253,7 +254,7 @@ def get_repository_file_obj(project: Project, filename, ref=None):
repository_tree = project.repository_tree( repository_tree = project.repository_tree(
ref=ref, all=True, retry_transient_errors=True ref=ref, all=True, retry_transient_errors=True
) )
logging.debug(repository_tree) # logging.debug(repository_tree)
fileobj = [f for f in repository_tree if f["name"] == filename] fileobj = [f for f in repository_tree if f["name"] == filename]
if len(fileobj) == 0: if len(fileobj) == 0:
......
...@@ -40,7 +40,13 @@ def read_keys_from_gitlab_ci_yml(gitlab_ci_yml): ...@@ -40,7 +40,13 @@ def read_keys_from_gitlab_ci_yml(gitlab_ci_yml):
def integrate_submodule_into( def integrate_submodule_into(
gitlab, project_name, submodule_name, new_revision, branch, commit_and_push=True gitlab,
project_name,
submodule_name,
new_revision,
branch,
commit_and_push=True,
force_clone=False,
): ):
gitlab_project = common.get_project(gitlab, project_name) gitlab_project = common.get_project(gitlab, project_name)
...@@ -56,9 +62,8 @@ def integrate_submodule_into( ...@@ -56,9 +62,8 @@ def integrate_submodule_into(
new_revision, new_revision,
branch, branch,
commit_and_push=commit_and_push, commit_and_push=commit_and_push,
force_clone=force_clone,
) )
if integration_branch_name is None:
return None
# ====================================== # ======================================
# Store the references for creating the integration # Store the references for creating the integration
# commit in the manifest later # commit in the manifest later
...@@ -185,7 +190,9 @@ def main(): ...@@ -185,7 +190,9 @@ def main():
res = integrate_submodule_into( res = integrate_submodule_into(
gitlab, p, args.submodule, args.revision, args.branch gitlab, p, args.submodule, args.revision, args.branch
) )
if res is not None: # Store in the list if commit is set (meaning there was an update or
# an exising integration branch)
if res["commit"] is not None:
project_integration[p] = res project_integration[p] = res
# Update submodule in manifest project # Update submodule in manifest project
...@@ -196,22 +203,19 @@ def main(): ...@@ -196,22 +203,19 @@ def main():
args.revision, args.revision,
args.branch, args.branch,
commit_and_push=False, commit_and_push=False,
force_clone=True,
) )
if manifest_project is not None:
project_integration[args.project] = manifest_project
# If submodule is already at specified revision in all projects, exit successfully
if len(project_integration) == 0:
print("No integration done, changes are already included in all projects.")
sys.exit(0)
branch = args.branch
if branch is None:
branch = manifest_project["project"].default_branch
# ======================================================= # =======================================================
# Create and merge merge_requests if needed # Create and merge merge_requests if needed
# ======================================================= # =======================================================
if args.merge: if args.merge:
# Get source merge request ( the one in the gitlab-ci repo) # Get source merge request ( the one in the gitlab-ci repo)
submodule_project_path, _ = get_submodule_project_path_and_revision( submodule_project_path, _ = get_submodule_project_path_and_revision(
manifest_project["project"], args.submodule, args.branch manifest_project["project"], args.submodule, branch
) )
submodule_project = common.get_project(gitlab, submodule_project_path) submodule_project = common.get_project(gitlab, submodule_project_path)
mrs = get_merge_requests( mrs = get_merge_requests(
...@@ -246,6 +250,9 @@ def main(): ...@@ -246,6 +250,9 @@ def main():
) )
# if this has rebased the integration commit needs to be adapted: # if this has rebased the integration commit needs to be adapted:
project_integration[p]["commit"] = integration_commit project_integration[p]["commit"] = integration_commit
# Save the target branch here, as the source branch gets deleted
# during merge
project_integration[p]["branch"] = mr.target_branch
if not merged: if not merged:
sys.exit( sys.exit(
...@@ -292,12 +299,12 @@ def main(): ...@@ -292,12 +299,12 @@ def main():
continue continue
# get BB_RECIPE_NAME from the projects .gitlab-ci.yml # get BB_RECIPE_NAME from the projects .gitlab-ci.yml
gitlab_ci_yml_file = os.path.join( # Use direct read from gitlab as we have not checked out
integration["repo"].working_tree_dir, ".gitlab-ci.yml" # the repo if the branch is already up to date
gitlab_ci_yml = common.get_repository_file_raw(
integration["project"], ".gitlab-ci.yml", ref=integration["branch"]
) )
logging.debug("Read recipe name from %s", gitlab_ci_yml_file)
with open(gitlab_ci_yml_file, "r", encoding="utf8") as fp:
gitlab_ci_yml = fp.read()
project_keys = read_keys_from_gitlab_ci_yml(gitlab_ci_yml) project_keys = read_keys_from_gitlab_ci_yml(gitlab_ci_yml)
new_srcrev = update_srcrev( new_srcrev = update_srcrev(
...@@ -319,6 +326,13 @@ def main(): ...@@ -319,6 +326,13 @@ def main():
manifest_project["repo"].git.add(args.srcrev_file) manifest_project["repo"].git.add(args.srcrev_file)
logging.debug(srcrev) logging.debug(srcrev)
# ========================================================
# Squash all commits on the integration branch to one
# ========================================================
manifest_project["repo"].remotes.origin.fetch(branch)
manifest_master = manifest_project["project"].branches.get(branch)
manifest_project["repo"].git.reset("--soft", manifest_master.commit["id"])
# ======================================================== # ========================================================
# Now commit and push the changes to the manifest repo # Now commit and push the changes to the manifest repo
# ======================================================== # ========================================================
......
...@@ -163,7 +163,7 @@ def integrate_into_manifest( ...@@ -163,7 +163,7 @@ def integrate_into_manifest(
"no recipe name is specified" % project.path "no recipe name is specified" % project.path
) )
with open(srcrev_filepath.as_posix(), "w", encoding="utf8") as fp: with open(srcrev_filepath.as_posix(), "w", encoding="utf8") as fp:
fp.write(new_manifest) fp.write(new_srcrev)
manifest_repo.index.add([srcrev_file]) manifest_repo.index.add([srcrev_file])
# Make an API request to create the gitlab.user object # Make an API request to create the gitlab.user object
......
...@@ -9,7 +9,7 @@ import tempfile ...@@ -9,7 +9,7 @@ import tempfile
from configparser import ConfigParser from configparser import ConfigParser
from furl import furl from furl import furl
from git import GitCommandError, Repo from git import GitCommandError, Repo
from gitlab import Gitlab from gitlab import Gitlab, GitlabGetError
from gitlab.v4.objects import Project from gitlab.v4.objects import Project
from ruamel.yaml import YAML from ruamel.yaml import YAML
...@@ -21,7 +21,7 @@ def get_submodule_project_path_and_revision(project: Project, submodule, branch= ...@@ -21,7 +21,7 @@ def get_submodule_project_path_and_revision(project: Project, submodule, branch=
logging.error("Submodule %s not found in %s.", submodule, project.name) logging.error("Submodule %s not found in %s.", submodule, project.name)
return None, None return None, None
logging.debug("Gitmodules: %s", gitmodules) # logging.debug("Gitmodules: %s", gitmodules)
cfgparse = ConfigParser() cfgparse = ConfigParser()
cfgparse.read_string(gitmodules) cfgparse.read_string(gitmodules)
...@@ -121,7 +121,12 @@ def clone_project_and_submodule(project: Project, submodule_name, branch=None): ...@@ -121,7 +121,12 @@ def clone_project_and_submodule(project: Project, submodule_name, branch=None):
submodule_relative_url = submodule.url submodule_relative_url = submodule.url
with submodule.config_writer() as writer: with submodule.config_writer() as writer:
writer.set("url", submodule_clone_url.url) writer.set("url", submodule_clone_url.url)
submodule.update(init=True) try:
submodule.update(init=True)
except GitCommandError:
# This seems to happen when a not existing commit is referenced
logging.error("Failed to initialize submodule %s", submodule_name)
with submodule.config_writer() as writer: with submodule.config_writer() as writer:
writer.set("url", submodule_relative_url) writer.set("url", submodule_relative_url)
...@@ -181,6 +186,7 @@ def update_submodule_and_include_ref( ...@@ -181,6 +186,7 @@ def update_submodule_and_include_ref(
new_revision, new_revision,
branch=None, branch=None,
commit_and_push=True, commit_and_push=True,
force_clone=False,
): ):
"""Update the submodule and include refs to the submodule in the given project. """Update the submodule and include refs to the submodule in the given project.
Create mergerequest if needed. Create mergerequest if needed.
...@@ -191,6 +197,7 @@ def update_submodule_and_include_ref( ...@@ -191,6 +197,7 @@ def update_submodule_and_include_ref(
new_revision (hex string): The sha hash of the commit to update the submodule to new_revision (hex string): The sha hash of the commit to update the submodule to
branch (string): branch to update, if None, the projects default branch is used branch (string): branch to update, if None, the projects default branch is used
commit_and_push: Set to false if no commit should be created. Changes are left in staging. commit_and_push: Set to false if no commit should be created. Changes are left in staging.
force_clone: Checkout repo and setup integration branch even if no update is needed
Returns: tuple of: Returns: tuple of:
project_repo (Repo): GitPython repo with the cloned project project_repo (Repo): GitPython repo with the cloned project
...@@ -198,23 +205,23 @@ def update_submodule_and_include_ref( ...@@ -198,23 +205,23 @@ def update_submodule_and_include_ref(
integration_commit (hexsha): Hash of the newly created commit integration_commit (hexsha): Hash of the newly created commit
message: Commit message based on the integrated changes. message: Commit message based on the integrated changes.
""" """
gitlab = project.manager.gitlab
submodule_update_needed = True
project_repo = None
integration_commit = None
if branch is None: if branch is None:
branch = project.default_branch branch = project.default_branch
logging.debug("Branch: %s", branch) logging.debug("Branch: %s", branch)
_, submodule_current_rev = get_submodule_project_path_and_revision( (
project, submodule_name, branch submodule_project_path,
) submodule_current_rev,
) = get_submodule_project_path_and_revision(project, submodule_name, branch)
# Check if revisions are different
if submodule_current_rev == new_revision:
print("Submodule is already at %s" % new_revision)
return (None, None, None, None)
project_repo, submodule_project = clone_project_and_submodule( # Get submodule project
project, submodule_name, branch submodule_project = common.get_project(gitlab, submodule_project_path)
)
# Get commits between current and new revision # Get commits between current and new revision
revision_range = submodule_current_rev + ".." + new_revision revision_range = submodule_current_rev + ".." + new_revision
...@@ -235,64 +242,134 @@ def update_submodule_and_include_ref( ...@@ -235,64 +242,134 @@ def update_submodule_and_include_ref(
break break
logging.debug("Integration branch suffix: %s", integration_branch_suffix) logging.debug("Integration branch suffix: %s", integration_branch_suffix)
# Setup integration branch # Construct integration branch name
integration_branch_name = common.integration_branch_name( integration_branch_name = common.integration_branch_name(
submodule_project.name, integration_branch_suffix submodule_project.name, integration_branch_suffix
) )
# Create integration branch
print("Creating/replacing integration branch %s" % integration_branch_name) # Construct commit message
project_repo.head.set_reference(project_repo.create_head(integration_branch_name))
# Update submodule to new revision
submodule_repo = common.get_submodule(project_repo, submodule_name)
update_submodule_in_repo(project_repo, submodule_repo, new_revision)
# Update the gitlab-ci.yml file to the new revision
# Now also update the project '.gitlab-ci.yml' file
gitlab_ci_yml_filename = os.path.join(
project_repo.working_tree_dir, ".gitlab-ci.yml"
)
with open(gitlab_ci_yml_filename, "r", encoding="utf8") as fp:
gitlab_ci_yml = fp.read()
logging.debug(gitlab_ci_yml)
new_gitlab_ci_yml = update_gitlab_ci_include(
gitlab_ci_yml,
submodule_project.web_url.split("//")[1].split("/", 1)[1],
new_revision,
)
if new_gitlab_ci_yml is None:
print("Failed to update the include revision in '.gitlab-ci.yml'")
else:
logging.debug(new_gitlab_ci_yml)
with open(gitlab_ci_yml_filename, "w", encoding="utf8") as fp:
fp.write(new_gitlab_ci_yml)
project_repo.git.add(os.path.basename(gitlab_ci_yml_filename))
# Construct commit message and commit the change
message = "Integrate %s/%s%s\n%s" % ( message = "Integrate %s/%s%s\n%s" % (
submodule_project.name, submodule_project.name,
integration_branch_suffix, integration_branch_suffix,
" and %d more" % (len(commits) - 1) if len(commits) > 1 else "", " and %d more" % (len(commits) - 1) if len(commits) > 1 else "",
common.list_commits(commits), common.list_commits(commits),
) )
# Commit the changes
if commit_and_push: # Check if revisions are different
if submodule_current_rev == new_revision:
# Make an API request to create the gitlab.user object print("Submodule is already at %s" % new_revision)
gitlab = project.manager.gitlab submodule_update_needed = False
gitlab.auth()
integration_commit = common.commit_and_push( # Check if we already have an integration branch (before we actually do the checkout)
project, # Check if integration branch already exists and if it is up to date
project_repo, # This is needed for one use case:
integration_branch_name, # It is possible to amend changes to the integration branch manually
message, # outside the pipeline.
gitlab.user.username, # When the submodule revision has not changed and the pipeline is run
gitlab.user.email, # again (due to merge or manually triggered) the manual change persists
less_verbose=True, # in the final commit
# For example rename a file in gitlab-ci repo that is included in a
# subproject, requires an adapted include statement here.
# To get this change 'atomic' the updated include statement should be
# in the same commit as the update of the submodule
if submodule_update_needed or force_clone:
existing_branch = None
try:
existing_branch = project.branches.get(integration_branch_name)
except GitlabGetError:
# Branch not found
pass
if existing_branch:
(
_,
integration_branch_submodule_rev,
) = get_submodule_project_path_and_revision(
project, submodule_name, integration_branch_name
)
logging.debug(
"Revision in integration branch '%s', new_revision '%s'",
integration_branch_submodule_rev,
new_revision,
)
if integration_branch_submodule_rev == new_revision:
print(
"Submodule is already at %s on branch %s"
% (new_revision, integration_branch_name)
)
integration_commit = existing_branch.commit["id"]
submodule_update_needed = False
# Clone the project, we need to do changes
if submodule_update_needed or force_clone:
clone_branch = branch
if existing_branch:
clone_branch = integration_branch_name
# Actually clone
project_repo, submodule_project = clone_project_and_submodule(
project, submodule_name, clone_branch
)
if existing_branch:
print("Using existing integration branch %s" % integration_branch_name)
else:
# Create branch
print("Creating integration branch %s" % integration_branch_name)
project_repo.head.set_reference(
project_repo.create_head(integration_branch_name)
)
if submodule_update_needed:
# Update submodule to new revision
submodule_repo = common.get_submodule(project_repo, submodule_name)
update_submodule_in_repo(project_repo, submodule_repo, new_revision)
# Update the gitlab-ci.yml file to the new revision
# Now also update the project '.gitlab-ci.yml' file
gitlab_ci_yml_filename = os.path.join(
project_repo.working_tree_dir, ".gitlab-ci.yml"
)
with open(gitlab_ci_yml_filename, "r", encoding="utf8") as fp:
gitlab_ci_yml = fp.read()
logging.debug(gitlab_ci_yml)
new_gitlab_ci_yml = update_gitlab_ci_include(
gitlab_ci_yml,
submodule_project.web_url.split("//")[1].split("/", 1)[1],
new_revision,
) )
else: if new_gitlab_ci_yml is None:
integration_commit = None print("Failed to update the include revision in '.gitlab-ci.yml'")
else:
logging.debug(new_gitlab_ci_yml)
with open(gitlab_ci_yml_filename, "w", encoding="utf8") as fp:
fp.write(new_gitlab_ci_yml)
project_repo.git.add(os.path.basename(gitlab_ci_yml_filename))
# Commit the changes
if commit_and_push:
# ========================================================
# Squash all commits on the integration branch to one
# ========================================================
project_repo.remotes.origin.fetch(branch)
gitlab_branch = project.branches.get(branch)
project_repo.git.reset("--soft", gitlab_branch.commit["id"])
# Make an API request to create the gitlab.user object
gitlab.auth()
# Push the changes
integration_commit = common.commit_and_push(
project,
project_repo,
integration_branch_name,
message,
gitlab.user.username,
gitlab.user.email,
less_verbose=True,
)
return project_repo, integration_branch_name, integration_commit, message return project_repo, integration_branch_name, integration_commit, message
......