Updated: refactor git management utils

This commit is contained in:
Hanzhang Zeng (Roger) 2019-03-14 13:56:26 -07:00
Родитель 76a305a349
Коммит 1f17815227
6 изменённых файлов: 124 добавлений и 60 удалений

Просмотреть файл

@ -0,0 +1,7 @@
class BaseException(Exception):
def __init__(self, message=None):
self.message = message
class GitOperationException(BaseException):
pass

Просмотреть файл

@ -0,0 +1,74 @@
import re
# Backward Compatible with Python 2.7
try:
from subprocess import DEVNULL
except ImportError:
DEVNULL = open(os.devnull, 'w')
from subprocess import STDOUT, check_call, check_output, CalledProcessError
from ..exceptions import GitOperationException
def does_git_exist():
try:
check_call("git", stdout=DEVNULL, stderr=STDOUT)
except CalledProcessError:
return False
return True
def does_git_remote_exist(remote_name):
command = ["git", "remote", "show"]
return remote_name in check_output(command).decode('utf-8')
def git_init():
command = ["git", "init"]
try:
check_call(command, stdout=DEVNULL, stderr=STDOUT)
except CalledProcessError:
raise GitOperationException(message=" ".join(command));
def git_add_remote(remote_name, remote_url):
command = ["git", "remote", "add", remote_name, remote_url]
try:
check_call(command, stdout=DEVNULL, stderr=STDOUT)
except CalledProcessError:
raise GitOperationException(message=" ".join(command))
def git_stage_all():
command = ["git", "add", "--all"]
try:
check_call(command, stdout=DEVNULL, stderr=STDOUT)
except CalledProcessError:
raise GitOperationException(message=" ".join(command))
def git_commit(message):
command = ["git", "commit", "--allow-empty", "--message", message]
try:
check_call(command, stdout=DEVNULL, stderr=STDOUT)
except CalledProcessError:
raise GitOperationException(message=" ".join(command))
def git_push(remote_name):
command = ["git", "push", "--all", remote_name]
try:
check_call(command, stdout=DEVNULL, stderr=STDOUT)
except CalledProcessError:
raise GitOperationException(message=" ".join(command))
def _sanitize_git_remote_name(organization_name, project_name, repository_name):
concatenated_remote_name = f"{organization_name}_{project_name}_{repository_name}"
sanitized_remote_name = re.sub(r"[^A-Za-z0-9_-]|\s", "-", concatenated_remote_name)
return sanitized_remote_name
def construct_git_remote_name(organization_name, project_name, repository_name, remote_prefix):
remote_name = "_{prefix}_{name}".format(
prefix=remote_prefix,
name=_sanitized_remote_name(organization_name, project_name, repository_name))
return remote_name
def construct_git_remote_url(organization_name, project_name, repository_name, domain_name="dev.azure.com"):
url = "https://{domain}/{org}/{proj}/_git/{repo}".format(
domain=domain_name,
org=organization_name,
proj=project_name,
repo=repository_name)
return url

Просмотреть файл

@ -3,9 +3,7 @@
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------
from .repository_response import RepositoryResponse
from .github_connection import GithubConnection
__all__ = [
'RepositoryResponse',
'GithubConnection'
]

Просмотреть файл

@ -1,10 +0,0 @@
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------
class RepositoryResponse(object):
def __init__(self, message, succeeded):
self.message = message
self.succeeded = succeeded

Просмотреть файл

