예제 #1
0
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
예제 #2
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
예제 #3
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
예제 #4
0
def main():
    """
    Main entry point for script.

    Handles argument parsing and validation and then calls the script payload.
    """

    usage = "%prog [options] config_descriptor target_path"

    desc = "Bake a self contained Toolkit config from a descriptor"

    epilog = """

Details and Examples
--------------------

In its simplest form, just provide a local path and target folder for the build.

> python bake_config.py ~/dev/tk-config-myconfig /tmp/baked_configurations

Or you can specify a version with a Toolkit config descriptor uri.

> python bake_config.py "sgtk:descriptor:dev?version=v1.0.9&path=../tk-config-myconfig" /tmp/baked_configurations

Any type of Toolkit config descriptor uri can be used, if a version is not specified, the latest for the descriptor is resolved.

> python bake_config.py "sgtk:descriptor:app_store?name=tk-config-basic" /tmp/baked_configurations

By default, all bundle types are cached. If you want to omit certain types, simply provide a comma seperated list
of bundle types to skip, e.g. --skip-bundle-types=app_store,shotgun,github_release.

{automated_setup_documentation}

For information about the various descriptors that can be used, see
http://developer.shotgridsoftware.com/tk-core/descriptor


""".format(
        automated_setup_documentation=automated_setup_documentation
    ).format(
        script_name="bake_config.py"
    )
    parser = OptionParserLineBreakingEpilog(
        usage=usage, description=desc, epilog=epilog
    )

    parser.add_option(
        "-d", "--debug", default=False, action="store_true", help="Enable debug logging"
    )

    parser.add_option(
        "-z", "--zip", default=False, action="store_true", help="Zip archive the config"
    )

    parser.add_option(
        "--skip-bundle-types",
        # You can't have an empty default optional value, so we'll pick something
        # and treat it accordingly.
        default="none",
        help="Comma separated list of bundle types to skip. Possible values are 'app_store', "
        "'git', 'git_branch', 'github_release', 'shotgun'. Empty by default.",
    )

    add_authentication_options(parser)

    # parse cmd line
    (options, remaining_args) = parser.parse_args()

    logger.info("Welcome to the Toolkit config baker.")
    logger.info("")

    if options.debug:
        LogManager().global_debug = True

    if len(remaining_args) != 2:
        parser.print_help()
        return 2

    # Get config descriptor
    config_descriptor = remaining_args[0]
    # Try to parse it, check if it is a local path if it fails
    try:
        descriptor_uri_to_dict(config_descriptor)
    except TankDescriptorError:
        # Check if it is a local path
        path = os.path.abspath(
            os.path.expanduser(os.path.expandvars(config_descriptor))
        )
        if os.path.isdir(path):
            logger.info("Using a dev descriptor for local path %s" % path)
            # Forge a dev descriptor, using "latest" for the version.
            # TODO: try to retrieve a valid version from the folder, e.g. with a
            # git tag from the folder.
            config_descriptor = "sgtk:descriptor:dev?name=%s&path=%s&version=latest" % (
                os.path.basename(path),
                path,
            )
        else:
            logger.error(
                "%s is not a valid descriptor nor a local path." % config_descriptor
            )
            raise
    # Get output path
    target_path = remaining_args[1]
    target_path = os.path.expanduser(os.path.expandvars(target_path))

    sg_user = authenticate(options)

    sg_connection = sg_user.create_sg_connection()
    # make sure we are properly connected
    try:
        sg_connection.find_one("HumanUser", [])
    except Exception as e:
        logger.error("Could not communicate with ShotGrid: %s" % e)
        return 3

    # Strip any extra whitespaces and make sure every bundle type exists.
    skip_bundle_types = options.skip_bundle_types.split(",")
    skip_bundle_types = [bundle_type.strip() for bundle_type in skip_bundle_types]
    for bundle_type in skip_bundle_types:
        if bundle_type not in [
            "app_store",
            "git",
            "git_branch",
            "github_release",
            "shotgun",
            "none",
        ]:
            logger.error("Unknown bundle type: %s" % bundle_type)
            return 4

    # we are all set.
    bake_config(
        sg_connection, config_descriptor, target_path, options.zip, skip_bundle_types,
    )

    # all good!
    return 0
예제 #5
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
예제 #6
0
def bake_config(sg_connection,
                config_uri,
                target_path,
                do_zip=False,
                sparse_caching=False):
    """
    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. Defaults to False.
    :param sparse_caching: Don't cache app_store bundles into the config. Defaults to False.
    """
    logger.info("Your Toolkit config '%s' will be processed." % config_uri)
    logger.info("Baking into '%s'" % (target_path))

    config_uri_dict = descriptor_uri_to_dict(config_uri)
    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.
    if sparse_caching:
        logger.info(
            "Performing sparse caching. Will not cache standard app_store bundles."
        )
        cache_apps(sg_connection, config_descriptor, bundle_cache_root,
                   _should_skip_caching_sparse)
    else:
        cache_apps(sg_connection, config_descriptor, bundle_cache_root)

    # 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 sparse_caching is True, check if we need to cache tk-core or not
        if not sparse_caching or not _should_skip_caching_sparse(
                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()
        else:
            logger.info(
                "No need to cache this core (%s), it will be cached at runtime."
                % config_descriptor.associated_core_descriptor)

    # 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)
예제 #7
0
def main():
    """
    Main entry point for script.

    Handles argument parsing and validation and then calls the script payload.
    """

    usage = "%prog [options] config_descriptor target_path"

    desc = "Bake a self contained Toolkit config from a descriptor"

    epilog = """

Details and Examples
--------------------

In its simplest form, just provide a local path and target folder for the build.

> python bake_config.py ~/dev/tk-config-myconfig /tmp/baked_configurations

Or you can specify a version with a Toolkit config descriptor uri.

> python bake_config.py "sgtk:descriptor:dev?version=v1.0.9&path=../tk-config-myconfig" /tmp/baked_configurations

Any type of Toolkit config descriptor uri can be used, if a version is not specified, the latest for the descriptor is resolved.

> python bake_config.py "sgtk:descriptor:app_store?name=tk-config-basic" /tmp/baked_configurations

{automated_setup_documentation}

For information about the various descriptors that can be used, see
http://developer.shotgunsoftware.com/tk-core/descriptor


""".format(automated_setup_documentation=automated_setup_documentation)
    parser = OptionParserLineBreakingEpilog(usage=usage,
                                            description=desc,
                                            epilog=epilog)

    parser.add_option("-d",
                      "--debug",
                      default=False,
                      action="store_true",
                      help="Enable debug logging")

    parser.add_option("-z",
                      "--zip",
                      default=False,
                      action="store_true",
                      help="Zip archive the config")

    parser.add_option("-r",
                      "--sparse",
                      default=False,
                      action="store_true",
                      help="Don't cache any app_store bundles")

    add_authentication_options(parser)

    # parse cmd line
    (options, remaining_args) = parser.parse_args()

    logger.info("Welcome to the Toolkit config baker.")
    logger.info("")

    if options.debug:
        LogManager().global_debug = True

    if len(remaining_args) != 2:
        parser.print_help()
        return 2

    # Get config descriptor
    config_descriptor = remaining_args[0]
    # Try to parse it, check if it is a local path if it fails
    try:
        descriptor_uri_to_dict(config_descriptor)
    except TankDescriptorError as e:
        # Check if it is a local path
        path = os.path.abspath(
            os.path.expanduser(os.path.expandvars(config_descriptor)))
        if os.path.isdir(path):
            logger.info("Using a dev descriptor for local path %s" % path)
            # Forge a dev descriptor, using "latest" for the version.
            # TODO: try to retrieve a valid version from the folder, e.g. with a
            # git tag from the folder.
            config_descriptor = "sgtk:descriptor:dev?name=%s&path=%s&version=latest" % (
                os.path.basename(path), path)
        else:
            logger.error("%s is not a valid descriptor nor a local path." %
                         config_descriptor)
            raise
    # Get output path
    target_path = remaining_args[1]
    target_path = os.path.expanduser(os.path.expandvars(target_path))

    sg_user = authenticate(options)

    sg_connection = sg_user.create_sg_connection()
    # make sure we are properly connected
    try:
        sg_connection.find_one("HumanUser", [])
    except Exception, e:
        logger.error("Could not communicate with Shotgun: %s" % e)
        return 3
예제 #8
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
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
예제 #10
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