Example #1
0
    def _set_version(args):

        new_version = args.set_version

        if not sdk_helpers.is_valid_version_syntax(new_version):
            raise SDKException(
                "{0} is not a valid version".format(new_version))

        new_version_int = list(map(int, (re.findall(r"\d+", new_version))))

        # Get absolute path_to_src
        path_to_src = os.path.abspath(args.package)

        # Get path to setup.py file
        path_setup_py_file = os.path.join(path_to_src,
                                          package_helpers.BASE_NAME_SETUP_PY)

        # Parse the setup.py file
        setup_py_attributes = package_helpers.parse_setup_py(
            path_setup_py_file,
            package_helpers.SUPPORTED_SETUP_PY_ATTRIBUTE_NAMES)

        package_name = setup_py_attributes.get("name", "")

        LOG.info("Setting Resilient Platform version for %s to %s",
                 package_name, new_version)

        # Get the customize file location.
        path_customize_py = package_helpers.get_configuration_py_file_path(
            "customize", setup_py_attributes)

        # Get customize.py ImportDefinition
        customize_py_import_definition = package_helpers.get_import_definition_from_customize_py(
            path_customize_py)

        old_version = customize_py_import_definition["server_version"][
            "version"]

        LOG.info("Old Version: %s", old_version)
        LOG.info("New Version: %s", new_version)

        # Set the new version
        customize_py_import_definition["server_version"][
            "version"] = new_version
        customize_py_import_definition["server_version"][
            "major"] = new_version_int[0]
        customize_py_import_definition["server_version"][
            "minor"] = new_version_int[1]
        customize_py_import_definition["server_version"][
            "build_number"] = new_version_int[2]

        LOG.info("Loading old customize.py file")

        # Load the customize.py module
        customize_py_module = package_helpers.load_customize_py_module(
            path_customize_py, warn=False)

        # Get the 'old_params' from customize.py
        old_params = customize_py_module.codegen_reload_data()

        # Rename the old customize.py with .bak
        path_customize_py_bak = sdk_helpers.rename_to_bak_file(
            path_customize_py)

        # If local export file exists then save it to a .bak file.
        # (Older packages may not have the /util/data/export.res file)
        # Figure out the path of the files first
        dir_customize_py = os.path.dirname(path_customize_py)
        path_local_export_res = os.path.join(
            dir_customize_py, package_helpers.PATH_LOCAL_EXPORT_RES)

        path_local_export_res_bak = None
        if os.path.isfile(path_local_export_res):
            path_local_export_res_bak = sdk_helpers.rename_to_bak_file(
                path_local_export_res)

        try:

            jinja_data = sdk_helpers.get_from_export(
                customize_py_import_definition,
                message_destinations=old_params.get("message_destinations"),
                functions=old_params.get("functions"),
                workflows=old_params.get("workflows"),
                rules=old_params.get("actions"),
                fields=old_params.get("incident_fields"),
                artifact_types=old_params.get("incident_artifact_types"),
                datatables=old_params.get("datatables"),
                tasks=old_params.get("automatic_tasks"),
                scripts=old_params.get("scripts"))

            jinja_data["export_data"] = sdk_helpers.minify_export(
                customize_py_import_definition,
                message_destinations=sdk_helpers.get_object_api_names(
                    ResilientObjMap.MESSAGE_DESTINATIONS,
                    jinja_data.get("message_destinations")),
                functions=sdk_helpers.get_object_api_names(
                    ResilientObjMap.FUNCTIONS, jinja_data.get("functions")),
                workflows=sdk_helpers.get_object_api_names(
                    ResilientObjMap.WORKFLOWS, jinja_data.get("workflows")),
                rules=sdk_helpers.get_object_api_names(
                    ResilientObjMap.RULES, jinja_data.get("rules")),
                fields=jinja_data.get("all_fields"),
                artifact_types=sdk_helpers.get_object_api_names(
                    ResilientObjMap.INCIDENT_ARTIFACT_TYPES,
                    jinja_data.get("artifact_types")),
                datatables=sdk_helpers.get_object_api_names(
                    ResilientObjMap.DATATABLES, jinja_data.get("datatables")),
                tasks=sdk_helpers.get_object_api_names(
                    ResilientObjMap.TASKS, jinja_data.get("tasks")),
                phases=sdk_helpers.get_object_api_names(
                    ResilientObjMap.PHASES, jinja_data.get("phases")),
                scripts=sdk_helpers.get_object_api_names(
                    ResilientObjMap.SCRIPTS, jinja_data.get("scripts")))

            # Add package_name to jinja_data
            jinja_data["package_name"] = package_name

            # Add version
            jinja_data["version"] = setup_py_attributes.get(
                "version", package_helpers.MIN_SETUP_PY_VERSION)

            # Instansiate Jinja2 Environment with path to Jinja2 templates for customize.py
            jinja_env = sdk_helpers.setup_jinja_env(
                "data/codegen/templates/package_template/package/util")
            jinja_template = jinja_env.get_template("customize.py.jinja2")

            LOG.info("Writing new customize.py file")

            # Render & write jinja2 template
            jinja_rendered_text = jinja_template.render(jinja_data)
            sdk_helpers.write_file(path_customize_py, jinja_rendered_text)

            # Instansiate Jinja2 Environment with path to Jinja2 templates for /util/dat/export.res
            #jinja_env = sdk_helpers.setup_jinja_env("data/codegen/templates/package_template/package/util/data")
            jinja_template = jinja_env.get_template("/data/export.res.jinja2")

            LOG.debug("Writing new /util/data/export.res file")

            # Render jinja2 template
            jinja_rendered_text = jinja_template.render(jinja_data)

            # Make sure the /util/data directory is there if it is not
            dir_local_export_res = os.path.dirname(path_local_export_res)
            if not os.path.exists(dir_local_export_res):
                os.makedirs(dir_local_export_res)

            # Write the file
            sdk_helpers.write_file(path_local_export_res, jinja_rendered_text)

            LOG.info("'dev --set-version' complete for '%s'", package_name)

        except Exception as err:
            LOG.error(
                u"Error running resilient-sdk dev --set-version\n\nERROR:%s",
                err)

        # This is required in finally block as user may kill using keyboard interrupt
        finally:
            # If an error occurred, customize.py does not exist, rename the backup file to original
            if not os.path.isfile(path_customize_py):
                LOG.info(
                    u"An error occurred. Renaming customize.py.bak to customize.py"
                )
                sdk_helpers.rename_file(path_customize_py_bak,
                                        package_helpers.BASE_NAME_CUSTOMIZE_PY)
            if path_local_export_res_bak and not os.path.isfile(
                    path_local_export_res):
                LOG.info(
                    u"An error occurred. Renaming /util/data/export.res.bak to export.res"
                )
                sdk_helpers.rename_file(
                    path_local_export_res_bak,
                    package_helpers.BASE_NAME_LOCAL_EXPORT_RES)
def create_extension(path_setup_py_file, path_apikey_permissions_file,
                     output_dir, path_built_distribution=None, path_extension_logo=None, path_company_logo=None, path_payload_samples=None, path_validate_report=None,
                     custom_display_name=None, repository_name=None, image_hash=None, keep_build_dir=False):
    """
    Function that creates The App.zip file from the given setup.py, customize and config files
    and copies it to the output_dir. Returns the path to the app.zip

    :param path_setup_py_file: abs path to the setup.py file
    :type path_setup_py_file: str
    :param path_apikey_permissions_file: abs path to the apikey_permissions.txt file
    :type path_apikey_permissions_file: str
    :param output_dir: abs path to the directory the App.zip should be produced
    :type output_dir: str
    :param path_built_distribution: abs path to a tar.gz Built Distribution
        - if provided uses that .tar.gz
        - else looks for it in the output_dir. E.g: output_dir/package_name.tar.gz
    :type path_built_distribution: str
    :param path_extension_logo: abs path to the app_logo.png. Has to be 200x72 and a .png file
        - if not provided uses default icon
    :type path_extension_logo: str
    :param path_company_logo: abs path to the company_logo.png. Has to be 100x100 and a .png file
        - if not provided uses default icon
    :type path_company_logo: str
    :param path_payload_samples: abs path to directory containing the files with a JSON schema and example output of the functions
    :type path_payload_samples: str
    :param path_validate_report: abs path to directory containing the validation report - to be copied to the build directory that will be zipped
    :type path_validate_report: str
    :param custom_display_name: will give the App that display name. Default: name from setup.py file
    :type custom_display_name: str
    :param repository_name: will over-ride the container repository name for the App. Default: 'ibmresilient'
    :type repository_name: str
    :param image_hash: if defined will append the hash to the image_name in the app.json file e.g. <repository_name>/<package_name>@sha256:<image_hash>. Default: <repository_name>/<package_name>:<version>
    :type image_hash: str
    :param keep_build_dir: if True, build/ will not be removed. Default: False
    :type keep_build_dir: bool
    :return: Path to new app.zip
    :rtype: str
    """

    LOG.info("Creating App")
    # Variables to hold path of files for customize and config as defined in setup.py.
    # Set initially default to 'None', actual paths will be calculated later.
    path_customize_py_file = None
    path_config_py_file = None

    # Ensure the output_dir exists, we have WRITE access and ensure we can READ setup.py and apikey_permissions.txt
    # files.
    sdk_helpers.validate_dir_paths(os.W_OK, output_dir)
    sdk_helpers.validate_file_paths(os.R_OK, path_setup_py_file, path_apikey_permissions_file)

    # Parse the setup.py file
    setup_py_attributes = parse_setup_py(path_setup_py_file, SUPPORTED_SETUP_PY_ATTRIBUTE_NAMES)

    # Validate setup.py attributes

    # Validate the name attribute. Raise exception if invalid
    if not sdk_helpers.is_valid_package_name(setup_py_attributes.get("name")):
        raise SDKException("'{0}' is not a valid App name. The name attribute must be defined and can only include 'a-z and _'.\nUpdate this value in the setup.py file located at: {1}".format(setup_py_attributes.get("name"), path_setup_py_file))

    # Validate the version attribute. Raise exception if invalid
    if not sdk_helpers.is_valid_version_syntax(setup_py_attributes.get("version")):
        raise SDKException("'{0}' is not a valid App version syntax. The version attribute must be defined. Example: version=\"1.0.0\".\nUpdate this value in the setup.py file located at: {1}".format(setup_py_attributes.get("version"), path_setup_py_file))

    # Validate the url supplied in the setup.py file, set to an empty string if not valid
    if not sdk_helpers.is_valid_url(setup_py_attributes.get("url")):
        LOG.warning("WARNING: '%s' is not a valid url. Ignoring.", setup_py_attributes.get("url"))
        setup_py_attributes["url"] = ""

    # Get the tag name
    tag_name = setup_py_attributes.get("name")

    # Get the customize file location.
    path_customize_py_file = get_configuration_py_file_path("customize", setup_py_attributes)

    # Get the config file location.
    path_config_py_file = get_configuration_py_file_path("config", setup_py_attributes)

    # Get ImportDefinition from the discovered customize file.
    if path_customize_py_file:
        import_definition = get_import_definition_from_customize_py(path_customize_py_file)
    else:
        # No 'customize.py' file found generate import definition with just mimimum server version.
        import_definition = {
            'server_version':
                IMPORT_MIN_SERVER_VERSION
        }

    # Add the tag to the import defintion
    import_definition = add_tag_to_import_definition(tag_name, SUPPORTED_RES_OBJ_NAMES, import_definition)

    # Parse the app.configs from the discovered config file
    if path_config_py_file:
        app_configs = get_configs_from_config_py(path_config_py_file)
    else:
        # No config file file found generate an empty definition.
        app_configs = ("", [])

    # Parse the api key permissions from the apikey_permissions.txt file
    apikey_permissions = get_apikey_permissions(path_apikey_permissions_file)

    # Generate the name for the extension
    extension_name = "{0}-{1}".format(setup_py_attributes.get("name"), setup_py_attributes.get("version"))

    # Generate the uuid
    uuid = sdk_helpers.generate_uuid_from_string(setup_py_attributes.get("name"))

    # Set the container repository name to default if value not passed in as argument.
    if not repository_name:
        repository_name = REPOSITORY_NAME

    # Generate paths to the directories and files we will use in the build directory
    path_build = os.path.join(output_dir, BASE_NAME_BUILD)
    path_extension_json = os.path.join(path_build, BASE_NAME_EXTENSION_JSON)
    path_export_res = os.path.join(path_build, BASE_NAME_EXPORT_RES)

    try:
        # If there is an old build directory, remove it first
        if os.path.exists(path_build):
            shutil.rmtree(path_build)

        # Create the directories for the path "/build/"
        os.makedirs(path_build)

        # If no path_built_distribution is given, use the default: "<output_dir>/<package-name>.tar.gz"
        if not path_built_distribution:
            path_built_distribution = os.path.join(output_dir, "{0}.tar.gz".format(extension_name))

        # Validate the built distribution exists and we have READ access
        sdk_helpers.validate_file_paths(os.R_OK, path_built_distribution)

        # Copy the built distribution to the build dir and enforce rename to .tar.gz
        shutil.copy(path_built_distribution, os.path.join(path_build, "{0}.tar.gz".format(extension_name)))

        # Get the extension_logo (icon) and company_logo (author.icon) as base64 encoded strings
        extension_logo = get_icon(
            icon_name=os.path.basename(PATH_DEFAULT_ICON_EXTENSION_LOGO),
            path_to_icon=path_extension_logo,
            width_accepted=constants.ICON_APP_LOGO_REQUIRED_WIDTH,
            height_accepted=constants.ICON_APP_LOGO_REQUIRED_HEIGHT,
            default_path_to_icon=PATH_DEFAULT_ICON_EXTENSION_LOGO)

        company_logo = get_icon(
            icon_name=os.path.basename(PATH_DEFAULT_ICON_COMPANY_LOGO),
            path_to_icon=path_company_logo,
            width_accepted=constants.ICON_COMPANY_LOGO_REQUIRED_WIDTH,
            height_accepted=constants.ICON_COMPANY_LOGO_REQUIRED_HEIGHT,
            default_path_to_icon=PATH_DEFAULT_ICON_COMPANY_LOGO)

        # Get the display name
        # Use --display-name if passed
        # If not use 'display_name' attribute in setup.py
        # If not set use the 'name' attribute in setup.py
        display_name = custom_display_name or setup_py_attributes.get("display_name") or setup_py_attributes.get("name")

        # Image string is all lowercase on quay.io
        image_name = "{0}/{1}:{2}".format(repository_name, setup_py_attributes.get("name"), setup_py_attributes.get("version"))

        if image_hash:

            if not sdk_helpers.is_valid_hash(image_hash):
                raise SDKException(u"image_hash '{0}' is not a valid SHA256 hash\nIt must be a valid hexadecimal and 64 characters long".format(image_hash))

            # If image_hash is defined append to image name e.g. <repository_name>/<package_name>@sha256:<image_hash>
            image_name = "{0}/{1}@sha256:{2}".format(repository_name, setup_py_attributes.get("name"), image_hash)

        image_name = image_name.lower()

        LOG.debug("image_name generated: %s", image_name)

        # Generate the contents for the extension.json file
        the_extension_json_file_contents = {
            "author": {
                "name": setup_py_attributes.get("author"),
                "website": setup_py_attributes.get("url"),
                "icon": {
                    "data": company_logo,
                    "media_type": "image/png"
                }
            },
            "description": {
                "content": setup_py_attributes.get("description"),
                "format": "text"
            },
            "display_name": display_name,
            "icon": {
                "data": extension_logo,
                "media_type": "image/png"
            },
            "long_description": {
                "content": u"<div>{0}</div>".format(setup_py_attributes.get("long_description")),
                "format": "html"
            },
            "minimum_resilient_version": {
                "major": import_definition.get("server_version").get("major", None),
                "minor": import_definition.get("server_version").get("minor", None),
                "build_number": import_definition.get("server_version").get("build_number", None),
                "version": import_definition.get("server_version").get("version", None)
            },
            "name": setup_py_attributes.get("name"),
            "tag": {
                "prefix": tag_name,
                "name": tag_name,
                "display_name": tag_name,
                "uuid": uuid
            },
            "uuid": uuid,
            "version": setup_py_attributes.get("version"),
            "current_installation": {
                "executables": [
                    {
                        "name": setup_py_attributes.get("name"),
                        "image": image_name,
                        "config_string": app_configs[0],
                        "permission_handles": apikey_permissions,
                        "uuid": uuid
                    }
                ]
            }
        }

        # Write the executable.json file
        sdk_helpers.write_file(path_extension_json, json.dumps(the_extension_json_file_contents, sort_keys=True))

        # Gather payload_samples file for each function and add to export.res file if exists
        if not path_payload_samples:
            LOG.warning("WARNING: No path for 'payload_samples' provided. Skipping adding them to the export.res file")

        else:
            for fn in import_definition.get("functions"):

                # Get paths to payload_samples
                fn_name = fn.get(ResilientObjMap.FUNCTIONS)
                path_payload_samples_fn = os.path.join(path_payload_samples, fn_name)
                path_payload_samples_schema = os.path.join(path_payload_samples_fn, BASE_NAME_PAYLOAD_SAMPLES_SCHEMA)
                path_payload_samples_example = os.path.join(path_payload_samples_fn, BASE_NAME_PAYLOAD_SAMPLES_EXAMPLE)

                try:
                    # Validate payload_files, add custom error message if we can't
                    sdk_helpers.validate_file_paths(os.R_OK, path_payload_samples_schema, path_payload_samples_example)
                except SDKException as err:
                    err.message += ("\nWARNING: could not access JSON file to add payload_samples. Continuing to create package.\n"
                                    "Add '--no-samples' flag to avoid looking for them and avoid this warning message.\n")
                    LOG.warning(err.message)
                    continue

                # Read in schema payload and add to function import definition
                payload_samples_schema_contents_dict = sdk_helpers.read_json_file(path_payload_samples_schema)
                LOG.debug("Adding JSON output schema to '%s' from file: %s", fn_name, path_payload_samples_schema)
                json_schema_key = os.path.splitext(BASE_NAME_PAYLOAD_SAMPLES_SCHEMA)[0]
                fn[json_schema_key] = json.dumps(payload_samples_schema_contents_dict)

                # Read in example payload and add to function import definition
                payload_samples_example_contents_dict = sdk_helpers.read_json_file(path_payload_samples_example)
                LOG.debug("Adding JSON output example to '%s' from file: %s", fn_name, path_payload_samples_example)
                json_example_key = os.path.splitext(BASE_NAME_PAYLOAD_SAMPLES_EXAMPLE)[0]
                fn[json_example_key] = json.dumps(payload_samples_example_contents_dict)

        if path_validate_report:
            path_zipped_validate_report = os.path.join(path_build, os.path.basename(path_validate_report))
            shutil.copy(path_validate_report, path_zipped_validate_report)
        else:
            LOG.warn("WARNING: If a validation report is not included with your submission, it will get rejected. Run this command with the '--validate' flag to include validations.")

        # Write the customize ImportDefinition to the app*.zip export.res file
        sdk_helpers.write_file(path_export_res, json.dumps(import_definition, sort_keys=True))

        # Copy the built distribution to the build dir, enforce rename to .tar.gz
        shutil.copy(path_built_distribution, os.path.join(path_build, "{0}.tar.gz".format(extension_name)))

        # Create the app.zip (Extension Zip) by zipping the build directory
        extension_zip_base_path = os.path.join(output_dir, "{0}{1}".format(PREFIX_EXTENSION_ZIP, extension_name))
        extension_zip_name = shutil.make_archive(base_name=extension_zip_base_path, format="zip", root_dir=path_build)
        path_the_extension_zip = os.path.join(extension_zip_base_path, extension_zip_name)

    except SDKException as err:
        raise err

    except Exception as err:
        raise SDKException(err)

    finally:
        # Remove the build dir. Keep it if user passes --keep-build-dir
        if not keep_build_dir:
            shutil.rmtree(path_build)

    LOG.info("App %s.zip created", "{0}{1}".format(PREFIX_EXTENSION_ZIP, extension_name))

    # Return the path to the extension zip
    return path_the_extension_zip
Example #3
0
def create_extension(path_setup_py_file,
                     path_apikey_permissions_file,
                     output_dir,
                     path_built_distribution=None,
                     path_extension_logo=None,
                     path_company_logo=None,
                     custom_display_name=None,
                     repository_name=None,
                     keep_build_dir=False):
    """
    TODO: update this docstring to new standard format
    Function that creates The App.zip file from the given setup.py, customize and config files
    and copies it to the output_dir. Returns the path to the App.zip
    - path_setup_py_file [String]: abs path to the setup.py file
    - path_apikey_permissions_file [String]: abs path to the apikey_permissions.txt file
    - output_dir [String]: abs path to the directory the App.zip should be produced
    - path_built_distribution [String]: abs path to a tar.gz Built Distribution
        - if provided uses that .tar.gz
        - else looks for it in the output_dir. E.g: output_dir/package_name.tar.gz
    - path_extension_logo [String]: abs path to the app_logo.png. Has to be 200x72 and a .png file
        - if not provided uses default icon
    - path_company_logo [String]: abs path to the company_logo.png. Has to be 100x100 and a .png file
        - if not provided uses default icon
    - custom_display_name [String]: will give the App that display name. Default: name from setup.py file
    - repository_name [String]: will over-ride the container repository name for the App. Default: 'ibmresilient'
    - keep_build_dir [Boolean]: if True, build/ will not be remove. Default: False
    """

    LOG.info("Creating App")
    # Variables to hold path of files for customize and config as defined in setup.py.
    # Set initially default to 'None', actual paths will be calculated later.
    path_customize_py_file = None
    path_config_py_file = None

    # Ensure the output_dir exists, we have WRITE access and ensure we can READ setup.py and apikey_permissions.txt
    # files.
    sdk_helpers.validate_dir_paths(os.W_OK, output_dir)
    sdk_helpers.validate_file_paths(os.R_OK, path_setup_py_file,
                                    path_apikey_permissions_file)

    # Parse the setup.py file
    setup_py_attributes = parse_setup_py(path_setup_py_file,
                                         SUPPORTED_SETUP_PY_ATTRIBUTE_NAMES)

    # Validate setup.py attributes

    # Validate the name attribute. Raise exception if invalid
    if not sdk_helpers.is_valid_package_name(setup_py_attributes.get("name")):
        raise SDKException(
            "'{0}' is not a valid App name. The name attribute must be defined and can only include 'a-z and _'.\nUpdate this value in the setup.py file located at: {1}"
            .format(setup_py_attributes.get("name"), path_setup_py_file))

    # Validate the version attribute. Raise exception if invalid
    if not sdk_helpers.is_valid_version_syntax(
            setup_py_attributes.get("version")):
        raise SDKException(
            "'{0}' is not a valid App version syntax. The version attribute must be defined. Example: version=\"1.0.0\".\nUpdate this value in the setup.py file located at: {1}"
            .format(setup_py_attributes.get("version"), path_setup_py_file))

    # Validate the url supplied in the setup.py file, set to an empty string if not valid
    if not sdk_helpers.is_valid_url(setup_py_attributes.get("url")):
        LOG.warning("WARNING: '%s' is not a valid url. Ignoring.",
                    setup_py_attributes.get("url"))
        setup_py_attributes["url"] = ""

    # Get the tag name
    tag_name = setup_py_attributes.get("name")

    # Get the customize file location.
    path_customize_py_file = get_configuration_py_file_path(
        "customize", setup_py_attributes)

    # Get the config file location.
    path_config_py_file = get_configuration_py_file_path(
        "config", setup_py_attributes)

    # Get ImportDefinition from the discovered customize file.
    if path_customize_py_file:
        import_definition = get_import_definition_from_customize_py(
            path_customize_py_file)
    else:
        # No 'customize.py' file found generate import definition with just mimimum server version.
        import_definition = {'server_version': IMPORT_MIN_SERVER_VERSION}

    # Add the tag to the import defintion
    import_definition = add_tag_to_import_definition(tag_name,
                                                     SUPPORTED_RES_OBJ_NAMES,
                                                     import_definition)

    # Parse the app.configs from the discovered config file
    if path_config_py_file:
        app_configs = get_configs_from_config_py(path_config_py_file)
    else:
        # No config file file found generate an empty definition.
        app_configs = ("", [])

    # Parse the api key permissions from the apikey_permissions.txt file
    apikey_permissions = get_apikey_permissions(path_apikey_permissions_file)

    # Generate the name for the extension
    extension_name = "{0}-{1}".format(setup_py_attributes.get("name"),
                                      setup_py_attributes.get("version"))

    # Generate the uuid
    uuid = sdk_helpers.generate_uuid_from_string(
        setup_py_attributes.get("name"))

    # Set the container repository name to default if value not passed in as argument.
    if not repository_name:
        repository_name = REPOSITORY_NAME

    # Generate paths to the directories and files we will use in the build directory
    path_build = os.path.join(output_dir, BASE_NAME_BUILD)
    path_extension_json = os.path.join(path_build, BASE_NAME_EXTENSION_JSON)
    path_export_res = os.path.join(path_build, BASE_NAME_EXPORT_RES)

    try:
        # If there is an old build directory, remove it first
        if os.path.exists(path_build):
            shutil.rmtree(path_build)

        # Create the directories for the path "/build/"
        os.makedirs(path_build)

        # If no path_built_distribution is given, use the default: "<output_dir>/<package-name>.tar.gz"
        if not path_built_distribution:
            path_built_distribution = os.path.join(
                output_dir, "{0}.tar.gz".format(extension_name))

        # Validate the built distribution exists and we have READ access
        sdk_helpers.validate_file_paths(os.R_OK, path_built_distribution)

        # Copy the built distribution to the build dir and enforce rename to .tar.gz
        shutil.copy(
            path_built_distribution,
            os.path.join(path_build, "{0}.tar.gz".format(extension_name)))

        # Get the extension_logo (icon) and company_logo (author.icon) as base64 encoded strings
        extension_logo = get_icon(
            icon_name=os.path.basename(PATH_DEFAULT_ICON_EXTENSION_LOGO),
            path_to_icon=path_extension_logo,
            width_accepted=200,
            height_accepted=72,
            default_path_to_icon=PATH_DEFAULT_ICON_EXTENSION_LOGO)

        company_logo = get_icon(
            icon_name=os.path.basename(PATH_DEFAULT_ICON_COMPANY_LOGO),
            path_to_icon=path_company_logo,
            width_accepted=100,
            height_accepted=100,
            default_path_to_icon=PATH_DEFAULT_ICON_COMPANY_LOGO)

        # Get the display name
        # Use --display-name if passed
        # If not use 'display_name' attribute in setup.py
        # If not set use the 'name' attribute in setup.py
        display_name = custom_display_name or setup_py_attributes.get(
            "display_name") or setup_py_attributes.get("name")

        # Image string is all lowercase on quay.io
        image_name = "{0}/{1}:{2}".format(repository_name,
                                          setup_py_attributes.get("name"),
                                          setup_py_attributes.get("version"))
        image_name = image_name.lower()

        # Generate the contents for the extension.json file
        the_extension_json_file_contents = {
            "author": {
                "name": setup_py_attributes.get("author"),
                "website": setup_py_attributes.get("url"),
                "icon": {
                    "data": company_logo,
                    "media_type": "image/png"
                }
            },
            "description": {
                "content": setup_py_attributes.get("description"),
                "format": "text"
            },
            "display_name": display_name,
            "icon": {
                "data": extension_logo,
                "media_type": "image/png"
            },
            "long_description": {
                "content":
                "<div>{0}</div>".format(
                    setup_py_attributes.get("long_description")),
                "format":
                "html"
            },
            "minimum_resilient_version": {
                "major":
                import_definition.get("server_version").get("major", None),
                "minor":
                import_definition.get("server_version").get("minor", None),
                "build_number":
                import_definition.get("server_version").get(
                    "build_number", None),
                "version":
                import_definition.get("server_version").get("version", None)
            },
            "name": setup_py_attributes.get("name"),
            "tag": {
                "prefix": tag_name,
                "name": tag_name,
                "display_name": tag_name,
                "uuid": uuid
            },
            "uuid": uuid,
            "version": setup_py_attributes.get("version"),
            "current_installation": {
                "executables": [{
                    "name": setup_py_attributes.get("name"),
                    "image": image_name,
                    "config_string": app_configs[0],
                    "permission_handles": apikey_permissions,
                    "uuid": uuid
                }]
            }
        }

        # Write the executable.json file
        sdk_helpers.write_file(
            path_extension_json,
            json.dumps(the_extension_json_file_contents, sort_keys=True))

        # Write the customize ImportDefinition to the app*.zip export.res file
        sdk_helpers.write_file(path_export_res,
                               json.dumps(import_definition, sort_keys=True))

        # Copy the built distribution to the build dir, enforce rename to .tar.gz
        shutil.copy(
            path_built_distribution,
            os.path.join(path_build, "{0}.tar.gz".format(extension_name)))

        # create The Extension Zip by zipping the build directory
        extension_zip_base_path = os.path.join(
            output_dir, "{0}{1}".format(PREFIX_EXTENSION_ZIP, extension_name))
        extension_zip_name = shutil.make_archive(
            base_name=extension_zip_base_path,
            format="zip",
            root_dir=path_build)
        path_the_extension_zip = os.path.join(extension_zip_base_path,
                                              extension_zip_name)

    except SDKException as err:
        raise err

    except Exception as err:
        raise SDKException(err)

    finally:
        # Remove the build dir. Keep it if user passes --keep-build-dir
        if not keep_build_dir:
            shutil.rmtree(path_build)

    LOG.info("App %s.zip created", "{0}{1}".format(PREFIX_EXTENSION_ZIP,
                                                   extension_name))

    # Return the path to the extension zip
    return path_the_extension_zip
Example #4
0
def test_is_valid_version_syntax():
    assert sdk_helpers.is_valid_version_syntax("1.0") is False
    assert sdk_helpers.is_valid_version_syntax("0") is False
    assert sdk_helpers.is_valid_version_syntax("1.0.0") is True
    assert sdk_helpers.is_valid_version_syntax("abc") is False