@ -3,13 +3,11 @@
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------
import os
# In python2.7 devnull is not defined in subprocess
# Backward Compatible with Python 2.7
try:
from subprocess import DEVNULL
except ImportError:
DEVNULL = open(os.devnull, 'w')
from subprocess import STDOUT, check_call, check_output, CalledProcessError
from msrest.service_client import ServiceClient
from msrest import Configuration, Deserializer
@ -18,7 +16,16 @@ import vsts.git.v4_1.models.git_repository_create_options as git_repository_crea
from ..base.base_manager import BaseManager
from . import models
from .local_git_utils import (
git_init,
git_add_remote,
git_stage_all,
git_commit,
does_git_exist,
does_git_remote_exist,
construct_git_remote_name,
construct_git_remote_url
)
class RepositoryManager(BaseManager):
""" Manage DevOps repositories
@ -35,6 +42,9 @@ class RepositoryManager(BaseManager):
self._deserialize = Deserializer(client_models)
super(RepositoryManager, self).__init__(creds, organization_name=organization_name, project_name=project_name)
def check_if_git_exist(self) -> bool:
return does_git_exist()
def create_repository(self, repository_name):
"""Create a new azure functions git repository"""
project = self._get_project_by_name(self._project_name)
@ -51,58 +61,41 @@ class RepositoryManager(BaseManager):
repository = self._get_repository_by_name(project, repository_name)
return self._git_client.get_commits(repository.id, None, project=project.id)
def setup_remote(self, repository_name, remote_name):
def get_local_git_remote_name(self, repository_name, remote_prefix):
return construct_git_remote_name(self._organization_name, self._project_name, repository_name, remote_prefix)
# Since the portal url and remote url are same. We only need one function to handle portal access and git push
def get_azure_devops_repo_url(self, repository_name):
return construct_git_remote_url(self._organization_name, self._project_name, repository_name)
# Check if the git repository exists first. If it does, check if the git remote exists.
def check_if_local_git_remote_exists(self, repository_name, remote_prefix):
if not self._repository_exists():
return False
remote_name = construct_git_remote_name(self._organization_name, self._project_name, repository_name, remote_prefix)
return does_git_remote_exist(remote_name)
# The function will initialize a git repo, create git remote, stage all changes, commit and push to remote
# Exceptions: GitOperationException
def setup_local_git_repository(self, repository_name, remote_prefix):
"""This command sets up a remote. It is normally used if a user already has a repository locally that they don't wish to get rid of"""
if self._remote_exists(remote_name):
message = """There is already an remote with this name."""
succeeded = False
else:
origin_command = ["git", "remote", "add", remote_name, "https://" + self._organization_name + \
".visualstudio.com/" + self._project_name + "/_git/" + repository_name]
check_call(origin_command, stdout=DEVNULL, stderr=STDOUT)
check_call('git add -A'.split(), stdout=DEVNULL, stderr=STDOUT)
try:
check_call(["git", "commit", "-a", "-m", "\"creating functions app\""], stdout=DEVNULL, stderr=STDOUT)
except CalledProcessError:
print("no need to commit anything")
check_call(('git push ' + remote_name + ' --all').split(), stdout=DEVNULL, stderr=STDOUT)
message = "succeeded"
succeeded = True
return models.repository_response.RepositoryResponse(message, succeeded)
def setup_repository(self, repository_name):
"""This command sets up the repository locally - it initialises the git file and creates the initial push ect"""
remote_name = construct_git_remote_name(self._organization_name, self._project_name, repository_name, remote_prefix)
remote_url = construct_git_remote_url(self._organization_name, self._project_name, repository_name)
if self._repository_exists():
message = """There is already an existing repository in this folder."""
succeeded = False
else:
origin_command = ["git", "remote", "add", "origin", "https://" + self._organization_name + \
".visualstudio.com/" + self._project_name + "/_git/" + repository_name]
check_call('git init'.split(), stdout=DEVNULL, stderr=STDOUT)
check_call('git add -A'.split(), stdout=DEVNULL, stderr=STDOUT)
check_call(["git", "commit", "-a", "-m", "\"creating functions app\""], stdout=DEVNULL, stderr=STDOUT)
check_call(origin_command, stdout=DEVNULL, stderr=STDOUT)
check_call('git push -u origin --all'.split(), stdout=DEVNULL, stderr=STDOUT)
message = "succeeded"
succeeded = True
return models.repository_response.RepositoryResponse(message, succeeded)
git_init()
def setup_github_repository(self):
check_call('git add -A'.split(), stdout=DEVNULL, stderr=STDOUT)
check_call(["git", "commit", "-a", "-m", "\"creating functions app\""], stdout=DEVNULL, stderr=STDOUT)
check_call('git push'.split(), stdout=DEVNULL, stderr=STDOUT)
git_add_remote(remote_name, remote_url)
git_stage_all()
git_commit("Create function app with azure devops build. Remote repository url: {url}".format(url=remote_url))
git_push(remote_name)
def _repository_exists(self):
"""Helper to see if gitfile exists"""
return bool(os.path.exists('.git'))
def _remote_exists(self, remote_name):
lines = (check_output('git remote show'.split())).decode('utf-8').split('\n')
for line in lines:
if line == remote_name:
return True
return False
def list_github_repositories(self):
"""List github repositories if there are any from the current connection"""
project = self._get_project_by_name(self._project_name)

Просмотреть файл

@ -44,6 +44,8 @@ class ServiceEndpointManager(BaseManager):
return self._service_endpoint_client.create_service_endpoint(service_endpoint, project.id)
# This function requires user permission of Microsoft.Authorization/roleAssignments/write
# i.e. only the owner of the subscription can use this function
def create_service_endpoint(self, servicePrincipalName):
"""Create a new service endpoint within a project with an associated service principal"""
project = self._get_project_by_name(self._project_name)
@ -59,7 +61,7 @@ class ServiceEndpointManager(BaseManager):
data["scopeLevel"] = "Subscription"
# A service principal name has to include the http to be valid
servicePrincipalNameHttp = "http://" + servicePrincipalName
servicePrincipalNameHttp = "https://dev.azure.com/" + servicePrincipalName
command = "az ad sp create-for-rbac --o json --name " + servicePrincipalNameHttp
token_resp = subprocess.check_output(command, shell=True).decode()
token_resp_dict = json.loads(token_resp)