Skip to content

Commit

Permalink
Merge pull request #61 from ynput/feature/OP-6631_Launcher-in-dev-mode
Browse files Browse the repository at this point in the history
Develop mode
  • Loading branch information
iLLiCiTiT authored Oct 19, 2023
2 parents 6221aab + 88a2a30 commit 97793f8
Show file tree
Hide file tree
Showing 10 changed files with 412 additions and 52 deletions.
12 changes: 9 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ AYON Launcher - Desktop application
Introduction
------------

Desktop application launcher for AYON pipeline. You need AYON launcher to be able to interact with any of the integrated applications. it acts as the main entry point into the pipeline for all artists publishing and loading data with AYON. Even though AYON launcher is a standalone destkop application, it doesn't do anything until it's connected to an AYON server instance.
Desktop application launcher for AYON pipeline. You need AYON launcher to be able to interact with any of the integrated applications. It acts as the main entry point into the pipeline for all artists publishing and loading data with AYON. Even though AYON launcher is a standalone desktop application, it doesn't do anything until it's connected to an AYON server instance.

The documentation is not up-to-date as development is still in progress and code is changing every day.
The main purpose of application is to distribute updates based on current server state and to start main logic of core addon. At this moment core addon is `openpype` (this will change in near future).

To get all the information about the project, go to [AYON.io](https://ayon.ynput.io)

Expand Down Expand Up @@ -80,7 +80,8 @@ There are reserver global arguments that cannot be used in any cli handling:
- `--verbose <LOG LEVEL>` - Change logging level to one of the following: DEBUG, INFO, WARNING, ERROR, CRITICAL.
- `--debug` - Simplified way how to change verbose to DEBUG. Also sets `AYON_DEBUG` environment variable to `1`.
- `--skip-headers` - Skip headers in the console output.
- `--use-staging` - Use staging settings, and use staging bundle, if bundle is not explicitly defined.
- `--use-dev` - Use dev bundle and settings, if bundle is not explicitly defined.
- `--use-staging` - Use staging settings, and use staging bundle, if bundle is not explicitly defined. Cannot be combined with staging.
- `--headless` - Tell AYON to run in headless mode. No UIs are shown during bootstrap. Affects `AYON_HEADLESS_MODE` environment variable. Custom logic must handle headless mode on own.
- `--skip-bootstrap` - Skip bootstrap process. Used for inner logic of distribution.

Expand All @@ -91,6 +92,7 @@ Environment variables that are set during startup:
- **AYON_LOG_LEVEL** - Log level that is used.
- **AYON_DEBUG** - Debug flag enabled when set to '1'.
- **AYON_USE_STAGING** - Use staging settings when set to '1'.
- **AYON_USE_DEV** - Use dev mode settings when set to '1'.
- **AYON_HEADLESS_MODE** - Headless mode flag enabled when set to '1'.
- **AYON_EXECUTABLE** - Path to executable that is used to run AYON.
- **AYON_ROOT** - Root to AYON launcher content.
Expand All @@ -110,3 +112,7 @@ Environment variables that are set for backwards compatibility with openpype add
- **OPENPYPE_ROOT** - Alias to **AYON_ROOT**.
- **OPENPYPE_REPOS_ROOT** - Alias to **AYON_ROOT**.
- **AVALON_LABEL** - Alias to **AYON_MENU_LABEL**.


## Developer mode
Developer mode enables to skip standard distribution process and use local sources of addon code. This is useful for development of addon. Developer mode must be enabled and configured on AYON server. To use it in AYON launcher create dev bundle and use `--use-dev` argument, or define bundle name `--bundle <dev bundle name>` in cli arguments.
2 changes: 2 additions & 0 deletions common/ayon_common/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
IS_BUILT_APPLICATION,
HEADLESS_MODE_ENABLED,
is_staging_enabled,
is_dev_mode_enabled,
get_local_site_id,
get_ayon_appdirs,
get_ayon_launch_args,
Expand All @@ -17,6 +18,7 @@
"IS_BUILT_APPLICATION",
"HEADLESS_MODE_ENABLED",
"is_staging_enabled",
"is_dev_mode_enabled",
"get_local_site_id",
"get_ayon_appdirs",
"get_ayon_launch_args",
Expand Down
13 changes: 5 additions & 8 deletions common/ayon_common/connection/credentials.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
get_local_site_id,
get_ayon_launch_args,
is_staging_enabled,
is_dev_mode_enabled,
)


Expand Down Expand Up @@ -450,21 +451,18 @@ def set_environments(url: str, token: str):
def create_global_connection():
"""Create global connection with site id and AYON launcher version.
Make sure the global connection in 'ayon_api' have entered site id and
AYON launcher version.
Make sure this function is called once during process runtime.
Set default settings variant to use based on 'is_staging_enabled'.
The global connection in 'ayon_api' have entered site id and
AYON launcher version.
"""

ayon_api.create_connection(
get_local_site_id(), os.environ.get("AYON_VERSION")
)
ayon_api.set_default_settings_variant(
"staging" if is_staging_enabled() else "production"
)


def is_token_valid(url, token):
def is_token_valid(url, token) -> bool:
"""Check if token is valid.
Note:
Expand Down Expand Up @@ -529,4 +527,3 @@ def confirm_server_login(url, token, username):
add_server(url, username)
store_token(url, token)
set_environments(url, token)
create_global_connection()
155 changes: 127 additions & 28 deletions common/ayon_common/distribution/control.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
HEADLESS_MODE_ENABLED,
extract_archive_file,
is_staging_enabled,
is_dev_mode_enabled,
get_executables_info_by_version,
get_downloads_dir,
)
Expand Down Expand Up @@ -895,7 +896,8 @@ class AyonDistribution:
value.
use_staging (Optional[bool]): Use staging versions of an addon.
If not passed, 'is_staging_enabled' is used as default value.
checked for value '1'.
use_dev (Optional[bool]): Use develop versions of an addon.
If not passed, 'is_dev_mode_enabled' is used as default value.
skip_installer_dist (Optional[bool]): Skip installer distribution. This
is for testing purposes and for running from code.
"""
Expand All @@ -911,6 +913,8 @@ def __init__(
bundles_info=NOT_SET,
bundle_name=NOT_SET,
use_staging=None,
use_dev=None,
active_user=None,
skip_installer_dist=False,
):
self._log = None
Expand Down Expand Up @@ -962,14 +966,25 @@ def __init__(
self._production_bundle = NOT_SET
# Bundle that should be used in staging
self._staging_bundle = NOT_SET
# Bundle that should be used in dev
self._dev_bundle = NOT_SET
# Boolean that defines if staging bundle should be used
self._use_staging = use_staging
self._use_dev = use_dev
self._active_user = active_user

# Specific bundle name should be used
self._bundle_name = bundle_name
# Final bundle that will be used
self._bundle = NOT_SET

@property
def active_user(self):
if self._active_user is None:
user = ayon_api.get_user()
self._active_user = user["name"]
return self._active_user

@property
def use_staging(self):
"""Staging version of a bundle should be used.
Expand All @@ -983,8 +998,40 @@ def use_staging(self):

if self._use_staging is None:
self._use_staging = is_staging_enabled()

if self._use_staging and self.use_dev:
self._use_staging = False
return self._use_staging

@property
def use_dev(self):
"""Develop version of a bundle should be used.
This value is completely ignored if specific bundle name should
be used.
Returns:
bool: True if staging version should be used.
"""

if self._use_dev is None:
if self._bundle_name is NOT_SET:
self._use_dev = is_dev_mode_enabled()
else:
bundle = next(
(
bundle
for bundle in self.bundle_items
if bundle.name == self._bundle_name
),
None
)
if bundle is not None:
self._use_dev = bundle.is_dev
else:
self._use_dev = False
return self._use_dev

@property
def log(self):
"""Helper to access logger.
Expand Down Expand Up @@ -1023,39 +1070,42 @@ def bundle_items(self):
]
return self._bundle_items

def _prepare_production_staging_bundles(self):
production_bundle = None
staging_bundle = None
for bundle in self.bundle_items:
if bundle.is_production:
production_bundle = bundle
if bundle.is_staging:
staging_bundle = bundle
self._production_bundle = production_bundle
self._staging_bundle = staging_bundle

@property
def production_bundle(self):
"""
Returns:
Union[Bundle, None]: Bundle that should be used in production.
"""

if self._production_bundle is NOT_SET:
self._prepare_production_staging_bundles()
self._prepare_bundles()
return self._production_bundle

@property
def staging_bundle(self):
"""
Returns:
Union[Bundle, None]: Bundle that should be used in staging.
"""

if self._staging_bundle is NOT_SET:
self._prepare_production_staging_bundles()
self._prepare_bundles()
return self._staging_bundle

@property
def dev_bundle(self):
"""
Returns:
Union[Bundle, None]: Bundle that should be used in dev.
"""

if self._dev_bundle is NOT_SET:
self._prepare_bundles()
return self._dev_bundle

@property
def bundle_to_use(self):
"""Bundle that will be used for distribution.
Expand All @@ -1072,24 +1122,32 @@ def bundle_to_use(self):
but is not available on server.
"""

if self._bundle is NOT_SET:
if self._bundle_name is not NOT_SET:
bundle = next(
(
bundle
for bundle in self.bundle_items
if bundle.name == self._bundle_name
),
None
)
if bundle is None:
raise BundleNotFoundError(self._bundle_name)
if self._bundle is not NOT_SET:
return self._bundle

self._bundle = bundle
elif self.use_staging:
if self._bundle_name is NOT_SET:
if self.use_staging:
self._bundle = self.staging_bundle
elif self.use_dev:
self._bundle = self.dev_bundle
else:
self._bundle = self.production_bundle
return self._bundle

bundle = next(
(
bundle
for bundle in self.bundle_items
if bundle.name == self._bundle_name
),
None
)
if bundle is None:
raise BundleNotFoundError(self._bundle_name)

if bundle.is_dev:
self._use_dev = bundle.is_dev
self._bundle = bundle
return self._bundle

@property
Expand Down Expand Up @@ -1439,14 +1497,38 @@ def dependency_package_item(self):
self._dependency_package_item = dependency_package_item
return self._dependency_package_item

def _prepare_bundles(self):
production_bundle = None
staging_bundle = None
dev_bundle = None
for bundle in self.bundle_items:
if bundle.is_production:
production_bundle = bundle
if bundle.is_staging:
staging_bundle = bundle
if bundle.is_dev and bundle.active_dev_user == self.active_user:
dev_bundle = bundle
self._production_bundle = production_bundle
self._staging_bundle = staging_bundle
self._dev_bundle = dev_bundle

def _prepare_current_addon_dist_items(self):
addons_metadata = self.get_addons_metadata()
output = []
addon_versions = {}
dev_addons = {}
bundle = self.bundle_to_use
if bundle is not None:
dev_addons = bundle.addons_dev_info
addon_versions = bundle.addon_versions

for addon_name, addon_item in self.addon_items.items():
# Dev mode can redirect addon directory elsewhere
if self.use_dev:
dev_addon_info = dev_addons.get(addon_name) or {}
if dev_addon_info.get("enabled") is True:
continue

addon_version = addon_versions.get(addon_name)
# Addon is not in bundle -> Skip
if addon_version is None:
Expand Down Expand Up @@ -1873,6 +1955,8 @@ def get_python_paths(self):
if unzip_dirpath and os.path.exists(unzip_dirpath):
output.append(unzip_dirpath)

output.extend(self._get_dev_sys_paths())

dependency_dist_item = self.get_dependency_dist_item()
if dependency_dist_item is not None:
dependencies_dir = None
Expand All @@ -1884,6 +1968,21 @@ def get_python_paths(self):
output.append(dependencies_dir)
return output

def _get_dev_sys_paths(self):
output = []
if not self.use_dev:
return output
dev_addons = {}
bundle = self.bundle_to_use
if bundle is not None:
dev_addons = bundle.addons_dev_info
for addon_name, addon_item in self.addon_items.items():
dev_addon_info = dev_addons.get(addon_name) or {}
if dev_addon_info.get("enabled") is not True:
continue
output.append(dev_addon_info["path"])
return output


def cli(*args):
raise NotImplementedError
6 changes: 6 additions & 0 deletions common/ayon_common/distribution/data_structures.py
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,9 @@ class Bundle:
dependency_packages = attr.ib(default=attr.Factory(dict))
is_production = attr.ib(default=False)
is_staging = attr.ib(default=False)
is_dev = attr.ib(default=False)
active_dev_user = attr.ib(default=None)
addons_dev_info = attr.ib(default=attr.Factory(dict))

@classmethod
def from_dict(cls, data):
Expand All @@ -292,4 +295,7 @@ def from_dict(cls, data):
dependency_packages=data.get("dependencyPackages", {}),
is_production=data["isProduction"],
is_staging=data["isStaging"],
is_dev=data.get("isDev", False),
active_dev_user=data.get("activeUser"),
addons_dev_info=data.get("addonDevelopment", {}),
)
Loading

0 comments on commit 97793f8

Please sign in to comment.