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)
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())
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_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_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)
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)
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 _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
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
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
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
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 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, 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)
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