Newer
Older
#!/usr/bin/env python3
import requests
import sys
from git import Actor, GitCommandError
from git.repo.base import Repo
from gitlab import GitlabAuthenticationError, GitlabGetError, GitlabMRRebaseError
from gitlab.v4.objects import Project
from gitlab.v4.objects import MergeRequest
srcrev_file = "SRCREV.conf"
pending_states = ["created", "waiting_for_resource", "preparing", "pending", "running"]
def integration_branch_name(project_name, branch_name):
"""Get integration branch name"""
return "integrate/" + project_name.lower() + "/" + branch_name
Jonas Höppner
committed
def find_gitlab_ci_integration_branch(repo: Repo, branch_name):
"""
# 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
"""
if not branch_name.startswith("integrate/gitlab-ci"):
return None
logging.debug("Integration of gitlab-ci: %s", branch_name)
for ref in repo.references:
refname = ref.name
logging.debug("Found ref: %s", refname)
if not refname.startswith("origin/"):
continue
# remove 'origin/' from the ref before compare
refname = ref.name.split("/", 1)[1]
logging.debug("Splitted refname: %s", refname)
if branch_name == refname:
integration_branch = refname
logging.debug(
"Found integration branch for gitlab-ci: %s", integration_branch
)
return integration_branch
"""Get a GitLab project by its name (including or excluding namespace)"""
# First try direct access, assuming name contains full path including namespace
try:
project = gitlab.projects.get(project_name, retry_transient_errors=True)
except GitlabGetError:
pass
# If not found, try searching for project, assuming only name given
if not project:
for p in gitlab.projects.list(
search=project_name, retry_transient_errors=True
):
if p.name == project_name:
project = p
if not project:
sys.exit("ERROR: project '%s' not found" % project_name)
except requests.ConnectionError:
sys.exit("ERROR: could not connect to GitLab server")
except GitlabAuthenticationError:
sys.exit("ERROR: authentication failed")
def get_latest_commit(project, branch_name):
branch = project.branches.get(branch_name, retry_transient_errors=True)
except GitlabGetError as e:
sys.exit(
"ERROR: could not get branch '%s' for project '%s': %s"
% (branch_name, project.name, e)
)
if not branch:
sys.exit(
"ERROR: branch '%s' not found in project %s" % (branch_name, project.name)
)
def rebase_merge_request(project, merge_request):
"""Attempt to rebase a merge request and return the updated merge request object"""
# Rebasing takes more than one API call, see:
# https://docs.gitlab.com/ce/api/merge_requests.html#rebase-a-merge-request
try:
merge_request.rebase()
except GitlabMRRebaseError as e:
merge_request.merge_error = "Could not rebase merge request: %s" % e
return merge_request
rebase_in_progress = True
while rebase_in_progress:
time.sleep(1)
updated_merge_request = project.mergerequests.get(
id=merge_request.iid,
query_parameters={"include_rebase_in_progress": "True"},
)
except GitlabGetError as e:
merge_request.merge_error = "Could not get updated merge request: %s" % e
rebase_in_progress = updated_merge_request.rebase_in_progress
def crosslink_merge_requests(source_mr: MergeRequest, integration_mr: MergeRequest):
"""Insert cross-links in merge requests"""
integration_mr.notes.create(
{"body": "Source merge request: %s" % source_mr.web_url}
)
source_mr.notes.create(
{"body": "Integration merge request: %s" % integration_mr.web_url}
)
def wait_until_merge_status_is_set(project: Project, mr: MergeRequest):
"""Periodically query MR until GitLab has checked its merge status"""
print("Waiting until merge status has been checked", end="", flush=True)
unchecked_states = ["unchecked", "checking", "cannot_be_merged_recheck"]
mr = project.mergerequests.get(mr.iid, retry_transient_errors=True)
while mr.merge_status in unchecked_states:
print(".", end="", flush=True)
time.sleep(1)
mr = project.mergerequests.get(mr.iid, retry_transient_errors=True)
print(" -> %s" % mr.merge_status)
def list_commits(commits):
"""Create a list of commits along with the commit messages"""
commit_list = ""
for commit in commits:
commit_list += "\n--\n\nCommit: %s\n\n%s" % (commit.web_url, commit.message)
return commit_list
def commit_and_push(project: Project, repo: Repo, branch, message, name, email):
"""Commit and push to a repo branch"""
author = Actor(name, email)
repo.index.commit(message, author=author, committer=author)
Jonas Höppner
committed
print(repo.git.log("--oneline", "-n", "5"))
# Push commit
try:
origin = repo.remote("origin")
Jonas Höppner
committed
logging.debug("Push branch %s to %s", branch, origin)
origin.push(branch, force=True)
except GitCommandError as e:
sys.exit("ERROR: could not commit changes\n" + str(e))
# Print commit information
revision = repo.head.commit.hexsha
print("Pushed new commit:")
print(project.web_url + "/-/commit/" + revision)
Jonas Höppner
committed
print(repo.git.show("--summary", "--decorate"))
return revision
def get_submodule(repo: Repo, submodule_name):
"""Find a submodule in a Git repository by its name"""
submodule = None
for sm in repo.submodules:
if sm.name == submodule_name:
submodule = sm
if submodule is None:
sys.exit("ERROR: submodule '%s' not found" % submodule_name)
return submodule
def extract_message_body(msg):
"""Extract message body out of a commit message"""
# Remove headline
msg = msg.split("\n", 1)[-1]
# Remove all newlines, whitespaces and hyphens from the beginning
while msg[0] in ["\n", " ", "-"]:
msg = msg[1:]
return msg
Jonas Höppner
committed
Jonas Höppner
committed
def get_merge_request(project: Project, merge_request):
"""Return a gitlab mergereqest specified either by id or by link"""
Jonas Höppner
committed
# MR may also be specified as
# SECO-Northern-Europe/yocto/infrastructure/ci-test/minimal-bar!115
if "!" in merge_request:
merge_request = int(merge_request.split("!")[-1])
logging.debug("Number of MR: %d", merge_request)
try:
mr = project.mergerequests.get(merge_request, retry_transient_errors=True)
except GitlabGetError:
return None
return mr