Exemplo n.º 1
0
def prepare_webapp_deploy(language, code_dir=None, proj_file_path=None):
    if not code_dir:
        code_dir = os.getcwd()
        logger.info('--code-dir not provided, defaulting to current working directory: %s\n'
                    'For more information, run "az bot prepare-deploy -h"', code_dir)
    elif not os.path.exists(code_dir):
        raise CLIError('Provided --code-dir value ({0}) does not exist'.format(code_dir))

    def does_file_exist(file_name):
        if os.path.exists(os.path.join(code_dir, file_name)):
            raise CLIError('%s found in %s\nPlease delete this %s before calling "az bot '   # pylint:disable=logging-not-lazy
                           'prepare-deploy"' % (file_name, code_dir, file_name))

    if language != 'Csharp':
        if proj_file_path:
            raise CLIError('--proj-file-path should not be passed in if language is not Csharp')
        does_file_exist('web.config')

        BotPublishPrep.prepare_publish_v4(logger, code_dir, proj_file_path, {'lang': language,
                                                                             'has_web_config': False,
                                                                             'has_iisnode_yml': True})
    else:
        if not proj_file_path:
            raise CLIError('--proj-file-path must be provided if language is Csharp')
        does_file_exist('.deployment')
        csproj_file = os.path.join(code_dir, proj_file_path)
        if not os.path.exists(csproj_file):
            raise CLIError('{0} file not found\nPlease verify the relative path to the .csproj file from the '
                           '--code-dir'.format(csproj_file))

        with open(os.path.join(code_dir, '.deployment'), 'w') as f:
            f.write('[config]\n')
            proj_file = proj_file_path.lower()
            proj_file = proj_file if proj_file.endswith('.csproj') else proj_file + '.csproj'
            f.write('SCM_SCRIPT_GENERATOR_ARGS=--aspNetCore "{0}"\n'.format(proj_file))
