diff --git a/openpype/cli.py b/openpype/cli.py index 1065152adb..e56a572c9c 100644 --- a/openpype/cli.py +++ b/openpype/cli.py @@ -96,11 +96,16 @@ def eventserver(debug, @main.command() @click.option("-d", "--debug", is_flag=True, help="Print debug messages") -def webpublisherwebserver(debug): +@click.option("-e", "--executable", help="Executable") +@click.option("-u", "--upload_dir", help="Upload dir") +def webpublisherwebserver(debug, executable, upload_dir): if debug: os.environ['OPENPYPE_DEBUG'] = "3" - PypeCommands().launch_webpublisher_webservercli() + PypeCommands().launch_webpublisher_webservercli( + upload_dir=upload_dir, + executable=executable + ) @main.command() @@ -140,6 +145,24 @@ def publish(debug, paths, targets): PypeCommands.publish(list(paths), targets) +@main.command() +@click.argument("paths", nargs=-1) +@click.option("-d", "--debug", is_flag=True, help="Print debug messages") +@click.option("-h", "--host", help="Host") +@click.option("-p", "--project", help="Project") +@click.option("-t", "--targets", help="Targets module", default=None, + multiple=True) +def remotepublish(debug, project, paths, host, targets=None): + """Start CLI publishing. + + Publish collects json from paths provided as an argument. + More than one path is allowed. + """ + if debug: + os.environ['OPENPYPE_DEBUG'] = '3' + PypeCommands.remotepublish(project, list(paths), host, targets=None) + + @main.command() @click.option("-d", "--debug", is_flag=True, help="Print debug messages") @click.option("-p", "--project", required=True, diff --git a/openpype/lib/applications.py b/openpype/lib/applications.py index fb86d06150..1d0d5dcbaa 100644 --- a/openpype/lib/applications.py +++ b/openpype/lib/applications.py @@ -1004,7 +1004,7 @@ class EnvironmentPrepData(dict): def get_app_environments_for_context( - project_name, asset_name, task_name, app_name, env=None + project_name, asset_name, task_name, app_name=None, env=None ): """Prepare environment variables by context. Args: @@ -1033,20 +1033,14 @@ def get_app_environments_for_context( "name": asset_name }) - # Prepare app object which can be obtained only from ApplciationManager - app_manager = ApplicationManager() - app = app_manager.applications[app_name] - # Project's anatomy anatomy = Anatomy(project_name) - data = EnvironmentPrepData({ + prep_dict = { "project_name": project_name, "asset_name": asset_name, "task_name": task_name, - "app": app, - "dbcon": dbcon, "project_doc": project_doc, "asset_doc": asset_doc, @@ -1054,7 +1048,15 @@ def get_app_environments_for_context( "anatomy": anatomy, "env": env - }) + } + + if app_name: + # Prepare app object which can be obtained only from ApplicationManager + app_manager = ApplicationManager() + app = app_manager.applications[app_name] + prep_dict["app"] = app + + data = EnvironmentPrepData(prep_dict) prepare_host_environments(data) prepare_context_environments(data) diff --git a/openpype/modules/webserver/webserver_cli.py b/openpype/modules/webserver/webserver_cli.py index b6317a5675..00caa24d27 100644 --- a/openpype/modules/webserver/webserver_cli.py +++ b/openpype/modules/webserver/webserver_cli.py @@ -1,16 +1,15 @@ -import attr +import os import time import json import datetime from bson.objectid import ObjectId import collections from aiohttp.web_response import Response +import subprocess from avalon.api import AvalonMongoDB from openpype.modules.avalon_apps.rest_api import _RestApiEndpoint -from openpype.api import get_hierarchy - class WebpublisherProjectsEndpoint(_RestApiEndpoint): """Returns list of project names.""" @@ -130,9 +129,51 @@ class WebpublisherHiearchyEndpoint(_RestApiEndpoint): ) +class WebpublisherTaskFinishEndpoint(_RestApiEndpoint): + """Returns list of project names.""" + async def post(self, request) -> Response: + output = {} + + print(request) + + json_path = os.path.join(self.resource.upload_dir, + "webpublisher.json") # temp - pull from request + + openpype_app = self.resource.executable + args = [ + openpype_app, + 'remotepublish', + json_path + ] + + if not openpype_app or not os.path.exists(openpype_app): + msg = "Non existent OpenPype executable {}".format(openpype_app) + raise RuntimeError(msg) + + add_args = { + "host": "webpublisher", + "project": request.query["project"] + } + + for key, value in add_args.items(): + args.append("--{}".format(key)) + args.append(value) + + print("args:: {}".format(args)) + + exit_code = subprocess.call(args, shell=True) + return Response( + status=200, + body=self.resource.encode(output), + content_type="application/json" + ) + + class RestApiResource: - def __init__(self, server_manager): + def __init__(self, server_manager, executable, upload_dir): self.server_manager = server_manager + self.upload_dir = upload_dir + self.executable = executable self.dbcon = AvalonMongoDB() self.dbcon.install() @@ -154,14 +195,16 @@ class RestApiResource: ).encode("utf-8") -def run_webserver(): +def run_webserver(*args, **kwargs): from openpype.modules import ModulesManager manager = ModulesManager() webserver_module = manager.modules_by_name["webserver"] webserver_module.create_server_manager() - resource = RestApiResource(webserver_module.server_manager) + resource = RestApiResource(webserver_module.server_manager, + upload_dir=kwargs["upload_dir"], + executable=kwargs["executable"]) projects_endpoint = WebpublisherProjectsEndpoint(resource) webserver_module.server_manager.add_route( "GET", @@ -176,6 +219,13 @@ def run_webserver(): hiearchy_endpoint.dispatch ) + task_finish_endpoint = WebpublisherTaskFinishEndpoint(resource) + webserver_module.server_manager.add_route( + "POST", + "/api/task_finish", + task_finish_endpoint.dispatch + ) + webserver_module.start_server() while True: time.sleep(0.5) diff --git a/openpype/pype_commands.py b/openpype/pype_commands.py index 6ccf10e8ce..d2726fd2a6 100644 --- a/openpype/pype_commands.py +++ b/openpype/pype_commands.py @@ -41,11 +41,11 @@ class PypeCommands: return run_event_server(*args) @staticmethod - def launch_webpublisher_webservercli(*args): + def launch_webpublisher_webservercli(*args, **kwargs): from openpype.modules.webserver.webserver_cli import ( run_webserver ) - return run_webserver(*args) + return run_webserver(*args, **kwargs) @staticmethod def launch_standalone_publisher(): @@ -53,7 +53,7 @@ class PypeCommands: standalonepublish.main() @staticmethod - def publish(paths, targets=None): + def publish(paths, targets=None, host=None): """Start headless publishing. Publish use json from passed paths argument. @@ -111,6 +111,62 @@ class PypeCommands: log.info("Publish finished.") uninstall() + @staticmethod + def remotepublish(project, paths, host, targets=None): + """Start headless publishing. + + Publish use json from passed paths argument. + + Args: + paths (list): Paths to jsons. + targets (string): What module should be targeted + (to choose validator for example) + host (string) + + Raises: + RuntimeError: When there is no path to process. + """ + if not any(paths): + raise RuntimeError("No publish paths specified") + + from openpype import install, uninstall + from openpype.api import Logger + + # Register target and host + import pyblish.api + import pyblish.util + + log = Logger.get_logger() + + install() + + if host: + pyblish.api.register_host(host) + + if targets: + if isinstance(targets, str): + targets = [targets] + for target in targets: + pyblish.api.register_target(target) + + os.environ["OPENPYPE_PUBLISH_DATA"] = os.pathsep.join(paths) + os.environ["AVALON_PROJECT"] = project + os.environ["AVALON_APP"] = host # to trigger proper plugings + + log.info("Running publish ...") + + # Error exit as soon as any error occurs. + error_format = "Failed {plugin.__name__}: {error} -- {error.traceback}" + + for result in pyblish.util.publish_iter(): + if result["error"]: + log.error(error_format.format(**result)) + uninstall() + sys.exit(1) + + log.info("Publish finished.") + uninstall() + def extractenvironments(output_json_path, project, asset, task, app): env = os.environ.copy() if all((project, asset, task, app)):