Merge pull request #3979 from pypeclub/enhancement/remove-staging-logic-on-version

Remove staging logic set by OpenPype version
This commit is contained in:
Ondřej Samohel 2022-11-11 18:43:16 +01:00 committed by GitHub
commit 86cd0e60f2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 327 additions and 424 deletions

View file

@ -57,11 +57,9 @@ class OpenPypeVersion(semver.VersionInfo):
"""Class for storing information about OpenPype version.
Attributes:
staging (bool): True if it is staging version
path (str): path to OpenPype
"""
staging = False
path = None
_VERSION_REGEX = re.compile(r"(?P<major>0|[1-9]\d*)\.(?P<minor>0|[1-9]\d*)\.(?P<patch>0|[1-9]\d*)(?:-(?P<prerelease>(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+(?P<buildmetadata>[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?") # noqa: E501
_installed_version = None
@ -82,12 +80,10 @@ class OpenPypeVersion(semver.VersionInfo):
build (str): an optional build string
version (str): if set, it will be parsed and will override
parameters like `major`, `minor` and so on.
staging (bool): set to True if version is staging.
path (Path): path to version location.
"""
self.path = None
self.staging = False
if "version" in kwargs.keys():
if not kwargs.get("version"):
@ -112,29 +108,8 @@ class OpenPypeVersion(semver.VersionInfo):
if "path" in kwargs.keys():
kwargs.pop("path")
if kwargs.get("staging"):
self.staging = kwargs.get("staging", False)
kwargs.pop("staging")
if "staging" in kwargs.keys():
kwargs.pop("staging")
if self.staging:
if kwargs.get("build"):
if "staging" not in kwargs.get("build"):
kwargs["build"] = f"{kwargs.get('build')}-staging"
else:
kwargs["build"] = "staging"
if kwargs.get("build") and "staging" in kwargs.get("build", ""):
self.staging = True
super().__init__(*args, **kwargs)
def __eq__(self, other):
result = super().__eq__(other)
return bool(result and self.staging == other.staging)
def __repr__(self):
return f"<{self.__class__.__name__}: {str(self)} - path={self.path}>"
@ -149,43 +124,11 @@ class OpenPypeVersion(semver.VersionInfo):
return True
if self.finalize_version() == other.finalize_version() and \
self.prerelease == other.prerelease and \
self.is_staging() and not other.is_staging():
self.prerelease == other.prerelease:
return True
return result
def set_staging(self) -> OpenPypeVersion:
"""Set version as staging and return it.
This will preserve current one.
Returns:
OpenPypeVersion: Set as staging.
"""
if self.staging:
return self
return self.replace(parts={"build": f"{self.build}-staging"})
def set_production(self) -> OpenPypeVersion:
"""Set version as production and return it.
This will preserve current one.
Returns:
OpenPypeVersion: Set as production.
"""
if not self.staging:
return self
return self.replace(
parts={"build": self.build.replace("-staging", "")})
def is_staging(self) -> bool:
"""Test if current version is staging one."""
return self.staging
def get_main_version(self) -> str:
"""Return main version component.
@ -215,21 +158,8 @@ class OpenPypeVersion(semver.VersionInfo):
if not m:
return None
version = OpenPypeVersion.parse(string[m.start():m.end()])
if "staging" in string[m.start():m.end()]:
version.staging = True
return version
@classmethod
def parse(cls, version):
"""Extends parse to handle ta handle staging variant."""
v = super().parse(version)
openpype_version = cls(major=v.major, minor=v.minor,
patch=v.patch, prerelease=v.prerelease,
build=v.build)
if v.build and "staging" in v.build:
openpype_version.staging = True
return openpype_version
def __hash__(self):
return hash(self.path) if self.path else hash(str(self))
@ -379,80 +309,28 @@ class OpenPypeVersion(semver.VersionInfo):
return False
@classmethod
def get_local_versions(
cls, production: bool = None,
staging: bool = None
) -> List:
def get_local_versions(cls) -> List:
"""Get all versions available on this machine.
Arguments give ability to specify if filtering is needed. If both
arguments are set to None all found versions are returned.
Args:
production (bool): Return production versions.
staging (bool): Return staging versions.
Returns:
list: of compatible versions available on the machine.
"""
# Return all local versions if arguments are set to None
if production is None and staging is None:
production = True
staging = True
elif production is None and not staging:
production = True
elif staging is None and not production:
staging = True
# Just return empty output if both are disabled
if not production and not staging:
return []
# 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)
filtered_versions = []
for version in versions:
if version.is_staging():
if staging:
filtered_versions.append(version)
elif production:
filtered_versions.append(version)
return list(sorted(set(filtered_versions)))
return list(sorted(set(versions)))
@classmethod
def get_remote_versions(
cls, production: bool = None,
staging: bool = None
) -> List:
def get_remote_versions(cls) -> List:
"""Get all versions available in OpenPype Path.
Arguments give ability to specify if filtering is needed. If both
arguments are set to None all found versions are returned.
Args:
production (bool): Return production versions.
staging (bool): Return staging versions.
Returns:
list of OpenPypeVersions: Versions found in OpenPype path.
"""
# Return all local versions if arguments are set to None
if production is None and staging is None:
production = True
staging = True
elif production is None and not staging:
production = True
elif staging is None and not production:
staging = True
# Just return empty output if both are disabled
if not production and not staging:
return []
dir_to_search = None
if cls.openpype_path_is_accessible():
@ -473,14 +351,7 @@ class OpenPypeVersion(semver.VersionInfo):
versions = cls.get_versions_from_directory(dir_to_search)
filtered_versions = []
for version in versions:
if version.is_staging():
if staging:
filtered_versions.append(version)
elif production:
filtered_versions.append(version)
return list(sorted(set(filtered_versions)))
return list(sorted(set(versions)))
@staticmethod
def get_versions_from_directory(
@ -559,7 +430,6 @@ class OpenPypeVersion(semver.VersionInfo):
@staticmethod
def get_latest_version(
staging: bool = False,
local: bool = None,
remote: bool = None
) -> Union[OpenPypeVersion, None]:
@ -568,7 +438,6 @@ class OpenPypeVersion(semver.VersionInfo):
The version does not contain information about path and source.
This is utility version to get the latest version from all found.
Build version is not listed if staging is enabled.
Arguments 'local' and 'remote' define if local and remote repository
versions are used. All versions are used if both are not set (or set
@ -577,7 +446,6 @@ class OpenPypeVersion(semver.VersionInfo):
'False' in that case only build version can be used.
Args:
staging (bool, optional): List staging versions if True.
local (bool, optional): List local versions if True.
remote (bool, optional): List remote versions if True.
@ -596,22 +464,9 @@ class OpenPypeVersion(semver.VersionInfo):
remote = True
installed_version = OpenPypeVersion.get_installed_version()
local_versions = []
remote_versions = []
if local:
local_versions = OpenPypeVersion.get_local_versions(
staging=staging
)
if remote:
remote_versions = OpenPypeVersion.get_remote_versions(
staging=staging
)
all_versions = local_versions + remote_versions
if not staging:
all_versions.append(installed_version)
if not all_versions:
return None
local_versions = OpenPypeVersion.get_local_versions() if local else []
remote_versions = OpenPypeVersion.get_remote_versions() if remote else [] # noqa: E501
all_versions = local_versions + remote_versions + [installed_version]
all_versions.sort()
return all_versions[-1]
@ -702,7 +557,7 @@ class BootstrapRepos:
"""Get path for specific version in list of OpenPype versions.
Args:
version (str): Version string to look for (1.2.4+staging)
version (str): Version string to look for (1.2.4-nightly.1+test)
version_list (list of OpenPypeVersion): list of version to search.
Returns:
@ -1130,14 +985,12 @@ class BootstrapRepos:
@staticmethod
def find_openpype_version(
version: Union[str, OpenPypeVersion],
staging: bool
version: Union[str, OpenPypeVersion]
) -> Union[OpenPypeVersion, None]:
"""Find location of specified OpenPype version.
Args:
version (Union[str, OpenPypeVersion): Version to find.
staging (bool): Filter staging versions.
Returns:
requested OpenPypeVersion.
@ -1150,9 +1003,7 @@ class BootstrapRepos:
if installed_version == version:
return installed_version
local_versions = OpenPypeVersion.get_local_versions(
staging=staging, production=not staging
)
local_versions = OpenPypeVersion.get_local_versions()
zip_version = None
for local_version in local_versions:
if local_version == version:
@ -1164,37 +1015,25 @@ class BootstrapRepos:
if zip_version is not None:
return zip_version
remote_versions = OpenPypeVersion.get_remote_versions(
staging=staging, production=not staging
)
for remote_version in remote_versions:
if remote_version == version:
return remote_version
return None
remote_versions = OpenPypeVersion.get_remote_versions()
return next(
(
remote_version for remote_version in remote_versions
if remote_version == version
), None)
@staticmethod
def find_latest_openpype_version(
staging: bool
) -> Union[OpenPypeVersion, None]:
def find_latest_openpype_version() -> Union[OpenPypeVersion, None]:
"""Find the latest available OpenPype version in all location.
Args:
staging (bool): True to look for staging versions.
Returns:
Latest OpenPype version on None if nothing was found.
"""
installed_version = OpenPypeVersion.get_installed_version()
local_versions = OpenPypeVersion.get_local_versions(
staging=staging
)
remote_versions = OpenPypeVersion.get_remote_versions(
staging=staging
)
all_versions = local_versions + remote_versions
if not staging:
all_versions.append(installed_version)
local_versions = OpenPypeVersion.get_local_versions()
remote_versions = OpenPypeVersion.get_remote_versions()
all_versions = local_versions + remote_versions + [installed_version]
if not all_versions:
return None
@ -1214,7 +1053,6 @@ class BootstrapRepos:
def find_openpype(
self,
openpype_path: Union[Path, str] = None,
staging: bool = False,
include_zips: bool = False
) -> Union[List[OpenPypeVersion], None]:
"""Get ordered dict of detected OpenPype version.
@ -1228,8 +1066,6 @@ class BootstrapRepos:
Args:
openpype_path (Path or str, optional): Try to find OpenPype on
the given path or url.
staging (bool, optional): Filter only staging version, skip them
otherwise.
include_zips (bool, optional): If set True it will try to find
OpenPype in zip files in given directory.
@ -1277,7 +1113,7 @@ class BootstrapRepos:
for dir_to_search in dirs_to_search:
try:
openpype_versions += self.get_openpype_versions(
dir_to_search, staging)
dir_to_search)
except ValueError:
# location is invalid, skip it
pass
@ -1642,15 +1478,11 @@ class BootstrapRepos:
return False
return True
def get_openpype_versions(
self,
openpype_dir: Path,
staging: bool = False) -> list:
def get_openpype_versions(self, openpype_dir: Path) -> list:
"""Get all detected OpenPype versions in directory.
Args:
openpype_dir (Path): Directory to scan.
staging (bool, optional): Find staging versions if True.
Returns:
list of OpenPypeVersion
@ -1668,8 +1500,7 @@ class BootstrapRepos:
for item in openpype_dir.iterdir():
# if the item is directory with major.minor version, dive deeper
if item.is_dir() and re.match(r"^\d+\.\d+$", item.name):
_versions = self.get_openpype_versions(
item, staging=staging)
_versions = self.get_openpype_versions(item)
if _versions:
openpype_versions += _versions
@ -1692,11 +1523,7 @@ class BootstrapRepos:
continue
detected_version.path = item
if staging and detected_version.is_staging():
openpype_versions.append(detected_version)
if not staging and not detected_version.is_staging():
openpype_versions.append(detected_version)
openpype_versions.append(detected_version)
return sorted(openpype_versions)

View file

@ -184,11 +184,7 @@ def get_openpype_path_from_settings(settings: dict) -> Union[str, None]:
if paths and isinstance(paths, str):
paths = [paths]
# Loop over paths and return only existing
for path in paths:
if os.path.exists(path):
return path
return None
return next((path for path in paths if os.path.exists(path)), None)
def get_expected_studio_version_str(
@ -206,10 +202,7 @@ def get_expected_studio_version_str(
mongo_url = os.environ.get("OPENPYPE_MONGO")
if global_settings is None:
global_settings = get_openpype_global_settings(mongo_url)
if staging:
key = "staging_version"
else:
key = "production_version"
key = "staging_version" if staging else "production_version"
return global_settings.get(key) or ""

View file

@ -16,14 +16,13 @@ from .pype_commands import PypeCommands
@click.option("--use-staging", is_flag=True,
expose_value=False, help="use staging variants")
@click.option("--list-versions", is_flag=True, expose_value=False,
help=("list all detected versions. Use With `--use-staging "
"to list staging versions."))
help="list all detected versions.")
@click.option("--validate-version", expose_value=False,
help="validate given version integrity")
@click.option("--debug", is_flag=True, expose_value=False,
help=("Enable debug"))
help="Enable debug")
@click.option("--verbose", expose_value=False,
help=("Change OpenPype log level (debug - critical or 0-50)"))
help="Change OpenPype log level (debug - critical or 0-50)")
def main(ctx):
"""Pype is main command serving as entry point to pipeline system.
@ -423,20 +422,18 @@ def unpack_project(zipfile, root):
@main.command()
def interactive():
"""Interative (Python like) console.
"""Interactive (Python like) console.
Helpfull command not only for development to directly work with python
Helpful command not only for development to directly work with python
interpreter.
Warning:
Executable 'openpype_gui' on windows won't work.
Executable 'openpype_gui' on Windows won't work.
"""
from openpype.version import __version__
banner = "OpenPype {}\nPython {} on {}".format(
__version__, sys.version, sys.platform
)
banner = f"OpenPype {__version__}\nPython {sys.version} on {sys.platform}"
code.interact(banner)

View file

@ -1368,6 +1368,7 @@ def get_app_environments_for_context(
from openpype.modules import ModulesManager
from openpype.pipeline import AvalonMongoDB, Anatomy
from openpype.lib.openpype_version import is_running_staging
# Avalon database connection
dbcon = AvalonMongoDB()
@ -1404,6 +1405,8 @@ def get_app_environments_for_context(
"env": env
})
data["env"].update(anatomy.root_environments())
if is_running_staging():
data["env"]["OPENPYPE_IS_STAGING"] = "1"
prepare_app_environments(data, env_group, modules_manager)
prepare_context_environments(data, env_group, modules_manager)

View file

@ -57,15 +57,66 @@ def is_running_from_build():
return True
def is_staging_enabled():
return os.environ.get("OPENPYPE_USE_STAGING") == "1"
def is_running_staging():
"""Currently used OpenPype is staging version.
This function is not 100% proper check of staging version. It is possible
to have enabled to use staging version but be in different one.
The function is based on 4 factors:
- env 'OPENPYPE_IS_STAGING' is set
- current production version
- current staging version
- use staging is enabled
First checks for 'OPENPYPE_IS_STAGING' environment which can be set to '1'.
The value should be set only when a process without access to
OpenPypeVersion is launched (e.g. in DCCs). If current version is same
as production version it is expected that it is not staging, and it
doesn't matter what would 'is_staging_enabled' return. If current version
is same as staging version it is expected we're in staging. In all other
cases 'is_staging_enabled' is used as source of outpu value.
The function is used to decide which icon is used. To check e.g. updates
the output should be combined with other functions from this file.
Returns:
bool: True if openpype version containt 'staging'.
bool: Using staging version or not.
"""
if "staging" in get_openpype_version():
if os.environ.get("OPENPYPE_IS_STAGING") == "1":
return True
return False
if not op_version_control_available():
return False
from openpype.settings import get_global_settings
global_settings = get_global_settings()
production_version = global_settings["production_version"]
latest_version = None
if not production_version or production_version == "latest":
latest_version = get_latest_version(local=False, remote=True)
production_version = latest_version
current_version = get_openpype_version()
if current_version == production_version:
return False
staging_version = global_settings["staging_version"]
if not staging_version or staging_version == "latest":
if latest_version is None:
latest_version = get_latest_version(local=False, remote=True)
staging_version = latest_version
if current_version == production_version:
return True
return is_staging_enabled()
# ----------------------------------------
@ -131,13 +182,11 @@ def get_remote_versions(*args, **kwargs):
return None
def get_latest_version(staging=None, local=None, remote=None):
def get_latest_version(local=None, remote=None):
"""Get latest version from repository path."""
if staging is None:
staging = is_running_staging()
if op_version_control_available():
return get_OpenPypeVersion().get_latest_version(
staging=staging,
local=local,
remote=remote
)
@ -146,9 +195,9 @@ def get_latest_version(staging=None, local=None, remote=None):
def get_expected_studio_version(staging=None):
"""Expected production or staging version in studio."""
if staging is None:
staging = is_running_staging()
if op_version_control_available():
if staging is None:
staging = is_staging_enabled()
return get_OpenPypeVersion().get_expected_studio_version(staging)
return None
@ -158,7 +207,7 @@ def get_expected_version(staging=None):
if expected_version is None:
# Look for latest if expected version is not set in settings
expected_version = get_latest_version(
staging=staging,
local=False,
remote=True
)
return expected_version

View file

@ -39,15 +39,21 @@ def get_liberation_font_path(bold=False, italic=False):
return font_path
def get_openpype_production_icon_filepath():
return get_resource("icons", "openpype_icon.png")
def get_openpype_staging_icon_filepath():
return get_resource("icons", "openpype_icon_staging.png")
def get_openpype_icon_filepath(staging=None):
if staging is None:
staging = is_running_staging()
if staging:
icon_file_name = "openpype_icon_staging.png"
else:
icon_file_name = "openpype_icon.png"
return get_resource("icons", icon_file_name)
return get_openpype_staging_icon_filepath()
return get_openpype_production_icon_filepath()
def get_openpype_splash_filepath(staging=None):

View file

@ -18,11 +18,12 @@ from .exceptions import (
)
from .lib import (
get_general_environments,
get_global_settings,
get_system_settings,
get_project_settings,
get_current_project_settings,
get_anatomy_settings,
get_local_settings
get_local_settings,
)
from .entities import (
SystemSettings,
@ -49,6 +50,7 @@ __all__ = (
"SaveWarningExc",
"get_general_environments",
"get_global_settings",
"get_system_settings",
"get_project_settings",
"get_current_project_settings",

View file

@ -123,10 +123,7 @@ from .dict_conditional import (
)
from .anatomy_entities import AnatomyEntity
from .op_version_entity import (
ProductionVersionsInputEntity,
StagingVersionsInputEntity
)
from .op_version_entity import VersionsInputEntity
__all__ = (
"DefaultsNotDefined",
@ -188,6 +185,5 @@ __all__ = (
"AnatomyEntity",
"ProductionVersionsInputEntity",
"StagingVersionsInputEntity"
"VersionsInputEntity",
)

View file

@ -66,24 +66,13 @@ class OpenPypeVersionInput(TextEntity):
return super(OpenPypeVersionInput, self).convert_to_valid_type(value)
class ProductionVersionsInputEntity(OpenPypeVersionInput):
class VersionsInputEntity(OpenPypeVersionInput):
"""Entity meant only for global settings to define production version."""
schema_types = ["production-versions-text"]
schema_types = ["versions-text"]
def _get_openpype_versions(self):
versions = get_remote_versions(staging=False, production=True)
versions = get_remote_versions()
if versions is None:
return []
versions.append(get_installed_version())
return sorted(versions)
class StagingVersionsInputEntity(OpenPypeVersionInput):
"""Entity meant only for global settings to define staging version."""
schema_types = ["staging-versions-text"]
def _get_openpype_versions(self):
versions = get_remote_versions(staging=True, production=False)
if versions is None:
return []
return sorted(versions)

View file

@ -146,12 +146,12 @@
"label": "Define explicit OpenPype version that should be used. Keep empty to use latest available version."
},
{
"type": "production-versions-text",
"type": "versions-text",
"key": "production_version",
"label": "Production version"
},
{
"type": "staging-versions-text",
"type": "versions-text",
"key": "staging_version",
"label": "Staging version"
},

View file

@ -181,7 +181,16 @@ class SettingsStateInfo:
@six.add_metaclass(ABCMeta)
class SettingsHandler:
class SettingsHandler(object):
global_keys = {
"openpype_path",
"admin_password",
"log_to_server",
"disk_mapping",
"production_version",
"staging_version"
}
@abstractmethod
def save_studio_settings(self, data):
"""Save studio overrides of system settings.
@ -328,6 +337,19 @@ class SettingsHandler:
"""
pass
@abstractmethod
def get_global_settings(self):
"""Studio global settings available across versions.
Output must contain all keys from 'global_keys'. If value is not set
the output value should be 'None'.
Returns:
Dict[str, Any]: Global settings same across versions.
"""
pass
# Clear methods - per version
# - clearing may be helpfull when a version settings were created for
# testing purposes
@ -566,19 +588,9 @@ class CacheValues:
class MongoSettingsHandler(SettingsHandler):
"""Settings handler that use mongo for storing and loading of settings."""
global_general_keys = (
"openpype_path",
"admin_password",
"log_to_server",
"disk_mapping",
"production_version",
"staging_version"
)
key_suffix = "_versioned"
_version_order_key = "versions_order"
_all_versions_keys = "all_versions"
_production_versions_key = "production_versions"
_staging_versions_key = "staging_versions"
def __init__(self):
# Get mongo connection
@ -605,6 +617,7 @@ class MongoSettingsHandler(SettingsHandler):
self.collection = settings_collection[database_name][collection_name]
self.global_settings_cache = CacheValues()
self.system_settings_cache = CacheValues()
self.project_settings_cache = collections.defaultdict(CacheValues)
self.project_anatomy_cache = collections.defaultdict(CacheValues)
@ -638,6 +651,23 @@ class MongoSettingsHandler(SettingsHandler):
self._prepare_project_settings_keys()
return self._attribute_keys
def get_global_settings_doc(self):
if self.global_settings_cache.is_outdated:
global_settings_doc = self.collection.find_one({
"type": GLOBAL_SETTINGS_KEY
}) or {}
self.global_settings_cache.update_data(global_settings_doc, None)
return self.global_settings_cache.data_copy()
def get_global_settings(self):
global_settings_doc = self.get_global_settings_doc()
global_settings = global_settings_doc.get("data", {})
return {
key: global_settings[key]
for key in self.global_keys
if key in global_settings
}
def _extract_global_settings(self, data):
"""Extract global settings data from system settings overrides.
@ -654,7 +684,7 @@ class MongoSettingsHandler(SettingsHandler):
general_data = data["general"]
# Add predefined keys to global settings if are set
for key in self.global_general_keys:
for key in self.global_keys:
if key not in general_data:
continue
# Pop key from values
@ -698,7 +728,7 @@ class MongoSettingsHandler(SettingsHandler):
# Check if data contain any key from predefined keys
any_key_found = False
if globals_data:
for key in self.global_general_keys:
for key in self.global_keys:
if key in globals_data:
any_key_found = True
break
@ -725,7 +755,7 @@ class MongoSettingsHandler(SettingsHandler):
system_settings_data["general"] = system_general
overridden_keys = system_general.get(M_OVERRIDDEN_KEY) or []
for key in self.global_general_keys:
for key in self.global_keys:
if key not in globals_data:
continue
@ -767,6 +797,10 @@ class MongoSettingsHandler(SettingsHandler):
global_settings = self._extract_global_settings(
system_settings_data
)
self.global_settings_cache.update_data(
global_settings,
None
)
system_settings_doc = self.collection.find_one(
{
@ -997,10 +1031,7 @@ class MongoSettingsHandler(SettingsHandler):
return
self._version_order_checked = True
from openpype.lib.openpype_version import (
get_OpenPypeVersion,
is_running_staging
)
from openpype.lib.openpype_version import get_OpenPypeVersion
OpenPypeVersion = get_OpenPypeVersion()
# Skip if 'OpenPypeVersion' is not available
@ -1012,25 +1043,11 @@ class MongoSettingsHandler(SettingsHandler):
if not doc:
doc = {"type": self._version_order_key}
if self._production_versions_key not in doc:
doc[self._production_versions_key] = []
if self._staging_versions_key not in doc:
doc[self._staging_versions_key] = []
if self._all_versions_keys not in doc:
doc[self._all_versions_keys] = []
if is_running_staging():
versions_key = self._staging_versions_key
else:
versions_key = self._production_versions_key
# Skip if current version is already available
if (
self._current_version in doc[self._all_versions_keys]
and self._current_version in doc[versions_key]
):
if self._current_version in doc[self._all_versions_keys]:
return
if self._current_version not in doc[self._all_versions_keys]:
@ -1047,18 +1064,6 @@ class MongoSettingsHandler(SettingsHandler):
str(version) for version in sorted(all_objected_versions)
]
if self._current_version not in doc[versions_key]:
objected_versions = [
OpenPypeVersion(version=self._current_version)
]
for version_str in doc[versions_key]:
objected_versions.append(OpenPypeVersion(version=version_str))
# Update versions list and push changes to Mongo
doc[versions_key] = [
str(version) for version in sorted(objected_versions)
]
self.collection.replace_one(
{"type": self._version_order_key},
doc,
@ -1298,9 +1303,7 @@ class MongoSettingsHandler(SettingsHandler):
def get_studio_system_settings_overrides(self, return_version):
"""Studio overrides of system settings."""
if self.system_settings_cache.is_outdated:
globals_document = self.collection.find_one({
"type": GLOBAL_SETTINGS_KEY
})
globals_document = self.get_global_settings_doc()
document, version = self._get_system_settings_overrides_doc()
last_saved_info = SettingsStateInfo.from_document(

View file

@ -1040,6 +1040,17 @@ def get_current_project_settings():
return get_project_settings(project_name)
@require_handler
def get_global_settings():
default_settings = load_openpype_default_settings()
default_values = default_settings["system_settings"]["general"]
studio_values = _SETTINGS_HANDLER.get_global_settings()
return {
key: studio_values.get(key, default_values.get(key))
for key in _SETTINGS_HANDLER.global_keys
}
def get_general_environments():
"""Get general environments.

View file

@ -2,7 +2,6 @@ import collections
import os
import sys
import atexit
import subprocess
import platform
@ -11,8 +10,9 @@ from Qt import QtCore, QtGui, QtWidgets
import openpype.version
from openpype import resources, style
from openpype.lib import (
get_openpype_execute_args,
Logger,
get_openpype_execute_args,
run_detached_process,
)
from openpype.lib.openpype_version import (
op_version_control_available,
@ -21,8 +21,9 @@ from openpype.lib.openpype_version import (
is_current_version_studio_latest,
is_current_version_higher_than_expected,
is_running_from_build,
is_running_staging,
get_openpype_version,
is_running_staging,
is_staging_enabled,
)
from openpype.modules import TrayModulesManager
from openpype.settings import (
@ -202,6 +203,68 @@ class VersionUpdateDialog(QtWidgets.QDialog):
self.accept()
class ProductionStagingDialog(QtWidgets.QDialog):
"""Tell user that he has enabled staging but is in production version.
This is showed only when staging is enabled with '--use-staging' and it's
version is the same as production's version.
"""
def __init__(self, parent=None):
super(ProductionStagingDialog, self).__init__(parent)
icon = QtGui.QIcon(resources.get_openpype_icon_filepath())
self.setWindowIcon(icon)
self.setWindowTitle("Production and Staging versions are the same")
self.setWindowFlags(
self.windowFlags()
| QtCore.Qt.WindowStaysOnTopHint
)
top_widget = QtWidgets.QWidget(self)
staging_pixmap = QtGui.QPixmap(
resources.get_openpype_staging_icon_filepath()
)
staging_icon_label = PixmapLabel(staging_pixmap, top_widget)
message = (
"Because production and staging versions are the same"
" your changes and work will affect both."
)
content_label = QtWidgets.QLabel(message, self)
content_label.setWordWrap(True)
top_layout = QtWidgets.QHBoxLayout(top_widget)
top_layout.setContentsMargins(0, 0, 0, 0)
top_layout.setSpacing(10)
top_layout.addWidget(
staging_icon_label, 0,
QtCore.Qt.AlignTop | QtCore.Qt.AlignHCenter
)
top_layout.addWidget(content_label, 1)
footer_widget = QtWidgets.QWidget(self)
ok_btn = QtWidgets.QPushButton("I understand", footer_widget)
footer_layout = QtWidgets.QHBoxLayout(footer_widget)
footer_layout.setContentsMargins(0, 0, 0, 0)
footer_layout.addStretch(1)
footer_layout.addWidget(ok_btn)
main_layout = QtWidgets.QVBoxLayout(self)
main_layout.addWidget(top_widget, 0)
main_layout.addStretch(1)
main_layout.addWidget(footer_widget, 0)
self.setStyleSheet(style.load_stylesheet())
self.resize(400, 140)
ok_btn.clicked.connect(self._on_ok_clicked)
def _on_ok_clicked(self):
self.close()
class BuildVersionDialog(QtWidgets.QDialog):
"""Build/Installation version is too low for current OpenPype version.
@ -462,6 +525,10 @@ class TrayManager:
dialog = BuildVersionDialog()
dialog.exec_()
elif is_staging_enabled() and not is_running_staging():
dialog = ProductionStagingDialog()
dialog.exec_()
def _validate_settings_defaults(self):
valid = True
try:
@ -562,9 +629,7 @@ class TrayManager:
logic will decide which version will be used.
"""
args = get_openpype_execute_args()
kwargs = {
"env": dict(os.environ.items())
}
envs = dict(os.environ.items())
# Create a copy of sys.argv
additional_args = list(sys.argv)
@ -573,31 +638,33 @@ class TrayManager:
if args[-1] == additional_args[0]:
additional_args.pop(0)
cleanup_additional_args = False
if use_expected_version:
cleanup_additional_args = True
expected_version = get_expected_version()
if expected_version is not None:
reset_version = False
kwargs["env"]["OPENPYPE_VERSION"] = str(expected_version)
envs["OPENPYPE_VERSION"] = str(expected_version)
else:
# Trigger reset of version if expected version was not found
reset_version = True
# Pop OPENPYPE_VERSION
if reset_version:
# Add staging flag if was running from staging
if is_running_staging():
args.append("--use-staging")
kwargs["env"].pop("OPENPYPE_VERSION", None)
cleanup_additional_args = True
envs.pop("OPENPYPE_VERSION", None)
if cleanup_additional_args:
_additional_args = []
for arg in additional_args:
if arg == "--use-staging" or arg.startswith("--use-version"):
continue
_additional_args.append(arg)
additional_args = _additional_args
args.extend(additional_args)
if platform.system().lower() == "windows":
flags = (
subprocess.CREATE_NEW_PROCESS_GROUP
| subprocess.DETACHED_PROCESS
)
kwargs["creationflags"] = flags
subprocess.Popen(args, **kwargs)
run_detached_process(args, env=envs)
self.exit()
def exit(self):

117
start.py
View file

@ -242,6 +242,9 @@ if "--debug" in sys.argv:
sys.argv.remove("--debug")
os.environ["OPENPYPE_DEBUG"] = "1"
if "--use-staging" in sys.argv:
sys.argv.remove("--use-staging")
os.environ["OPENPYPE_USE_STAGING"] = "1"
import igniter # noqa: E402
from igniter import BootstrapRepos # noqa: E402
@ -484,7 +487,6 @@ def _process_arguments() -> tuple:
"""
# check for `--use-version=3.0.0` argument and `--use-staging`
use_version = None
use_staging = False
commands = []
# OpenPype version specification through arguments
@ -516,8 +518,6 @@ def _process_arguments() -> tuple:
if m and m.group('version'):
use_version = m.group('version')
_print(f">>> Requested version [ {use_version} ]")
if "+staging" in use_version:
use_staging = True
break
if use_version is None:
@ -544,10 +544,6 @@ def _process_arguments() -> tuple:
" proper version string."))
sys.exit(1)
if "--use-staging" in sys.argv:
use_staging = True
sys.argv.remove("--use-staging")
if "--list-versions" in sys.argv:
commands.append("print_versions")
sys.argv.remove("--list-versions")
@ -570,7 +566,7 @@ def _process_arguments() -> tuple:
sys.argv.pop(idx)
sys.argv.insert(idx, "tray")
return use_version, use_staging, commands
return use_version, commands
def _determine_mongodb() -> str:
@ -682,8 +678,7 @@ def _find_frozen_openpype(use_version: str = None,
Path: Path to version to be used.
Raises:
RuntimeError: If no OpenPype version are found or no staging version
(if requested).
RuntimeError: If no OpenPype version are found.
"""
# Collect OpenPype versions
@ -698,13 +693,10 @@ def _find_frozen_openpype(use_version: str = None,
if use_version.lower() == "latest":
# Version says to use latest version
_print(">>> Finding latest version defined by use version")
openpype_version = bootstrap.find_latest_openpype_version(
use_staging)
openpype_version = bootstrap.find_latest_openpype_version()
else:
_print(f">>> Finding specified version \"{use_version}\"")
openpype_version = bootstrap.find_openpype_version(
use_version, use_staging
)
openpype_version = bootstrap.find_openpype_version(use_version)
if openpype_version is None:
raise OpenPypeVersionNotFound(
@ -714,8 +706,7 @@ def _find_frozen_openpype(use_version: str = None,
elif studio_version is not None:
# Studio has defined a version to use
_print(f">>> Finding studio version \"{studio_version}\"")
openpype_version = bootstrap.find_openpype_version(
studio_version, use_staging)
openpype_version = bootstrap.find_openpype_version(studio_version)
if openpype_version is None:
raise OpenPypeVersionNotFound((
"Requested OpenPype version "
@ -728,20 +719,15 @@ def _find_frozen_openpype(use_version: str = None,
_print((
">>> Finding latest version "
f"with [ {installed_version} ]"))
openpype_version = bootstrap.find_latest_openpype_version(
use_staging)
openpype_version = bootstrap.find_latest_openpype_version()
if openpype_version is None:
if use_staging:
reason = "Didn't find any staging versions."
else:
reason = "Didn't find any versions."
raise OpenPypeVersionNotFound(reason)
raise OpenPypeVersionNotFound("Didn't find any versions.")
# get local frozen version and add it to detected version so if it is
# newer it will be used instead.
if installed_version == openpype_version:
version_path = _bootstrap_from_code(use_version, use_staging)
version_path = _bootstrap_from_code(use_version)
openpype_version = OpenPypeVersion(
version=BootstrapRepos.get_version(version_path),
path=version_path)
@ -805,8 +791,8 @@ def _find_frozen_openpype(use_version: str = None,
return openpype_version.path
def _bootstrap_from_code(use_version, use_staging):
"""Bootstrap live code (or the one coming with frozen OpenPype.
def _bootstrap_from_code(use_version):
"""Bootstrap live code (or the one coming with frozen OpenPype).
Args:
use_version: (str): specific version to use.
@ -829,33 +815,25 @@ def _bootstrap_from_code(use_version, use_staging):
local_version = bootstrap.get_version(Path(_openpype_root))
switch_str = f" - will switch to {use_version}" if use_version and use_version != local_version else "" # noqa
_print(f" - booting version: {local_version}{switch_str}")
assert local_version
if not local_version:
raise OpenPypeVersionNotFound(
f"Cannot find version at {_openpype_root}")
else:
# get current version of OpenPype
local_version = OpenPypeVersion.get_installed_version_str()
# All cases when should be used different version than build
if (use_version and use_version != local_version) or use_staging:
if use_version and use_version != local_version:
if use_version:
# Explicit version should be used
version_to_use = bootstrap.find_openpype_version(
use_version, use_staging
)
version_to_use = bootstrap.find_openpype_version(use_version)
if version_to_use is None:
raise OpenPypeVersionIncompatible(
f"Requested version \"{use_version}\" was not found.")
else:
# Staging version should be used
version_to_use = bootstrap.find_latest_openpype_version(
use_staging
)
version_to_use = bootstrap.find_latest_openpype_version()
if version_to_use is None:
if use_staging:
reason = "Didn't find any staging versions."
else:
# This reason is backup for possible bug in code
reason = "Didn't find any versions."
raise OpenPypeVersionNotFound(reason)
raise OpenPypeVersionNotFound("Didn't find any versions.")
# Start extraction of version if needed
if version_to_use.path.is_file():
@ -913,10 +891,7 @@ def _bootstrap_from_code(use_version, use_staging):
def _boot_validate_versions(use_version, local_version):
_print(f">>> Validating version [ {use_version} ]")
openpype_versions = bootstrap.find_openpype(include_zips=True,
staging=True)
openpype_versions += bootstrap.find_openpype(include_zips=True,
staging=False)
openpype_versions = bootstrap.find_openpype(include_zips=True)
v: OpenPypeVersion
found = [v for v in openpype_versions if str(v) == use_version]
if not found:
@ -932,14 +907,7 @@ def _boot_validate_versions(use_version, local_version):
_print(f'{">>> " if valid else "!!! "}{message}')
def _boot_print_versions(use_staging, local_version, openpype_root):
if not use_staging:
_print("--- This will list only non-staging versions detected.")
_print(" To see staging versions, use --use-staging argument.")
else:
_print("--- This will list only staging versions detected.")
_print(" To see other version, omit --use-staging argument.")
def _boot_print_versions(openpype_root):
if getattr(sys, 'frozen', False):
local_version = bootstrap.get_version(Path(openpype_root))
else:
@ -947,16 +915,12 @@ def _boot_print_versions(use_staging, local_version, openpype_root):
compatible_with = OpenPypeVersion(version=local_version)
if "--all" in sys.argv:
compatible_with = None
_print("--- Showing all version (even those not compatible).")
else:
_print(("--- Showing only compatible versions "
f"with [ {compatible_with.major}.{compatible_with.minor} ]"))
openpype_versions = bootstrap.find_openpype(
include_zips=True,
staging=use_staging,
)
openpype_versions = bootstrap.find_openpype(include_zips=True)
openpype_versions = [
version for version in openpype_versions
if version.is_compatible(
@ -966,12 +930,11 @@ def _boot_print_versions(use_staging, local_version, openpype_root):
list_versions(openpype_versions, local_version)
def _boot_handle_missing_version(local_version, use_staging, message):
def _boot_handle_missing_version(local_version, message):
_print(message)
if os.environ.get("OPENPYPE_HEADLESS_MODE") == "1":
openpype_versions = bootstrap.find_openpype(
include_zips=True, staging=use_staging
)
include_zips=True)
list_versions(openpype_versions, local_version)
else:
igniter.show_message_dialog("Version not found", message)
@ -997,7 +960,8 @@ def boot():
# Process arguments
# ------------------------------------------------------------------------
use_version, use_staging, commands = _process_arguments()
use_version, commands = _process_arguments()
use_staging = os.environ.get("OPENPYPE_USE_STAGING") == "1"
if os.getenv("OPENPYPE_VERSION"):
if use_version:
@ -1005,7 +969,6 @@ def boot():
"is overridden by command line argument."))
else:
_print(">>> version set by environment variable")
use_staging = "staging" in os.getenv("OPENPYPE_VERSION")
use_version = os.getenv("OPENPYPE_VERSION")
# ------------------------------------------------------------------------
@ -1059,7 +1022,7 @@ def boot():
os.environ["OPENPYPE_PATH"] = openpype_path
if "print_versions" in commands:
_boot_print_versions(use_staging, local_version, OPENPYPE_ROOT)
_boot_print_versions(OPENPYPE_ROOT)
sys.exit(1)
# ------------------------------------------------------------------------
@ -1072,7 +1035,7 @@ def boot():
try:
version_path = _find_frozen_openpype(use_version, use_staging)
except OpenPypeVersionNotFound as exc:
_boot_handle_missing_version(local_version, use_staging, str(exc))
_boot_handle_missing_version(local_version, str(exc))
sys.exit(1)
except RuntimeError as e:
@ -1088,10 +1051,10 @@ def boot():
_print("--- version is valid")
else:
try:
version_path = _bootstrap_from_code(use_version, use_staging)
version_path = _bootstrap_from_code(use_version)
except OpenPypeVersionNotFound as exc:
_boot_handle_missing_version(local_version, use_staging, str(exc))
_boot_handle_missing_version(local_version, str(exc))
sys.exit(1)
# set this to point either to `python` from venv in case of live code
@ -1172,10 +1135,10 @@ def get_info(use_staging=None) -> list:
inf.append(("OpenPype variant", "staging"))
else:
inf.append(("OpenPype variant", "production"))
inf.append(
("Running OpenPype from", os.environ.get('OPENPYPE_REPOS_ROOT'))
inf.extend([
("Running OpenPype from", os.environ.get('OPENPYPE_REPOS_ROOT')),
("Using mongodb", components["host"])]
)
inf.append(("Using mongodb", components["host"]))
if os.environ.get("FTRACK_SERVER"):
inf.append(("Using FTrack at",
@ -1194,11 +1157,13 @@ def get_info(use_staging=None) -> list:
mongo_components = get_default_components()
if mongo_components["host"]:
inf.append(("Logging to MongoDB", mongo_components["host"]))
inf.append((" - port", mongo_components["port"] or "<N/A>"))
inf.append((" - database", Logger.log_database_name))
inf.append((" - collection", Logger.log_collection_name))
inf.append((" - user", mongo_components["username"] or "<N/A>"))
inf.extend([
("Logging to MongoDB", mongo_components["host"]),
(" - port", mongo_components["port"] or "<N/A>"),
(" - database", Logger.log_database_name),
(" - collection", Logger.log_collection_name),
(" - user", mongo_components["username"] or "<N/A>")
])
if mongo_components["auth_db"]:
inf.append((" - auth source", mongo_components["auth_db"]))

View file

@ -33,11 +33,11 @@ def test_openpype_version(printer):
assert str(v2) == "1.2.3-x"
assert v1 > v2
v3 = OpenPypeVersion(1, 2, 3, staging=True)
assert str(v3) == "1.2.3+staging"
v3 = OpenPypeVersion(1, 2, 3)
assert str(v3) == "1.2.3"
v4 = OpenPypeVersion(1, 2, 3, staging="True", prerelease="rc.1")
assert str(v4) == "1.2.3-rc.1+staging"
v4 = OpenPypeVersion(1, 2, 3, prerelease="rc.1")
assert str(v4) == "1.2.3-rc.1"
assert v3 > v4
assert v1 > v4
assert v4 < OpenPypeVersion(1, 2, 3, prerelease="rc.1")
@ -73,7 +73,7 @@ def test_openpype_version(printer):
OpenPypeVersion(4, 8, 10),
OpenPypeVersion(4, 8, 20),
OpenPypeVersion(4, 8, 9),
OpenPypeVersion(1, 2, 3, staging=True),
OpenPypeVersion(1, 2, 3),
OpenPypeVersion(1, 2, 3, build="foo")
]
res = sorted(sort_versions)
@ -104,27 +104,26 @@ def test_openpype_version(printer):
with pytest.raises(ValueError):
_ = OpenPypeVersion(version="booobaa")
v11 = OpenPypeVersion(version="4.6.7-foo+staging")
v11 = OpenPypeVersion(version="4.6.7-foo")
assert v11.major == 4
assert v11.minor == 6
assert v11.patch == 7
assert v11.staging is True
assert v11.prerelease == "foo"
def test_get_main_version():
ver = OpenPypeVersion(1, 2, 3, staging=True, prerelease="foo")
ver = OpenPypeVersion(1, 2, 3, prerelease="foo")
assert ver.get_main_version() == "1.2.3"
def test_get_version_path_from_list():
versions = [
OpenPypeVersion(1, 2, 3, path=Path('/foo/bar')),
OpenPypeVersion(3, 4, 5, staging=True, path=Path("/bar/baz")),
OpenPypeVersion(3, 4, 5, path=Path("/bar/baz")),
OpenPypeVersion(6, 7, 8, prerelease="x", path=Path("boo/goo"))
]
path = BootstrapRepos.get_version_path_from_list(
"3.4.5+staging", versions)
"3.4.5", versions)
assert path == Path("/bar/baz")
@ -362,12 +361,15 @@ def test_find_openpype(fix_bootstrap, tmp_path_factory, monkeypatch, printer):
result = fix_bootstrap.find_openpype(include_zips=True)
# we should have results as file were created
assert result is not None, "no OpenPype version found"
# latest item in `result` should be latest version found.
# latest item in `result` should be the latest version found.
# this will be `7.2.10-foo+staging` even with *staging* in since we've
# dropped the logic to handle staging separately and in alphabetical
# sorting it is after `strange`.
expected_path = Path(
d_path / "{}{}{}".format(
test_versions_2[3].prefix,
test_versions_2[3].version,
test_versions_2[3].suffix
test_versions_2[4].prefix,
test_versions_2[4].version,
test_versions_2[4].suffix
)
)
assert result, "nothing found"

View file

@ -54,14 +54,10 @@ The default locations are:
### Staging vs. Production
You can have version of OpenPype with experimental features you want to try somewhere but you
don't want to disrupt your production. You can tag version as **staging** simply by appending `+staging`
to its name.
You can have version of OpenPype with experimental features you want to try somewhere, but you
don't want to disrupt your production. You can set such version in th Settings.
So if you have OpenPype version like `OpenPype-v3.0.0.zip` just name it `OpenPype-v3.0.0+staging.zip`.
When both these versions are present, production one will always take precedence over staging.
You can run OpenPype with `--use-staging` argument to add use staging versions.
You can run OpenPype with `--use-staging` argument to use staging version specified in the Settings.
:::note
Running staging version is identified by orange **P** icon in system tray.

View file

@ -22,7 +22,7 @@ openpype_console --use-version=3.0.0-foo+bar
`--use-staging` - to use staging versions of OpenPype.
`--list-versions [--use-staging]` - to list available versions.
`--list-versions` - to list available versions.
`--validate-version` - to validate integrity of given version

View file

@ -43,8 +43,7 @@ You can use following command line arguments:
openpype_console --use-version=3.0.1
```
`--use-staging` - to specify you prefer staging version. In that case it will be used
(if found) instead of production one.
`--use-staging` - to specify you prefer staging version. In that case it will be used instead of production one.
:::tip List available versions
To list all available versions, use:
@ -52,8 +51,6 @@ To list all available versions, use:
```shell
openpype_console --list-versions
```
You can add `--use-staging` to list staging versions.
:::
If you want to validate integrity of some available version, you can use: