示例#1
0
    def test_cached_config_associated_core_descriptor(self):
        """
        Ensures core_api.yml is handled properly.
        """
        descriptor_dict = {
            "path":
            os.path.join("$TK_TEST_FIXTURES", "descriptor_tests",
                         "cached_configuration"),
            "type":
            "path",
        }
        # Make sure we see the core descriptor.
        desc = create_descriptor(self.mockgun, Descriptor.CONFIG,
                                 descriptor_dict)
        self.assertDictEqual(
            desc.associated_core_descriptor,
            {
                "type": "app_store",
                "version": "v0.18.91",
                "name": "tk-core"
            },
        )

        descriptor_dict = {
            "path":
            os.path.join("$TK_TEST_FIXTURES", "descriptor_tests",
                         "cached_configuration_no_core"),
            "type":
            "path",
        }
        # Make sure we see the core descriptor.
        desc = create_descriptor(self.mockgun, Descriptor.CONFIG,
                                 descriptor_dict)
        self.assertIsNone(desc.associated_core_descriptor)
示例#2
0
    def test_self_contained_config_core_descriptor(self):
        """
        Ensures that a configuration with a local bundle cache can return a core
        descriptor that points inside the configuration if the core is cached there.
        """
        config_root = os.path.join(self.tank_temp, "self_contained_config")
        core_location = os.path.join(config_root, "bundle_cache", "app_store",
                                     "tk-core", "v0.18.133")
        self.create_file(os.path.join(core_location, "info.yml"), "")
        self.create_file(
            os.path.join(config_root, "core", "core_api.yml"),
            yaml.dump({
                "location": {
                    "type": "app_store",
                    "name": "tk-core",
                    "version": "v0.18.133",
                }
            }),
        )

        config_desc = create_descriptor(
            self.mockgun,
            Descriptor.CONFIG,
            "sgtk:descriptor:path?path={0}".format(config_root),
        )
        core_desc = config_desc.resolve_core_descriptor()
        self.assertEqual(core_desc.get_path(), core_location)
    def test_zero_config(self):
        """
        Tests the paths for a zero-config configuration.
        """
        config_root = os.path.join(self.tank_temp, "zero_config")

        config_desc = create_descriptor(
            self.mockgun, Descriptor.CONFIG, "sgtk:descriptor:path?path=%s" %
            os.path.join(self.fixtures_root, "config"))
        cw = ConfigurationWriter(
            tank.util.ShotgunPath.from_current_os_path(config_root),
            self.mockgun)

        cw.ensure_project_scaffold()
        config_desc.copy(os.path.join(config_root, "config"))
        cw.write_pipeline_config_file(None, self._project["id"], "basic", [],
                                      config_desc)
        cw.update_roots_file(config_desc)
        cw.write_install_location_file()

        # Fake a core installation.
        core_install_folder = os.path.join(config_root, "install", "core")
        self.create_file(os.path.join(core_install_folder,
                                      "_core_upgrader.py"))
        self.assertTrue(tank.pipelineconfig_utils.is_localized(config_root))

        pc = tank.pipelineconfig.PipelineConfiguration(config_root)

        self._test_core_locations(pc, config_root, True)
        self._test_config_locations(pc, config_root, config_desc.get_path())
def _process_configuration(sg_connection, config_uri_str):
    """
    Resolve and download the given Toolkit configuration.

    :param sg_connection: Shotgun connection.
    :param config_uri_str: Toolkit config descriptor as a string.
    :returns: Resolved config descriptor object.
    :raises: ValueError for "baked" descriptors.
    """
    logger.info("Analyzing configuration")

    config_uri_dict = descriptor_uri_to_dict(config_uri_str)
    if config_uri_dict["type"] == bootstrap_constants.BAKED_DESCRIPTOR_TYPE:
        raise ValueError("The given config is already baked")

    # If the config descriptor does not contain a version number, get the
    # latest.
    using_latest_config = is_descriptor_version_missing(config_uri_dict)
    if using_latest_config:
        logger.info(
            "Your configuration definition does not contain a version number. "
            "Retrieving the latest version..."
        )
    cfg_descriptor = create_descriptor(
        sg_connection,
        Descriptor.CONFIG,
        config_uri_dict,
        resolve_latest=using_latest_config,
    )
    cfg_descriptor.ensure_local()
    logger.info("Resolved config %r" % cfg_descriptor)
    return cfg_descriptor
    def test_zero_config(self):
        """
        Tests the paths for a zero-config configuration.
        """
        config_root = os.path.join(self.tank_temp, "zero_config")

        config_desc = create_descriptor(
            self.mockgun,
            Descriptor.CONFIG,
            "sgtk:descriptor:path?path=%s" % os.path.join(self.fixtures_root, "config")
        )
        cw = ConfigurationWriter(
            tank.util.ShotgunPath.from_current_os_path(config_root),
            self.mockgun
        )

        cw.ensure_project_scaffold()
        config_desc.copy(os.path.join(config_root, "config"))
        cw.write_pipeline_config_file(None, self._project["id"], "basic", [], config_desc)
        cw.update_roots_file(config_desc)
        cw.write_install_location_file()

        # Fake a core installation.
        core_install_folder = os.path.join(config_root, "install", "core")
        self.create_file(os.path.join(core_install_folder, "_core_upgrader.py"))
        self.assertTrue(tank.pipelineconfig_utils.is_localized(config_root))

        pc = tank.pipelineconfig.PipelineConfiguration(config_root)

        self._test_core_locations(pc, config_root, True)
        self._test_config_locations(pc, config_root, config_desc.get_path())
示例#6
0
    def test_self_contained_config_core_descriptor(self):
        """
        Ensures that a configuration with a local bundle cache can return a core
        descriptor that points inside the configuration if the core is cached there.
        """
        config_root = os.path.join(self.tank_temp, "self_contained_config")
        core_location = os.path.join(
            config_root, "bundle_cache", "app_store", "tk-core", "v0.18.133"
        )
        self.create_file(
            os.path.join(core_location, "info.yml"),
            ""
        )
        self.create_file(
            os.path.join(config_root, "core", "core_api.yml"),
            yaml.dump({"location": {"type": "app_store", "name": "tk-core", "version": "v0.18.133"}})
        )

        config_desc = create_descriptor(
            self.mockgun,
            Descriptor.CONFIG,
            "sgtk:descriptor:path?path={0}".format(config_root)
        )
        core_desc = config_desc.resolve_core_descriptor()
        self.assertEqual(
            core_desc.get_path(),
            core_location
        )
示例#7
0
    def test_cached_config_manifest(self):
        """
        Ensures we can read the manifest file.
        """
        descriptor_dict = {
            "path": os.path.join("$TK_TEST_FIXTURES", "descriptor_tests", "cached_configuration"),
            "type": "path"
        }
        # Make sure we see the core descriptor.
        desc = create_descriptor(self.mockgun, Descriptor.CONFIG, descriptor_dict)

        self.assertEqual(
            desc.display_name,
            "Descriptor Tests Cached Configuration with core."
        )

        self.assertEqual(
            desc.description,
            "This configuration has a core_api.yml file."
        )

        self.assertEqual(
            desc.version_constraints["min_sg"],
            "v6.3.0"
        )

        self.assertEqual(
            desc.version_constraints["min_core"],
            "v0.18.18"
        )
    def test_cached_config_manifest(self):
        """
        Ensures we can read the manifest file.
        """
        descriptor_dict = {
            "path": os.path.join("$TK_TEST_FIXTURES", "descriptor_tests", "cached_configuration"),
            "type": "path"
        }
        # Make sure we see the core descriptor.
        desc = create_descriptor(self.mockgun, Descriptor.CONFIG, descriptor_dict)

        self.assertEqual(
            desc.display_name,
            "Descriptor Tests Cached Configuration with core."
        )

        self.assertEqual(
            desc.description,
            "This configuration has a core_api.yml file."
        )

        self.assertEqual(
            desc.version_constraints["min_sg"],
            "v6.3.0"
        )

        self.assertEqual(
            desc.version_constraints["min_core"],
            "v0.18.18"
        )
示例#9
0
    def test_cached_config_associated_core_descriptor(self):
        """
        Ensures core_api.yml is handled properly.
        """
        descriptor_dict = {
            "path": os.path.join("$TK_TEST_FIXTURES", "descriptor_tests", "cached_configuration"),
            "type": "path"
        }
        # Make sure we see the core descriptor.
        desc = create_descriptor(self.mockgun, Descriptor.CONFIG, descriptor_dict)
        self.assertDictEqual(
            desc.associated_core_descriptor,
            {"type": "app_store", "version": "v0.18.91", "name": "tk-core"}
        )

        descriptor_dict = {
            "path": os.path.join("$TK_TEST_FIXTURES", "descriptor_tests", "cached_configuration_no_core"),
            "type": "path"
        }
        # Make sure we see the core descriptor.
        desc = create_descriptor(self.mockgun, Descriptor.CONFIG, descriptor_dict)
        self.assertIsNone(desc.associated_core_descriptor)
示例#10
0
def _cache_descriptor(sg, desc_type, desc_dict, target_path):
    """
    Cache the given descriptor into a new bundle cache.

    :param sg: Shotgun API instance
    :param desc_type: Descriptor.ENGINE | Descriptor.APP | Descriptor.FRAMEWORK
    :param desc_dict: descriptor dict or uri
    :param target_path: bundle cache root to cache into
    """
    desc = create_descriptor(sg, desc_type, desc_dict, fallback_roots=[target_path])
    desc.ensure_local()
    desc_size_kb = filesystem.compute_folder_size(desc.get_path()) / 1024
    logger.info("Caching %s into plugin bundle cache (size %d KiB)" % (desc, desc_size_kb))
    if not desc._io_descriptor.is_immutable():
        logger.warning("Descriptor %r may not work for other users using the plugin!" % desc)
    desc.clone_cache(target_path)
示例#11
0
def _cache_descriptor(sg, desc_type, desc_dict, target_path):
    """
    Cache the given descriptor into a new bundle cache.

    :param sg: Shotgun API instance
    :param desc_type: Descriptor.ENGINE | Descriptor.APP | Descriptor.FRAMEWORK
    :param desc_dict: descriptor dict or uri
    :param target_path: bundle cache root to cache into
    """
    desc = create_descriptor(sg, desc_type, desc_dict, fallback_roots=[target_path])
    desc.ensure_local()
    desc_size_kb = filesystem.compute_folder_size(desc.get_path()) / 1024
    logger.info("Caching %s into plugin bundle cache (size %d KiB)" % (desc, desc_size_kb))
    if not desc._io_descriptor.is_immutable():
        logger.warning("Descriptor %r may not work for other users using the plugin!" % desc)
    desc.clone_cache(target_path)
示例#12
0
def _bake_configuration(sg_connection, manifest_data):
    """
    Bake the given configuration by ensuring it is locally cached and by modifying
    the manifest_data.

    :param sg_connection: Shotgun connection
    :param manifest_data: Manifest data as a dictionary
    :returns: The baked descriptor dictionary issued from configuration descriptor.
    """
    logger.info(
        "Baking your configuration definition into an immutable state. "
        "This means that the plugin will be frozen and no automatic updates "
        "will be performed at startup."
    )
    base_config_def = manifest_data["base_configuration"]
    if isinstance(base_config_def, str):
        # convert to dict so we can introspect
        base_config_uri_dict = descriptor_uri_to_dict(base_config_def)
    else:
        base_config_uri_dict = base_config_def

    using_latest_config = is_descriptor_version_missing(base_config_uri_dict)
    if using_latest_config:
        logger.info(
            "Your configuration definition does not contain a version number. "
            "Retrieving the latest version of the configuration for baking."
        )

    cfg_descriptor = create_descriptor(
        sg_connection,
        Descriptor.CONFIG,
        base_config_uri_dict,
        resolve_latest=using_latest_config
    )
    cfg_descriptor.ensure_local()
    local_path = cfg_descriptor.get_path()
    if not local_path:
        raise ValueError("Unable to get a local copy of %s" % cfg_descriptor)
    baked_descriptor = {
        "type": bootstrap_constants.BAKED_DESCRIPTOR_TYPE,
        "path": local_path,
        "name": cfg_descriptor.system_name,
        "version": cfg_descriptor.version
    }
    manifest_data["base_configuration"] = baked_descriptor
    return baked_descriptor
示例#13
0
 def _get_app_path(self, version=None):
     """ Return the platform specific app path, performing version substitution. """
     if self.__app_path is None:
         if self.get_setting("app_location"):
             app_descriptor = create_descriptor(
                 self.shotgun, Descriptor.THIRD_PARTY,
                 self.get_setting("app_location"))
             self.__app_path = app_descriptor.get_path()
         else:
             platform_name = {
                 "linux2": "linux",
                 "darwin": "mac",
                 "win32": "windows"
             }[sys.platform]
             raw_app_path = self.get_setting("%s_path" % platform_name, "")
             self.__app_path = self._apply_version_to_setting(
                 raw_app_path, version)
     return self.__app_path
示例#14
0
def _bake_configuration(sg_connection, manifest_data):
    """
    Bake the given configuration by ensuring it is locally cached and by modifying
    the manifest_data.

    :param sg_connection: Shotgun connection
    :param manifest_data: Manifest data as a dictionary
    :returns: The baked descriptor dictionary issued from configuration descriptor.
    """
    logger.info(
        "Baking your configuration definition into an immutable state. "
        "This means that the plugin will be frozen and no automatic updates "
        "will be performed at startup.")
    base_config_def = manifest_data["base_configuration"]
    if isinstance(base_config_def, str):
        # convert to dict so we can introspect
        base_config_uri_dict = descriptor_uri_to_dict(base_config_def)
    else:
        base_config_uri_dict = base_config_def

    using_latest_config = is_descriptor_version_missing(base_config_uri_dict)
    if using_latest_config:
        logger.info(
            "Your configuration definition does not contain a version number. "
            "Retrieving the latest version of the configuration for baking.")

    cfg_descriptor = create_descriptor(
        sg_connection,
        Descriptor.CONFIG,
        base_config_uri_dict,
        resolve_latest=using_latest_config,
    )
    cfg_descriptor.ensure_local()
    local_path = cfg_descriptor.get_path()
    if not local_path:
        raise ValueError("Unable to get a local copy of %s" % cfg_descriptor)
    baked_descriptor = {
        "type": bootstrap_constants.BAKED_DESCRIPTOR_TYPE,
        "path": local_path,
        "name": cfg_descriptor.system_name,
        "version": cfg_descriptor.version,
    }
    manifest_data["base_configuration"] = baked_descriptor
    return baked_descriptor
def _process_configuration(sg_connection, source_path, target_path,
                           bundle_cache_root, manifest_data, use_system_core):
    """
    Given data in the plugin manifest, download resolve and
    cache the configuration.

    :param sg_connection: Shotgun connection
    :param source_path: Root path of plugin source.
    :param target_path: Build target path
    :param bundle_cache_root: Bundle cache root
    :param manifest_data: Manifest data as a dictionary
    :param bool use_system_core: If True, use a globally installed tk-core instead
                                 of the one specified in the configuration.
    :return: (Resolved config descriptor object, config descriptor uri to use at runtime, install path)
    """
    logger.info("Analyzing configuration")

    install_path = None

    # get config def from info yml and generate both
    # dict and string uris.
    base_config_def = manifest_data["base_configuration"]
    if isinstance(base_config_def, str):
        # convert to dict so we can introspect
        base_config_uri_dict = descriptor_uri_to_dict(base_config_def)
        base_config_uri_str = base_config_def
    else:
        base_config_uri_dict = base_config_def
        base_config_uri_str = descriptor_dict_to_uri(base_config_def)

    # Special case - check for the 'baked' descriptor type
    # and process it. A baked descriptor is a special concept
    # that only exists in the build script. The baked descriptor
    # takes a single path parameter which can be a local or absolute
    # path. The path is copied across by the build script into a
    # manual descriptor, with a version number based on the current date.
    # This ensures that the manual descriptor will be correctly
    # re-cached at bootstrap time.
    if base_config_uri_dict[
            "type"] == bootstrap_constants.BAKED_DESCRIPTOR_TYPE:
        logger.info("Baked descriptor detected.")

        baked_path = os.path.expanduser(
            os.path.expandvars(base_config_uri_dict["path"]))

        # if it's a relative path, expand it
        if not os.path.isabs(baked_path):
            full_baked_path = os.path.abspath(
                os.path.join(source_path, baked_path))

            # if it's a relative path, we have already copied it to the build
            # target location. In this case, attempt to locate it and remove it.
            baked_target_path = os.path.abspath(
                os.path.join(target_path, baked_path))
            if baked_target_path.startswith(baked_target_path):
                logger.debug("Removing '%s' from build" % baked_target_path)
                shutil.rmtree(baked_target_path)
        else:
            # path is absolute
            full_baked_path = os.path.abspath(baked_path)

        logger.info("Will bake an immutable config into the plugin from '%s'" %
                    full_baked_path)

        # A baked config descriptor does not require a name nor a version, so
        # if these keys are not available, use the current date time for the
        # version and an arbitrary name for the config. Please note that this
        # only happens if the baked descriptor was set in the original config.
        # When baking a plugin with the --bake option, which is the recommended
        # workflow, these values are automatically set.
        baked_name = base_config_uri_dict.get("name") or "tk-config-plugin"
        baked_version = (base_config_uri_dict.get("version")
                         or datetime.datetime.now().strftime("%Y%m%d_%H%M%S"))
        install_path = os.path.join(
            bundle_cache_root,
            bootstrap_constants.BAKED_DESCRIPTOR_FOLDER_NAME, baked_name,
            baked_version)

        cfg_descriptor = create_descriptor(sg_connection, Descriptor.CONFIG, {
            "type": "path",
            "path": full_baked_path
        })

        BakedConfiguration.bake_config_scaffold(install_path, sg_connection,
                                                manifest_data["plugin_id"],
                                                cfg_descriptor)

        # now lastly,
        base_config_uri_str = descriptor_dict_to_uri({
            "type":
            bootstrap_constants.BAKED_DESCRIPTOR_TYPE,
            "name":
            baked_name,
            "version":
            baked_version
        })
        if use_system_core:
            # If asked to use a globally installed tk-core instead of the one
            # specified by the config, we remove the local copy which was created
            # in the scaffold step.
            logger.info("Removing core reference in %s" % install_path)
            wipe_folder(os.path.join(install_path, "install"))
            # And make sure we don't have any reference to a tk-core in the config,
            # otherwise it would be picked up when bootstrapping.
            filesystem.safe_delete_file(
                os.path.join(install_path, "config", "core", "core_api.yml"))
        else:
            # Workaround for tk-core bootstrap needing a shotgun.yml file: when swapping
            # tk-core, this file is checked to see if a script user was specified and
            # should be used in place of the authenticated user. So we create a dummy
            # file with an "unspecified" host, as the key is required by the tk-core
            # code parsing the file.
            # It is not clear if this workaround is needed for non baked configs as
            # their workflow is different, so for now we just keep it for bake configs
            # only.
            shotgun_yaml_path = os.path.join(install_path, "config", "core",
                                             "shotgun.yml")
            if not os.path.exists(shotgun_yaml_path):
                logger.info("Patching %s" % shotgun_yaml_path)
                with open(shotgun_yaml_path, "w") as pf:
                    pf.write(
                        "# Workaround for tk-core bootstrap\nhost: unspecified"
                    )
    else:

        # if the descriptor in the config contains a version number
        # we will go into a fixed update mode.
        using_latest_config = is_descriptor_version_missing(
            base_config_uri_dict)
        if using_latest_config:
            logger.info(
                "Your configuration definition does not contain a version number. "
                "This means that the plugin will attempt to auto update at startup."
            )
        else:
            logger.info(
                "Your configuration definition contains a version number. "
                "This means that the plugin will be frozen and no automatic updates "
                "will be performed at startup.")

        cfg_descriptor = create_descriptor(sg_connection,
                                           Descriptor.CONFIG,
                                           base_config_uri_dict,
                                           resolve_latest=using_latest_config)
    logger.info("Resolved config %r" % cfg_descriptor)
    logger.info("Runtime config descriptor uri will be %s" %
                base_config_uri_str)
    if install_path:
        logger.info("The config was baked in %s" % install_path)
    return cfg_descriptor, base_config_uri_str, install_path
示例#16
0
def _process_configuration(sg_connection, source_path, target_path,
                           bundle_cache_root, manifest_data, buildable):
    """
    Given data in the plugin manifest, download resolve and
    cache the configuration.

    :param sg_connection: Shotgun connection
    :param source_path: Root path of plugin source.
    :param target_path: Build target path
    :param bundle_cache_root: Bundle cache root
    :param manifest_data: Manifest data as a dictionary
    :param buildable: True if the generated build should be buildable
    :return: (Resolved config descriptor object, config descriptor uri to use at runtime)
    """
    logger.info("Analyzing configuration")

    # get config def from info yml and generate both
    # dict and string uris.
    base_config_def = manifest_data["base_configuration"]
    if isinstance(base_config_def, str):
        # convert to dict so we can introspect
        base_config_uri_dict = descriptor_uri_to_dict(base_config_def)
        base_config_uri_str = base_config_def
    else:
        base_config_uri_dict = base_config_def
        base_config_uri_str = descriptor_dict_to_uri(base_config_def)

    # Special case - check for the 'baked' descriptor type
    # and process it. A baked descriptor is a special concept
    # that only exists in the build script. The baked descriptor
    # takes a single path parameter which can be a local or absolute
    # path. The path is copied across by the build script into a
    # manual descriptor, with a version number based on the current date.
    # This ensures that the manual descriptor will be correctly
    # re-cached at bootstrap time.
    if base_config_uri_dict[
            "type"] == bootstrap_constants.BAKED_DESCRIPTOR_TYPE:
        logger.info("Baked descriptor detected.")

        baked_path = os.path.expanduser(
            os.path.expandvars(base_config_uri_dict["path"]))

        # if it's a relative path, expand it
        if not os.path.isabs(baked_path):
            full_baked_path = os.path.abspath(
                os.path.join(source_path, baked_path))

            # if it's a relative path, we have already copied it to the build
            # target location. In this case, attempt to locate it and remove it.
            baked_target_path = os.path.abspath(
                os.path.join(target_path, baked_path))
            if baked_target_path.startswith(
                    baked_target_path) and not buildable:
                logger.debug("Removing '%s' from build" % baked_target_path)
                shutil.rmtree(baked_target_path)
        else:
            # path is absolute
            full_baked_path = os.path.abspath(baked_path)

        logger.info("Will bake an immutable config into the plugin from '%s'" %
                    full_baked_path)

        install_path = os.path.join(
            bundle_cache_root,
            bootstrap_constants.BAKED_DESCRIPTOR_FOLDER_NAME,
            BAKED_BUNDLE_NAME, BAKED_BUNDLE_VERSION)

        cfg_descriptor = create_descriptor(sg_connection, Descriptor.CONFIG, {
            "type": "path",
            "path": full_baked_path
        })

        BakedConfiguration.bake_config_scaffold(install_path, sg_connection,
                                                manifest_data["plugin_id"],
                                                cfg_descriptor)

        # now lastly,
        base_config_uri_str = descriptor_dict_to_uri({
            "type":
            bootstrap_constants.BAKED_DESCRIPTOR_TYPE,
            "name":
            BAKED_BUNDLE_NAME,
            "version":
            BAKED_BUNDLE_VERSION
        })

    else:

        # if the descriptor in the config contains a version number
        # we will go into a fixed update mode.
        if "version" in base_config_uri_dict:
            logger.info(
                "Your configuration definition contains a version number. "
                "This means that the plugin will be frozen and no automatic updates "
                "will be performed at startup.")
            using_latest_config = False
        else:
            logger.info(
                "Your configuration definition does not contain a version number. "
                "This means that the plugin will attempt to auto update at startup."
            )
            using_latest_config = True

        cfg_descriptor = create_descriptor(sg_connection,
                                           Descriptor.CONFIG,
                                           base_config_uri_dict,
                                           resolve_latest=using_latest_config)

    logger.info("Resolved config %r" % cfg_descriptor)
    logger.info("Runtime config descriptor uri will be %s" %
                base_config_uri_str)
    return cfg_descriptor, base_config_uri_str
示例#17
0
def _process_configuration(sg_connection, source_path, target_path, bundle_cache_root, manifest_data, buildable):
    """
    Given data in the plugin manifest, download resolve and
    cache the configuration.

    :param sg_connection: Shotgun connection
    :param source_path: Root path of plugin source.
    :param target_path: Build target path
    :param bundle_cache_root: Bundle cache root
    :param manifest_data: Manifest data as a dictionary
    :param buildable: True if the generated build should be buildable
    :return: (Resolved config descriptor object, config descriptor uri to use at runtime)
    """
    logger.info("Analyzing configuration")

    # get config def from info yml and generate both
    # dict and string uris.
    base_config_def = manifest_data["base_configuration"]
    if isinstance(base_config_def, str):
        # convert to dict so we can introspect
        base_config_uri_dict = descriptor_uri_to_dict(base_config_def)
        base_config_uri_str = base_config_def
    else:
        base_config_uri_dict = base_config_def
        base_config_uri_str = descriptor_dict_to_uri(base_config_def)

    # Special case - check for the 'baked' descriptor type
    # and process it. A baked descriptor is a special concept
    # that only exists in the build script. The baked descriptor
    # takes a single path parameter which can be a local or absolute
    # path. The path is copied across by the build script into a
    # manual descriptor, with a version number based on the current date.
    # This ensures that the manual descriptor will be correctly
    # re-cached at bootstrap time.
    if base_config_uri_dict["type"] == bootstrap_constants.BAKED_DESCRIPTOR_TYPE:
        logger.info("Baked descriptor detected.")

        baked_path = os.path.expanduser(os.path.expandvars(base_config_uri_dict["path"]))

        # if it's a relative path, expand it
        if not os.path.isabs(baked_path):
            full_baked_path = os.path.abspath(os.path.join(source_path, baked_path))

            # if it's a relative path, we have already copied it to the build
            # target location. In this case, attempt to locate it and remove it.
            baked_target_path = os.path.abspath(os.path.join(target_path, baked_path))
            if baked_target_path.startswith(baked_target_path) and not buildable:
                logger.debug("Removing '%s' from build" % baked_target_path)
                shutil.rmtree(baked_target_path)
        else:
            # path is absolute
            full_baked_path = os.path.abspath(baked_path)

        logger.info("Will bake an immutable config into the plugin from '%s'" % full_baked_path)

        install_path = os.path.join(
            bundle_cache_root,
            bootstrap_constants.BAKED_DESCRIPTOR_FOLDER_NAME,
            BAKED_BUNDLE_NAME,
            BAKED_BUNDLE_VERSION
        )

        cfg_descriptor = create_descriptor(
            sg_connection,
            Descriptor.CONFIG,
            {"type": "path", "path": full_baked_path}
        )

        BakedConfiguration.bake_config_scaffold(
            install_path,
            sg_connection,
            manifest_data["plugin_id"],
            cfg_descriptor
        )

        # now lastly,
        base_config_uri_str = descriptor_dict_to_uri(
            {
                "type": bootstrap_constants.BAKED_DESCRIPTOR_TYPE,
                "name": BAKED_BUNDLE_NAME,
                "version": BAKED_BUNDLE_VERSION
             }
        )

    else:

        # if the descriptor in the config contains a version number
        # we will go into a fixed update mode.
        if "version" in base_config_uri_dict:
            logger.info(
                "Your configuration definition contains a version number. "
                "This means that the plugin will be frozen and no automatic updates "
                "will be performed at startup."
            )
            using_latest_config = False
        else:
            logger.info(
                "Your configuration definition does not contain a version number. "
                "This means that the plugin will attempt to auto update at startup."
            )
            using_latest_config = True

        cfg_descriptor = create_descriptor(
            sg_connection,
            Descriptor.CONFIG,
            base_config_uri_dict,
            resolve_latest=using_latest_config
        )

    logger.info("Resolved config %r" % cfg_descriptor)
    logger.info("Runtime config descriptor uri will be %s" % base_config_uri_str)
    return cfg_descriptor, base_config_uri_str
示例#18
0
def build_plugin(sg_connection, source_path, target_path, bootstrap_core_uri=None, do_bake=False, use_system_core=False):
    """
    Perform a build of a plugin.

    This will introspect the info.yml in the source path,
    copy over everything in the source path into target path
    and then establish a bundle cache containing a reflection
    of all items required by the config.

    :param sg_connection: Shotgun connection
    :param source_path: Path to plugin.
    :param target_path: Path to build
    :param bootstrap_core_uri: Custom bootstrap core uri. If None,
                               the latest core from the app store will be used.
    :param bool do_bake: If True, bake the plugin prior to building it.
    :param bool use_system_core: If True, use a globally installed tk-core instead
                                 of the one specified in the configuration.
    """
    logger.info("Your toolkit plugin in '%s' will be processed." % source_path)
    logger.info("The build will %s into '%s'" % (["generated", "baked"][do_bake], target_path))

    # check for existence
    if not os.path.exists(source_path):
        raise TankError("Source path '%s' cannot be found on disk!" % source_path)

    # check manifest
    manifest_data = _validate_manifest(source_path)

    if do_bake:
        baked_descriptor = _bake_configuration(
            sg_connection,
            manifest_data,
        )
        # When baking we control the output path by adding a folder based on the
        # configuration descriptor and version.
        target_path = os.path.join(target_path, "%s-%s" % (
            baked_descriptor["name"],
            baked_descriptor["version"]
        ))

    # check that target path doesn't exist
    if os.path.exists(target_path):
        logger.info("The folder '%s' already exists on disk. Removing it" % target_path)
        wipe_folder(target_path)

    # try to create target path
    filesystem.ensure_folder_exists(target_path)

    # copy all plugin data across
    # skip info.yml, this is baked into the manifest python code
    logger.info("Copying plugin data across...")
    filesystem.copy_folder(source_path, target_path)

    # create bundle cache
    logger.info("Creating bundle cache folder...")
    bundle_cache_root = os.path.join(target_path, BUNDLE_CACHE_ROOT_FOLDER_NAME)
    filesystem.ensure_folder_exists(bundle_cache_root)

    # resolve config descriptor
    # the config_uri_str returned by the method contains the fully resolved
    # uri to use at runtime - in the case of baked descriptors, the config_uri_str
    # contains a manual descriptor uri and install_path is set with the baked
    # folder.
    (cfg_descriptor, config_uri_str, install_path) = _process_configuration(
        sg_connection,
        source_path,
        target_path,
        bundle_cache_root,
        manifest_data,
        use_system_core
    )

    # cache config in bundle cache
    logger.info("Downloading and caching config...")

    # copy the config payload across to the plugin bundle cache
    cfg_descriptor.clone_cache(bundle_cache_root)

    # cache all apps, engines and frameworks
    cache_apps(sg_connection, cfg_descriptor, bundle_cache_root)

    if use_system_core:
        logger.info("An external core will be used for this plugin, not caching it")
        bootstrap_core_desc = None
    else:
        # get core - cache it directly into the plugin root folder
        if bootstrap_core_uri:
            logger.info("Caching custom core for boostrap (%s)" % bootstrap_core_uri)
            bootstrap_core_desc = create_descriptor(
                sg_connection,
                Descriptor.CORE,
                bootstrap_core_uri,
                resolve_latest=is_descriptor_version_missing(bootstrap_core_uri),
                bundle_cache_root_override=bundle_cache_root
            )
            # cache it
            bootstrap_core_desc.ensure_local()

        elif not cfg_descriptor.associated_core_descriptor:
            # by default, use latest core for bootstrap
            logger.info("Caching latest official core to use when bootstrapping plugin.")
            logger.info("(To use a specific config instead, specify a --bootstrap-core-uri flag.)")

            bootstrap_core_desc = create_descriptor(
                sg_connection,
                Descriptor.CORE,
                {"type": "app_store", "name": "tk-core"},
                resolve_latest=True,
                bundle_cache_root_override=bundle_cache_root
            )

            # cache it
            bootstrap_core_desc.ensure_local()
        else:
            # The bootstrap core will be derived from the associated core desc below.
            bootstrap_core_desc = None

    # now analyze what core the config needs
    if not use_system_core and cfg_descriptor.associated_core_descriptor:
        logger.info("Config is specifying a custom core in config/core/core_api.yml.")
        logger.info("This will be used when the config is executing.")
        logger.info("Ensuring this core (%s) is cached..." % cfg_descriptor.associated_core_descriptor)
        associated_core_desc = create_descriptor(
            sg_connection,
            Descriptor.CORE,
            cfg_descriptor.associated_core_descriptor,
            bundle_cache_root_override=bundle_cache_root
        )
        associated_core_desc.ensure_local()
        if bootstrap_core_desc is None:
            # Use the same version as the one specified by the config.
            if install_path:
                # Install path is set only if the config was baked. We re-use the
                # install path as an optimisation to avoid core swapping when the
                # config is bootstrapped.
                logger.info(
                    "Bootstrapping will use installed %s required by the config" %
                    associated_core_desc
                )
                # If the core was installed we directly use it.
                bootstrap_core_desc = create_descriptor(
                    sg_connection,
                    Descriptor.CORE, {
                        "type": "path",
                        "name": "tk-core",
                        "path": os.path.join(install_path, "install", "core"),
                        "version": associated_core_desc.version,
                    },
                    resolve_latest=False,
                    bundle_cache_root_override=bundle_cache_root
                )
            else:
                logger.info(
                    "Bootstrapping will use core %s required by the config" %
                    associated_core_desc
                )
                bootstrap_core_desc = associated_core_desc

    # make a python folder where we put our manifest
    logger.info("Creating configuration manifest...")

    # bake out the manifest into python files.
    _bake_manifest(
        manifest_data,
        config_uri_str,
        bootstrap_core_desc,
        target_path
    )

    cleanup_bundle_cache(bundle_cache_root)

    logger.info("")
    logger.info("Build complete!")
    logger.info("")
    logger.info("- Your plugin is ready in '%s'" % target_path)
    logger.info("- Plugin uses config %r" % cfg_descriptor)
    if bootstrap_core_desc:
        logger.info("- Bootstrap core is %r" % bootstrap_core_desc)
    else:
        logger.info("- Plugin will need an external installed core.")
    logger.info("- All dependencies have been baked out into the bundle_cache folder")
    logger.info("")
    logger.info("")
    logger.info("")
示例#19
0
def build_plugin(sg_connection, source_path, target_path, buildable, bootstrap_core_uri=None):
    """
    Perform a build of a plugin.

    This will introspect the info.yml in the source path,
    copy over everything in the source path into target path
    and then establish a bundle cache containing a reflection
    of all items required by the config.

    :param sg_connection: Shotgun connection
    :param source_path: Path to plugin.
    :param target_path: Path to build
    :param buildable: True if the resulting plugin build should be buildable
    :param bootstrap_core_uri: Custom bootstrap core uri. If None,
                               the latest core from the app store will be used.
    """
    logger.info("Your toolkit plugin in '%s' will be processed." % source_path)
    logger.info("The build will generated into '%s'" % target_path)

    # check for existence
    if not os.path.exists(source_path):
        raise TankError("Source path '%s' cannot be found on disk!" % source_path)

    # check that target path doesn't exist
    if os.path.exists(target_path):
        logger.info("The folder '%s' already exists on disk. Moving it to backup location" % target_path)
        filesystem.backup_folder(target_path)
        shutil.rmtree(target_path)

    # try to create target path
    filesystem.ensure_folder_exists(target_path)

    # check manifest
    manifest_data = _validate_manifest(source_path)

    # copy all plugin data across
    # skip info.yml, this is baked into the manifest python code
    logger.info("Copying plugin data across...")
    skip_list = [] if buildable else [".git", "info.yml"]
    filesystem.copy_folder(source_path, target_path, skip_list=skip_list)

    # create bundle cache
    logger.info("Creating bundle cache folder...")
    bundle_cache_root = os.path.join(target_path, BUNDLE_CACHE_ROOT_FOLDER_NAME)
    filesystem.ensure_folder_exists(bundle_cache_root)

    # resolve config descriptor
    # the config_uri_str returned by the method contains the fully resolved
    # uri to use at runtime - in the case of baked descriptors, the config_uri_str
    # contains a manual descriptor uri.
    (cfg_descriptor, config_uri_str) = _process_configuration(
        sg_connection,
        source_path,
        target_path,
        bundle_cache_root,
        manifest_data,
        buildable
    )

    # cache config in bundle cache
    logger.info("Downloading and caching config...")

    # copy the config payload across to the plugin bundle cache
    cfg_descriptor.clone_cache(bundle_cache_root)

    # cache all apps, engines and frameworks
    _cache_apps(sg_connection, cfg_descriptor, bundle_cache_root)

    # get latest core - cache it directly into the plugin root folder
    if bootstrap_core_uri:
        logger.info("Caching custom core for boostrap (%s)" % bootstrap_core_uri)
        bootstrap_core_desc = create_descriptor(
            sg_connection,
            Descriptor.CORE,
            bootstrap_core_uri,
            bundle_cache_root_override=bundle_cache_root
        )

    else:
        # by default, use latest core for bootstrap
        logger.info("Caching latest official core to use when bootstrapping plugin.")
        logger.info("(To use a specific config instead, specify a --bootstrap-core-uri flag.)")

        bootstrap_core_desc = create_descriptor(
            sg_connection,
            Descriptor.CORE,
            {"type": "app_store", "name": "tk-core"},
            resolve_latest=True,
            bundle_cache_root_override=bundle_cache_root
        )

    # cache it
    bootstrap_core_desc.ensure_local()

    # make a python folder where we put our manifest
    logger.info("Creating configuration manifest...")

    # bake out the manifest into python files.
    _bake_manifest(
        manifest_data,
        config_uri_str,
        bootstrap_core_desc,
        target_path
    )

    # now analyze what core the config needs
    if cfg_descriptor.associated_core_descriptor:
        logger.info("Config is specifying a custom core in config/core/core_api.yml.")
        logger.info("This will be used when the config is executing.")
        logger.info("Ensuring this core (%s) is cached..." % cfg_descriptor.associated_core_descriptor)
        associated_core_desc = create_descriptor(
            sg_connection,
            Descriptor.CORE,
            cfg_descriptor.associated_core_descriptor,
            bundle_cache_root_override=bundle_cache_root
        )
        associated_core_desc.ensure_local()


    logger.info("")
    logger.info("Build complete!")
    logger.info("")
    logger.info("- Your plugin is ready in '%s'" % target_path)
    logger.info("- Plugin uses config %r" % cfg_descriptor)
    logger.info("- Bootstrap core is %r" % bootstrap_core_desc)
    logger.info("- All dependencies have been baked out into the bundle_cache folder")
    if buildable:
        logger.info("- The plugin can be used as a source for building further plugins.")
    logger.info("")
    logger.info("")
    logger.info("")
示例#20
0
def build_plugin(sg_connection,
                 source_path,
                 target_path,
                 buildable,
                 bootstrap_core_uri=None):
    """
    Perform a build of a plugin.

    This will introspect the info.yml in the source path,
    copy over everything in the source path into target path
    and then establish a bundle cache containing a reflection
    of all items required by the config.

    :param sg_connection: Shotgun connection
    :param source_path: Path to plugin.
    :param target_path: Path to build
    :param buildable: True if the resulting plugin build should be buildable
    :param bootstrap_core_uri: Custom bootstrap core uri. If None,
                               the latest core from the app store will be used.
    """
    logger.info("Your toolkit plugin in '%s' will be processed." % source_path)
    logger.info("The build will generated into '%s'" % target_path)

    # check for existence
    if not os.path.exists(source_path):
        raise TankError("Source path '%s' cannot be found on disk!" %
                        source_path)

    # check that target path doesn't exist
    if os.path.exists(target_path):
        logger.info(
            "The folder '%s' already exists on disk. Moving it to backup location"
            % target_path)
        filesystem.backup_folder(target_path)
        shutil.rmtree(target_path)

    # try to create target path
    filesystem.ensure_folder_exists(target_path)

    # check manifest
    manifest_data = _validate_manifest(source_path)

    # copy all plugin data across
    # skip info.yml, this is baked into the manifest python code
    logger.info("Copying plugin data across...")
    skip_list = [] if buildable else [".git", "info.yml"]
    filesystem.copy_folder(source_path, target_path, skip_list=skip_list)

    # create bundle cache
    logger.info("Creating bundle cache folder...")
    bundle_cache_root = os.path.join(target_path,
                                     BUNDLE_CACHE_ROOT_FOLDER_NAME)
    filesystem.ensure_folder_exists(bundle_cache_root)

    # resolve config descriptor
    # the config_uri_str returned by the method contains the fully resolved
    # uri to use at runtime - in the case of baked descriptors, the config_uri_str
    # contains a manual descriptor uri.
    (cfg_descriptor,
     config_uri_str) = _process_configuration(sg_connection, source_path,
                                              target_path, bundle_cache_root,
                                              manifest_data, buildable)

    # cache config in bundle cache
    logger.info("Downloading and caching config...")

    # copy the config payload across to the plugin bundle cache
    cfg_descriptor.clone_cache(bundle_cache_root)

    # cache all apps, engines and frameworks
    _cache_apps(sg_connection, cfg_descriptor, bundle_cache_root)

    # get latest core - cache it directly into the plugin root folder
    if bootstrap_core_uri:
        logger.info("Caching custom core for boostrap (%s)" %
                    bootstrap_core_uri)
        bootstrap_core_desc = create_descriptor(
            sg_connection,
            Descriptor.CORE,
            bootstrap_core_uri,
            bundle_cache_root_override=bundle_cache_root)

    else:
        # by default, use latest core for bootstrap
        logger.info(
            "Caching latest official core to use when bootstrapping plugin.")
        logger.info(
            "(To use a specific config instead, specify a --bootstrap-core-uri flag.)"
        )

        bootstrap_core_desc = create_descriptor(
            sg_connection,
            Descriptor.CORE, {
                "type": "app_store",
                "name": "tk-core"
            },
            resolve_latest=True,
            bundle_cache_root_override=bundle_cache_root)

    # cache it
    bootstrap_core_desc.ensure_local()

    # make a python folder where we put our manifest
    logger.info("Creating configuration manifest...")

    # bake out the manifest into python files.
    _bake_manifest(manifest_data, config_uri_str, bootstrap_core_desc,
                   target_path)

    # now analyze what core the config needs
    if cfg_descriptor.associated_core_descriptor:
        logger.info(
            "Config is specifying a custom core in config/core/core_api.yml.")
        logger.info("This will be used when the config is executing.")
        logger.info("Ensuring this core (%s) is cached..." %
                    cfg_descriptor.associated_core_descriptor)
        associated_core_desc = create_descriptor(
            sg_connection,
            Descriptor.CORE,
            cfg_descriptor.associated_core_descriptor,
            bundle_cache_root_override=bundle_cache_root)
        associated_core_desc.ensure_local()

    logger.info("")
    logger.info("Build complete!")
    logger.info("")
    logger.info("- Your plugin is ready in '%s'" % target_path)
    logger.info("- Plugin uses config %r" % cfg_descriptor)
    logger.info("- Bootstrap core is %r" % bootstrap_core_desc)
    logger.info(
        "- All dependencies have been baked out into the bundle_cache folder")
    if buildable:
        logger.info(
            "- The plugin can be used as a source for building further plugins."
        )
    logger.info("")
    logger.info("")
    logger.info("")
def build_plugin(sg_connection,
                 source_path,
                 target_path,
                 bootstrap_core_uri=None,
                 do_bake=False,
                 use_system_core=False):
    """
    Perform a build of a plugin.

    This will introspect the info.yml in the source path,
    copy over everything in the source path into target path
    and then establish a bundle cache containing a reflection
    of all items required by the config.

    :param sg_connection: Shotgun connection
    :param source_path: Path to plugin.
    :param target_path: Path to build
    :param bootstrap_core_uri: Custom bootstrap core uri. If None,
                               the latest core from the app store will be used.
    :param bool do_bake: If True, bake the plugin prior to building it.
    :param bool use_system_core: If True, use a globally installed tk-core instead
                                 of the one specified in the configuration.
    """
    logger.info("Your toolkit plugin in '%s' will be processed." % source_path)
    logger.info("The build will %s into '%s'" %
                (["generated", "baked"][do_bake], target_path))

    # check for existence
    if not os.path.exists(source_path):
        raise TankError("Source path '%s' cannot be found on disk!" %
                        source_path)

    # check manifest
    manifest_data = _validate_manifest(source_path)

    if do_bake:
        baked_descriptor = _bake_configuration(
            sg_connection,
            manifest_data,
        )
        # When baking we control the output path by adding a folder based on the
        # configuration descriptor and version.
        target_path = os.path.join(
            target_path,
            "%s-%s" % (baked_descriptor["name"], baked_descriptor["version"]))

    # check that target path doesn't exist
    if os.path.exists(target_path):
        logger.info("The folder '%s' already exists on disk. Removing it" %
                    target_path)
        wipe_folder(target_path)

    # try to create target path
    filesystem.ensure_folder_exists(target_path)

    # copy all plugin data across
    # skip info.yml, this is baked into the manifest python code
    logger.info("Copying plugin data across...")
    filesystem.copy_folder(source_path, target_path)

    # create bundle cache
    logger.info("Creating bundle cache folder...")
    bundle_cache_root = os.path.join(target_path,
                                     BUNDLE_CACHE_ROOT_FOLDER_NAME)
    filesystem.ensure_folder_exists(bundle_cache_root)

    # resolve config descriptor
    # the config_uri_str returned by the method contains the fully resolved
    # uri to use at runtime - in the case of baked descriptors, the config_uri_str
    # contains a manual descriptor uri and install_path is set with the baked
    # folder.
    (cfg_descriptor, config_uri_str,
     install_path) = _process_configuration(sg_connection, source_path,
                                            target_path, bundle_cache_root,
                                            manifest_data, use_system_core)

    # cache config in bundle cache
    logger.info("Downloading and caching config...")

    # copy the config payload across to the plugin bundle cache
    cfg_descriptor.clone_cache(bundle_cache_root)

    # cache all apps, engines and frameworks
    cache_apps(sg_connection, cfg_descriptor, bundle_cache_root)

    if use_system_core:
        logger.info(
            "An external core will be used for this plugin, not caching it")
        bootstrap_core_desc = None
    else:
        # get core - cache it directly into the plugin root folder
        if bootstrap_core_uri:
            logger.info("Caching custom core for boostrap (%s)" %
                        bootstrap_core_uri)
            bootstrap_core_desc = create_descriptor(
                sg_connection,
                Descriptor.CORE,
                bootstrap_core_uri,
                resolve_latest=is_descriptor_version_missing(
                    bootstrap_core_uri),
                bundle_cache_root_override=bundle_cache_root)
            # cache it
            bootstrap_core_desc.ensure_local()

        elif not cfg_descriptor.associated_core_descriptor:
            # by default, use latest core for bootstrap
            logger.info(
                "Caching latest official core to use when bootstrapping plugin."
            )
            logger.info(
                "(To use a specific config instead, specify a --bootstrap-core-uri flag.)"
            )

            bootstrap_core_desc = create_descriptor(
                sg_connection,
                Descriptor.CORE, {
                    "type": "app_store",
                    "name": "tk-core"
                },
                resolve_latest=True,
                bundle_cache_root_override=bundle_cache_root)

            # cache it
            bootstrap_core_desc.ensure_local()
        else:
            # The bootstrap core will be derived from the associated core desc below.
            bootstrap_core_desc = None

    # now analyze what core the config needs
    if not use_system_core and cfg_descriptor.associated_core_descriptor:
        logger.info(
            "Config is specifying a custom core in config/core/core_api.yml.")
        logger.info("This will be used when the config is executing.")
        logger.info("Ensuring this core (%s) is cached..." %
                    cfg_descriptor.associated_core_descriptor)
        associated_core_desc = create_descriptor(
            sg_connection,
            Descriptor.CORE,
            cfg_descriptor.associated_core_descriptor,
            bundle_cache_root_override=bundle_cache_root)
        associated_core_desc.ensure_local()
        if bootstrap_core_desc is None:
            # Use the same version as the one specified by the config.
            if install_path:
                # Install path is set only if the config was baked. We re-use the
                # install path as an optimisation to avoid core swapping when the
                # config is bootstrapped.
                logger.info(
                    "Bootstrapping will use installed %s required by the config"
                    % associated_core_desc)
                # If the core was installed we directly use it.
                bootstrap_core_desc = create_descriptor(
                    sg_connection,
                    Descriptor.CORE, {
                        "type": "path",
                        "name": "tk-core",
                        "path": os.path.join(install_path, "install", "core"),
                        "version": associated_core_desc.version,
                    },
                    resolve_latest=False,
                    bundle_cache_root_override=bundle_cache_root)
            else:
                logger.info(
                    "Bootstrapping will use core %s required by the config" %
                    associated_core_desc)
                bootstrap_core_desc = associated_core_desc

    # make a python folder where we put our manifest
    logger.info("Creating configuration manifest...")

    # bake out the manifest into python files.
    _bake_manifest(manifest_data, config_uri_str, bootstrap_core_desc,
                   target_path)

    cleanup_bundle_cache(bundle_cache_root)

    logger.info("")
    logger.info("Build complete!")
    logger.info("")
    logger.info("- Your plugin is ready in '%s'" % target_path)
    logger.info("- Plugin uses config %r" % cfg_descriptor)
    if bootstrap_core_desc:
        logger.info("- Bootstrap core is %r" % bootstrap_core_desc)
    else:
        logger.info("- Plugin will need an external installed core.")
    logger.info(
        "- All dependencies have been baked out into the bundle_cache folder")
    logger.info("")
    logger.info("")
    logger.info("")
def bake_config(sg_connection, config_uri, target_path, do_zip, skip_bundles_types):
    """
    Bake a Toolkit Pipeline configuration.

    This will ensure a local copy of the configuration, copy it over into target
    path and then establish a bundle cache containing a reflection of all items
    required by the config.

    :param sg_connection: Shotgun connection
    :param config_uri: A TK config descriptor uri.
    :param target_path: Path to build
    :param do_zip: Optionally zip up config once it's baked.
    :param skip_bundles: Bundle types to skip.
    """
    logger.info("Your Toolkit config '%s' will be processed." % config_uri)
    logger.info("Baking into '%s'" % (target_path))

    should_skip_functor = functools.partial(_should_skip, skip_bundles_types)

    config_descriptor = _process_configuration(sg_connection, config_uri)
    # Control the output path by adding a folder based on the
    # configuration descriptor and version.
    target_path = os.path.join(
        target_path,
        "%s-%s" % (config_descriptor.system_name, config_descriptor.version,),
    )

    # Check that target path doesn't exist
    if os.path.exists(target_path):
        logger.info("The folder '%s' already exists on disk. Removing it" % target_path)
        wipe_folder(target_path)

    # Create target path
    filesystem.ensure_folder_exists(target_path)
    # Copy the config data
    logger.info("Copying config data across...")
    filesystem.copy_folder(config_descriptor.get_path(), target_path)

    # Create bundle cache and cache all apps, engines and frameworks
    logger.info("Creating bundle cache folder...")
    bundle_cache_root = os.path.join(target_path, BUNDLE_CACHE_ROOT_FOLDER_NAME)
    filesystem.ensure_folder_exists(bundle_cache_root)
    logger.info("Downloading and caching config...")
    config_descriptor.clone_cache(bundle_cache_root)

    # If sparse_caching is True, we use our own descriptor filter which skips
    # app_store descriptors to keep our bundle cache small and lets Toolkit
    # download the bundles from the app store at runtime.
    cache_apps(sg_connection, config_descriptor, bundle_cache_root, should_skip_functor)

    # Now analyze what core the config needs and cache it if needed.
    core_descriptor = config_descriptor.associated_core_descriptor
    if core_descriptor:
        logger.info("Config defines a specific core in config/core/core_api.yml.")
        logger.info("This will be used when the config is executing.")
        if not should_skip_functor(core_descriptor):
            logger.info("Ensuring this core (%s) is cached..." % core_descriptor)
            associated_core_desc = create_descriptor(
                sg_connection,
                Descriptor.CORE,
                core_descriptor,
                bundle_cache_root_override=bundle_cache_root,
            )
            associated_core_desc.ensure_local()

    # Remove unwanted files, e.g. git history.
    cleanup_bundle_cache(bundle_cache_root)

    logger.info("")
    logger.info("Bake complete")
    logger.info("")
    logger.info(
        "- Your configuration %r is ready in '%s'" % (config_descriptor, target_path)
    )
    logger.info("- All dependencies have been baked out into the bundle_cache folder")
    logger.info("")
    logger.info("")
    logger.info("")
    if do_zip:
        logger.info("Zip archiving the baked configuration...")
        archive_path = shutil.make_archive(target_path, "zip", root_dir=target_path)
        logger.info("Zip archive available here: %s" % archive_path)
示例#23
0
def _process_configuration(sg_connection, source_path, target_path, bundle_cache_root, manifest_data, use_system_core):
    """
    Given data in the plugin manifest, download resolve and
    cache the configuration.

    :param sg_connection: Shotgun connection
    :param source_path: Root path of plugin source.
    :param target_path: Build target path
    :param bundle_cache_root: Bundle cache root
    :param manifest_data: Manifest data as a dictionary
    :param bool use_system_core: If True, use a globally installed tk-core instead
                                 of the one specified in the configuration.
    :return: (Resolved config descriptor object, config descriptor uri to use at runtime, install path)
    """
    logger.info("Analyzing configuration")

    install_path = None

    # get config def from info yml and generate both
    # dict and string uris.
    base_config_def = manifest_data["base_configuration"]
    if isinstance(base_config_def, str):
        # convert to dict so we can introspect
        base_config_uri_dict = descriptor_uri_to_dict(base_config_def)
        base_config_uri_str = base_config_def
    else:
        base_config_uri_dict = base_config_def
        base_config_uri_str = descriptor_dict_to_uri(base_config_def)

    # Special case - check for the 'baked' descriptor type
    # and process it. A baked descriptor is a special concept
    # that only exists in the build script. The baked descriptor
    # takes a single path parameter which can be a local or absolute
    # path. The path is copied across by the build script into a
    # manual descriptor, with a version number based on the current date.
    # This ensures that the manual descriptor will be correctly
    # re-cached at bootstrap time.
    if base_config_uri_dict["type"] == bootstrap_constants.BAKED_DESCRIPTOR_TYPE:
        logger.info("Baked descriptor detected.")

        baked_path = os.path.expanduser(os.path.expandvars(base_config_uri_dict["path"]))

        # if it's a relative path, expand it
        if not os.path.isabs(baked_path):
            full_baked_path = os.path.abspath(os.path.join(source_path, baked_path))

            # if it's a relative path, we have already copied it to the build
            # target location. In this case, attempt to locate it and remove it.
            baked_target_path = os.path.abspath(os.path.join(target_path, baked_path))
            if baked_target_path.startswith(baked_target_path):
                logger.debug("Removing '%s' from build" % baked_target_path)
                shutil.rmtree(baked_target_path)
        else:
            # path is absolute
            full_baked_path = os.path.abspath(baked_path)

        logger.info("Will bake an immutable config into the plugin from '%s'" % full_baked_path)

        # A baked config descriptor does not require a name nor a version, so
        # if these keys are not available, use the current date time for the
        # version and an arbitrary name for the config. Please note that this
        # only happens if the baked descriptor was set in the original config.
        # When baking a plugin with the --bake option, which is the recommended
        # workflow, these values are automatically set.
        baked_name = base_config_uri_dict.get("name") or "tk-config-plugin"
        baked_version = (
            base_config_uri_dict.get("version") or
            datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
        )
        install_path = os.path.join(
            bundle_cache_root,
            bootstrap_constants.BAKED_DESCRIPTOR_FOLDER_NAME,
            baked_name,
            baked_version
        )

        cfg_descriptor = create_descriptor(
            sg_connection,
            Descriptor.CONFIG,
            {"type": "path", "path": full_baked_path}
        )

        BakedConfiguration.bake_config_scaffold(
            install_path,
            sg_connection,
            manifest_data["plugin_id"],
            cfg_descriptor
        )

        # now lastly,
        base_config_uri_str = descriptor_dict_to_uri(
            {
                "type": bootstrap_constants.BAKED_DESCRIPTOR_TYPE,
                "name": baked_name,
                "version": baked_version
            }
        )
        if use_system_core:
            # If asked to use a globally installed tk-core instead of the one
            # specified by the config, we remove the local copy which was created
            # in the scaffold step.
            logger.info("Removing core reference in %s" % install_path)
            wipe_folder(os.path.join(install_path, "install"))
            # And make sure we don't have any reference to a tk-core in the config,
            # otherwise it would be picked up when bootstrapping.
            filesystem.safe_delete_file(os.path.join(install_path, "config", "core", "core_api.yml"))
        else:
            # Workaround for tk-core bootstrap needing a shotgun.yml file: when swapping
            # tk-core, this file is checked to see if a script user was specified and
            # should be used in place of the authenticated user. So we create a dummy
            # file with an "unspecified" host, as the key is required by the tk-core
            # code parsing the file.
            # It is not clear if this workaround is needed for non baked configs as
            # their workflow is different, so for now we just keep it for bake configs
            # only.
            shotgun_yaml_path = os.path.join(install_path, "config", "core", "shotgun.yml")
            if not os.path.exists(shotgun_yaml_path):
                logger.info("Patching %s" % shotgun_yaml_path)
                with open(shotgun_yaml_path, "w") as pf:
                    pf.write("# Workaround for tk-core bootstrap\nhost: unspecified")
    else:

        # if the descriptor in the config contains a version number
        # we will go into a fixed update mode.
        using_latest_config = is_descriptor_version_missing(base_config_uri_dict)
        if using_latest_config:
            logger.info(
                "Your configuration definition does not contain a version number. "
                "This means that the plugin will attempt to auto update at startup."
            )
        else:
            logger.info(
                "Your configuration definition contains a version number. "
                "This means that the plugin will be frozen and no automatic updates "
                "will be performed at startup."
            )

        cfg_descriptor = create_descriptor(
            sg_connection,
            Descriptor.CONFIG,
            base_config_uri_dict,
            resolve_latest=using_latest_config
        )
    logger.info("Resolved config %r" % cfg_descriptor)
    logger.info("Runtime config descriptor uri will be %s" % base_config_uri_str)
    if install_path:
        logger.info("The config was baked in %s" % install_path)
    return cfg_descriptor, base_config_uri_str, install_path