From 5b006d089cd61e8a09435ca6310a8bf450915e77 Mon Sep 17 00:00:00 2001 From: Ralf Grubenmann Date: Wed, 7 Apr 2021 09:43:25 +0200 Subject: [PATCH] feat(core): adds node-js detection for rerun/update --- README.rst | 5 +++++ renku/core/commands/rerun.py | 2 +- renku/core/commands/update.py | 2 +- renku/core/errors.py | 12 ++++++++++++ renku/core/incubation/command.py | 28 ++++++++++++++++++++++++++++ renku/core/incubation/graph.py | 2 +- 6 files changed, 48 insertions(+), 3 deletions(-) diff --git a/README.rst b/README.rst index c701e5719c..fd3bcc9979 100644 --- a/README.rst +++ b/README.rst @@ -84,6 +84,11 @@ Renku commands with the `-S` flag, as in `renku -S `. More information on Git LFS usage in renku can be found in the `Data in Renku `_ section of the docs. +Renku uses CWL to execute recorded workflows when calling `renku update` +or `renku rerun`. CWL depends on NodeJs to execute the workflows, so installing +`NodeJs `_ is required if +you want to use those features. + .. _pipx-before-reference: diff --git a/renku/core/commands/rerun.py b/renku/core/commands/rerun.py index 8daf2ae669..dd3d6d9d88 100644 --- a/renku/core/commands/rerun.py +++ b/renku/core/commands/rerun.py @@ -24,7 +24,7 @@ def rerun_workflows(): """Recreate files generated by a sequence of ``run`` commands.""" - return Command().command(_rerun_workflows).require_migration().require_clean().with_commit() + return Command().command(_rerun_workflows).require_migration().require_clean().with_commit().require_nodejs() def _rerun_workflows(client, revision, roots, siblings, inputs, paths): diff --git a/renku/core/commands/update.py b/renku/core/commands/update.py index 58372878d8..11598c8533 100644 --- a/renku/core/commands/update.py +++ b/renku/core/commands/update.py @@ -37,7 +37,7 @@ def update_workflows(): """Update existing files by rerunning their outdated workflow.""" - return Command().command(_update_workflows).require_migration().require_clean().with_commit() + return Command().command(_update_workflows).require_migration().require_clean().with_commit().require_nodejs() def _update_workflows(client, revision, no_output, update_all, siblings, paths): diff --git a/renku/core/errors.py b/renku/core/errors.py index 6bd1cce3d2..738f03c6c3 100644 --- a/renku/core/errors.py +++ b/renku/core/errors.py @@ -462,3 +462,15 @@ class RenkuSaveError(RenkuException): class DatasetImageError(RenkuException): """Raised when a local dataset image is not accessible.""" + + +class NodeNotFoundError(RenkuException): + """Raised when NodeJs is not installed on the system.""" + + def __init__(self): + """Build a custom message.""" + msg = ( + "NodeJs could not be found on this system\n" + "Please install it, for details see https://nodejs.org/en/download/package-manager/" + ) + super(NodeNotFoundError, self).__init__(msg) diff --git a/renku/core/incubation/command.py b/renku/core/incubation/command.py index e2432024dc..7f0c6a58da 100644 --- a/renku/core/incubation/command.py +++ b/renku/core/incubation/command.py @@ -19,6 +19,7 @@ import contextlib import functools +import shutil from collections import defaultdict import click @@ -251,6 +252,11 @@ def require_clean(self): """Check that the repository is clean.""" return RequireClean(self) + @check_finalized + def require_nodejs(self): + """Ensure nodejs is installed.""" + return RequireNodeJs(self) + @check_finalized def with_communicator(self, communicator): """Create a communicator.""" @@ -407,6 +413,28 @@ def build(self): return self._builder.build() +class RequireNodeJs(Command): + """Check that node.js is installed and available on the system.""" + + DEFAULT_ORDER = 3 + + def __init__(self, builder): + """__init__ of DatasetLock.""" + self._builder = builder + + def _pre_hook(self, builder, context, *args, **kwargs): + """Check node is available.""" + if not shutil.which("nodejs") and not shutil.which("node"): + raise errors.NodeNotFoundError() + + @check_finalized + def build(self): + """Build the command.""" + self._builder.add_pre_hook(self.DEFAULT_ORDER, self._pre_hook) + + return self._builder.build() + + class Communicator(Command): """Hook for logging and interaction with user.""" diff --git a/renku/core/incubation/graph.py b/renku/core/incubation/graph.py index 20e601523c..0716d13f87 100644 --- a/renku/core/incubation/graph.py +++ b/renku/core/incubation/graph.py @@ -173,7 +173,7 @@ def _status(client): def update(): """Return a command for generating the graph.""" command = Command().command(_update).lock_project() - return command.require_migration().with_commit(commit_if_empty=False).require_clean() + return command.require_migration().with_commit(commit_if_empty=False).require_clean().require_nodejs() def _update(client, dry_run):