Repository: broadinstitute/cromshell

Issue:
`update-server` command
Port the `update-server` command over from 1.0

Allows the user to programmatically change the server name in the config file, without having to know how to do this manually.

Discussion:
sjfleming: Thanks @bshifaw .  Let me know what to do about merging.  Will you do that?  Seems like if we all try to merge at once without rebasing on each others' merges, there could be problems.
bshifaw: Feel free to merge after rebasing

Patch:
diff --git a/src/cromshell/__main__.py b/src/cromshell/__main__.py
index 8060d9a..e48c4a8 100644
--- a/src/cromshell/__main__.py
+++ b/src/cromshell/__main__.py
@@ -13,6 +13,7 @@ from .slim_metadata import command as slim_metadata
 from .status import command as status
 from .submit import command as submit
 from .timing import command as timing
+from .update_server import command as update_server
 
 # Version number is automatically set via bumpversion.
 # DO NOT MODIFY:
@@ -129,6 +130,7 @@ main_entry.add_command(status.main)
 main_entry.add_command(submit.main)
 main_entry.add_command(slim_metadata.main)
 main_entry.add_command(metadata.main)
+main_entry.add_command(update_server.main)
 main_entry.add_command(timing.main)
 
 
diff --git a/src/cromshell/update_server/__init__.py b/src/cromshell/update_server/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/src/cromshell/update_server/command.py b/src/cromshell/update_server/command.py
new file mode 100644
index 0000000..ffaa7a2
--- /dev/null
+++ b/src/cromshell/update_server/command.py
@@ -0,0 +1,44 @@
+import json
+import logging
+import os
+from pathlib import Path
+from typing import Dict
+
+import click
+
+LOGGER = logging.getLogger(__name__)
+CROMWELL_SERVER_KEY = "cromwell_server"
+
+
+@click.command(name="update-server")
+@click.argument("cromwell_server_url", required=True, nargs=1)
+@click.pass_obj
+def main(config, cromwell_server_url):
+    """Update the default Cromwell server in the config file"""
+
+    LOGGER.info("update-server")
+
+    # location of the config file
+    cromshell_config_path = os.path.join(
+        config.config_dir, config.CROMSHELL_CONFIG_FILE_NAME
+    )
+    assert os.access(
+        cromshell_config_path, mode=os.W_OK
+    ), f"Cannot write to Cromshell config file {cromshell_config_path}"
+
+    # the contents of the config file as a dict
+    cromshell_config: Dict = config.cromshell_config_options
+
+    # update the cromwell server
+    if CROMWELL_SERVER_KEY not in cromshell_config.keys():
+        cromshell_config.update({CROMWELL_SERVER_KEY: cromwell_server_url})
+    else:
+        cromshell_config[CROMWELL_SERVER_KEY] = cromwell_server_url
+
+    # write the modified config file
+    config_contents = json.dumps(cromshell_config, indent=2)
+    with Path(cromshell_config_path).open("w") as crom_config_file:
+        crom_config_file.write(config_contents)
+
+    LOGGER.info(f"Cromshell config file at {cromshell_config_path}")
+    LOGGER.info(f"Default Cromwell server updated to {cromwell_server_url}")
diff --git a/src/cromshell/utilities/cromshellconfig.py b/src/cromshell/utilities/cromshellconfig.py
index 35d6127..10e9d76 100644
--- a/src/cromshell/utilities/cromshellconfig.py
+++ b/src/cromshell/utilities/cromshellconfig.py
@@ -197,7 +197,7 @@ def __load_cromshell_config_file(
 
 
 def __get_cromwell_server(config_options: dict):
-    """Get Cromshell Server URL from configuration options"""
+    """Get Cromwell Server URL from configuration options"""
 
     if not config_options["cromwell_server"]:
         raise FileNotFoundError('Cromshell config file is missing "cromwell_server"')


Test patch:
diff --git a/tests/integration/test_update_server.py b/tests/integration/test_update_server.py
new file mode 100644
index 0000000..f7874d9
--- /dev/null
+++ b/tests/integration/test_update_server.py
@@ -0,0 +1,66 @@
+import os
+from traceback import print_exception
+
+from click.testing import CliRunner
+
+from cromshell.__main__ import main_entry as cromshell
+from cromshell.utilities import cromshellconfig
+
+
+def get_current_cromwell_server():
+    cromshell_config_dict = cromshellconfig.__load_cromshell_config_file(
+        config_directory=cromshellconfig.config_dir,
+        config_file_name=cromshellconfig.CROMSHELL_CONFIG_FILE_NAME,
+        config_file_template=None,
+    )
+    print(
+        os.path.join(
+            cromshellconfig.config_dir, cromshellconfig.CROMSHELL_CONFIG_FILE_NAME
+        )
+    )
+    print(cromshell_config_dict)
+    return cromshell_config_dict["cromwell_server"]
+
+
+def run_update_server(runner, new_cromwell_server):
+    """Run cromshell update-server in a specific CliRunner context"""
+
+    result = runner.invoke(
+        cromshell,
+        [
+            "update-server",
+            new_cromwell_server,
+        ],
+    )
+
+    # print any exceptions so pytest will show them
+    print_exception(*result.exc_info)
+
+
+def test_update_server():
+    """Run cromshell update-server using CliRunner and ensure server changes"""
+
+    print("Running a test of cromshell update-server...")
+
+    test_server_name = "http://test.server"
+
+    runner = CliRunner(mix_stderr=False)
+    # The test is being run in temp directory created by CliRunner, and
+    # so its modification of the default server only happens in this context
+    with runner.isolated_filesystem():
+
+        # the current default server
+        current_cromwell_server = get_current_cromwell_server()
+        print(f"Initial Cromwell server: {current_cromwell_server}")
+
+        # update the server and check that it worked
+        run_update_server(
+            runner=runner,
+            new_cromwell_server=test_server_name,
+        )
+        tmp_cromwell_server = get_current_cromwell_server()
+        print(
+            f"After attempting to update to '{test_server_name}', "
+            f"the config file has '{tmp_cromwell_server}'"
+        )
+        assert tmp_cromwell_server == test_server_name, "cromshell update-server failed"