diff --git a/cwltool/command_line_tool.py b/cwltool/command_line_tool.py index fbed5b89b..71ca6f475 100644 --- a/cwltool/command_line_tool.py +++ b/cwltool/command_line_tool.py @@ -403,6 +403,17 @@ class CommandLineTool(Process): def __init__(self, toolpath_object: CommentedMap, loadingContext: LoadingContext) -> None: """Initialize this CommandLineTool.""" super().__init__(toolpath_object, loadingContext) + base_command = self.tool.get("baseCommand") + if isinstance(base_command, str) and len(base_command.split()) > 1: + _logger.warning( + SourceLine(self.tool, "baseCommand", str).makeError( + "'baseCommand' is a single string containing whitespace: %r. " + "It will be passed to the operating system as one program name, " + "which is unlikely to exist. If you meant a program plus arguments, " + "provide a list instead, e.g. [%s]." + % (base_command, ", ".join(repr(p) for p in base_command.split())) + ) + ) self.prov_obj = loadingContext.prov_obj self.path_check_mode: PathCheckingMode = ( PathCheckingMode.RELAXED diff --git a/tests/test_validate.py b/tests/test_validate.py index 8e664d9aa..c7c0d5570 100644 --- a/tests/test_validate.py +++ b/tests/test_validate.py @@ -4,6 +4,8 @@ import logging import re +import pytest + from .util import get_data, get_main_output @@ -125,3 +127,37 @@ def test_validate_custom_logger() -> None: assert "tests/CometAdapter.cwl#out' previously defined" not in stdout assert "tests/CometAdapter.cwl#out' previously defined" not in stderr assert "tests/CometAdapter.cwl#out' previously defined" in custom_log_text + + +def test_validate_warns_on_basecommand_with_space() -> None: + """A 'baseCommand' string containing whitespace warns but still validates.""" + custom_log = io.StringIO() + handler = logging.StreamHandler(custom_log) + handler.setLevel(logging.DEBUG) + exit_code, stdout, stderr = get_main_output( + ["--validate", get_data("tests/wf/2240-basecommand-space.cwl")], + logger_handler=handler, + ) + custom_log_text = re.sub(r"\s\s+", " ", custom_log.getvalue()) + assert exit_code == 0 + assert "is valid CWL" in stdout + assert "'baseCommand' is a single string containing whitespace" in custom_log_text + assert "'tar xf'" in custom_log_text + assert "['tar', 'xf']" in custom_log_text + + +@pytest.mark.parametrize( + "cwl_file", + ["tests/wf/2240-basecommand-list.cwl", "tests/CometAdapter.cwl"], +) +def test_validate_no_basecommand_space_warning(cwl_file: str) -> None: + """A list-form (or absent) 'baseCommand' does not trigger the whitespace warning.""" + custom_log = io.StringIO() + handler = logging.StreamHandler(custom_log) + handler.setLevel(logging.DEBUG) + exit_code, _, _ = get_main_output( + ["--validate", get_data(cwl_file)], + logger_handler=handler, + ) + assert exit_code == 0 + assert "single string containing whitespace" not in custom_log.getvalue() diff --git a/tests/wf/2240-basecommand-list.cwl b/tests/wf/2240-basecommand-list.cwl new file mode 100644 index 000000000..90ba070df --- /dev/null +++ b/tests/wf/2240-basecommand-list.cwl @@ -0,0 +1,6 @@ +#!/usr/bin/env cwl-runner +cwlVersion: v1.2 +class: CommandLineTool +baseCommand: [tar, xf] +inputs: [] +outputs: [] diff --git a/tests/wf/2240-basecommand-space.cwl b/tests/wf/2240-basecommand-space.cwl new file mode 100644 index 000000000..5847d9b86 --- /dev/null +++ b/tests/wf/2240-basecommand-space.cwl @@ -0,0 +1,6 @@ +#!/usr/bin/env cwl-runner +cwlVersion: v1.2 +class: CommandLineTool +baseCommand: "tar xf" +inputs: [] +outputs: []