Exemplo n.º 2
0
def publish_app(
        cmd,
        client,
        resource_group_name,
        resource_name,
        code_dir=None,
        proj_file_path=None,
        version='v4',  # pylint:disable=too-many-statements
        keep_node_modules=None,
        timeout=None):
    if version == 'v4':
        logger.warning(
            'DEPRECATION WARNING: `az bot publish` is deprecated for v4 bots. We recommend using `az webapp`'
            ' to deploy your bot to Azure. For more information on how to deploy a v4 bot, see '
            'https://aka.ms/deploy-your-bot.')
    # Get the bot information and ensure it's not only a registration bot.
    bot = client.bots.get(resource_group_name=resource_group_name,
                          resource_name=resource_name)
    if bot.kind == 'bot':
        raise CLIError(
            'Bot kind is \'bot\', meaning it is a registration bot. '
            'Source publish is not supported for registration only bots.')

    # If the user does not pass in a path to the local bot project, get the current working directory.
    if not code_dir:
        code_dir = os.getcwd()

        logger.info(
            'Parameter --code-dir not provided, defaulting to current working directory, %s. '
            'For more information, run \'az bot publish -h\'', code_dir)

    code_dir = code_dir.strip()
    if not os.path.isdir(code_dir):
        raise CLIError(
            'The path %s is not a valid directory. '
            'Please supply a valid directory path containing your source code.'
            % code_dir)

    # If local IIS Node.js files exist, this means two things:
    # 1. We may not need to download the necessary web.config and iisnode.yml files to deploy a Node.js bot on IIS.
    # 2. We shouldn't delete their local web.config and issnode.yml files (if they exist).
    iis_publish_info = {
        'lang':
        CSHARP if not os.path.exists(os.path.join(code_dir, 'package.json'))
        else JAVASCRIPT,
        'has_web_config':
        True
        if os.path.exists(os.path.join(code_dir, 'web.config')) else False,
        'has_iisnode_yml':
        True
        if os.path.exists(os.path.join(code_dir, 'iisnode.yml')) else False
    }

    # Ensure that the directory contains appropriate post deploy scripts folder
    if 'PostDeployScripts' not in os.listdir(code_dir):
        if version == 'v4':

            logger.info(
                'Detected SDK version v4. Running prepare publish in code directory %s and for project file %s'  # pylint:disable=logging-not-lazy
                % (code_dir, proj_file_path))

            # Automatically run prepare-publish in case of v4.
            BotPublishPrep.prepare_publish_v4(logger, code_dir, proj_file_path,
                                              iis_publish_info)
        else:
            logger.info(
                'Detected SDK version v3. PostDeploymentScripts folder not found in directory provided: %s',
                code_dir)

            raise CLIError(
                'Publish directory provided is uses Bot Builder SDK V3, and as a legacy bot needs to be '
                'prepared for deployment. Please run prepare-publish. For more information, run \'az bot '
                'prepare-publish -h\'.')

    zip_filepath = BotPublishPrep.create_upload_zip(logger,
                                                    code_dir,
                                                    include_node_modules=False)
    logger.info('Zip file path created, at %s.', zip_filepath)

    kudu_client = KuduClient(cmd, resource_group_name, resource_name, bot,
                             logger)

    output = kudu_client.publish(zip_filepath, timeout, keep_node_modules,
                                 iis_publish_info['lang'])

    logger.info(
        'Bot source published. Preparing bot application to run the new source.'
    )
    os.remove('upload.zip')
    # If the bot is a Node.js bot and did not initially have web.config, delete web.config and iisnode.yml.
    if iis_publish_info['lang'] == JAVASCRIPT:
        if not iis_publish_info['has_web_config'] and os.path.exists(
                os.path.join(code_dir, 'web.config')):
            os.remove(os.path.join(code_dir, 'web.config'))
        if not iis_publish_info['has_iisnode_yml'] and os.path.exists(
                os.path.join(code_dir, 'iisnode.yml')):
            os.remove(os.path.join(code_dir, 'iisnode.yml'))

        if not iis_publish_info['has_iisnode_yml'] and not iis_publish_info[
                'has_web_config']:
            logger.info(
                "web.config and iisnode.yml for Node.js bot were fetched from Azure for deployment using IIS. "
                "These files have now been removed from %s."
                "To see the two files used for your deployment, either visit your bot's Kudu site or download "
                "the files from https://icscratch.blob.core.windows.net/bot-packages/node_v4_publish.zip",
                code_dir)
        elif not iis_publish_info['has_iisnode_yml']:
            logger.info(
                "iisnode.yml for Node.js bot was fetched from Azure for deployment using IIS. To see this file "
                "that was used for your deployment, either visit your bot's Kudu site or download the file "
                "from https://icscratch.blob.core.windows.net/bot-packages/node_v4_publish.zip"
            )
        elif not iis_publish_info['has_web_config']:
            logger.info(
                "web.config for Node.js bot was fetched from Azure for deployment using IIS. To see this file "
                "that was used for your deployment, either visit your bot's Kudu site or download the file "
                "from https://icscratch.blob.core.windows.net/bot-packages/node_v4_publish.zip"
            )

    if os.path.exists(os.path.join('.',
                                   'package.json')) and not keep_node_modules:
        logger.info(
            'Detected language javascript. Installing node dependencies in remote bot.'
        )
        kudu_client.install_node_dependencies()

    if output.get('active'):
        logger.info('Deployment successful!')

    if not output.get('active'):
        scm_url = output.get('url')
        deployment_id = output.get('id')
        # Instead of replacing "latest", which would could be in the bot name, we replace "deployments/latest"
        deployment_url = scm_url.replace('deployments/latest',
                                         'deployments/%s' % deployment_id)
        logger.error(
            'Deployment failed. To find out more information about this deployment, please visit %s.',
            deployment_url)
    return output
Exemplo n.º 3
0
def publish_app(cmd, client, resource_group_name, resource_name, code_dir=None, proj_name=None, version='v3'):
    """Publish local bot code to Azure.

    This method is directly called via "bot publish"

    :param cmd:
    :param client:
    :param resource_group_name:
    :param resource_name:
    :param code_dir:
    :param proj_name:
    :param version:
    :return:
    """
    # Get the bot information and ensure it's not only a registration bot.
    bot = client.bots.get(
        resource_group_name=resource_group_name,
        resource_name=resource_name
    )
    if bot.kind == 'bot':
        raise CLIError('Bot kind is \'bot\', meaning it is a registration bot. '
                       'Source publish is not supported for registration only bots.')

    # If the user does not pass in a path to the local bot project, get the current working directory.
    if not code_dir:
        code_dir = os.getcwd()

        logger.info('Parameter --code-dir not provided, defaulting to current working directory, %s. '
                    'For more information, run \'az bot publish -h\'', code_dir)

    code_dir = code_dir.strip()
    if not os.path.isdir(code_dir):
        raise CLIError('The path %s is not a valid directory. '
                       'Please supply a valid directory path containing your source code.' % code_dir)

    # Ensure that the directory contains appropriate post deploy scripts folder
    if 'PostDeployScripts' not in os.listdir(code_dir):
        if version == 'v4':

            logger.info('Detected SDK version v4. Running prepare publish in code directory %s and for project file %s'  # pylint:disable=logging-not-lazy
                        % (code_dir, proj_name))

            # Automatically run prepare-publish in case of v4.
            BotPublishPrep.prepare_publish_v4(logger, code_dir, proj_name)
        else:
            logger.info('Detected SDK version v3. PostDeploymentScripts folder not found in directory provided: %s',
                        code_dir)

            raise CLIError('Publish directory provided is uses Bot Builder SDK V3, and as a legacy bot needs to be '
                           'prepared for deployment. Please run prepare-publish. For more information, run \'az bot '
                           'prepare-publish -h\'.')

    logger.info('Creating upload zip file.')
    zip_filepath = BotPublishPrep.create_upload_zip(logger, code_dir, include_node_modules=False)
    logger.info('Zip file path created, at %s.', zip_filepath)

    kudu_client = KuduClient(cmd, resource_group_name, resource_name, bot, logger)
    output = kudu_client.publish(zip_filepath)

    logger.info('Bot source published. Preparing bot application to run the new source.')
    os.remove('upload.zip')

    if os.path.exists(os.path.join('.', 'package.json')):
        logger.info('Detected language javascript. Installing node dependencies in remote bot.')
        kudu_client.install_node_dependencies()

    logger.info('Bot publish completed successfully.')

    return output
Exemplo n.º 4
0
def publish_app(cmd, client, resource_group_name, resource_name, code_dir=None, proj_name=None, version='v3'):
    """Publish local bot code to Azure.

    This method is directly called via "bot publish"

    :param cmd:
    :param client:
    :param resource_group_name:
    :param resource_name:
    :param code_dir:
    :param proj_name:
    :param version:
    :return:
    """
    if version == 'v3':
        return publish_appv3(cmd, client, resource_group_name, resource_name, code_dir)

    # Get the bot information and ensure it's not only a registration bot.
    bot = client.bots.get(
        resource_group_name=resource_group_name,
        resource_name=resource_name
    )
    if bot.kind == 'bot':
        raise CLIError('Bot kind is \'bot\', meaning it is a registration bot. '
                       'Source publish is not supported for registration only bots.')

    # If the user does not pass in a path to the local bot project, get the current working directory.
    if not code_dir:
        code_dir = os.getcwd()

        logger.info('Parameter --code-dir not provided, defaulting to current working directory, %s. '
                    'For more information, run \'az bot publish -h\'', code_dir)

    if not os.path.isdir(code_dir):
        raise CLIError('The path %s is not a valid directory. '
                       'Please supply a valid directory path containing your source code.' % code_dir)

    # Ensure that the directory contains appropriate post deploy scripts folder
    if 'PostDeployScripts' not in os.listdir(code_dir):
        BotPublishPrep.prepare_publish_v4(logger, code_dir, proj_name)

    logger.info('Creating upload zip file.')
    zip_filepath = BotPublishPrep.create_upload_zip(logger, code_dir, include_node_modules=False)
    logger.info('Zip file path created, at %s.', zip_filepath)

    kudu_client = KuduClient(cmd, resource_group_name, resource_name, bot)
    output = kudu_client.publish(zip_filepath)
    logger.info('Bot source published. Preparing bot application to run the new source.')
    os.remove('upload.zip')
    if os.path.exists(os.path.join('.', 'package.json')):
        logger.info('Detected language javascript. Installing node dependencies in remote bot.')
        __install_node_dependencies(kudu_client)

    if output.get('active'):
        logger.info('Deployment successful!')

    if not output.get('active'):
        scm_url = output.get('url')
        deployment_id = output.get('id')
        # Instead of replacing "latest", which would could be in the bot name, we replace "deployments/latest"
        deployment_url = scm_url.replace('deployments/latest', 'deployments/%s' % deployment_id)
        logger.error('Deployment failed. To find out more information about this deployment, please visit %s.'
                     % deployment_url)

    return output
Exemplo n.º 5
0
def publish_app(
        cmd,
        client,
        resource_group_name,
        resource_name,
        code_dir=None,
        proj_file_path=None,
        version='v3',  # pylint:disable=too-many-statements
        keep_node_modules=None,
        timeout=None):
    """Publish local bot code to Azure.

    This method is directly called via "bot publish"

    :param cmd:
    :param client:
    :param resource_group_name:
    :param resource_name:
    :param code_dir:
    :param proj_file_path:
    :param version:
    :param keep_node_modules:
    :param timeout:
    :return:
    """
    # Get the bot information and ensure it's not only a registration bot.
    bot = client.bots.get(resource_group_name=resource_group_name,
                          resource_name=resource_name)
    if bot.kind == 'bot':
        raise CLIError(
            'Bot kind is \'bot\', meaning it is a registration bot. '
            'Source publish is not supported for registration only bots.')

    # If the user does not pass in a path to the local bot project, get the current working directory.
    if not code_dir:
        code_dir = os.getcwd()

        logger.info(
            'Parameter --code-dir not provided, defaulting to current working directory, %s. '
            'For more information, run \'az bot publish -h\'', code_dir)

    code_dir = code_dir.strip()
    if not os.path.isdir(code_dir):
        raise CLIError(
            'The path %s is not a valid directory. '
            'Please supply a valid directory path containing your source code.'
            % code_dir)

    # If local IIS Node.js files exist, this means two things:
    # 1. We may not need to download the necessary web.config and iisnode.yml files to deploy a Node.js bot on IIS.
    # 2. We shouldn't delete their local web.config and issnode.yml files (if they exist).
    iis_publish_info = {
        'lang':
        'Csharp' if not os.path.exists(os.path.join(code_dir, 'package.json'))
        else 'Node',
        'has_web_config':
        True
        if os.path.exists(os.path.join(code_dir, 'web.config')) else False,
        'has_iisnode_yml':
        True
        if os.path.exists(os.path.join(code_dir, 'iisnode.yml')) else False
    }

    # Ensure that the directory contains appropriate post deploy scripts folder
    if 'PostDeployScripts' not in os.listdir(code_dir):
        if version == 'v4':

            logger.info(
                'Detected SDK version v4. Running prepare publish in code directory %s and for project file %s'  # pylint:disable=logging-not-lazy
                % (code_dir, proj_file_path))

            # Automatically run prepare-publish in case of v4.
            BotPublishPrep.prepare_publish_v4(logger, code_dir, proj_file_path,
                                              iis_publish_info)
        else:
            logger.info(
                'Detected SDK version v3. PostDeploymentScripts folder not found in directory provided: %s',
                code_dir)

            raise CLIError(
                'Publish directory provided is uses Bot Builder SDK V3, and as a legacy bot needs to be '
                'prepared for deployment. Please run prepare-publish. For more information, run \'az bot '
                'prepare-publish -h\'.')

    zip_filepath = BotPublishPrep.create_upload_zip(logger,
                                                    code_dir,
                                                    include_node_modules=False)
    logger.info('Zip file path created, at %s.', zip_filepath)

    kudu_client = KuduClient(cmd, resource_group_name, resource_name, bot,
                             logger)

    app_settings = WebAppOperations.get_app_settings(
        cmd=cmd,
        resource_group_name=resource_group_name,
        name=kudu_client.bot_site_name)
    scm_do_build = [
        item['value'] for item in app_settings
        if item['name'] == 'SCM_DO_BUILD_DURING_DEPLOYMENT'
    ]
    if scm_do_build and scm_do_build[0] == 'true':
        logger.info(
            'Detected SCM_DO_BUILD_DURING_DEPLOYMENT with value of "true" in App Service\'s Application '
            'Settings. Build will commence during deployment. For more information, see '
            'https://github.com/projectkudu/kudu/wiki/Deploying-from-a-zip-file'
        )
    else:
        logger.warning(
            'Didn\'t detect SCM_DO_BUILD_DURING_DEPLOYMENT or its value was "false" in App Service\'s '
            'Application Settings. Build may not commence during deployment. To learn how to trigger a build'
            ' when deploying to your App Service, see '
            'https://github.com/projectkudu/kudu/wiki/Deploying-from-a-zip-file'
        )
        subscription_id = get_subscription_id(cmd.cli_ctx)
        logger.warning(
            'To change the Application Setting via az cli, use the following command:\naz webapp config '  # pylint:disable=logging-not-lazy
            'appsettings set -n %s -g %s --subscription %s --settings SCM_DO_BUILD_DURING_DEPLOYMENT=true'
            %
            (kudu_client.bot_site_name, resource_group_name, subscription_id))

    output = kudu_client.publish(zip_filepath, timeout, keep_node_modules,
                                 iis_publish_info['lang'])

    logger.info(
        'Bot source published. Preparing bot application to run the new source.'
    )
    os.remove('upload.zip')
    # If the bot is a Node.js bot and did not initially have web.config, delete web.config and iisnode.yml.
    if iis_publish_info['lang'] == 'Node':
        if not iis_publish_info['has_web_config'] and os.path.exists(
                os.path.join(code_dir, 'web.config')):
            os.remove(os.path.join(code_dir, 'web.config'))
        if not iis_publish_info['has_iisnode_yml'] and os.path.exists(
                os.path.join(code_dir, 'iisnode.yml')):
            os.remove(os.path.join(code_dir, 'iisnode.yml'))

        if not iis_publish_info['has_iisnode_yml'] and not iis_publish_info[
                'has_web_config']:
            logger.info(
                "web.config and iisnode.yml for Node.js bot were fetched from Azure for deployment using IIS. "
                "These files have now been removed from %s."
                "To see the two files used for your deployment, either visit your bot's Kudu site or download "
                "the files from https://icscratch.blob.core.windows.net/bot-packages/node_v4_publish.zip",
                code_dir)
        elif not iis_publish_info['has_iisnode_yml']:
            logger.info(
                "iisnode.yml for Node.js bot was fetched from Azure for deployment using IIS. To see this file "
                "that was used for your deployment, either visit your bot's Kudu site or download the file "
                "from https://icscratch.blob.core.windows.net/bot-packages/node_v4_publish.zip"
            )
        elif not iis_publish_info['has_web_config']:
            logger.info(
                "web.config for Node.js bot was fetched from Azure for deployment using IIS. To see this file "
                "that was used for your deployment, either visit your bot's Kudu site or download the file "
                "from https://icscratch.blob.core.windows.net/bot-packages/node_v4_publish.zip"
            )

    if os.path.exists(os.path.join('.',
                                   'package.json')) and not keep_node_modules:
        logger.info(
            'Detected language javascript. Installing node dependencies in remote bot.'
        )
        kudu_client.install_node_dependencies()

    if output.get('active'):
        logger.info('Deployment successful!')

    if not output.get('active'):
        scm_url = output.get('url')
        deployment_id = output.get('id')
        # Instead of replacing "latest", which would could be in the bot name, we replace "deployments/latest"
        deployment_url = scm_url.replace('deployments/latest',
                                         'deployments/%s' % deployment_id)
        logger.error(
            'Deployment failed. To find out more information about this deployment, please visit %s.',
            deployment_url)
    return output
