Skip to content

Instantly share code, notes, and snippets.

@alexsavio
Created June 25, 2014 14:19
Show Gist options
  • Save alexsavio/ca1bd22ceebd55e427a3 to your computer and use it in GitHub Desktop.
Save alexsavio/ca1bd22ceebd55e427a3 to your computer and use it in GitHub Desktop.
GitHub to Gitlab repository version copier/control.
#content of the config file .python-gitlab.cfg:
#[global]
#default = local
#ssl_verify = true
#[local]
#url = http://***************
#private_token = **************
repos = [('[email protected]:numpy/numpy.git', 'v1.8.1'),
('[email protected]:cython/cython.git', '0.20.2'),
('[email protected]:scipy/scipy.git', 'v0.14.0'),
('[email protected]:git/git.git', 'v2.0.0'),
('[email protected]:gpocentek/python-gitlab.git', ''),
('[email protected]:gitpython-developers/GitPython.git', ''),
('[email protected]:python-git/python.git', ''),
('[email protected]:BVLC/caffe.git', ''),
('[email protected]:schwarty/nignore.git', ''),
('[email protected]:Theano/Theano.git', ''),
('[email protected]:akeshavan/nbpapaya.git', ''),
('[email protected]:nipy/nibabel.git', '1.3.0'),
('[email protected]:nipy/nipy.git', '0.3.0'),
('[email protected]:nipy/nipy-labs.git', ''),
('[email protected]:nipy/PySurfer.git', 'v0.5'),
('[email protected]:nipy/nitime.git', '0.5'),
('[email protected]:nipy/dipy.git', '0.7.1'),
('[email protected]:nipy/nipype.git', '0.9.2'),
('[email protected]:nipy/brainx.git', ''),
('[email protected]:nipy/PyLocator.git', ''),
('[email protected]:nipy/pbrain.git', ''),
('[email protected]:nipy/nidoodles.git', ''),
('[email protected]:FCP-INDI/C-PAC.git', ''),
('https://github.com/FBK-NILab/NeuroBox', ''),
('[email protected]:mwaskom/moss.git', ''),
('[email protected]:rii-mango/Papaya.git', ''),
('[email protected]:scikit-learn/scikit-learn.git', '0.15'),
('[email protected]:scikit-fuzzy/scikit-fuzzy.git', ''),
('[email protected]:scikit-image/scikit-image.git', ''),
('[email protected]:wimleers/fileconveyor.git', ''),
('[email protected]:danginsburg/webgl-brain-viewer.git', ''),
('[email protected]:andsens/homeshick.git', ''),]
#gitlab_cfg = dict(ssl_verify = true,
# url = 'http://mygitlab.somewhere.com',
# private_token = 'asdadsqe1420980fadmmJyqhg')
import os
import logging
logging.basicConfig(level=logging.INFO, #filename='repysitories.log',
format="%(asctime)-15s %(filename)s %(funcName)s "
"%(levelname)s %(message)s")
log = logging.getLogger(__name__)
try:
from ConfigParser import ConfigParser
except:
from configparser import ConfigParser
import gitlab
import git
class LoggedError(Exception):
def __init__(self, message):
Exception.__init__(self, message)
log.error(message)
class GitlabConfigError(LoggedError):
pass
class GitlabConnectionError(LoggedError):
pass
class GitlabNamespaceNotFound(LoggedError):
pass
class GitlabUserNotFound(LoggedError):
pass
class GitlabProjectNotFound(LoggedError):
pass
class GitlabProjectAlreadyExists(LoggedError):
pass
class GitlabCommandError(LoggedError):
pass
class GitCommandError(LoggedError):
pass
class GitLocalRepoNotSet(LoggedError):
pass
class PathDoesNotExist(LoggedError):
pass
class GitlabConnection(object):
"""
This class manages the configuration and connection to the Gitlab server.
"""
config_paths = ['/etc/python-gitlab.cfg',
os.path.expanduser('~/.python-gitlab.cfg')]
def __init__(self, gitlab_url=None, ssl_verify=True, gitlab_id=None):
self.ssl_verify = ssl_verify
self.gitlab_url = gitlab_url
self.gitlab_id = gitlab_id
self.verbose = False
self.connection = None
try:
self._from_cfgfile()
except:
pass
def auth(self):
"""
Creates the connection to the GitLab server
"""
try:
self.connection = gitlab.Gitlab(self.gitlab_url,
private_token=self.gitlab_token,
ssl_verify=self.ssl_verify)
self.connection.auth()
except:
raise GitlabConnectionError("Could not connect to "
"GitLab ({0})".format(self.gitlab_url))
def get_current_user_id(self):
"""
:return:
"""
if self.connection is None:
self.auth()
return self.connection.user.id
def get_all_namespaces(self):
"""
:return:
"""
if self.connection is None:
self.auth()
namespaces = []
namespaces.extend(self.connection.list(gitlab.User))
namespaces.extend(self.connection.list(gitlab.Group))
return namespaces
def has_namespace(self, namespace_id):
"""
Return True if the Gitlab Server has a namespace with name or id
namespace_id
:param namespace_id: int or str
:return: bool
"""
if self.connection is None:
self.auth()
namespace_name = None
if namespace_id is None:
namespace_id = self.get_current_user_id()
if isinstance(namespace_id, str):
namespace_name = namespace_id
namespace_id = self.get_namespace_id(namespace_name)
if namespace_name is None:
namespace_name = self.get_namespace_name(namespace_id)
for ns in self.get_all_namespaces():
if namespace_name == ns.name:
return True
return False
def is_authenticated(self):
return self.connection is None
def logout(self):
"""
Delete the connection variable
:return:
"""
self.connection = None
def exists_project(self, name, namespace_id=None):
"""
Return True if the project named name in the namespace_id exists
:param name: str
Name of the project
:param namespace_id: str or int
Usually namespaces are treated by their numbered id.
If you give a string it will look for the name of the namespace.
:return: bool
"""
try:
prj = self.get_project(name, namespace_id)
except:
return None
return prj is not None
def get_project(self, name, namespace_id=None):
"""
Return the gitlab.Project named name in the namespace_id.
:param name: str
Name of the project
:param namespace_id: str or int
Usually namespaces are treated by their numbered id.
If you give a string it will look for the name of the namespace.
If empty, will try to return the project in user's namespace, if it
does not exist, will return the project found by name.
:return: gitlab.Project
"""
if self.connection is None:
self.auth()
namespace_name = None
if namespace_id is None:
namespace_id = self.get_current_user_id()
if isinstance(namespace_id, str):
namespace_name = namespace_id
namespace_id = self.get_namespace_id(namespace_name)
if namespace_name is None:
namespace_name = self.get_namespace_name(namespace_id)
prob_proj = None
proj_lst = self.connection.all_projects()
for proj in proj_lst:
if proj.name == name:
if namespace_id is None:
return proj
if proj.namespace.id == namespace_id:
return proj
#saving a probable project if the namespace doesn't match
prob_proj = proj
if prob_proj is not None:
return prob_proj
else:
#could not find the project
msg = 'Could not find project {0}/{1} in ' \
'Gitlab server'.format(namespace_name, name)
raise GitlabProjectNotFound(msg)
def get_namespace_id(self, namespace_name):
"""
:param namespace_name: str
:return: int
namespace_id if found, None otherwise
"""
all_namespaces = self.get_all_namespaces()
for ns in all_namespaces:
if ns.name == namespace_name:
return ns.id
return None
def get_namespace_name(self, namespace_id):
"""
:param namespace_id: int
:return: str
namespace_name if found, None otherwise
"""
all_namespaces = self.get_all_namespaces()
for ns in all_namespaces:
if ns.id == namespace_id:
return ns.name
return None
def get_project_namespace_name(self, name, namespace_id):
"""
Return the project named name in the namespace_id.
:param name: str
Name of the project
:param namespace_id: int
Usually namespaces are treated by their numbered id.
:return: gitlab.Project
"""
proj = self.get_project(name, namespace_id)
if proj is not None:
return proj.name
else:
msg = 'Could not find project {0}/{1} in ' \
'Gitlab server'.format(namespace_id, name)
raise GitlabProjectNotFound(msg)
def create_new_project(self, creation_args):
"""
Push an existing local repository to Gitlab
:param repo_path: str
Absolute path to the repository
:param creation_args: dict
See python-gitlab create project arguments
:param name: str
:param namespace_id: int
:param description: str
:param default_branch: str
:param public: bool, setting to True is the same as visibility_level: 20
:param visibility_level: int, choices={'0', '10', '20'}
:param snippets_enabled: bool
:param wiki_enabled: bool
:param merge_requests_enabled: bool
:param issues_enabled: bool
:param wall_enabled: bool
"""
if not self.is_authenticated():
self.auth()
try:
proj_name = creation_args.get('name')
log.info('Creating new project '
'{0} on {1}.'.format(proj_name, self.connection._url))
p = self.connection.Project(creation_args)
p.save()
return p
except Exception as exc:
raise LoggedError(str(exc))
def _from_cfgfile(self):
"""
Parse the configuration files for self config
"""
# read the config
config = ConfigParser()
config.read(self.config_paths)
if self.gitlab_id is None:
try:
self.gitlab_id = config.get('global', 'default')
except:
raise GitlabConfigError("Impossible to get the gitlab id "
"(not specified in config file)")
try:
self.gitlab_url = config.get(self.gitlab_id, 'url')
self.gitlab_token = config.get(self.gitlab_id, 'private_token')
except:
raise GitlabConfigError("Impossible to get gitlab informations "
"from configuration (%s)" % self.gitlab_id)
try:
self.ssl_verify = config.getboolean('global', 'ssl_verify')
except:
pass
try:
self.ssl_verify = config.getboolean(self.gitlab_id, 'ssl_verify')
except:
pass
def from_dict(self, config):
"""
Configs self through dictionary args:
ssl_verify: boolean
url: string
private_token: string
"""
self.ssl_verify = config.get('ssl_verify', True)
self.gitlab_url = config.get('url', None)
self.gitlab_token = config.get('private_token', None)
class GitRepository(object):
"""
This class manages a git remote URL and a local copy of the repository.
"""
def __init__(self, url=None, path=None, name=None, tag=None):
self.giturl = None
self.tag = tag
self.path = None
self.local_repo = None
if url is not None:
self.giturl = GitRemoteURL(url, name)
if path is not None:
self.set_local_repo(path)
def clone(self, to_path, mkdir=False, checkout_tag=False):
"""
Clone the remote URL to dir_path
:param dir_path: string
:param checkout_tag: bool
Indicates whether to checkout the self.tag version after setup
:return git.Repo
"""
if not os.path.exists(to_path):
if mkdir:
os.mkdir(to_path)
try:
log.info('Clonning repo {0} in {1}.'.format(self.giturl.url,
to_path))
self.local_repo = git.Repo.clone_from(self.giturl.url, to_path)
self._update(checkout_tag)
return self.local_repo
except Exception as exc:
raise LoggedError(exc)
def set_local_repo(self, path, mkdir=False, checkout_tag=False):
"""
Configure self.local_repo using the repo that is in path.
:param path: str
Full path to where the repo is.
:param mkdir: bool
If True, will create the path and initialize an empty repo there.
:param checkout_tag: bool
Indicates whether to checkout the self.tag version after setup
:return git.Repo
"""
if not os.path.exists(path):
if mkdir:
try:
os.mkdir(path)
self.init_local(path)
except:
raise
else:
raise PathDoesNotExist('Repository folder path {0} does '
'not exist.'.format(path))
else:
try:
self.local_repo = git.Repo(path)
except Exception as exc:
raise LoggedError(str(exc))
self._update(checkout_tag)
return self.local_repo
def has_working_copy(self, check_head_tag=False):
"""
Return whether the repo is locally cloned in self.path with the same
version
:param check_head_tag: bool
If True will also check if the tag is the same as the one in git_remote.
:return: bool
"""
if self.local_repo is None:
return False
if self.path is None:
return False
if os.path.exists(self.path):
try:
repo = git.Repo(self.path)
if check_head_tag:
if repo.head.ref.name != self.tag:
return False
return True
except:
return False
return True
def init_local(self, path):
"""
Initialize an empty local_repo in path.
:param path: str
Full path to where the local repo will be created
:return git.Repo
"""
try:
self.local_repo = git.Repo.init(path, mkdir=True)
self._update(checkout=False)
return self.local_repo
except Exception as exc:
raise LoggedError(str(exc))
@property
def name(self):
return self.giturl.name
@property
def url(self):
return self.giturl.url
def checkout(self, tag_name=None):
"""
Perform a checktout in local_repo, to checktout a specific tag.
:param tag_name: str
Name of the tag to be checked out
"""
self._check_local_repo_exists()
try:
self.local_repo.git.checkout(tag_name)
except GitCommandError as gce:
raise gce
def pull(self):
"""
git pull the repository
:returns std output
"""
self._check_local_repo_exists()
try:
return self.local_repo.git.pull()
except Exception as exc:
raise GitCommandError(str(exc))
def add_remote(self, name, url):
"""
Add the remote repository to local_repo.
:param name: str
Name of the remote repository
:param url: str
URL of the remote repository
:return git.Remote
Remote reference
"""
self._check_local_repo_exists()
try:
return self.local_repo.create_remote(name, url)
except git.GitCommandError as gce:
raise GitCommandError(str(gce))
def get_remote(self, name):
"""
Return git.Remote reference of local_repo remote repository named name
:param name: str
Name of the remote reference in local repo
:return: git.Remote
Remote reference
"""
self._check_local_repo_exists()
for remo in self.local_repo.remotes:
if remo.name == name:
return remo
return None
def has_remote(self, name):
"""
Return True if local_repo has a remote repository named name
:param name: str
Name of the remote reference in local repo
:return: bool
"""
return self.get_remote(name) is not None
def rm_remote(self, name):
"""
Return git.Remote reference of local_repo remote repository named name
:param name: str
Name of the remote reference in local repo
:return: git.Remote
Remote reference
"""
self._check_local_repo_exists()
try:
git.Remote.rm(self.local_repo, name)
except git.GitCommandError as gce:
raise GitCommandError(str(gce))
def push(self, remote_name='origin', *args):
"""
Push the modifications made in local_repo to the remote named name.
:param remote_name: str
Name of the remote repository where to push.
:param args: Additional arguments to be passed to git-push
:return: IterableList(PushInfo, ...)
iterable list of PushInfo instances, each
one informing about an individual head which had been updated on the
remote side.
If the push contains rejected heads, these will have the PushInfo.ERROR
bit set in their flags.
If the operation fails completely, the length of the returned
IterableList will be null.
"""
self._check_local_repo_exists()
remote = self.get_remote(remote_name)
if remote is not None:
push_infos = remote.push(*args)
return push_infos
else:
log.error("Could not find remote '{0}' in "
"repo in {1}.".format(remote_name, self.path))
def clear(self):
"""
Clears self local_repo related variables and deletes all files of the
local repo
"""
self.clean_files()
self.local_repo = None
self.path = None
def _check_local_repo_exists(self):
"""
Raises an exception if self.local_repo is None
"""
if self.local_repo is None:
msg = 'Local repository for {0} is not set.'.format(self.name)
raise GitLocalRepoNotSet(msg)
def _update(self, checkout=False):
"""
Update member variables related to local_repo
"""
self.path = self.local_repo.working_dir
if checkout:
self.checkout(self.tag)
def clean_files(self):
"""
Recursively delete repository folder
"""
if os.path.exists(self.path):
os.removedirs(self.path)
class GitRepositorySet(object):
def __init__(self, repo_lst=None):
self.repos = [] if repo_lst is None else repo_lst
@classmethod
def from_list(cls, repo_list):
"""
Creates a list of GitRepository from a list of tuples: (url, tag)
:param repo_list: list of 2-tuple
[(url, tag), ...]
:return
GitRepositorySet
"""
repos = []
for repo in repo_list:
url = repo[0]
tag = repo[1]
if not tag:
tag = None
repos.append(GitRepository(url, tag=tag))
return cls(repos)
@classmethod
def from_dict(cls, repo_dict):
"""
Creates a list of GitRepository from a dict of dict:
{name: {'url': url, 'tag': tag}}
:param repo_dict: dict
:return
GitRepositorySet
"""
repos = []
for repo in repos:
name = repo
url = repos[repo]['url']
tag = repos[repo].get('tag', None)
repos.append(GitRepository(url, name=name, tag=tag))
return cls(repos)
def __iter__(self):
return self.repos.__iter__()
def __next__(self):
return self.repos.__next__()
def next(self):
return self.repos.__next__()
def __getitem__(self, item):
if hasattr(self.repos, '__getitem__'):
return self.repos[item]
else:
log.error('Repository set has no __getitem__ implemented.')
return None
class GitRemoteURL(object):
"""
Manages the string of a git remote URL
"""
def __init__(self, url, name=None):
self.url = url
self.name = self.name() if name is None else name
def name(self):
return self.url.split('/')[-1].replace('.git', '')
@property
def is_ssh(self):
if self.url.find('@') > -1:
return True
else:
return False
@property
def is_http(self):
return not self.is_ssh
def __repr__(self):
return self.url
class GitlabProjectManager(object):
"""
This class manages a GitRepository with local copy and
a GitLabConnection.
It can create new projects in the Gilab Server and push/update source to it
"""
def __init__(self, gitlab_conn, url=None, name=None, tag=None,
work_dir=None):
"""
:param gitlab_conn: GitlabConnection
:param url: str
:param name: str
:param tag: str
:param work_dir: str
"""
self.git_repo = None
self.work_dir = '/tmp' if work_dir is None else work_dir
if url is not None:
self.set_repository(url, name, tag)
self.gitlab = gitlab_conn
self.git_remote_name = 'gitlab'
def set_repository(self, url, name, tag):
"""
:param url: str
URL to the repository
:param name: str
Name of the repository
:param tag: str
Version or tag required for this repository
"""
self.git_repo = GitRepository(url, name, tag)
def set_repository_from_repo(self, repo):
"""
:param repo: GitRepository
"""
self.git_repo = repo
def clone(self):
"""
Clones the remote git URL to work_dir
"""
self.git_repo.clone(self.work_dir)
def create(self, name=None, namespace_id=None,
get_if_exists=False, args=None):
"""
Pushes the cloned repository to the gitlab server
:param name: str
Name of the Gitlab project
:param namespace_id: int or str
Identification int or string of the project namespace
:param get_if_exists: bool
If True will return the project if it already exists
:params args: dict
See python-gitlab create project arguments
:param name: str
:param namespace_id: int
:param description: str
:param default_branch: str
:param public: bool, setting to True is the same as visibility_level: 20
:param visibility_level: int, choices={'0', '10', '20'}
:param snippets_enabled: bool
:param wiki_enabled: bool
:param merge_requests_enabled: bool
:param issues_enabled: bool
:param wall_enabled: bool
:return gitlab.Project
"""
if name is None:
name = args.get('name', self.git_repo.name)
if namespace_id is None:
try:
namespace_id = args.get('namespace_id',
self.gitlab.get_current_user_id())
except Exception as exc:
raise GitlabUserNotFound(str(exc))
#get the int namespace_id if it is a string
if isinstance(namespace_id, str):
namespace_name = namespace_id
namespace_id = self.get_namespace_id(namespace_name)
#check if the project exists in gitlab, if not, we create it
try:
ns_name = self.gitlab.get_project_namespace_name(name,
namespace_id)
except GitlabProjectNotFound as gpnf:
ns_name = None
pass
#if the project exists
if ns_name is not None:
msg = 'Project {0}/{1} already exists in ' \
'Gitlab server.'.format(ns_name, name)
log.info(msg)
if get_if_exists:
return self.gitlab.get_project(name, namespace_id)
else:
raise GitlabProjectAlreadyExists(msg)
#the project does not exist in Gitlab, let's create it
args['name'] = name
if namespace_id:
args['namespace_id'] = namespace_id
try:
gl_proj = self.gitlab.create_new_project(args)
log.info('Project {0}/{1} created in '
'Gitlab server: {2}.'.format(gl_proj.namespace.name,
name,
gl_proj.ssh_url_to_repo))
return gl_proj
except Exception as exc:
raise LoggedError(str(exc))
def get_namespace_id(self, namespace_name):
"""
:param namespace_name:
:return:
"""
if not namespace_name:
return self.gitlab.get_current_user_id()
namespace_id = self.gitlab.get_namespace_id(namespace_name)
if namespace_id is None:
msg = 'Could not find namespace {0} in ' \
'Gitlab server'.format(namespace_id)
raise GitlabNamespaceNotFound(msg)
return namespace_id
def get_default_branch(self, proj_name, proj_namespace_id=None):
"""
:param proj_name:
:param proj_namespace_id:
:return:
"""
try:
proj = self.gitlab.get_project(proj_name, proj_namespace_id)
except GitlabProjectNotFound as exc:
return None
if proj.default_branch is None:
return None
return proj.default_branch
def _prepare_to_push(self, proj_name, proj_namespace_id,
remote_name=None):
"""
:param proj_name:
:param proj_namespace_id:
:param remote_name:
:return:
"""
try:
proj = self.gitlab.get_project(proj_name, proj_namespace_id)
except GitlabProjectNotFound as exc:
raise exc
if self.git_repo is None:
raise GitRepositorySet('Repository has not been set for project '
'{0}/{1}.'.format(proj.namespace.name,
proj.name))
if not self.git_repo.has_working_copy():
#clone the repo in self.working_dir/proj_name
repo_path = os.path.join(self.work_dir, proj_name)
self.git_repo.clone(repo_path)
#check if the remote is available in the repository
if remote_name is None:
remote_name = self.git_remote_name
if self.git_repo.has_remote(remote_name):
self.git_repo.rm_remote(remote_name)
#add the gitlab remote to the local copy
self.git_repo.add_remote(remote_name, proj.ssh_url_to_repo)
return proj, self.git_repo
def _push(self, repo, remote_name=None, *push_args):
"""
:param repo: GitRepository
:param remote_name: str
Name of the remote to push to
:param push_args: Additional arguments to be passed to git-push
:return: IterableList(PushInfo, ...)
iterable list of PushInfo instances, each
one informing about an individual head which had been updated on the
remote side.
If the push contains rejected heads, these will have the PushInfo.ERROR
bit set in their flags.
If the operation fails completely, the length of the returned
IterableList will be null.
"""
#push the contents to the gitlab remote
if remote_name is None:
remote_name = self.git_remote_name
try:
infos = repo.push(remote_name, *push_args)
for info in infos:
log.info('Push info: {0} {1} {2} {3}:'
' {4}.'.format(info.flags,
info.local_ref,
info.remote_ref,
info.old_commit,
info.summary))
return infos
except:
raise GitCommandError('Could not push from {0} '
'to {1}'.format(repo.path,
remote_name))
def push_tag(self, proj_name, proj_namespace_id,
remote_name=None, tag_name=None):
"""
:param proj_name: str
:param proj_namespace_id: str or int
:param remote_name: str
Name of the remote to push to
:param tag_name: str
Name of the tag or branch to be pushed to the project
:return: IterableList(PushInfo, ...)
iterable list of PushInfo instances, each
one informing about an individual head which had been updated on the
remote side.
If the push contains rejected heads, these will have the PushInfo.ERROR
bit set in their flags.
If the operation fails completely, the length of the returned
IterableList will be null.
"""
try:
proj, repo = self._prepare_to_push(proj_name, proj_namespace_id,
remote_name)
except:
return None
if tag_name is None:
tag_name = 'master'
#self.git_remote.commit(msg='First commit.')
log.info('Pushing {0}:{4} to {1}/{2} created in '
'Gitlab server {3}.'.format(repo.path,
proj.namespace.name,
proj.name,
proj.ssh_url_to_repo,
tag_name))
return self._push(repo, remote_name, tag_name)
def push_master(self, proj_name, proj_namespace_id, remote_name=None):
"""
:param proj_name: str
:param proj_namespace_id: str or int
:param remote_name: str
Name of the remote to push to
:return: IterableList(PushInfo, ...)
iterable list of PushInfo instances, each
one informing about an individual head which had been updated on the remote
side.
If the push contains rejected heads, these will have the PushInfo.ERROR bit set
in their flags.
If the operation fails completely, the length of the returned IterableList will
be null.
"""
return self.push_tag(proj_name, proj_namespace_id,
remote_name, 'master')
def does_repo_match_remote(self, repo_path):
"""
Returns True if the contents of the repo_path folder matches
the configuration version of self.git_remote
:param repo_path: str
Path to a local copy of a repo
:return:
"""
if self.git_repo is None:
raise GitLocalRepoNotSet('Repository not set.')
remote = GitRepository(self.git_repo.url, self.git_repo.name,
self.git_repo.tag)
remote.set_local_repo(repo_path)
return remote.has_working_copy(check_head_tag=True)
def exists_in_gitlab(self, name, namespace_id=None):
"""
Return True if the project with name in the namespace_id exists,
False otherwise.
:param name: str
:param namespace_id: str or int
:return bool
"""
return self.gitlab.exists_project(name, namespace_id)
def push_project(self, repo, namespace_id=None, creation_args=None):
"""
:param repo:
:return:
"""
#check if namespace exists
if not self.gitlab.has_namespace(namespace_id):
raise GitlabNamespaceNotFound('Namespace {0} does not exits in '
'Gitlab server {1}.'.format(namespace_id,
self.gitlab.gitlab_url))
#try to get the project from Gitlab Server
try:
proj = self.gitlab.get_project(repo.name,
namespace_id=namespace_id)
except:
pass
#add repo to self
self.set_repository_from_repo(repo)
#check if there is a local copy of the repository
#self.set_repository_from_repo(repo)
repo_path = os.path.join(work_dir, repo.name)
has_been_pulled = False
while os.path.exists(repo_path):
repo.set_local_repo(repo_path)
if repo.has_working_copy():
try:
log.info('Pulling repo {0} from '
'{1} to {2}'.format(repo.name, repo.url,
repo_path))
repo.pull()
has_been_pulled = True
break
except:
log.error('Could not pull repo '
'{0} in {1}'.format(repo.name, repo.path))
repo_path += '+'
#clone the repo from the original URL
if not has_been_pulled:
log.info('Clonning repo {0} from '
'{1} to {2}'.format(repo.name, repo.url, repo_path))
repo.clone(repo_path, mkdir=True)
#check if the gitlab project
if not self.exists_in_gitlab(repo.name, namespace_id):
self.create(name=repo.name, namespace_id=namespace_id,
args=creation_args)
if self.get_default_branch(repo.name, namespace_id) is None:
self.push_master(proj_name=repo.name,
proj_namespace_id=namespace_id)
self.push_tag(proj_name=repo.name,
proj_namespace_id=namespace_id,
tag_name=repo.tag)
return self.gitlab.get_project(repo.name, namespace_id=namespace_id)
def clean_files(self):
"""
Removes the cloned repo files in self.work_dir
"""
self.git_repo.clear()
if __name__ == '__main__':
import socket
hn = socket.gethostname()
if hn == 'finn':
work_dir = os.path.expanduser('~/Software')
elif hn == 'corsair' or hn == 'buccaneer':
work_dir = os.path.expanduser('~/Desktop/repos')
#create a default gitlab connection through config files
#read python-gitlab for configuration help
glc = GitlabConnection()
gitlab_mgr = GitlabProjectManager(glc, work_dir=work_dir)
if isinstance(repos, set) or isinstance(repos, list):
repo_set = GitRepositorySet.from_list(repos)
elif isinstance(repos, dict):
repo_set = GitRepositorySet.from_dict(repos)
#test
namespace_id = 6 #bcc_library
creation_args = {'visibility_level': 10,
'snippets_enabled': True,
'wiki_enabled': True,
'merge_requests_enabled': False,
'issues_enabled': True,
'wall_enabled': False}
#creation_args['namespace_id'] = namespace_id
#creation_args['name'] = repo.name
for repo in repo_set:
try:
proj = gitlab_mgr.push_project(repo, namespace_id, creation_args)
except Exception as exc:
log.error(str(exc))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment