Updated: refactor git management utils
This commit is contained in:
Родитель
76a305a349
Коммит
1f17815227
|
@ -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.
|
# 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
|
from .github_connection import GithubConnection
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'RepositoryResponse',
|
|
||||||
'GithubConnection'
|
'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.
|
# Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
# --------------------------------------------------------------------------------------------
|
# --------------------------------------------------------------------------------------------
|
||||||
import os
|
import os
|
||||||
|
# Backward Compatible with Python 2.7
|
||||||
# In python2.7 devnull is not defined in subprocess
|
|
||||||
try:
|
try:
|
||||||
from subprocess import DEVNULL
|
from subprocess import DEVNULL
|
||||||
except ImportError:
|
except ImportError:
|
||||||
DEVNULL = open(os.devnull, 'w')
|
DEVNULL = open(os.devnull, 'w')
|
||||||
|
|
||||||
from subprocess import STDOUT, check_call, check_output, CalledProcessError
|
from subprocess import STDOUT, check_call, check_output, CalledProcessError
|
||||||
from msrest.service_client import ServiceClient
|
from msrest.service_client import ServiceClient
|
||||||
from msrest import Configuration, Deserializer
|
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 ..base.base_manager import BaseManager
|
||||||
from . import models
|
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):
|
class RepositoryManager(BaseManager):
|
||||||
""" Manage DevOps repositories
|
""" Manage DevOps repositories
|
||||||
|
@ -35,6 +42,9 @@ class RepositoryManager(BaseManager):
|
||||||
self._deserialize = Deserializer(client_models)
|
self._deserialize = Deserializer(client_models)
|
||||||
super(RepositoryManager, self).__init__(creds, organization_name=organization_name, project_name=project_name)
|
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):
|
def create_repository(self, repository_name):
|
||||||
"""Create a new azure functions git repository"""
|
"""Create a new azure functions git repository"""
|
||||||
project = self._get_project_by_name(self._project_name)
|
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)
|
repository = self._get_repository_by_name(project, repository_name)
|
||||||
return self._git_client.get_commits(repository.id, None, project=project.id)
|
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"""
|
"""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):
|
remote_name = construct_git_remote_name(self._organization_name, self._project_name, repository_name, remote_prefix)
|
||||||
"""This command sets up the repository locally - it initialises the git file and creates the initial push ect"""
|
remote_url = construct_git_remote_url(self._organization_name, self._project_name, repository_name)
|
||||||
|
|
||||||
if self._repository_exists():
|
if self._repository_exists():
|
||||||
message = """There is already an existing repository in this folder."""
|
git_init()
|
||||||
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)
|
|
||||||
|
|
||||||
def setup_github_repository(self):
|
git_add_remote(remote_name, remote_url)
|
||||||
check_call('git add -A'.split(), stdout=DEVNULL, stderr=STDOUT)
|
git_stage_all()
|
||||||
check_call(["git", "commit", "-a", "-m", "\"creating functions app\""], stdout=DEVNULL, stderr=STDOUT)
|
git_commit("Create function app with azure devops build. Remote repository url: {url}".format(url=remote_url))
|
||||||
check_call('git push'.split(), stdout=DEVNULL, stderr=STDOUT)
|
git_push(remote_name)
|
||||||
|
|
||||||
def _repository_exists(self):
|
def _repository_exists(self):
|
||||||
"""Helper to see if gitfile exists"""
|
"""Helper to see if gitfile exists"""
|
||||||
return bool(os.path.exists('.git'))
|
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):
|
def list_github_repositories(self):
|
||||||
"""List github repositories if there are any from the current connection"""
|
"""List github repositories if there are any from the current connection"""
|
||||||
project = self._get_project_by_name(self._project_name)
|
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)
|
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):
|
def create_service_endpoint(self, servicePrincipalName):
|
||||||
"""Create a new service endpoint within a project with an associated service principal"""
|
"""Create a new service endpoint within a project with an associated service principal"""
|
||||||
project = self._get_project_by_name(self._project_name)
|
project = self._get_project_by_name(self._project_name)
|
||||||
|
@ -59,7 +61,7 @@ class ServiceEndpointManager(BaseManager):
|
||||||
data["scopeLevel"] = "Subscription"
|
data["scopeLevel"] = "Subscription"
|
||||||
|
|
||||||
# A service principal name has to include the http to be valid
|
# 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
|
command = "az ad sp create-for-rbac --o json --name " + servicePrincipalNameHttp
|
||||||
token_resp = subprocess.check_output(command, shell=True).decode()
|
token_resp = subprocess.check_output(command, shell=True).decode()
|
||||||
token_resp_dict = json.loads(token_resp)
|
token_resp_dict = json.loads(token_resp)
|
||||||
|
|
Загрузка…
Ссылка в новой задаче