Skip to content
Snippets Groups Projects
Commit befb5fcb authored by Lorenzo Pagliai's avatar Lorenzo Pagliai
Browse files

Insert accept_layer_merge_request.py file

* The script is a direct derivation of the original one and can be
merged in a near future
* The script search for the latest updated MR in the project repository
to retrieve the source branch
* Then merge the MR on the layer project according to the recontructed
integration branch
parent ac01de8d
No related branches found
No related tags found
No related merge requests found
#!/usr/bin/env python3
import common
import argparse
import logging
import sys
import time
from gitlab import (
Gitlab,
GitlabGetError,
GitlabMRClosedError,
)
from get_merge_requests import get_merge_requests
critical_error = (
"This is a critical error! Please make sure to:\n"
" 1. merge the above-mentioned merge request by hand as soon as possible\n"
" (this is important, otherwise following merge requests will get stuck, too)\n"
" 2. examine why this has happened and fix it in the CI pipeline"
)
def get_source_integration_requests(
project, state=None, target_branch=None, commit=None
):
"""Get merge request by target branch and optionally commit sha"""
merge_requests = []
try:
all_merge_requests = project.mergerequests.list(
target_branch=target_branch,
state=state if state else "all",
all=True,
retry_transient_errors=True,
order_by='updated_at'
)
except GitlabGetError as e:
sys.exit(
"ERROR: could not list merge requests for project '%s': %s"
% (project.name, e)
)
if commit:
for mr in all_merge_requests:
if mr.sha == commit or mr.squash_commit_sha == commit:
merge_requests.append(mr)
elif all_merge_requests:
merge_requests = all_merge_requests
# Get complete objects
full_merge_requests = []
for mr in merge_requests:
mr = project.mergerequests.get(mr.iid, retry_transient_errors=True)
full_merge_requests.append(mr)
return full_merge_requests[0].source_branch
def accept_merge_request(project, mr, rebase=False, should_remove_source_branch=True):
"""Attempt to merge a merge request, rebase if necessary"""
merged = False
pipeline_pending = False
while not merged:
# Update merge request before trying to merge it in order to get the latest
# pipeline status
try:
updated_mr = project.mergerequests.get(
id=mr.iid, retry_transient_errors=True
)
mr = updated_mr
except GitlabGetError as e:
print("WARNING: Could not update merge request object: %s" % e)
# Try to merge the merge request
try:
mr.merge(should_remove_source_branch=should_remove_source_branch)
if pipeline_pending:
print("")
if mr.state == "merged":
merged = True
else:
if mr.merge_error:
print("Merge error: %s" % mr.merge_error)
else:
print("Merge reported success, but MR state is '%s'" % mr.state)
return False, mr.sha
except GitlabMRClosedError as e:
# See HTTP error codes for merge requests here:
# https://docs.gitlab.com/ce/api/merge_requests.html#accept-mr
logging.debug("Error from gitlab: %d", e.response_code)
if e.response_code == 405:
# Not allowed (draft, closed, pipeline pending or failed)
# Contrary to the documentation, this response is also issued when the
# merge failed due to a merge conflict. See GitLab issue:
# https://gitlab.com/gitlab-org/gitlab/-/issues/364102
if mr.has_conflicts:
# Merge conflict, automatic rebase not possible
if pipeline_pending:
print("")
print("Merge not possible, has to be rebased manually")
return False, mr.sha
# If pipeline is running, wait for completion
if not mr.head_pipeline:
# No pipeline created yet
print("No pipeline created yet")
time.sleep(1)
elif mr.head_pipeline["status"] in common.pending_states:
# Pipeline pending
if not pipeline_pending:
print("Waiting for pending pipeline", end="", flush=True)
pipeline_pending = True
print(".", end="", flush=True)
time.sleep(1)
else:
# Merge failed due to some other reason
if pipeline_pending:
print("")
print("Merge not possible for unkown reason")
return False, mr.sha
elif e.response_code == 406:
# Merge conflict, automatic rebase is possible
if pipeline_pending:
print("")
pipeline_pending = False
print("Merge not possible, but branch can be automatically rebased")
if not rebase:
return False, mr.sha
print("Trying to rebase...")
mr = common.rebase_merge_request(project, mr)
if mr.merge_error:
print("ERROR: rebase not possible\n'%s'" % mr.merge_error)
sys.exit(critical_error)
print("Sucessfully rebased")
else:
if pipeline_pending:
print("")
print("ERROR: merge not possible: %s" % e)
sys.exit(critical_error)
return True, mr.sha
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(
"--project",
help="""name of the GitLab project""",
dest="project",
required=True,
)
parser.add_argument(
"--layer-project",
help="""name of the GitLab layer project""",
dest="layer_project",
required=True,
)
parser.add_argument(
"--target-branch",
help="""target branch of the merge request""",
dest="target_branch",
required=True,
)
parser.add_argument(
"--layer-target-branch",
help="""target branch of the layer merge request""",
dest="layer_target_branch",
required=True,
)
parser.add_argument(
"--recipe-name",
help="""recipe name of the merge request""",
dest="recipe_name",
required=True,
)
parser.add_argument(
"--rebase",
help="""attempt to automatically rebase merge request if necessary""",
dest="rebase",
action="store_true",
required=False,
)
args, _ = parser.parse_known_args()
gitlab = Gitlab(args.gitlab_url, private_token=args.token)
project = common.get_project(gitlab, args.project)
layer_project = common.get_project(gitlab, args.layer_project)
#Retrieving the source branch from the last MR merged in the project
try:
integration_branch_name = get_source_integration_requests(
project,
target_branch=args.target_branch,
state="merged",
)
print("This is the source branch of the latest MR merged: ", integration_branch_name)
#If the last merged branch is the gitlab-ci integration do nothing
if "integrate/gitlab-ci" in integration_branch_name:
print("The job was triggered by a merge of the gitlab-ci projects into the master branch, doing nothing!")
return ""
except GitlabGetError as e:
sys.exit("Could not get integration branch name for latest project MR: %s" % e)
integration_branch_name = args.recipe_name + "/" + integration_branch_name
try:
merge_request = get_merge_requests(
layer_project,
source_branch=integration_branch_name,
target_branch=args.layer_target_branch,
state="opened",
)
except GitlabGetError as e:
sys.exit("Could not get merge request: %s" % e)
print("This job is going to merge MR %s in project %s", merge_request[0], layer_project)
if accept_merge_request(layer_project, merge_request[0], rebase=args.rebase):
print("Successfully merged")
else:
sys.exit(1)
if __name__ == "__main__":
main()
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment