From e42aebc1f277cef9dc3f195954a0ba817697127b Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 3 Apr 2023 11:12:02 +0200 Subject: [PATCH] Global: custom location for OP local versions (#4673) * OP-5221 - updated settings for custom location of artist zip folder * OP-5221 - updated igniter for custom location of artist zip folder Introduced new function Updated data_dir only after access to Mongo (DB) * OP-5221 - pushed resolving of local folder to OpenPypeVersion Logic in OpenPypeVersion is used even in openpype_version.py * OP-5221 - updates after review * OP-5221 - fix paths should be single paths * OP-5221 - refactor to cls * OP-5221 - refactor Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> * OP-5221 - fix defaults for single paths Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> * OP-5221 - remove unwanted line Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> * OP-5221 - update look of Settings Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- igniter/bootstrap_repos.py | 40 +++++++++++++++---- igniter/install_thread.py | 15 ++++++- igniter/tools.py | 20 ++++++++++ igniter/update_thread.py | 2 + .../defaults/system_settings/general.json | 5 +++ .../schemas/system_schema/schema_general.json | 18 ++++++++- openpype/settings/handlers.py | 1 + start.py | 7 +++- 8 files changed, 97 insertions(+), 11 deletions(-) diff --git a/igniter/bootstrap_repos.py b/igniter/bootstrap_repos.py index 6c7c834062..4cf00375bf 100644 --- a/igniter/bootstrap_repos.py +++ b/igniter/bootstrap_repos.py @@ -25,7 +25,8 @@ from .user_settings import ( from .tools import ( get_openpype_global_settings, get_openpype_path_from_settings, - get_expected_studio_version_str + get_expected_studio_version_str, + get_local_openpype_path_from_settings ) @@ -61,6 +62,8 @@ class OpenPypeVersion(semver.VersionInfo): """ path = None + + _local_openpype_path = None # this should match any string complying with https://semver.org/ _VERSION_REGEX = re.compile(r"(?P0|[1-9]\d*)\.(?P0|[1-9]\d*)\.(?P0|[1-9]\d*)(?:-(?P[a-zA-Z\d\-.]*))?(?:\+(?P[a-zA-Z\d\-.]*))?") # noqa: E501 _installed_version = None @@ -289,6 +292,23 @@ class OpenPypeVersion(semver.VersionInfo): """ return os.getenv("OPENPYPE_PATH") + @classmethod + def get_local_openpype_path(cls): + """Path to unzipped versions. + + By default it should be user appdata, but could be overridden by + settings. + """ + if cls._local_openpype_path: + return cls._local_openpype_path + + settings = get_openpype_global_settings(os.environ["OPENPYPE_MONGO"]) + data_dir = get_local_openpype_path_from_settings(settings) + if not data_dir: + data_dir = Path(user_data_dir("openpype", "pypeclub")) + cls._local_openpype_path = data_dir + return data_dir + @classmethod def openpype_path_is_set(cls): """Path to OpenPype zip directory is set.""" @@ -319,9 +339,8 @@ class OpenPypeVersion(semver.VersionInfo): list: of compatible versions available on the machine. """ - # DEPRECATED: backwards compatible way to look for versions in root - dir_to_search = Path(user_data_dir("openpype", "pypeclub")) - versions = OpenPypeVersion.get_versions_from_directory(dir_to_search) + dir_to_search = cls.get_local_openpype_path() + versions = cls.get_versions_from_directory(dir_to_search) return list(sorted(set(versions))) @@ -533,17 +552,15 @@ class BootstrapRepos: """ # vendor and app used to construct user data dir - self._vendor = "pypeclub" - self._app = "openpype" + self._message = message self._log = log.getLogger(str(__class__)) - self.data_dir = Path(user_data_dir(self._app, self._vendor)) + self.set_data_dir(None) self.secure_registry = OpenPypeSecureRegistry("mongodb") self.registry = OpenPypeSettingsRegistry() self.zip_filter = [".pyc", "__pycache__"] self.openpype_filter = [ "openpype", "schema", "LICENSE" ] - self._message = message # dummy progress reporter def empty_progress(x: int): @@ -554,6 +571,13 @@ class BootstrapRepos: progress_callback = empty_progress self._progress_callback = progress_callback + def set_data_dir(self, data_dir): + if not data_dir: + self.data_dir = Path(user_data_dir("openpype", "pypeclub")) + else: + self._print(f"overriding local folder: {data_dir}") + self.data_dir = data_dir + @staticmethod def get_version_path_from_list( version: str, version_list: list) -> Union[Path, None]: diff --git a/igniter/install_thread.py b/igniter/install_thread.py index 4723e6adfb..1d55213de7 100644 --- a/igniter/install_thread.py +++ b/igniter/install_thread.py @@ -14,7 +14,11 @@ from .bootstrap_repos import ( OpenPypeVersion ) -from .tools import validate_mongo_connection +from .tools import ( + get_openpype_global_settings, + get_local_openpype_path_from_settings, + validate_mongo_connection +) class InstallThread(QtCore.QThread): @@ -80,6 +84,15 @@ class InstallThread(QtCore.QThread): return os.environ["OPENPYPE_MONGO"] = self._mongo + if not validate_mongo_connection(self._mongo): + self.message.emit(f"Cannot connect to {self._mongo}", True) + self._set_result(-1) + return + + global_settings = get_openpype_global_settings(self._mongo) + data_dir = get_local_openpype_path_from_settings(global_settings) + bs.set_data_dir(data_dir) + self.message.emit( f"Detecting installed OpenPype versions in {bs.data_dir}", False) diff --git a/igniter/tools.py b/igniter/tools.py index 79235b2329..af5cbe70a9 100644 --- a/igniter/tools.py +++ b/igniter/tools.py @@ -188,6 +188,26 @@ def get_openpype_path_from_settings(settings: dict) -> Union[str, None]: return next((path for path in paths if os.path.exists(path)), None) +def get_local_openpype_path_from_settings(settings: dict) -> Union[str, None]: + """Get OpenPype local path from global settings. + + Used to download and unzip OP versions. + Args: + settings (dict): settings from DB. + + Returns: + path to OpenPype or None if not found + """ + path = ( + settings + .get("local_openpype_path", {}) + .get(platform.system().lower()) + ) + if path: + return Path(path) + return None + + def get_expected_studio_version_str( staging=False, global_settings=None ) -> str: diff --git a/igniter/update_thread.py b/igniter/update_thread.py index e98c95f892..0223477d0a 100644 --- a/igniter/update_thread.py +++ b/igniter/update_thread.py @@ -48,6 +48,8 @@ class UpdateThread(QtCore.QThread): """ bs = BootstrapRepos( progress_callback=self.set_progress, message=self.message) + + bs.set_data_dir(OpenPypeVersion.get_local_openpype_path()) version_path = bs.install_version(self._openpype_version) self._set_result(version_path) diff --git a/openpype/settings/defaults/system_settings/general.json b/openpype/settings/defaults/system_settings/general.json index d2994d1a62..496c37cd4d 100644 --- a/openpype/settings/defaults/system_settings/general.json +++ b/openpype/settings/defaults/system_settings/general.json @@ -15,6 +15,11 @@ "darwin": [], "linux": [] }, + "local_openpype_path": { + "windows": "", + "darwin": "", + "linux": "" + }, "production_version": "", "staging_version": "", "version_check_interval": 5 diff --git a/openpype/settings/entities/schemas/system_schema/schema_general.json b/openpype/settings/entities/schemas/system_schema/schema_general.json index d6c22fe54c..2609441061 100644 --- a/openpype/settings/entities/schemas/system_schema/schema_general.json +++ b/openpype/settings/entities/schemas/system_schema/schema_general.json @@ -128,8 +128,12 @@ { "type": "collapsible-wrap", "label": "OpenPype deployment control", - "collapsible": false, + "collapsible": true, "children": [ + { + "type": "label", + "label": "Define location accessible by artist machine to check for zip updates with Openpype code." + }, { "type": "path", "key": "openpype_path", @@ -138,6 +142,18 @@ "multipath": true, "require_restart": true }, + { + "type": "label", + "label": "Define custom location for artist machine where to unzip versions of Openpype code. By default it is in user app data folder." + }, + { + "type": "path", + "key": "local_openpype_path", + "label": "Custom Local Versions Folder", + "multiplatform": true, + "multipath": false, + "require_restart": true + }, { "type": "splitter" }, diff --git a/openpype/settings/handlers.py b/openpype/settings/handlers.py index ab7cdd058c..1d4c838f1a 100644 --- a/openpype/settings/handlers.py +++ b/openpype/settings/handlers.py @@ -189,6 +189,7 @@ class SettingsStateInfo: class SettingsHandler(object): global_keys = { "openpype_path", + "local_openpype_path", "admin_password", "log_to_server", "disk_mapping", diff --git a/start.py b/start.py index f8d65dc221..2160f03493 100644 --- a/start.py +++ b/start.py @@ -268,6 +268,7 @@ from igniter import BootstrapRepos # noqa: E402 from igniter.tools import ( get_openpype_global_settings, get_openpype_path_from_settings, + get_local_openpype_path_from_settings, validate_mongo_connection, OpenPypeVersionNotFound, OpenPypeVersionIncompatible @@ -1039,6 +1040,10 @@ def boot(): # find its versions there and bootstrap them. openpype_path = get_openpype_path_from_settings(global_settings) + # Check if local versions should be installed in custom folder and not in + # user app data + data_dir = get_local_openpype_path_from_settings(global_settings) + bootstrap.set_data_dir(data_dir) if getattr(sys, 'frozen', False): local_version = bootstrap.get_version(Path(OPENPYPE_ROOT)) else: @@ -1076,7 +1081,7 @@ def boot(): _print(f"!!! {e}", True) sys.exit(1) # validate version - _print(f">>> Validating version [ {str(version_path)} ]") + _print(f">>> Validating version in frozen [ {str(version_path)} ]") result = bootstrap.validate_openpype_version(version_path) if not result[0]: _print(f"!!! Invalid version: {result[1]}", True)