diff --git a/src/service_name.json b/src/service_name.json index dbf56710844..8fbe1d1286f 100644 --- a/src/service_name.json +++ b/src/service_name.json @@ -448,6 +448,11 @@ "Command": "az vm", "AzureServiceName": "Azure Virtual Machines", "URL": "https://docs.microsoft.com/en-us/azure/virtual-machines/" + }, + { + "Command": "az webapp", + "AzureServiceName": "Azure Web Apps", + "URL": "https://docs.microsoft.com/en-us/azure/app-service/" } ] diff --git a/src/webapp/azext_webapp/__init__.py b/src/webapp/azext_webapp/__init__.py index 69d1f948171..8b647747cae 100644 --- a/src/webapp/azext_webapp/__init__.py +++ b/src/webapp/azext_webapp/__init__.py @@ -19,14 +19,9 @@ def __init__(self, cli_ctx=None): operations_tmpl='azext_webapp.custom#{}') super(WebappExtCommandLoader, self).__init__(cli_ctx=cli_ctx, custom_command_type=webapp_custom, - resource_type=ResourceType.MGMT_CONTAINERREGISTRY) + resource_type=ResourceType.MGMT_APPSERVICE) def load_command_table(self, _): - with self.command_group('webapp') as g: - g.custom_command('container up', 'create_deploy_container_app', exception_handler=ex_handler_factory()) - g.custom_command('remote-connection create', 'create_tunnel') - g.custom_command('deploy', 'perform_onedeploy') - with self.command_group('webapp scan') as g: g.custom_command('start', 'start_scan') g.custom_command('show-result', 'get_scan_result') @@ -39,43 +34,12 @@ def load_command_table(self, _): def load_arguments(self, _): # pylint: disable=line-too-long # PARAMETER REGISTRATION - webapp_name_arg_type = CLIArgumentType(configured_default='web', options_list=['--name', '-n'], metavar='NAME', - completer=get_resource_name_completion_list('Microsoft.Web/sites'), id_part='name', - help="name of the webapp. You can configure the default using 'az configure --defaults web='") - with self.argument_context('webapp container up') as c: - c.argument('name', arg_type=webapp_name_arg_type) - c.argument('source_location', options_list=['--source-location', '-s'], - help='the path to the web app source directory') - c.argument('docker_custom_image_name', options_list=['--docker-custom-image-name', '-i'], - help='the container image name and optionally the tag name (currently public DockerHub images only)') - c.argument('dryrun', help="show summary of the create and deploy operation instead of executing it", default=False, action='store_true') - c.argument('registry_rg', help="the resource group of the Azure Container Registry") - c.argument('registry_name', help="the name of the Azure Container Registry") - c.argument('slot', help="Name of the deployment slot to use") - with self.argument_context('webapp remote-connection create') as c: - c.argument('port', options_list=['--port', '-p'], - help='Port for the remote connection. Default: Random available port', type=int) - c.argument('name', options_list=['--name', '-n'], help='Name of the webapp to connect to') - c.argument('slot', help="Name of the deployment slot to use") with self.argument_context('webapp scan') as c: c.argument('name', options_list=['--name', '-n'], help='Name of the webapp to connect to') c.argument('scan_id', options_list=['--scan-id'], help='Unique scan id') c.argument('timeout', options_list=['--timeout'], help='Timeout for operation in milliseconds') c.argument('slot', help="Name of the deployment slot to use") - with self.argument_context('webapp deploy') as c: - c.argument('name', options_list=['--name'], help='Name of the webapp to connect to') - c.argument('src_path', options_list=['--src-path'], help='Path of the file to be deployed. Example: /mnt/apps/myapp.war') - c.argument('src_url', options_list=['--src-url'], help='url to download the package from. Example: http://mysite.com/files/myapp.war?key=123') - c.argument('target_path', options_list=['--target-path'], help='Target path relative to wwwroot to which the file will be deployed to.') - c.argument('artifact_type', options_list=['--type'], help='Type of deployment requested') - c.argument('is_async', options_list=['--async'], help='Asynchronous deployment', choices=['true', 'false']) - c.argument('restart', options_list=['--restart'], help='restart or not. default behavior is to restart.', choices=['true', 'false']) - c.argument('clean', options_list=['--clean'], help='clean or not. default is target-type specific.', choices=['true', 'false']) - c.argument('ignore_stack', options_list=['--ignore-stack'], help='should override the default stack check', choices=['true', 'false']) - c.argument('timeout', options_list=['--timeout'], help='Timeout for operation in milliseconds') - c.argument('slot', help="Name of the deployment slot to use") - COMMAND_LOADER_CLS = WebappExtCommandLoader diff --git a/src/webapp/azext_webapp/_help.py b/src/webapp/azext_webapp/_help.py index 082fcc234b0..9888cd05c24 100644 --- a/src/webapp/azext_webapp/_help.py +++ b/src/webapp/azext_webapp/_help.py @@ -5,16 +5,6 @@ from knack.help_files import helps -helps['webapp remote-connection'] = """ - type: group - short-summary: Create a remote connection using a tcp tunnel to your web app -""" - -helps['webapp remote-connection create'] = """ - type: command - short-summary: Creates a remote connection using a tcp tunnel to your web app -""" - helps['webapp scan'] = """ type: group short-summary: Holds group of commands which cater to webapp scans. Currently available only for Linux based webapps. @@ -44,28 +34,3 @@ type: command short-summary: Stops the current executing scan. Does nothing if no scan is executing. """ - -helps['webapp container'] = """ - type: group - short-summary: Group of commands related to webapp container operations -""" - -helps['webapp container up'] = """ - type: command - short-summary: Experimental command to create and deploy a container webapp. - examples: - - name: Deploy a container using an image from DockerHub. This example uses nginx. - text: az webapp container up -n AppName -i nginx - - name: Upload files from the current directory to an Azure Container Registry, then build a container image and deploy it to a web app. The Azure Container Registry must already exist. - text: az webapp container up -n AppName --registry-rg ContainerRegistryResourceGroup --registry-name ContainerRegistryName -""" - -helps['webapp deploy'] = """ - type: command - short-summary: Deploys a provided artifact to Azure Web Apps. - examples: - - name: Deploy a war file asynchronously. - text: az webapp deploy --resource-group ResouceGroup --name AppName --src-path SourcePath --type war --async IsAsync - - name: Deploy a static text file to wwwroot/staticfiles/test.txt - text: az webapp deploy --resource-group ResouceGroup --name AppName --src-path SourcePath --type static --target-path staticfiles/test.txt -""" diff --git a/src/webapp/azext_webapp/acr_util.py b/src/webapp/azext_webapp/acr_util.py deleted file mode 100644 index 3e86b30c47c..00000000000 --- a/src/webapp/azext_webapp/acr_util.py +++ /dev/null @@ -1,95 +0,0 @@ -# -------------------------------------------------------------------------------------------- -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. See License.txt in the project root for license information. -# -------------------------------------------------------------------------------------------- - -import uuid -import tempfile -from datetime import datetime - -from knack.log import get_logger -from knack.util import CLIError - -from azure.cli.command_modules.acr._archive_utils import upload_source_code -from azure.cli.command_modules.acr._stream_utils import stream_logs -from azure.cli.core.commands.client_factory import get_mgmt_service_client -from azure.cli.core.commands import LongRunningOperation - -logger = get_logger(__name__) -VERSION_2019_06_01_PREVIEW = "2019-06-01-preview" - - -# pylint:disable=import-outside-toplevel -def queue_acr_build(cmd, registry_rg, registry_name, img_name, src_dir): - import os - client_registries = get_acr_service_client(cmd.cli_ctx, VERSION_2019_06_01_PREVIEW).registries - - if not os.path.isdir(src_dir): - raise CLIError("Source directory should be a local directory path.") - - docker_file_path = os.path.join(src_dir, "Dockerfile") - if not os.path.isfile(docker_file_path): - raise CLIError("Unable to find '{}'.".format(docker_file_path)) - - # NOTE: os.path.basename is unable to parse "\" in the file path - original_docker_file_name = os.path.basename( - docker_file_path.replace("\\", "/")) - docker_file_in_tar = '{}_{}'.format( - uuid.uuid4().hex, original_docker_file_name) - tar_file_path = os.path.join(tempfile.gettempdir(), 'build_archive_{}.tar.gz'.format(uuid.uuid4().hex)) - - source_location = upload_source_code( - cmd, - client_registries, registry_name, registry_rg, - src_dir, tar_file_path, - docker_file_path, docker_file_in_tar) - # For local source, the docker file is added separately into tar as the new file name (docker_file_in_tar) - # So we need to update the docker_file_path - docker_file_path = docker_file_in_tar - - from azure.cli.core.profiles import ResourceType - OS, Architecture = cmd.get_models('OS', 'Architecture', resource_type=ResourceType.MGMT_CONTAINERREGISTRY) - # Default platform values - platform_os = OS.linux.value - platform_arch = Architecture.amd64.value - platform_variant = None - - DockerBuildRequest, PlatformProperties = cmd.get_models('DockerBuildRequest', 'PlatformProperties', - resource_type=ResourceType.MGMT_CONTAINERREGISTRY) - docker_build_request = DockerBuildRequest( - image_names=[img_name], - is_push_enabled=True, - source_location=source_location, - platform=PlatformProperties( - os=platform_os, - architecture=platform_arch, - variant=platform_variant - ), - docker_file_path=docker_file_path, - timeout=None, - arguments=[]) - - queued_build = LongRunningOperation(cmd.cli_ctx)(client_registries.schedule_run( - resource_group_name=registry_rg, - registry_name=registry_name, - run_request=docker_build_request)) - - run_id = queued_build.run_id - logger.warning("Queued a build with ID: %s", run_id) - logger.warning("Waiting for agent...") - - client_runs = get_acr_service_client(cmd.cli_ctx, VERSION_2019_06_01_PREVIEW).runs - - return stream_logs(client_runs, run_id, registry_name, registry_rg, False, True) - - -def get_acr_service_client(cli_ctx, api_version=None): - """Returns the client for managing container registries. """ - from azure.mgmt.containerregistry import ContainerRegistryManagementClient - return get_mgmt_service_client(cli_ctx, ContainerRegistryManagementClient, api_version=api_version) - - -def generate_img_name(src_dir): - import os - img_name = os.path.basename(src_dir) + ':' + datetime.now().strftime('%Y%m%d_%H%M%S') - return img_name diff --git a/src/webapp/azext_webapp/azext_metadata.json b/src/webapp/azext_webapp/azext_metadata.json index abe025f68e3..506b61632df 100644 --- a/src/webapp/azext_webapp/azext_metadata.json +++ b/src/webapp/azext_webapp/azext_metadata.json @@ -1,4 +1,4 @@ { - "azext.minCliCoreVersion": "2.0.46", + "azext.minCliCoreVersion": "2.23.0", "azext.isPreview": true } \ No newline at end of file diff --git a/src/webapp/azext_webapp/custom.py b/src/webapp/azext_webapp/custom.py index 52af6b3cc19..c33763db7a6 100644 --- a/src/webapp/azext_webapp/custom.py +++ b/src/webapp/azext_webapp/custom.py @@ -6,139 +6,13 @@ from __future__ import print_function from knack.log import get_logger from knack.util import CLIError -from azure.mgmt.web.models import (AppServicePlan, SkuDescription) from azure.cli.command_modules.appservice.custom import ( show_webapp, _get_site_credential, - _get_scm_url, - list_publish_profiles, - get_site_configs, - update_container_settings, - create_webapp, - get_sku_name, - _check_zip_deployment_status) -from azure.cli.command_modules.appservice._appservice_utils import _generic_site_operation -from azure.cli.command_modules.appservice._create_util import ( - should_create_new_rg, - create_resource_group, - web_client_factory, - should_create_new_app -) -from .acr_util import (queue_acr_build, generate_img_name) + _get_scm_url) logger = get_logger(__name__) -# pylint:disable=no-member,too-many-lines,too-many-locals,too-many-statements,too-many-branches,line-too-long,import-outside-toplevel -def create_deploy_container_app(cmd, name, source_location=None, docker_custom_image_name=None, dryrun=False, registry_rg=None, registry_name=None): # pylint: disable=too-many-statements - import os - import json - if not source_location: - # the dockerfile is expected to be in the current directory the command is running from - source_location = os.getcwd() - - client = web_client_factory(cmd.cli_ctx) - _create_new_rg = True - _create_new_asp = True - _create_new_app = True - _create_acr_img = True - - if docker_custom_image_name: - logger.warning('Image will be pulled from DockerHub') - img_name = docker_custom_image_name - _create_acr_img = False - else: - logger.warning('Source code will be uploaded and built in Azure Container Registry') - if not registry_name: - raise CLIError("--registry-name not specified") - if not registry_rg: - raise CLIError("--registry-rg not specified") - img_name = generate_img_name(source_location) - - sku = 'P1V2' - full_sku = get_sku_name(sku) - location = 'Central US' - loc_name = 'centralus' - asp = "appsvc_asp_linux_{}".format(loc_name) - rg_name = "appsvc_rg_linux_{}".format(loc_name) - # Resource group: check if default RG is set - _create_new_rg = should_create_new_rg(cmd, rg_name, True) - - rg_str = "{}".format(rg_name) - - dry_run_str = r""" { - "name" : "%s", - "serverfarm" : "%s", - "resourcegroup" : "%s", - "sku": "%s", - "location" : "%s" - } - """ % (name, asp, rg_str, full_sku, location) - create_json = json.loads(dry_run_str) - - if dryrun: - logger.warning("Web app will be created with the below configuration,re-run command " - "without the --dryrun flag to create & deploy a new app") - return create_json - - if _create_acr_img: - logger.warning("Starting ACR build") - queue_acr_build(cmd, registry_rg, registry_name, img_name, source_location) - logger.warning("ACR build done. Deploying web app.") - - # create RG if the RG doesn't already exist - if _create_new_rg: - logger.warning("Creating Resource group '%s' ...", rg_name) - create_resource_group(cmd, rg_name, location) - logger.warning("Resource group creation complete") - _create_new_asp = True - else: - logger.warning("Resource group '%s' already exists.", rg_name) - _create_new_asp = _should_create_new_asp(cmd, rg_name, asp, location) - # create new ASP if an existing one cannot be used - if _create_new_asp: - logger.warning("Creating App service plan '%s' ...", asp) - sku_def = SkuDescription(tier=full_sku, name=sku, capacity=1) - plan_def = AppServicePlan(location=loc_name, app_service_plan_name=asp, - sku=sku_def, reserved=True) - client.app_service_plans.create_or_update(rg_name, asp, plan_def) - logger.warning("App service plan creation complete") - _create_new_app = True - else: - logger.warning("App service plan '%s' already exists.", asp) - _create_new_app = should_create_new_app(cmd, rg_name, name) - - # create the app - if _create_new_app: - logger.warning("Creating app '%s' ....", name) - # TODO: Deploy without container params and update separately instead? - # deployment_container_image_name=docker_custom_image_name) - create_webapp(cmd, rg_name, name, asp, deployment_container_image_name=img_name) - logger.warning("Webapp creation complete") - else: - logger.warning("App '%s' already exists", name) - - # Set up the container - if _create_acr_img: - logger.warning("Configuring ACR container settings.") - registry_url = 'https://' + registry_name + '.azurecr.io' - acr_img_name = registry_name + '.azurecr.io/' + img_name - update_container_settings(cmd, rg_name, name, registry_url, acr_img_name) - - logger.warning("All done.") - return create_json - - -def _ping_scm_site(cmd, resource_group, name): - # wakeup kudu, by making an SCM call - import requests - # work around until the timeout limits issue for linux is investigated & fixed - user_name, password = _get_site_credential(cmd.cli_ctx, resource_group, name) - scm_url = _get_scm_url(cmd, resource_group, name) - import urllib3 - authorization = urllib3.util.make_headers(basic_auth='{}:{}'.format(user_name, password)) - requests.get(scm_url + '/api/settings', headers=authorization) - - def start_scan(cmd, resource_group_name, name, timeout="", slot=None): webapp = show_webapp(cmd, resource_group_name, name, slot) is_linux = webapp.reserved @@ -239,289 +113,3 @@ def stop_scan(cmd, resource_group_name, name, slot=None): headers['content-type'] = 'application/octet-stream' requests.delete(stop_scan_url, headers=authorization) - - -def _get_app_url(cmd, rg_name, app_name): - site = _generic_site_operation(cmd.cli_ctx, rg_name, app_name, 'get') - return "https://" + site.enabled_host_names[0] - - -def _check_for_ready_tunnel(remote_debugging, tunnel_server): - default_port = tunnel_server.is_port_set_to_default() - if default_port is not remote_debugging: - return True - return False - - -def create_tunnel(cmd, resource_group_name, name, port=None, slot=None): - logger.warning("remote-connection is deprecated and moving to cli-core, use `webapp create-remote-connection`") - - webapp = show_webapp(cmd, resource_group_name, name, slot) - is_linux = webapp.reserved - if not is_linux: - logger.error("Only Linux App Service Plans supported, Found a Windows App Service Plan") - return - import time - profiles = list_publish_profiles(cmd, resource_group_name, name, slot) - user_name = next(p['userName'] for p in profiles) - user_password = next(p['userPWD'] for p in profiles) - import threading - from .tunnel import TunnelServer - - if port is None: - port = 0 # Will auto-select a free port from 1024-65535 - logger.info('No port defined, creating on random free port') - host_name = name - if slot is not None: - host_name += "-" + slot - tunnel_server = TunnelServer('', port, host_name, user_name, user_password) - config = get_site_configs(cmd, resource_group_name, name, slot) - _ping_scm_site(cmd, resource_group_name, name) - - t = threading.Thread(target=_start_tunnel, args=(tunnel_server, config.remote_debugging_enabled)) - t.daemon = True - t.start() - - # Wait indefinitely for CTRL-C - while True: - time.sleep(5) - - -def _start_tunnel(tunnel_server, remote_debugging_enabled): - import time - if not _check_for_ready_tunnel(remote_debugging_enabled, tunnel_server): - logger.warning('Tunnel is not ready yet, please wait (may take up to 1 minute)') - while True: - time.sleep(1) - logger.warning('.') - if _check_for_ready_tunnel(remote_debugging_enabled, tunnel_server): - break - if remote_debugging_enabled is False: - logger.warning('SSH is available { username: root, password: Docker! }') - tunnel_server.start_server() - - -def _should_create_new_asp(cmd, rg_name, asp_name, location): - # get all appservice plans from RG - client = web_client_factory(cmd.cli_ctx) - for item in list(client.app_service_plans.list_by_resource_group(rg_name)): - if (item.name.lower() == asp_name.lower() and - item.location.replace(" ", "").lower() == location or - item.location == location): - return False - return True - - -# OneDeploy -def perform_onedeploy(cmd, - resource_group_name, - name, - src_path=None, - src_url=None, - target_path=None, - artifact_type=None, - is_async=None, - restart=None, - clean=None, - ignore_stack=None, - timeout=None, - slot=None): - params = OneDeployParams() - - params.cmd = cmd - params.resource_group_name = resource_group_name - params.webapp_name = name - params.src_path = src_path - params.src_url = src_url - params.target_path = target_path - params.artifact_type = artifact_type - params.is_async_deployment = is_async - params.should_restart = restart - params.is_clean_deployment = clean - params.should_ignore_stack = ignore_stack - params.timeout = timeout - params.slot = slot - - return _perform_onedeploy_internal(params) - - -# Class for OneDeploy parameters -# pylint: disable=too-many-instance-attributes,too-few-public-methods -class OneDeployParams(object): - def __init__(self): - self.cmd = None - self.resource_group_name = None - self.webapp_name = None - self.src_path = None - self.src_url = None - self.artifact_type = None - self.is_async_deployment = None - self.target_path = None - self.should_restart = None - self.is_clean_deployment = None - self.should_ignore_stack = None - self.timeout = None - self.slot = None -# pylint: enable=too-many-instance-attributes,too-few-public-methods - - -def _validate_onedeploy_params(params): - if params.src_path and params.src_url: - raise CLIError('Only one of --src-path and --src-url can be specified') - - if not params.src_path and not params.src_url: - raise CLIError('Either of --src-path or --src-url must be specified') - - if params.src_url and not params.artifact_type: - raise CLIError('Deployment type is mandatory when deploying from URLs. Use --type') - - -def _build_onedeploy_url(params): - scm_url = _get_scm_url(params.cmd, params.resource_group_name, params.webapp_name, params.slot) - deploy_url = scm_url + '/api/publish?type=' + params.artifact_type - - if params.is_async_deployment is not None: - deploy_url = deploy_url + '&async=' + str(params.is_async_deployment) - - if params.should_restart is not None: - deploy_url = deploy_url + '&restart=' + str(params.should_restart) - - if params.is_clean_deployment is not None: - deploy_url = deploy_url + '&clean=' + str(params.is_clean_deployment) - - if params.should_ignore_stack is not None: - deploy_url = deploy_url + '&ignorestack=' + str(params.should_ignore_stack) - - if params.target_path is not None: - deploy_url = deploy_url + '&path=' + params.target_path - - return deploy_url - - -def _get_onedeploy_status_url(params): - scm_url = _get_scm_url(params.cmd, params.resource_group_name, params.webapp_name, params.slot) - return scm_url + '/api/deployments/latest' - - -def _get_basic_headers(params): - import urllib3 - from azure.cli.core.util import ( - get_az_user_agent, - ) - - user_name, password = _get_site_credential(params.cmd.cli_ctx, params.resource_group_name, params.webapp_name, params.slot) - - if params.src_path: - content_type = 'application/octet-stream' - elif params.src_url: - content_type = 'application/json' - else: - raise CLIError('Unable to determine source location of the artifact being deployed') - - headers = urllib3.util.make_headers(basic_auth='{0}:{1}'.format(user_name, password)) - headers['Cache-Control'] = 'no-cache' - headers['User-Agent'] = get_az_user_agent() - headers['Content-Type'] = content_type - - return headers - - -def _get_onedeploy_request_body(params): - import os - import json - - if params.src_path: - logger.info('Deploying from local path: %s', params.src_path) - try: - with open(os.path.realpath(os.path.expanduser(params.src_path)), 'rb') as fs: - body = fs.read() - except Exception as e: - raise CLIError("Either '{}' is not a valid local file path or you do not have permissions to access it".format(params.src_path)) from e - elif params.src_url: - logger.info('Deploying from URL: %s', params.src_url) - body = json.dumps({ - "packageUri": params.src_url - }) - else: - raise CLIError('Unable to determine source location of the artifact being deployed') - - return body - - -def _update_artifact_type(params): - import ntpath - - if params.artifact_type is not None: - return - - # Interpret deployment type from the file extension if the type parameter is not passed - file_name = ntpath.basename(params.src_path) - file_extension = file_name.split(".", 1)[1] - if file_extension in ('war', 'jar', 'ear', 'zip'): - params.artifact_type = file_extension - elif file_extension in ('sh', 'bat'): - params.artifact_type = 'startup' - else: - params.artifact_type = 'static' - logger.warning("Deployment type: %s. To override deloyment type, please specify the --type parameter. " - "Possible values: war, jar, ear, zip, startup, script, static", params.artifact_type) - - -def _make_onedeploy_request(params): - import requests - - from azure.cli.core.util import ( - should_disable_connection_verify, - ) - - # Build the request body, headers, API URL and status URL - body = _get_onedeploy_request_body(params) - headers = _get_basic_headers(params) - deploy_url = _build_onedeploy_url(params) - deployment_status_url = _get_onedeploy_status_url(params) - - logger.info("Deployment API: %s", deploy_url) - response = requests.post(deploy_url, data=body, headers=headers, verify=not should_disable_connection_verify()) - - # For debugging purposes only, you can change the async deployment into a sync deployment by polling the API status - # For that, set poll_async_deployment_for_debugging=True - poll_async_deployment_for_debugging = False - - # check the status of async deployment - if response.status_code == 202: - if poll_async_deployment_for_debugging: - logger.info('Polloing the status of async deployment') - response_body = _check_zip_deployment_status(params.cmd, params.resource_group_name, params.webapp_name, deployment_status_url, headers, params.timeout) - logger.info('Async deployment complete. Server response: %s', response_body) - return - - if response.status_code == 200: - return - - # API not available yet! - if response.status_code == 404: - raise CLIError("This API isn't available in this environment yet!") - - # check if there's an ongoing process - if response.status_code == 409: - raise CLIError("Another deployment is in progress. You can track the ongoing deployment at {}".format(deployment_status_url)) - - # check if an error occured during deployment - if response.status_code: - raise CLIError("An error occured during deployment. Status Code: {}, Details: {}".format(response.status_code, response.text)) - - -# OneDeploy -def _perform_onedeploy_internal(params): - - # Do basic paramter validation - _validate_onedeploy_params(params) - - # Update artifact type, if required - _update_artifact_type(params) - - # Now make the OneDeploy API call - logger.info("Initiating deployment") - _make_onedeploy_request(params) - - return logger.info("Deployment has completed successfully") diff --git a/src/webapp/setup.py b/src/webapp/setup.py index f2f35859b73..7d944399537 100644 --- a/src/webapp/setup.py +++ b/src/webapp/setup.py @@ -8,7 +8,7 @@ from codecs import open from setuptools import setup, find_packages -VERSION = "0.3.1" +VERSION = "0.4.0" CLASSIFIERS = [ 'Development Status :: 4 - Beta',