pax_global_header00006660000000000000000000000064133077177150014525gustar00rootroot0000000000000052 comment=2649d236be94d119b13e0ac607964c94a9e51fde vsts-cd-manager-1.0.2/000077500000000000000000000000001330771771500145205ustar00rootroot00000000000000vsts-cd-manager-1.0.2/.gitignore000066400000000000000000000020561330771771500165130ustar00rootroot00000000000000# PyCharm folder .idea/ # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] *$py.class # C extensions *.so # Distribution / packaging .Python env/ build/ develop-eggs/ dist/ downloads/ eggs/ .eggs/ lib/ lib64/ parts/ sdist/ var/ *.egg-info/ .installed.cfg *.egg # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest *.spec # Installer logs pip-log.txt pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ .coverage .coverage.* .cache nosetests.xml coverage.xml *,cover .hypothesis/ # Translations *.mo *.pot # Django stuff: *.log local_settings.py # Flask stuff: instance/ .webassets-cache # Scrapy stuff: .scrapy # Sphinx documentation docs/_build/ # PyBuilder target/ # IPython Notebook .ipynb_checkpoints # pyenv .python-version # celery beat schedule file celerybeat-schedule # dotenv .env # virtualenv venv/ ENV/ # Spyder project settings .spyderproject # Rope project settings .ropeproject vsts-cd-manager-1.0.2/LICENSE000066400000000000000000000022371330771771500155310ustar00rootroot00000000000000 MIT License Copyright (c) Microsoft Corporation. All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE vsts-cd-manager-1.0.2/README.rst000066400000000000000000000031521330771771500162100ustar00rootroot00000000000000Visual Studio Team Services Continuous Delivery Manager ======================================================= This project provides the class ContinuousDeliveryManager and supporting classes. This CD manager class allows the caller to manage Azure Continuous Delivery pipelines that are maintained within a VSTS account. Contribute Code =============== This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. Packaging ========= The released packages for this code can be found here https://pypi.python.org/pypi/vsts-cd-manager. Use the standard PYPI packaging flow to push a new release. Make sure to increment the version number appropriately. *Example* :: python setup.py sdist python -m twine upload dist/* :: Running Tests ============= The only class we have unit tests for is the ContinuousDeliveryManager class. As features are added, add tests and maintain a high (>80%) code coverage number for this class. You can run these tests in the following way: :: python tests/test_continuous_delivery_manager.py :: Code Coverage ============= Code coverage for the vsts_cd_manager.py file should be kept current with any new features. Most of the other code is boiler plate REST API code that could be generated for the most part. You can find out more about how to run code coverage here: https://coverage.readthedocs.io/en/coverage-4.4vsts-cd-manager-1.0.2/__init__.py000066400000000000000000000005321330771771500166310ustar00rootroot00000000000000# -------------------------------------------------------------------------------------------- # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License.txt in the project root for license information. # -------------------------------------------------------------------------------------------- vsts-cd-manager-1.0.2/aex_accounts/000077500000000000000000000000001330771771500171745ustar00rootroot00000000000000vsts-cd-manager-1.0.2/aex_accounts/__init__.py000066400000000000000000000006231330771771500213060ustar00rootroot00000000000000# coding=utf-8 # -------------------------------------------------------------------------- # Code generated by Microsoft (R) AutoRest Code Generator 1.0.1.0 # Changes may cause incorrect behavior and will be lost if the code is # regenerated. # -------------------------------------------------------------------------- from .account import Account from .version import VERSION __all__ = ['Account'] vsts-cd-manager-1.0.2/aex_accounts/account.py000066400000000000000000000047621330771771500212130ustar00rootroot00000000000000 from __future__ import print_function from sys import stderr from msrest.service_client import ServiceClient from msrest import Configuration, Deserializer from msrest.exceptions import HttpOperationError from .version import VERSION from . import models class AccountConfiguration(Configuration): def __init__(self, api_version, base_url=None): super(AccountConfiguration, self).__init__(base_url) self.add_user_agent('azurecli/vsts/{}'.format(VERSION)) self.api_version = api_version class Account(object): def __init__(self, api_version, base_url=None, creds=None): self.config = AccountConfiguration(api_version, base_url) self._client = ServiceClient(creds, self.config) client_models = {k: v for k, v in models.__dict__.items() if isinstance(v, type)} self._deserialize = Deserializer(client_models) self.api_version = api_version def create_account(self, collection_name, preferred_region): # Construct URL url = '/_apis/hostacquisition/collections' # Construct parameters query_parameters = {} query_parameters["api-version"] = self.api_version query_parameters["collectionName"] = collection_name query_parameters["preferredRegion"] = preferred_region # Construct and send request request = self._client.post(url, query_parameters) response = self._client.send(request) # Handle Response deserialized = None if response.status_code not in [200]: print("POST", request.url, file=stderr) print("response:", response.status_code, file=stderr) print(response.text, file=stderr) raise HttpOperationError(self._deserialize, response) else: deserialized = self._deserialize('Collection', response) return deserialized def regions(self): # Construct URL url = '/_apis/hostacquisition/regions' # Construct and send request request = self._client.get(url) response = self._client.send(request) # Handle Response deserialized = None if response.status_code not in [200]: print("GET", request.url, file=stderr) print("response:", response.status_code, file=stderr) print(response.text, file=stderr) raise HttpOperationError(self._deserialize, response) else: deserialized = self._deserialize('Regions', response) return deserializedvsts-cd-manager-1.0.2/aex_accounts/models/000077500000000000000000000000001330771771500204575ustar00rootroot00000000000000vsts-cd-manager-1.0.2/aex_accounts/models/__init__.py000066400000000000000000000010701330771771500225660ustar00rootroot00000000000000# coding=utf-8 # -------------------------------------------------------------------------- # Code generated by Microsoft (R) AutoRest Code Generator 1.0.1.0 # Changes may cause incorrect behavior and will be lost if the code is # regenerated. # -------------------------------------------------------------------------- from .collection import Collection from .name_availability import NameAvailability from .region_details import RegionDetails from .regions import Regions __all__ = [ 'Collection', 'NameAvailability', 'RegionDetails', 'Regions', ]vsts-cd-manager-1.0.2/aex_accounts/models/collection.py000066400000000000000000000004251330771771500231650ustar00rootroot00000000000000 from msrest.serialization import Model class Collection(Model): _attribute_map = { 'id': {'key': 'id', 'type': 'str'}, 'name': {'key': 'name', 'type': 'str'}, } def __init__(self, id=None, name=None): self.id = id self.name = namevsts-cd-manager-1.0.2/aex_accounts/models/name_availability.py000066400000000000000000000007661330771771500245140ustar00rootroot00000000000000 from msrest.serialization import Model class NameAvailability(Model): _attribute_map = { 'name': {'key': 'name', 'type': 'str'}, 'is_available': {'key': 'is_available', 'type': 'str'}, 'unavailability_reason': {'key': 'unavailability_reason', 'type': 'str'}, } def __init__(self, name=None, is_available=None, unavailability_reason=None): self.name = name self.is_available = is_available self.unavailability_reason = unavailability_reasonvsts-cd-manager-1.0.2/aex_accounts/models/region_details.py000066400000000000000000000006731330771771500240270ustar00rootroot00000000000000 from msrest.serialization import Model class RegionDetails(Model): _attribute_map = { 'name': {'key': 'name', 'type': 'str'}, 'display_name': {'key': 'displayName', 'type': 'str'}, 'is_default': {'key': 'is_default', 'type': 'str'}, } def __init__(self, name=None, display_name=None, is_default=None): self.name = name self.display_name = display_name self.is_default = is_defaultvsts-cd-manager-1.0.2/aex_accounts/models/regions.py000066400000000000000000000004621330771771500225010ustar00rootroot00000000000000 from msrest.serialization import Model class Regions(Model): _attribute_map = { 'count': {'key': 'count', 'type': 'int'}, 'value': {'key': 'value', 'type': '[RegionDetails]'}, } def __init__(self, count=None, value=None): self.count = count self.value = valuevsts-cd-manager-1.0.2/aex_accounts/version.py000066400000000000000000000005531330771771500212360ustar00rootroot00000000000000# coding=utf-8 # -------------------------------------------------------------------------- # Code generated by Microsoft (R) AutoRest Code Generator 1.0.1.0 # Changes may cause incorrect behavior and will be lost if the code is # regenerated. # -------------------------------------------------------------------------- #PyPI version of package VERSION = "1.0" vsts-cd-manager-1.0.2/continuous_delivery/000077500000000000000000000000001330771771500206315ustar00rootroot00000000000000vsts-cd-manager-1.0.2/continuous_delivery/__init__.py000066400000000000000000000007151330771771500227450ustar00rootroot00000000000000# coding=utf-8 # -------------------------------------------------------------------------- # Code generated by Microsoft (R) AutoRest Code Generator 1.0.1.0 # Changes may cause incorrect behavior and will be lost if the code is # regenerated. # -------------------------------------------------------------------------- from .continuous_delivery import ContinuousDelivery from .version import VERSION __all__ = ['ContinuousDelivery'] __version__ = VERSION vsts-cd-manager-1.0.2/continuous_delivery/continuous_delivery.py000066400000000000000000000161021330771771500253140ustar00rootroot00000000000000# coding=utf-8 # -------------------------------------------------------------------------- # Code generated by Microsoft (R) AutoRest Code Generator 1.0.1.0 # Changes may cause incorrect behavior and will be lost if the code is # regenerated. # -------------------------------------------------------------------------- from __future__ import print_function from sys import stderr from msrest.service_client import ServiceClient from msrest import Configuration, Serializer, Deserializer from msrest.pipeline import ClientRawResponse from msrest.exceptions import HttpOperationError from .version import VERSION from . import models class ContinuousDeliveryConfiguration(Configuration): """Configuration for Continuous Delivery Note that all parameters used to create this instance are saved as instance attributes. :param api_version: Version of the API to use. This should be set to '3.2-preview' to use this version of the api. :type api_version: str :param str base_url: Service URL """ def __init__( self, api_version, base_url=None): if api_version is None: raise ValueError("Parameter 'api_version' must not be None.") if not isinstance(api_version, str): raise TypeError("Parameter 'api_version' must be str.") if not base_url: base_url = 'https://{account}.portalext.visualstudio.com/' super(ContinuousDeliveryConfiguration, self).__init__(base_url) self.add_user_agent('azurecli/{} continuousdelivery/{}'.format(VERSION, VERSION)) self.api_version = api_version class ContinuousDelivery(object): """ContinuousDelivery :ivar config: Configuration for client. :vartype config: AzureTfsConfiguration :param api_version: Version of the API to use. This should be set to '3.2-preview' to use this version of the api. :type api_version: str :param str base_url: Service URL :param Credentials creds: credentials for vsts """ def __init__( self, api_version, base_url=None, creds=None): self.config = ContinuousDeliveryConfiguration(api_version, base_url) self._client = ServiceClient(creds, self.config) client_models = {k: v for k, v in models.__dict__.items() if isinstance(v, type)} self.api_version = '3.2' if not api_version else api_version self._serialize = Serializer(client_models) self._deserialize = Deserializer(client_models) def provisioning_configuration( self, body, custom_headers=None, raw=False, **operation_config): """ProvisioningConfiguration. :param body: :type body: :class:`ContinuousDeploymentConfiguration ` :param dict custom_headers: headers that will be added to the request :param bool raw: returns the direct response alongside the deserialized response :param operation_config: :ref:`Operation configuration overrides`. :rtype: :class:`ContinuousDeploymentOperation ` :rtype: :class:`ClientRawResponse` if raw=true :raises: :class:`HttpOperationError` """ # Construct URL url = '/_apis/continuousdelivery/provisioningconfigurations' # Construct parameters query_parameters = {} if self.api_version: query_parameters["api-version"] = self.api_version # Construct headers header_parameters = {} header_parameters['Content-Type'] = 'application/json; charset=utf-8' if custom_headers: header_parameters.update(custom_headers) # Construct body body_content = self._serialize.body(body, 'ProvisioningConfiguration') # Construct and send request request = self._client.post(url, query_parameters) response = self._client.send( request, header_parameters, body_content, **operation_config) if response.status_code not in [200, 202]: print("POST", request.url, file=stderr) print("response:", response.status_code, file=stderr) print(response.text, file=stderr) raise HttpOperationError(self._deserialize, response) deserialized = None if response.status_code == 200: deserialized = self._deserialize('ProvisioningConfiguration', response) if response.status_code == 202: deserialized = self._deserialize('ProvisioningConfiguration', response) if raw: client_raw_response = ClientRawResponse(deserialized, response) return client_raw_response return deserialized def get_provisioning_configuration( self, provisioning_configuration_id, custom_headers=None, raw=False, **operation_config): """GetContinuousDeploymentOperation. :param provisioning_configuration_id: :type provisioning_configuration_id: str :param dict custom_headers: headers that will be added to the request :param bool raw: returns the direct response alongside the deserialized response :param operation_config: :ref:`Operation configuration overrides`. :rtype: :class:`ContinuousDeploymentOperation ` :rtype: :class:`ClientRawResponse` if raw=true :raises: :class:`HttpOperationError` """ # Construct URL url = '/_apis/continuousdelivery/provisioningconfigurations/{provisioningConfigurationId}' path_format_arguments = { 'provisioningConfigurationId': self._serialize.url("provisioning_configuration_id", provisioning_configuration_id, 'str') } url = self._client.format_url(url, **path_format_arguments) # Construct parameters query_parameters = {} # Construct headers header_parameters = {} header_parameters['Content-Type'] = 'application/json; charset=utf-8' if custom_headers: header_parameters.update(custom_headers) # Construct and send request request = self._client.get(url, query_parameters) response = self._client.send(request, header_parameters, **operation_config) if response.status_code not in [200]: print("GET", request.url, file=stderr) print("response:", response.status_code, file=stderr) print(response.text, file=stderr) raise HttpOperationError(self._deserialize, response) deserialized = None if response.status_code == 200: deserialized = self._deserialize('ProvisioningConfiguration', response) if raw: client_raw_response = ClientRawResponse(deserialized, response) return client_raw_response return deserialized vsts-cd-manager-1.0.2/continuous_delivery/credentials.py000066400000000000000000000006731330771771500235060ustar00rootroot00000000000000# coding=utf-8 # -------------------------------------------------------------------------- # Code generated by Microsoft (R) AutoRest Code Generator 1.0.1.0 # Changes may cause incorrect behavior and will be lost if the code is # regenerated. # -------------------------------------------------------------------------- from msrest.authentication import ( BasicAuthentication, BasicTokenAuthentication, OAuthTokenAuthentication) vsts-cd-manager-1.0.2/continuous_delivery/exceptions.py000066400000000000000000000010401330771771500233570ustar00rootroot00000000000000# coding=utf-8 # -------------------------------------------------------------------------- # Code generated by Microsoft (R) AutoRest Code Generator 1.0.1.0 # Changes may cause incorrect behavior and will be lost if the code is # regenerated. # -------------------------------------------------------------------------- from msrest.exceptions import ( ClientException, SerializationError, DeserializationError, TokenExpiredError, ClientRequestError, AuthenticationError, HttpOperationError, ValidationError, ) vsts-cd-manager-1.0.2/continuous_delivery/models/000077500000000000000000000000001330771771500221145ustar00rootroot00000000000000vsts-cd-manager-1.0.2/continuous_delivery/models/__init__.py000066400000000000000000000024551330771771500242330ustar00rootroot00000000000000# coding=utf-8 # -------------------------------------------------------------------------- # Code generated by Microsoft (R) AutoRest Code Generator 1.0.1.0 # Changes may cause incorrect behavior and will be lost if the code is # regenerated. # -------------------------------------------------------------------------- from .authorization_info import AuthorizationInfo from .authorization_info_parameters import AuthorizationInfoParameters from .build_configuration import BuildConfiguration from .ci_artifact import CiArtifact from .ci_configuration import CiConfiguration from .ci_result import CiResult from .provisioning_configuration import ProvisioningConfiguration from .provisioning_configuration_source import ProvisioningConfigurationSource from .provisioning_configuration_target import ProvisioningConfigurationTarget from .slot_swap_configuration import SlotSwapConfiguration from .source_repository import SourceRepository from .create_options import CreateOptions __all__ = [ 'AuthorizationInfo', 'AuthorizationInfoParameters', 'BuildConfiguration', 'CiArtifact', 'CiConfiguration', 'CiResult', 'ProvisioningConfiguration', 'ProvisioningConfigurationSource', 'ProvisioningConfigurationTarget', 'SlotSwapConfiguration', 'SourceRepository', 'CreateOptions', ] vsts-cd-manager-1.0.2/continuous_delivery/models/authorization_info.py000066400000000000000000000012511330771771500264000ustar00rootroot00000000000000# coding=utf-8 # -------------------------------------------------------------------------- # Code generated by Microsoft (R) AutoRest Code Generator 1.0.1.0 # Changes may cause incorrect behavior and will be lost if the code is # regenerated. # -------------------------------------------------------------------------- from msrest.serialization import Model class AuthorizationInfo(Model): _attribute_map = { 'scheme': {'key': 'scheme', 'type': 'str'}, 'parameters': {'key': 'parameters', 'type': 'AuthorizationInfoParameters'}, } def __init__(self, scheme=None, parameters=None): self.scheme = scheme self.parameters = parameters vsts-cd-manager-1.0.2/continuous_delivery/models/authorization_info_parameters.py000066400000000000000000000016251330771771500306300ustar00rootroot00000000000000# coding=utf-8 # -------------------------------------------------------------------------- # Code generated by Microsoft (R) AutoRest Code Generator 1.0.1.0 # Changes may cause incorrect behavior and will be lost if the code is # regenerated. # -------------------------------------------------------------------------- from msrest.serialization import Model class AuthorizationInfoParameters(Model): _attribute_map = { 'authorization': {'key': 'Authorization', 'type': 'str'}, 'access_token': {'key': 'AccessToken', 'type': 'str'}, 'username': {'key': 'username', 'type': 'str'}, 'password': {'key': 'password', 'type': 'str'} } def __init__(self, authorization=None, access_token=None, username=None, password=None): self.authorization = authorization self.access_token = access_token self.username = username self.password = passwordvsts-cd-manager-1.0.2/continuous_delivery/models/build_configuration.py000066400000000000000000000025711330771771500265210ustar00rootroot00000000000000# coding=utf-8 # -------------------------------------------------------------------------- # Code generated by Microsoft (R) AutoRest Code Generator 1.0.1.0 # Changes may cause incorrect behavior and will be lost if the code is # regenerated. # -------------------------------------------------------------------------- from msrest.serialization import Model class BuildConfiguration(Model): _attribute_map = { 'type': {'key': 'type', 'type': 'str'}, 'working_directory': {'key': 'workingDirectory', 'type': 'str'}, 'node_type': {'key': 'NodeJsTaskRunner', 'type': 'str'}, 'python_framework': {'key': 'pythonFramework', 'type': 'str'}, 'python_version': {'key': 'pythonExtensionId', 'type': 'str'}, 'django_settings_module': {'key': 'djangoSettingsModule', 'type': 'str'}, 'flask_project_name': {'key': 'flaskProjectName', 'type': 'str'} } def __init__(self, type=None, working_directory=None, node_type=None, python_framework=None, python_version=None, django_settings_module=None, flask_project_name=None): self.type = type self.working_directory = working_directory self.node_type = node_type self.python_framework = python_framework self.python_version = python_version self.django_settings_module = django_settings_module self.flask_project_name = flask_project_name vsts-cd-manager-1.0.2/continuous_delivery/models/ci_artifact.py000066400000000000000000000012471330771771500247420ustar00rootroot00000000000000# coding=utf-8 # -------------------------------------------------------------------------- # Code generated by Microsoft (R) AutoRest Code Generator 1.0.1.0 # Changes may cause incorrect behavior and will be lost if the code is # regenerated. # -------------------------------------------------------------------------- from msrest.serialization import Model class CiArtifact(Model): _attribute_map = { 'id': {'key': 'id', 'type': 'str'}, 'name': {'key': 'name', 'type': 'str'}, 'url': {'key': 'url', 'type': 'str'}, } def __init__(self, id=None, name=None, url=None): self.id = id self.name = name self.url = url vsts-cd-manager-1.0.2/continuous_delivery/models/ci_configuration.py000066400000000000000000000017021330771771500260100ustar00rootroot00000000000000# coding=utf-8 # -------------------------------------------------------------------------- # Code generated by Microsoft (R) AutoRest Code Generator 1.0.1.0 # Changes may cause incorrect behavior and will be lost if the code is # regenerated. # -------------------------------------------------------------------------- from msrest.serialization import Model class CiConfiguration(Model): _attribute_map = { 'project': {'key': 'project', 'type': 'CiArtifact'}, 'build_definition': {'key': 'buildDefinition', 'type': 'CiArtifact'}, 'release_definition': {'key': 'releaseDefinition', 'type': 'CiArtifact'}, 'result': {'key': 'result', 'type': 'CiResult'}, } def __init__(self, project=None, build_definition=None, release_definition=None, result=None): self.project = project self.build_definition = build_definition self.release_definition = release_definition self.result = result vsts-cd-manager-1.0.2/continuous_delivery/models/ci_result.py000066400000000000000000000012331330771771500244560ustar00rootroot00000000000000# coding=utf-8 # -------------------------------------------------------------------------- # Code generated by Microsoft (R) AutoRest Code Generator 1.0.1.0 # Changes may cause incorrect behavior and will be lost if the code is # regenerated. # -------------------------------------------------------------------------- from msrest.serialization import Model class CiResult(Model): _attribute_map = { 'status': {'key': 'status', 'type': 'str'}, 'status_message': {'key': 'statusMessage', 'type': 'str'}, } def __init__(self, status=None, status_message=None): self.status = status self.status_message = status_message vsts-cd-manager-1.0.2/continuous_delivery/models/create_options.py000066400000000000000000000011711330771771500255040ustar00rootroot00000000000000from msrest.serialization import Model class CreateOptions(Model): _attribute_map = { 'app_service_plan_name': {'key': 'appServicePlanName', 'type': 'str'}, 'app_service_pricing_tier': {'key': 'appServicePricingTier', 'type': 'str'}, 'base_app_service_name': {'key': 'baseAppServiceName', 'type': 'str'} } def __init__(self, app_service_plan_name=None, app_service_pricing_tier=None, base_app_service_name=None): self.app_service_plan_name = app_service_plan_name self.app_service_pricing_tier = app_service_pricing_tier self.base_app_service_name = base_app_service_namevsts-cd-manager-1.0.2/continuous_delivery/models/provisioning_configuration.py000066400000000000000000000016511330771771500301460ustar00rootroot00000000000000# coding=utf-8 # -------------------------------------------------------------------------- # Code generated by Microsoft (R) AutoRest Code Generator 1.0.1.0 # Changes may cause incorrect behavior and will be lost if the code is # regenerated. # -------------------------------------------------------------------------- from msrest.serialization import Model class ProvisioningConfiguration(Model): _attribute_map = { 'id': {'key': 'id', 'type': 'str'}, 'source': {'key': 'source', 'type': 'ProvisioningConfigurationSource'}, 'targets': {'key': 'targets', 'type': '[ProvisioningConfigurationTarget]'}, 'ci_configuration': {'key': 'ciConfiguration', 'type': 'CiConfiguration'}, } def __init__(self, id=None, source=None, targets=None, ci_configuration=None): self.id = id self.source = source self.targets = targets self.ci_configuration = ci_configuration vsts-cd-manager-1.0.2/continuous_delivery/models/provisioning_configuration_source.py000066400000000000000000000015171330771771500315270ustar00rootroot00000000000000# coding=utf-8 # -------------------------------------------------------------------------- # Code generated by Microsoft (R) AutoRest Code Generator 1.0.1.0 # Changes may cause incorrect behavior and will be lost if the code is # regenerated. # -------------------------------------------------------------------------- from msrest.serialization import Model class ProvisioningConfigurationSource(Model): _attribute_map = { 'type': {'key': 'type', 'type': 'str'}, 'repository': {'key': 'repository', 'type': 'SourceRepository'}, 'build_configuration': {'key': 'buildConfiguration', 'type': 'BuildConfiguration'}, } def __init__(self, type=None, repository=None, build_configuration=None): self.type = type self.repository = repository self.build_configuration = build_configuration vsts-cd-manager-1.0.2/continuous_delivery/models/provisioning_configuration_target.py000066400000000000000000000043741330771771500315210ustar00rootroot00000000000000# coding=utf-8 # -------------------------------------------------------------------------- # Code generated by Microsoft (R) AutoRest Code Generator 1.0.1.0 # Changes may cause incorrect behavior and will be lost if the code is # regenerated. # -------------------------------------------------------------------------- from msrest.serialization import Model class ProvisioningConfigurationTarget(Model): _attribute_map = { 'provider': {'key': 'provider', 'type': 'str'}, 'target_type': {'key': 'type', 'type': 'str'}, 'environment_type': {'key': 'environmentType', 'type': 'str'}, 'friendly_name': {'key': 'friendlyName', 'type': 'str'}, 'subscription_id': {'key': 'subscriptionId', 'type': 'str'}, 'subscription_name': {'key': 'subscriptionName', 'type': 'str'}, 'tenant_id': {'key': 'tenantId', 'type': 'str'}, 'resource_identifier': {'key': 'resourceIdentifier', 'type': 'str'}, 'resource_group_name': {'key': 'resourceGroupName', 'type': 'str'}, 'location': {'key': 'location', 'type': 'str'}, 'authorization_info': {'key': 'authorizationInfo', 'type': 'AuthorizationInfo'}, 'slot_swap_configuration': {'key': 'slotSwapConfiguration', 'type': 'SlotSwapConfiguration'}, 'create_options': {'key': 'createOptions', 'type': 'CreateOptions'} } def __init__(self, provider=None, target_type=None, environment_type=None, friendly_name=None, subscription_id=None, subscription_name=None, tenant_id=None, resource_identifier=None, resource_group_name=None, location=None, authorization_info=None, slot_swap_configuration=None, create_options=None): self.provider = provider self.target_type = target_type self.environment_type = environment_type self.friendly_name = friendly_name self.subscription_id = subscription_id self.subscription_name = subscription_name self.tenant_id = tenant_id self.resource_identifier = resource_identifier self.resource_group_name = resource_group_name self.location = location self.authorization_info = authorization_info self.slot_swap_configuration = slot_swap_configuration self.create_options = create_optionsvsts-cd-manager-1.0.2/continuous_delivery/models/slot_swap_configuration.py000066400000000000000000000010611330771771500274260ustar00rootroot00000000000000# coding=utf-8 # -------------------------------------------------------------------------- # Code generated by Microsoft (R) AutoRest Code Generator 1.0.1.0 # Changes may cause incorrect behavior and will be lost if the code is # regenerated. # -------------------------------------------------------------------------- from msrest.serialization import Model class SlotSwapConfiguration(Model): _attribute_map = { 'slot_name': {'key': 'slotName', 'type': 'str'}, } def __init__(self, slot_name=None): self.slot_name = slot_name vsts-cd-manager-1.0.2/continuous_delivery/models/source_repository.py000066400000000000000000000016541330771771500262730ustar00rootroot00000000000000# coding=utf-8 # -------------------------------------------------------------------------- # Code generated by Microsoft (R) AutoRest Code Generator 1.0.1.0 # Changes may cause incorrect behavior and will be lost if the code is # regenerated. # -------------------------------------------------------------------------- from msrest.serialization import Model class SourceRepository(Model): _attribute_map = { 'type': {'key': 'type', 'type': 'str'}, 'identifier': {'key': 'id', 'type': 'str'}, 'default_branch': {'key': 'defaultBranch', 'type': 'str'}, 'authorization_info': {'key': 'authorizationInfo', 'type': 'AuthorizationInfo'}, } def __init__(self, type=None, identifier=None, default_branch=None, authorization_info = None): self.type = type self.identifier = identifier self.default_branch = default_branch self.authorization_info = authorization_info vsts-cd-manager-1.0.2/continuous_delivery/version.py000066400000000000000000000005231330771771500226700ustar00rootroot00000000000000# coding=utf-8 # -------------------------------------------------------------------------- # Code generated by Microsoft (R) AutoRest Code Generator 1.0.1.0 # Changes may cause incorrect behavior and will be lost if the code is # regenerated. # -------------------------------------------------------------------------- VERSION = "3.2" vsts-cd-manager-1.0.2/setup.py000066400000000000000000000017301330771771500162330ustar00rootroot00000000000000#------------------------------------------------------------------------- # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License.txt in the project root for # license information. #-------------------------------------------------------------------------- from setuptools import setup, find_packages NAME = "vsts-cd-manager" VERSION = "1.0.2" # To install the library, run the following # # python setup.py install # # prerequisite: setuptools # http://pypi.python.org/pypi/setuptools REQUIRES = ["msrest>=0.2.0", 'mock'] setup( name=NAME, version=VERSION, description="Python wrapper around some of the VSTS APIs", author_email="jpricket@microsoft.com", url="https://github.com/microsoft/vsts-cd-manager", keywords=["Microsoft", "VSTS", "Team Services", "SDK", "AzureTfs"], install_requires=REQUIRES, packages=find_packages(), include_package_data=True, long_description="""\ """ ) vsts-cd-manager-1.0.2/tests/000077500000000000000000000000001330771771500156625ustar00rootroot00000000000000vsts-cd-manager-1.0.2/tests/test_continuous_delivery_manager.py000066400000000000000000000272321330771771500251040ustar00rootroot00000000000000# -------------------------------------------------------------------------------------------- # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License.txt in the project root for license information. # -------------------------------------------------------------------------------------------- from __future__ import print_function import unittest from continuous_delivery.models import CiResult from continuous_delivery.models import CiArtifact from continuous_delivery.models import CiConfiguration from continuous_delivery.models import ProvisioningConfigurationSource from continuous_delivery.models import ProvisioningConfiguration from mock import patch, Mock from aex_accounts.models import Collection from vsts_info_provider.models import TeamProjectInfo, RepositoryInfo, CollectionInfo, VstsInfo from vsts_cd_manager.continuous_delivery_manager import ContinuousDeliveryManager class TestContinousDeliveryManager(unittest.TestCase): def fake_callback(self): pass def test_constructor(self): cdman = ContinuousDeliveryManager(None) cdman = ContinuousDeliveryManager(self.fake_callback) def test_get_vsts_app_id(self): cdman = ContinuousDeliveryManager(None) self.assertEqual('499b84ac-1321-427f-aa17-267ca6975798', cdman.get_vsts_app_id()) def test_set_azure_web_info(self): cdman = ContinuousDeliveryManager(None) cdman.set_azure_web_info('group1', 'web1', 'fakeCreds', 'sub1', 'subname1', 'tenant1', 'South Central US') self.assertEqual('fakeCreds', cdman._azure_info.credentials) self.assertEqual('group1', cdman._azure_info.resource_group_name) self.assertEqual('sub1', cdman._azure_info.subscription_id) self.assertEqual('subname1', cdman._azure_info.subscription_name) self.assertEqual('tenant1', cdman._azure_info.tenant_id) self.assertEqual('South Central US', cdman._azure_info.webapp_location) self.assertEqual('web1', cdman._azure_info.website_name) cdman.set_azure_web_info(None, None, None, None, None, None, None) self.assertEqual(None, cdman._azure_info.credentials) self.assertEqual(None, cdman._azure_info.resource_group_name) self.assertEqual(None, cdman._azure_info.subscription_id) self.assertEqual(None, cdman._azure_info.subscription_name) self.assertEqual(None, cdman._azure_info.tenant_id) self.assertEqual(None, cdman._azure_info.webapp_location) self.assertEqual(None, cdman._azure_info.website_name) def test_set_repository_info(self): cdman = ContinuousDeliveryManager(None) cdman.set_repository_info('repoUrl1', 'master1', 'token1', 'username', 'password') self.assertEqual('master1', cdman._repo_info.branch) self.assertEqual('token1', cdman._repo_info.git_token) self.assertEqual('repoUrl1', cdman._repo_info.url) self.assertEqual('username', cdman._repo_info._private_repo_username) self.assertEqual('password', cdman._repo_info._private_repo_password) cdman.set_repository_info(None, None, None, None, None) self.assertEqual(None, cdman._repo_info.branch) self.assertEqual(None, cdman._repo_info.git_token) self.assertEqual(None, cdman._repo_info.url) self.assertEqual(None, cdman._repo_info._private_repo_username) self.assertEqual(None, cdman._repo_info._private_repo_password) @patch("vsts_cd_manager.continuous_delivery_manager.ContinuousDelivery") @patch("vsts_cd_manager.continuous_delivery_manager.Account") def test_setup_continuous_delivery___create_account(self, mock_account, mock_cd): # Mock the CD Client mocked_cd = mock_cd.return_value mocked_cd.provisioning_configuration.return_value = self._get_provisioning_config('queued', '') mocked_cd.get_provisioning_configuration.return_value = self._get_provisioning_config('succeeded', '') # Mock the Account Client mocked_account = mock_account.return_value mocked_account.create_account.return_value = Collection('111', 'collection111') mocked_account.account_exists.return_value = False # create CD manager cdman = ContinuousDeliveryManager(None) # Mock the vsts info call cdman._get_vsts_info = self._mock_get_vsts_info # set required values cdman.set_azure_web_info('group1', 'web1', 'fakeCreds', 'sub1', 'subname1', 'tenant1', 'South Central US') cdman.set_repository_info('repoUrl1', 'master1', 'token1', None, None) cd_app_type = 'AspNet' app_type_details = self.create_cd_app_type_details_map(cd_app_type, None, None, None, None) # call setup result = cdman.setup_continuous_delivery('staging', app_type_details, "https://account1.visualstudio.com", True, 'token2', None, None) self.assertEqual('SUCCESS', result.status) self.assertTrue("The Team Services account 'https://account1.visualstudio.com' was created" in result.status_message) self.assertEqual('https://portal.azure.com/#resource/subscriptions/sub1/resourceGroups/group1/providers/Microsoft.Web/sites/web1/vstscd', result.azure_continuous_delivery_url) self.assertEqual('group1', result.azure_resource_group) self.assertEqual('sub1', result.azure_subscription_id) self.assertEqual('web1', result.azure_website_name) self.assertEqual(True, result.vsts_account_created) self.assertEqual('https://account1.visualstudio.com', result.vsts_account_url) self.assertEqual('https://account1.visualstudio.com/333/_build?_a=simple-process&definitionId=123', result.vsts_build_def_url) self.assertEqual('https://account1.visualstudio.com/333/_apps/hub/ms.vss-releaseManagement-web.hub-explorer?definitionId=321&_a=releases', result.vsts_release_def_url) # call setup mocked_account.create_account.return_value = Collection(None, 'collection111') with self.assertRaises(RuntimeError) as context: cdman.setup_continuous_delivery('staging', app_type_details, "https://account1.visualstudio.com", True, 'token2', None, None) self.assertTrue('Account creation failed' in str(context.exception)) def test_get_provisioning_configuration_target(self): cdman = ContinuousDeliveryManager(None) cdman.set_azure_web_info('group1', 'web1', 'fakeCreds', 'sub1', 'subname1', 'tenant1', 'South Central US') target = cdman.get_provisioning_configuration_target('authInfo', 'staging', 'test1', None) self.assertEqual(target[0].authorization_info, 'authInfo') self.assertEqual(target[0].environment_type, 'production') self.assertEqual(target[0].friendly_name, 'Production') self.assertEqual(target[0].location, 'South Central US') self.assertEqual(target[0].provider, 'azure') self.assertEqual(target[0].resource_group_name, 'group1') self.assertEqual(target[0].resource_identifier, 'web1') self.assertEqual(target[0].subscription_id, 'sub1') self.assertEqual(target[0].target_type, 'windowsAppService') self.assertEqual(target[0].tenant_id, 'tenant1') self.assertEqual(target[0].slot_swap_configuration.slot_name, 'staging') self.assertEqual(target[1].authorization_info, 'authInfo') self.assertEqual(target[1].environment_type, 'test') self.assertEqual(target[1].friendly_name, 'Load Test') self.assertEqual(target[1].location, 'South Central US') self.assertEqual(target[1].provider, 'azure') self.assertEqual(target[1].resource_group_name, 'group1') self.assertEqual(target[1].resource_identifier, 'test1') self.assertEqual(target[1].slot_swap_configuration, None) self.assertEqual(target[1].subscription_id, 'sub1') self.assertEqual(target[1].subscription_name, 'subname1') self.assertEqual(target[1].target_type, 'windowsAppService') self.assertEqual(target[1].tenant_id, 'tenant1') def test_build_configuration(self): # create CD manager cdman = ContinuousDeliveryManager(None) cd_app_type = None nodejs_task_runner = None python_framework = None python_version = None app_working_dir = None test_case_count = 8 for i in range(test_case_count): cd_app_type, nodejs_task_runner, python_framework, python_version, app_working_dir = self._set_build_configuration_variables(i) app_type_details = self.create_cd_app_type_details_map(cd_app_type, nodejs_task_runner, python_framework, python_version, app_working_dir) if(i<3) : # Verifying build configuration outputs build_configuration = cdman._get_build_configuration(app_type_details) self.assertEqual(build_configuration.node_type, nodejs_task_runner) self.assertEqual(build_configuration.python_framework, python_framework) self.assertEqual(build_configuration.working_directory, app_working_dir) if(python_version is not None) : self.assertEqual(build_configuration.python_version, python_version.replace(" ", "").replace(".", "")) cd_app_type = 'AspNetWap' if cd_app_type == 'AspNet' else cd_app_type self.assertEqual(build_configuration.type, cd_app_type) else : # Verifying exceptions with self.assertRaises(RuntimeError): cdman._get_build_configuration(app_type_details) def _set_build_configuration_variables(self, i): if(i==0): return 'Python', None, 'Django', 'Python 2.7.12 x64', 'app_working_dir' elif(i==1): return 'NodeJS', 'Gulp', None, None, None elif(i==2): return 'AspNet', None, None, None, None elif(i==3): return None, None, None, None, None elif(i==4): return 'UnacceptedAppType', None, None, None, None elif(i==5): return 'Python', None, 'UnacceptedFramework', 'Python 2.7.12 x64', None elif(i==6): return 'Python', 'Django', None, 'UnexpectedVersion', None elif(i==7): return 'NodeJS', 'UnexpectedNodeJSTaskRunner', None, None, None def _mock_get_vsts_info(self, vsts_repo_url, cred): collection_info = CollectionInfo('111', 'collection111', 'https://collection111.visualstudio.com') project_info = TeamProjectInfo('333', 'project1', 'https://collection111.visualstudio.com/project1', 'good', '1') repository_info = RepositoryInfo('222', 'repo222', 'https://collection111.visualstudio.com/project1/_git/repo222', project_info) return VstsInfo('server1', collection_info, repository_info) def _get_provisioning_config(self, status, status_message): ci_config = CiConfiguration( CiArtifact('333', 'project1', 'https://collection111.visualstudio.com/project1'), CiArtifact('123', 'builddef123', 'https://collection111.visualstudio.com/project1/build/definition/123'), CiArtifact('321', 'releasedef321', 'https://collection111.visualstudio.com/project1/release/definition/321'), CiResult(status, status_message)) return ProvisioningConfiguration('abcd', None, None, ci_config) def create_cd_app_type_details_map(self, cd_app_type, nodejs_task_runner, python_framework, python_version, app_working_dir): return { 'cd_app_type' : cd_app_type, 'nodejs_task_runner' : nodejs_task_runner, 'python_framework' : python_framework, 'python_version' : python_version, 'app_working_dir' : app_working_dir } if __name__ == '__main__': unittest.main()vsts-cd-manager-1.0.2/vsts_cd_manager/000077500000000000000000000000001330771771500176575ustar00rootroot00000000000000vsts-cd-manager-1.0.2/vsts_cd_manager/__init__.py000066400000000000000000000006561330771771500217770ustar00rootroot00000000000000# -------------------------------------------------------------------------------------------- # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License.txt in the project root for license information. # -------------------------------------------------------------------------------------------- import vsts_cd_manager.continuous_delivery_manager # pylint: disable=unused-import vsts-cd-manager-1.0.2/vsts_cd_manager/continuous_delivery_manager.py000066400000000000000000000465541330771771500260520ustar00rootroot00000000000000# -------------------------------------------------------------------------------------------- # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License.txt in the project root for license information. # -------------------------------------------------------------------------------------------- from __future__ import print_function import re import time import uuid try: from urllib.parse import quote, urlparse except ImportError: from urllib import quote #pylint: disable=no-name-in-module from urlparse import urlparse #pylint: disable=import-error from vsts_info_provider import VstsInfoProvider from continuous_delivery import ContinuousDelivery from continuous_delivery.models import (AuthorizationInfo, AuthorizationInfoParameters, BuildConfiguration, CiArtifact, CiConfiguration, ProvisioningConfiguration, ProvisioningConfigurationSource, ProvisioningConfigurationTarget, SlotSwapConfiguration, SourceRepository, CreateOptions) from aex_accounts import Account # Use this class to setup or remove continuous delivery mechanisms for Azure web sites using VSTS build and release class ContinuousDeliveryManager(object): def __init__(self, progress_callback): """ Use this class to setup or remove continuous delivery mechanisms for Azure web sites using VSTS build and release :param progress_callback: method of the form func(count, total, message) """ self._update_progress = progress_callback or self._skip_update_progress self._azure_info = _AzureInfo() self._repo_info = _RepositoryInfo() def get_vsts_app_id(self): """ Use this method to get the 'resource' value for creating an Azure token to be used by VSTS :return: App id for VSTS """ return '499b84ac-1321-427f-aa17-267ca6975798' def set_azure_web_info(self, resource_group_name, website_name, credentials, subscription_id, subscription_name, tenant_id, webapp_location): """ Call this method before attempting to setup continuous delivery to setup the azure settings :param resource_group_name: :param website_name: :param credentials: :param subscription_id: :param subscription_name: :param tenant_id: :param webapp_location: :return: """ self._azure_info.resource_group_name = resource_group_name self._azure_info.website_name = website_name self._azure_info.credentials = credentials self._azure_info.subscription_id = subscription_id self._azure_info.subscription_name = subscription_name self._azure_info.tenant_id = tenant_id self._azure_info.webapp_location = webapp_location def set_repository_info(self, repo_url, branch, git_token, private_repo_username, private_repo_password): """ Call this method before attempting to setup continuous delivery to setup the source control settings :param repo_url: URL of the code repo :param branch: repo branch :param git_token: git token :param private_repo_username: private repo username :param private_repo_password: private repo password :return: """ self._repo_info.url = repo_url self._repo_info.branch = branch self._repo_info.git_token = git_token self._repo_info._private_repo_username = private_repo_username self._repo_info._private_repo_password = private_repo_password def remove_continuous_delivery(self): """ To be Implemented :return: """ # TODO: this would be called by appservice web source-control delete return def setup_continuous_delivery(self, swap_with_slot, app_type_details, cd_project_url, create_account, vsts_app_auth_token, test, webapp_list): """ Use this method to setup Continuous Delivery of an Azure web site from a source control repository. :param swap_with_slot: the slot to use for deployment :param app_type_details: the details of app that will be deployed. i.e. app_type = Python, python_framework = Django etc. :param cd_project_url: CD Project url in the format of https://.visualstudio.com/ :param create_account: Boolean value to decide if account need to be created or not :param vsts_app_auth_token: Authentication token for vsts app :param test: Load test webapp name :param webapp_list: Existing webapp list :return: a message indicating final status and instructions for the user """ branch = self._repo_info.branch or 'refs/heads/master' self._validate_cd_project_url(cd_project_url) vsts_account_name = self._get_vsts_account_name(cd_project_url) # Verify inputs before we start generating tokens source_repository, account_name, team_project_name = self._get_source_repository(self._repo_info.url, self._repo_info.git_token, branch, self._azure_info.credentials, self._repo_info._private_repo_username, self._repo_info._private_repo_password) self._verify_vsts_parameters(vsts_account_name, source_repository) vsts_account_name = vsts_account_name or account_name cd_project_name = team_project_name or self._azure_info.website_name account_url = 'https://{}.visualstudio.com'.format(quote(vsts_account_name)) portalext_account_url = 'https://{}.portalext.visualstudio.com'.format(quote(vsts_account_name)) # VSTS Account using AEX APIs account_created = False if create_account: self.create_vsts_account(self._azure_info.credentials, vsts_account_name) account_created = True # Create ContinuousDelivery client cd = ContinuousDelivery('3.2-preview.1', portalext_account_url, self._azure_info.credentials) # Construct the config body of the continuous delivery call build_configuration = self._get_build_configuration(app_type_details) source = ProvisioningConfigurationSource('codeRepository', source_repository, build_configuration) auth_info = AuthorizationInfo('Headers', AuthorizationInfoParameters('Bearer ' + vsts_app_auth_token)) target = self.get_provisioning_configuration_target(auth_info, swap_with_slot, test, webapp_list) ci_config = CiConfiguration(CiArtifact(name=cd_project_name)) config = ProvisioningConfiguration(None, source, target, ci_config) # Configure the continuous deliver using VSTS as a backend response = cd.provisioning_configuration(config) if response.ci_configuration.result.status == 'queued': final_status = self._wait_for_cd_completion(cd, response) return self._get_summary(final_status, account_url, vsts_account_name, account_created, self._azure_info.subscription_id, self._azure_info.resource_group_name, self._azure_info.website_name) else: raise RuntimeError('Unknown status returned from provisioning_configuration: ' + response.ci_configuration.result.status) def create_vsts_account(self, creds, vsts_account_name): aex_url = 'https://app.vsaex.visualstudio.com' accountClient = Account('4.0-preview.1', aex_url, creds) self._update_progress(0, 100, 'Creating or getting Team Services account information') regions = accountClient.regions() if regions.count == 0: raise RuntimeError('Region details not found.') region_name = regions.value[0].name create_account_reponse = accountClient.create_account(vsts_account_name, region_name) if create_account_reponse.id: self._update_progress(5, 100, 'Team Services account created') else: raise RuntimeError('Account creation failed.') def _validate_cd_project_url(self, cd_project_url): if -1 == cd_project_url.find('visualstudio.com') or -1 == cd_project_url.find('https://'): raise RuntimeError('Project URL should be in format https://.visualstudio.com/') def _get_vsts_account_name(self, cd_project_url): return (cd_project_url.split('.visualstudio.com', 1)[0]).split('https://', 1)[1] def get_provisioning_configuration_target(self, auth_info, swap_with_slot, test, webapp_list): swap_with_slot_config = None if swap_with_slot is None else SlotSwapConfiguration(swap_with_slot) slotTarget = ProvisioningConfigurationTarget('azure', 'windowsAppService', 'production', 'Production', self._azure_info.subscription_id, self._azure_info.subscription_name, self._azure_info.tenant_id, self._azure_info.website_name, self._azure_info.resource_group_name, self._azure_info.webapp_location, auth_info, swap_with_slot_config) target = [slotTarget] if test is not None: create_options = None if webapp_list is not None and not any(s.name == test for s in webapp_list) : app_service_plan_name = 'ServicePlan'+ str(uuid.uuid4())[:13] create_options = CreateOptions(app_service_plan_name, 'Standard', self._azure_info.website_name) testTarget = ProvisioningConfigurationTarget('azure', 'windowsAppService', 'test', 'Load Test', self._azure_info.subscription_id, self._azure_info.subscription_name, self._azure_info.tenant_id, test, self._azure_info.resource_group_name, self._azure_info.webapp_location, auth_info, None, create_options) target.append(testTarget) return target def _verify_vsts_parameters(self, cd_account, source_repository): # if provider is vsts and repo is not vsts then we need the account name if source_repository.type in ['Github', 'ExternalGit'] and not cd_account: raise RuntimeError('You must provide a value for cd-account since your repo-url is not a Team Services repository.') def _get_build_configuration(self, app_type_details): accepted_app_types = ['AspNet', 'AspNetCore', 'NodeJS', 'PHP', 'Python'] accepted_nodejs_task_runners = ['None', 'Gulp', 'Grunt'] accepted_python_frameworks = ['Bottle', 'Django', 'Flask'] accepted_python_versions = ['Python 2.7.12 x64', 'Python 2.7.12 x86', 'Python 2.7.13 x64', 'Python 2.7.13 x86', 'Python 3.5.3 x64', 'Python 3.5.3 x86', 'Python 3.6.0 x64', 'Python 3.6.0 x86', 'Python 3.6.2 x64', 'Python 3.6.1 x86'] build_configuration = None working_directory = app_type_details.get('app_working_dir') app_type = app_type_details.get('cd_app_type') if (app_type == 'AspNet') : build_configuration = BuildConfiguration('AspNetWap', working_directory) elif (app_type == 'AspNetCore') or (app_type == 'PHP') : build_configuration = BuildConfiguration(app_type, working_directory) elif app_type == 'NodeJS' : nodejs_task_runner = app_type_details.get('nodejs_task_runner') if any(s == nodejs_task_runner for s in accepted_nodejs_task_runners) : build_configuration = BuildConfiguration(app_type, working_directory, nodejs_task_runner) else: raise RuntimeError("The nodejs_task_runner %s was not understood. Accepted values: %s." % (nodejs_task_runner, accepted_nodejs_task_runners)) elif app_type == 'Python' : python_framework = app_type_details.get('python_framework') python_version = app_type_details.get('python_version') django_setting_module = 'DjangoProjectName.settings' flask_project_name = 'FlaskProjectName' if any(s == python_framework for s in accepted_python_frameworks) : if any(s == python_version for s in accepted_python_versions) : python_version = python_version.replace(" ", "").replace(".", "") build_configuration = BuildConfiguration(app_type, working_directory, None, python_framework, python_version, django_setting_module, flask_project_name) else : raise RuntimeError("The python_version %s was not understood. Accepted values: %s." % (python_version, accepted_python_versions)) else: raise RuntimeError("The python_framework %s was not understood. Accepted values: %s." % (python_framework, accepted_python_frameworks)) else: raise RuntimeError("The app_type %s was not understood. Accepted values: %s." % (app_type, accepted_app_types)) return build_configuration def _get_source_repository(self, uri, token, branch, cred, username, password): # Determine the type of repository (TfsGit, github, tfvc, externalGit) # Find the identifier and set the properties. # Default is externalGit type = 'Git' identifier = uri account_name = None team_project_name = None auth_info = AuthorizationInfo('UsernamePassword', AuthorizationInfoParameters(None, None, username, password)) match = re.match(r'[htps]+\:\/\/(.+)\.visualstudio\.com.*\/_git\/(.+)', uri, re.IGNORECASE) if match: type = 'TfsGit' account_name = match.group(1) # we have to get the repo id as the identifier info = self._get_vsts_info(uri, cred) identifier = info.repository_info.id team_project_name = info.repository_info.project_info.name auth_info = None else: match = re.match(r'[htps]+\:\/\/github\.com\/(.+)', uri, re.IGNORECASE) if match: if token is not None: type = 'Github' identifier = match.group(1).replace(".git", "") auth_info = AuthorizationInfo('PersonalAccessToken', AuthorizationInfoParameters(None, token)) else: match = re.match(r'[htps]+\:\/\/(.+)\.visualstudio\.com\/(.+)', uri, re.IGNORECASE) if match: type = 'TFVC' identifier = match.group(2) account_name = match.group(1) auth_info = None sourceRepository = SourceRepository(type, identifier, branch, auth_info) return sourceRepository, account_name, team_project_name def _get_vsts_info(self, vsts_repo_url, cred): vsts_info_client = VstsInfoProvider('3.2-preview', vsts_repo_url, cred) return vsts_info_client.get_vsts_info() def _wait_for_cd_completion(self, cd, response): # Wait for the configuration to finish and report on the status step = 5 max = 100 self._update_progress(step, max, 'Setting up Team Services continuous deployment') config = cd.get_provisioning_configuration(response.id) while config.ci_configuration.result.status == 'queued' or config.ci_configuration.result.status == 'inProgress': step += 5 if step + 5 < max else 0 self._update_progress(step, max, 'Setting up Team Services continuous deployment (' + config.ci_configuration.result.status + ')') time.sleep(2) config = cd.get_provisioning_configuration(response.id) if config.ci_configuration.result.status == 'failed': self._update_progress(max, max, 'Setting up Team Services continuous deployment (FAILED)') raise RuntimeError(config.ci_configuration.result.status_message) self._update_progress(max, max, 'Setting up Team Services continuous deployment (SUCCEEDED)') return config def _get_summary(self, provisioning_configuration, account_url, account_name, account_created, subscription_id, resource_group_name, website_name): summary = '\n' if not provisioning_configuration: return None # Add the vsts account info if not account_created: summary += "The Team Services account '{}' was updated to handle the continuous delivery.\n".format(account_url) else: summary += "The Team Services account '{}' was created to handle the continuous delivery.\n".format(account_url) # Add the subscription info website_url = 'https://portal.azure.com/#resource/subscriptions/{}/resourceGroups/{}/providers/Microsoft.Web/sites/{}/vstscd'.format( quote(subscription_id), quote(resource_group_name), quote(website_name)) summary += 'You can check on the status of the Azure web site deployment here:\n' summary += website_url + '\n' # setup the build url and release url build_url = '' release_url = '' if provisioning_configuration.ci_configuration and provisioning_configuration.ci_configuration.project: project_id = provisioning_configuration.ci_configuration.project.id if provisioning_configuration.ci_configuration.build_definition: build_url = '{}/{}/_build?_a=simple-process&definitionId={}'.format( account_url, quote(project_id), quote(provisioning_configuration.ci_configuration.build_definition.id)) if provisioning_configuration.ci_configuration.release_definition: release_url = '{}/{}/_apps/hub/ms.vss-releaseManagement-web.hub-explorer?definitionId={}&_a=releases'.format( account_url, quote(project_id), quote(provisioning_configuration.ci_configuration.release_definition.id)) return ContinuousDeliveryResult(account_created, account_url, resource_group_name, subscription_id, website_name, website_url, summary, build_url, release_url, provisioning_configuration) def _skip_update_progress(self, count, total, message): return class _AzureInfo(object): def __init__(self): self.resource_group_name = None self.website_name = None self.credentials = None self.subscription_id = None self.subscription_name = None self.tenant_id = None self.webapp_location = None class _RepositoryInfo(object): def __init__(self): self.url = None self.branch = None self.git_token = None self.private_repo_username = None self.private_repo_password = None class ContinuousDeliveryResult(object): def __init__(self, account_created, account_url, resource_group, subscription_id, website_name, cd_url, message, build_url, release_url, final_status): self.vsts_account_created = account_created self.vsts_account_url = account_url self.vsts_build_def_url = build_url self.vsts_release_def_url = release_url self.azure_resource_group = resource_group self.azure_subscription_id = subscription_id self.azure_website_name = website_name self.azure_continuous_delivery_url = cd_url self.status = 'SUCCESS' self.status_message = message self.status_details = final_status vsts-cd-manager-1.0.2/vsts_info_provider/000077500000000000000000000000001330771771500204445ustar00rootroot00000000000000vsts-cd-manager-1.0.2/vsts_info_provider/__init__.py000066400000000000000000000007101330771771500225530ustar00rootroot00000000000000# coding=utf-8 # -------------------------------------------------------------------------- # Code generated by Microsoft (R) AutoRest Code Generator 1.0.1.0 # Changes may cause incorrect behavior and will be lost if the code is # regenerated. # -------------------------------------------------------------------------- from .vsts_info_provider import VstsInfoProvider from .version import VERSION __all__ = ['VstsInfoProvider'] __version__ = VERSION vsts-cd-manager-1.0.2/vsts_info_provider/exceptions.py000066400000000000000000000010401330771771500231720ustar00rootroot00000000000000# coding=utf-8 # -------------------------------------------------------------------------- # Code generated by Microsoft (R) AutoRest Code Generator 1.0.1.0 # Changes may cause incorrect behavior and will be lost if the code is # regenerated. # -------------------------------------------------------------------------- from msrest.exceptions import ( ClientException, SerializationError, DeserializationError, TokenExpiredError, ClientRequestError, AuthenticationError, HttpOperationError, ValidationError, ) vsts-cd-manager-1.0.2/vsts_info_provider/models/000077500000000000000000000000001330771771500217275ustar00rootroot00000000000000vsts-cd-manager-1.0.2/vsts_info_provider/models/__init__.py000066400000000000000000000011131330771771500240340ustar00rootroot00000000000000# coding=utf-8 # -------------------------------------------------------------------------- # Code generated by Microsoft (R) AutoRest Code Generator 1.0.1.0 # Changes may cause incorrect behavior and will be lost if the code is # regenerated. # -------------------------------------------------------------------------- from .collection_info import CollectionInfo from .repository_info import RepositoryInfo from .team_project_info import TeamProjectInfo from .vsts_info import VstsInfo __all__ = [ 'CollectionInfo', 'RepositoryInfo', 'TeamProjectInfo', 'VstsInfo', ] vsts-cd-manager-1.0.2/vsts_info_provider/models/collection_info.py000066400000000000000000000014651330771771500254550ustar00rootroot00000000000000# coding=utf-8 # -------------------------------------------------------------------------- # Code generated by Microsoft (R) AutoRest Code Generator 1.0.1.0 # Changes may cause incorrect behavior and will be lost if the code is # regenerated. # -------------------------------------------------------------------------- from msrest.serialization import Model class CollectionInfo(Model): """CollectionInfo. :param id: :type id: str :param name: :type name: str :param url: :type url: str """ _attribute_map = { 'id': {'key': 'id', 'type': 'str'}, 'name': {'key': 'name', 'type': 'str'}, 'url': {'key': 'url', 'type': 'str'}, } def __init__(self, id=None, name=None, url=None): self.id = id self.name = name self.url = url vsts-cd-manager-1.0.2/vsts_info_provider/models/repository_info.py000066400000000000000000000022331330771771500255330ustar00rootroot00000000000000# coding=utf-8 # -------------------------------------------------------------------------- # Code generated by Microsoft (R) AutoRest Code Generator 1.0.1.0 # Changes may cause incorrect behavior and will be lost if the code is # regenerated. # -------------------------------------------------------------------------- from msrest.serialization import Model class RepositoryInfo(Model): """RepositoryInfo. :param id: :type id: str :param name: :type name: str :param url: :type url: str :param remote_url: :type remote_url: str :param project_info: :type project_info: TeamProjectInfo """ _attribute_map = { 'id': {'key': 'id', 'type': 'str'}, 'name': {'key': 'name', 'type': 'str'}, 'url': {'key': 'url', 'type': 'str'}, 'remote_url': {'key': 'remoteUrl', 'type': 'str'}, 'project_info': {'key': 'project', 'type': 'TeamProjectInfo'}, } def __init__(self, id=None, name=None, url=None, remote_url=None, project_info=None): self.id = id self.name = name self.url = url self.remote_url = remote_url self.project_info = project_info vsts-cd-manager-1.0.2/vsts_info_provider/models/team_project_info.py000066400000000000000000000021141330771771500257660ustar00rootroot00000000000000# coding=utf-8 # -------------------------------------------------------------------------- # Code generated by Microsoft (R) AutoRest Code Generator 1.0.1.0 # Changes may cause incorrect behavior and will be lost if the code is # regenerated. # -------------------------------------------------------------------------- from msrest.serialization import Model class TeamProjectInfo(Model): """TeamProjectInfo. :param id: :type id: str :param name: :type name: str :param url: :type url: str :param state: :type state: int :param revision: :type revision: int """ _attribute_map = { 'id': {'key': 'id', 'type': 'str'}, 'name': {'key': 'name', 'type': 'str'}, 'url': {'key': 'url', 'type': 'str'}, 'state': {'key': 'state', 'type': 'int'}, 'revision': {'key': 'revision', 'type': 'int'}, } def __init__(self, id=None, name=None, url=None, state=None, revision=None): self.id = id self.name = name self.url = url self.state = state self.revision = revision vsts-cd-manager-1.0.2/vsts_info_provider/models/vsts_info.py000066400000000000000000000020231330771771500243100ustar00rootroot00000000000000# coding=utf-8 # -------------------------------------------------------------------------- # Code generated by Microsoft (R) AutoRest Code Generator 1.0.1.0 # Changes may cause incorrect behavior and will be lost if the code is # regenerated. # -------------------------------------------------------------------------- from msrest.serialization import Model class VstsInfo(Model): """VstsInfo. :param server_url: :type server_url: str :param collection_info: :type collection_info: object :param repository_info: :type repository_info: object """ _attribute_map = { 'server_url': {'key': 'serverUrl', 'type': 'str'}, 'collection_info': {'key': 'collection', 'type': 'CollectionInfo'}, 'repository_info': {'key': 'repository', 'type': 'RepositoryInfo'}, } def __init__(self, server_url=None, collection_info=None, repository_info=None): self.server_url = server_url self.collection_info = collection_info self.repository_info = repository_info vsts-cd-manager-1.0.2/vsts_info_provider/version.py000066400000000000000000000005231330771771500225030ustar00rootroot00000000000000# coding=utf-8 # -------------------------------------------------------------------------- # Code generated by Microsoft (R) AutoRest Code Generator 1.0.1.0 # Changes may cause incorrect behavior and will be lost if the code is # regenerated. # -------------------------------------------------------------------------- VERSION = "3.2" vsts-cd-manager-1.0.2/vsts_info_provider/vsts_info_provider.py000066400000000000000000000102351330771771500247430ustar00rootroot00000000000000# coding=utf-8 # -------------------------------------------------------------------------- # Code generated by Microsoft (R) AutoRest Code Generator 1.0.1.0 # Changes may cause incorrect behavior and will be lost if the code is # regenerated. # -------------------------------------------------------------------------- from __future__ import print_function from sys import stderr from msrest.service_client import ServiceClient from msrest import Configuration, Serializer, Deserializer from msrest.pipeline import ClientRawResponse from msrest.exceptions import HttpOperationError from . import models from .version import VERSION class VstsInfoProviderConfiguration(Configuration): """Configuration for VstsInfoProvider Note that all parameters used to create this instance are saved as instance attributes. :param api_version: Version of the API to use. This should be set to '3.2-preview' to use this version of the api. :type api_version: str :param str base_url: Service URL """ def __init__( self, api_version, base_url=None): if api_version is None: raise ValueError("Parameter 'api_version' must not be None.") if not isinstance(api_version, str): raise TypeError("Parameter 'api_version' must be str.") if not base_url: base_url = 'https://{}.visualstudio.com/' super(VstsInfoProviderConfiguration, self).__init__(base_url) self.add_user_agent('azurecli/{} vstsinfo/{}'.format(VERSION, VERSION)) self.api_version = api_version class VstsInfoProvider(object): """VstsInfoProvider :ivar config: Configuration for client. :vartype config: AzureTfsConfiguration :param api_version: Version of the API to use. This should be set to '3.2-preview' to use this version of the api. :type api_version: str :param str vsts_git_url: vsts git URL :param Credentials creds: credentials for vsts """ def __init__( self, api_version, vsts_git_url, creds=None): self.config = VstsInfoProviderConfiguration(api_version, vsts_git_url) self._client = ServiceClient(creds, self.config) client_models = {k: v for k, v in models.__dict__.items() if isinstance(v, type)} self.api_version = '3.2' if not api_version else api_version self._serialize = Serializer(client_models) self._deserialize = Deserializer(client_models) def get_vsts_info( self, custom_headers=None, raw=False, **operation_config): """GetContinuousDeploymentOperation. :param dict custom_headers: headers that will be added to the request :param bool raw: returns the direct response alongside the deserialized response :param operation_config: :ref:`Operation configuration overrides`. :rtype: :class:`ContinuousDeploymentOperation ` :rtype: :class:`ClientRawResponse` if raw=true :raises: :class:`HttpOperationError` """ # Construct URL url = '/vsts/info' # Construct parameters query_parameters = {} # Construct headers header_parameters = {} header_parameters['Content-Type'] = 'application/json; charset=utf-8' if custom_headers: header_parameters.update(custom_headers) # Construct and send request request = self._client.get(url, query_parameters) response = self._client.send(request, header_parameters, **operation_config) if response.status_code not in [200]: print("response:", response.status_code, file=stderr) print(response.text, file=stderr) raise HttpOperationError(self._deserialize, response) deserialized = None if response.status_code == 200: deserialized = self._deserialize('VstsInfo', response) if raw: client_raw_response = ClientRawResponse(deserialized, response) return client_raw_response return deserialized