Exemplo n.º 6
0
def publish_app(cmd, client, resource_group_name, resource_name, code_dir=None, proj_file_path=None, version='v3',  # pylint:disable=too-many-statements
                keep_node_modules=None, timeout=None):
    """Publish local bot code to Azure.

    This method is directly called via "bot publish"

    :param cmd:
    :param client:
    :param resource_group_name:
    :param resource_name:
    :param code_dir:
    :param proj_file_path:
    :param version:
    :param keep_node_modules:
    :param timeout:
    :return:
    """
    # Get the bot information and ensure it's not only a registration bot.
    bot = client.bots.get(
        resource_group_name=resource_group_name,
        resource_name=resource_name
    )
    if bot.kind == 'bot':
        raise CLIError('Bot kind is \'bot\', meaning it is a registration bot. '
                       'Source publish is not supported for registration only bots.')

    # If the user does not pass in a path to the local bot project, get the current working directory.
    if not code_dir:
        code_dir = os.getcwd()

        logger.info('Parameter --code-dir not provided, defaulting to current working directory, %s. '
                    'For more information, run \'az bot publish -h\'', code_dir)

    code_dir = code_dir.strip()
    if not os.path.isdir(code_dir):
        raise CLIError('The path %s is not a valid directory. '
                       'Please supply a valid directory path containing your source code.' % code_dir)

    # If local IIS Node.js files exist, this means two things:
    # 1. We may not need to download the necessary web.config and iisnode.yml files to deploy a Node.js bot on IIS.
    # 2. We shouldn't delete their local web.config and issnode.yml files (if they exist).
    iis_publish_info = {
        'lang': 'Csharp' if not os.path.exists(os.path.join(code_dir, 'package.json')) else 'Node',
        'has_web_config': True if os.path.exists(os.path.join(code_dir, 'web.config')) else False,
        'has_iisnode_yml': True if os.path.exists(os.path.join(code_dir, 'iisnode.yml')) else False
    }

    # Ensure that the directory contains appropriate post deploy scripts folder
    if 'PostDeployScripts' not in os.listdir(code_dir):
        if version == 'v4':

            logger.info('Detected SDK version v4. Running prepare publish in code directory %s and for project file %s'  # pylint:disable=logging-not-lazy
                        % (code_dir, proj_file_path))

            # Automatically run prepare-publish in case of v4.
            BotPublishPrep.prepare_publish_v4(logger, code_dir, proj_file_path, iis_publish_info)
        else:
            logger.info('Detected SDK version v3. PostDeploymentScripts folder not found in directory provided: %s',
                        code_dir)

            raise CLIError('Publish directory provided is uses Bot Builder SDK V3, and as a legacy bot needs to be '
                           'prepared for deployment. Please run prepare-publish. For more information, run \'az bot '
                           'prepare-publish -h\'.')

    zip_filepath = BotPublishPrep.create_upload_zip(logger, code_dir, include_node_modules=False)
    logger.info('Zip file path created, at %s.', zip_filepath)

    kudu_client = KuduClient(cmd, resource_group_name, resource_name, bot, logger)

    app_settings = WebAppOperations.get_app_settings(
        cmd=cmd,
        resource_group_name=resource_group_name,
        name=kudu_client.bot_site_name
    )
    scm_do_build = [item['value'] for item in app_settings if item['name'] == 'SCM_DO_BUILD_DURING_DEPLOYMENT']
    if scm_do_build and scm_do_build[0] == 'true':
        logger.info('Detected SCM_DO_BUILD_DURING_DEPLOYMENT with value of "true" in App Service\'s Application '
                    'Settings. Build will commence during deployment. For more information, see '
                    'https://github.com/projectkudu/kudu/wiki/Deploying-from-a-zip-file')
    else:
        logger.warning('Didn\'t detect SCM_DO_BUILD_DURING_DEPLOYMENT or its value was "false" in App Service\'s '
                       'Application Settings. Build may not commence during deployment. To learn how to trigger a build'
                       ' when deploying to your App Service, see '
                       'https://github.com/projectkudu/kudu/wiki/Deploying-from-a-zip-file')
        subscription_id = get_subscription_id(cmd.cli_ctx)
        logger.warning('To change the Application Setting via az cli, use the following command:\naz webapp config '  # pylint:disable=logging-not-lazy
                       'appsettings set -n %s -g %s --subscription %s --settings SCM_DO_BUILD_DURING_DEPLOYMENT=true' %
                       (kudu_client.bot_site_name, resource_group_name, subscription_id))

    output = kudu_client.publish(zip_filepath, timeout, keep_node_modules, iis_publish_info['lang'])

    logger.info('Bot source published. Preparing bot application to run the new source.')
    os.remove('upload.zip')
    # If the bot is a Node.js bot and did not initially have web.config, delete web.config and iisnode.yml.
    if iis_publish_info['lang'] == 'Node':
        if not iis_publish_info['has_web_config'] and os.path.exists(os.path.join(code_dir, 'web.config')):
            os.remove(os.path.join(code_dir, 'web.config'))
        if not iis_publish_info['has_iisnode_yml'] and os.path.exists(os.path.join(code_dir, 'iisnode.yml')):
            os.remove(os.path.join(code_dir, 'iisnode.yml'))

        if not iis_publish_info['has_iisnode_yml'] and not iis_publish_info['has_web_config']:
            logger.info("web.config and iisnode.yml for Node.js bot were fetched from Azure for deployment using IIS. "
                        "These files have now been removed from %s."
                        "To see the two files used for your deployment, either visit your bot's Kudu site or download "
                        "the files from https://icscratch.blob.core.windows.net/bot-packages/node_v4_publish.zip",
                        code_dir)
        elif not iis_publish_info['has_iisnode_yml']:
            logger.info("iisnode.yml for Node.js bot was fetched from Azure for deployment using IIS. To see this file "
                        "that was used for your deployment, either visit your bot's Kudu site or download the file "
                        "from https://icscratch.blob.core.windows.net/bot-packages/node_v4_publish.zip")
        elif not iis_publish_info['has_web_config']:
            logger.info("web.config for Node.js bot was fetched from Azure for deployment using IIS. To see this file "
                        "that was used for your deployment, either visit your bot's Kudu site or download the file "
                        "from https://icscratch.blob.core.windows.net/bot-packages/node_v4_publish.zip")

    if os.path.exists(os.path.join('.', 'package.json')) and not keep_node_modules:
        logger.info('Detected language javascript. Installing node dependencies in remote bot.')
        kudu_client.install_node_dependencies()

    if output.get('active'):
        logger.info('Deployment successful!')

    if not output.get('active'):
        scm_url = output.get('url')
        deployment_id = output.get('id')
        # Instead of replacing "latest", which would could be in the bot name, we replace "deployments/latest"
        deployment_url = scm_url.replace('deployments/latest', 'deployments/%s' % deployment_id)
        logger.error('Deployment failed. To find out more information about this deployment, please visit %s.',
                     deployment_url)
    return output