Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/remote/configuration/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
class RemoteConfig:
"""Single remote connection description"""

# remote machine's hostname
# remote machine's hostname, with optional username@ prefix
host: str
# relative path to the working directory on remote machine starting from user home dir
directory: Path
Expand Down
4 changes: 2 additions & 2 deletions src/remote/configuration/shared.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
from pathlib import Path

DEFAULT_REMOTE_ROOT = ".remotes"
HOST_REGEX = r"[-\w]+(\.[-\w]+)*"
PATH_REGEX = r"/?[-.\w\s]+(/[-.\w\s]+)*/?"
HOST_REGEX = r"([-\w]+@)?[-\w]+(\.[-\w]+)*"
PATH_REGEX = r"/?[-.\w\s][-.\w\s:]*(/[-.\w\s:]+)*/?"


def hash_path(path: Path):
Expand Down
14 changes: 8 additions & 6 deletions src/remote/entrypoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,12 +79,14 @@ def _add_remote_host(config: WorkspaceConfig, connection: str):
"""Add a new remote host to the workspace config, check the connection, and save it if connection is ok

:param config: the workspace config decription object
:param connection: connection string in format of 'host-name[:remote_dir]'
:param connection: connection string in format of '[user@]host-name[:remote_dir]'
"""
parts = connection.split(":")
remote_host = parts[0]
remote_host, sep, path = connection.partition(":")
config_medium = get_configuration_medium(config)
remote_dir = config_medium.generate_remote_directory(config) if len(parts) == 1 else Path(parts[1])
if sep:
remote_dir = Path(path)
else:
remote_dir = config_medium.generate_remote_directory(config)

added, index = config.add_remote_host(remote_host, remote_dir)
if not added:
Expand All @@ -108,7 +110,7 @@ def _add_remote_host(config: WorkspaceConfig, connection: str):


@click.command(context_settings=DEFAULT_CONTEXT_SETTINGS)
@click.argument("connection", metavar="host-name[:remote_dir]", callback=validate_connection_string)
@click.argument("connection", metavar="[user@]host-name[:remote_dir]", callback=validate_connection_string)
@log_exceptions
def remote_add(connection: str):
"""Add one more host for remote connection to a config file"""
Expand All @@ -118,7 +120,7 @@ def remote_add(connection: str):


@click.command(context_settings=DEFAULT_CONTEXT_SETTINGS)
@click.argument("connection", metavar="host-name[:remote_dir]", callback=validate_connection_string)
@click.argument("connection", metavar="[user@]host-name[:remote_dir]", callback=validate_connection_string)
@log_exceptions
def remote_init(connection: str):
"""Initiate workspace for the remote execution in the current working directory"""
Expand Down
43 changes: 25 additions & 18 deletions test/test_entrypoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,24 @@
TEST_DIR = ".remotes/my project"
TEST_CONFIG = f"{TEST_HOST}:{shlex.quote(TEST_DIR)}"

TEST_HOSTS = [
("host", True),
("host123", True),
("host.domain.com", True),
("ho-st.dom-ain.as1234", True),
("ho-st.dom-ain.as1234:/home/dir", True),
("ho-st.dom-ain.as1234:.home/dir.dir", True),
("ho-st.dom-ain.as1234:.home/dir.dir/123/", True),
("ho-st.dom-ain.as1234:.home/dir.dir/123/:something", True),
("ho-st.dom-ain.as1234::/home/dir", False),
("some_user@host", True),
("some user@host", False),
("some:user@host", False),
("user@host:", False),
("user@:/home/dir", False),
("@host:/home/dir", False),
]


@contextmanager
def cwd(path):
Expand Down Expand Up @@ -76,20 +94,7 @@ def test_function(num):
test_function(0)


@pytest.mark.parametrize(
"connection, is_valid",
[
("host", True),
("host123", True),
("host.domain.com", True),
("ho-st.dom-ain.as1234", True),
("ho-st.dom-ain.as1234:/home/dir", True),
("ho-st.dom-ain.as1234:.home/dir.dir", True),
("ho-st.dom-ain.as1234:.home/dir.dir/123/", True),
("ho-st.dom-ain.as1234:.home/dir.dir/123/:something", False),
("ho-st.dom-ain.as1234::/home/dir", False),
],
)
@pytest.mark.parametrize("connection, is_valid", TEST_HOSTS)
def test_validate_connection_string(connection, is_valid):
if is_valid:
entrypoints.validate_connection_string(None, None, connection)
Expand Down Expand Up @@ -278,11 +283,12 @@ def test_remote_init_fails_if_workspace_is_already_initated(tmp_workspace):
assert (tmp_workspace / CONFIG_FILE_NAME).read_text() == f"{TEST_CONFIG}\n"


def test_remote_init_fails_on_input_validation(tmp_path):
@pytest.mark.parametrize("connection", [connection for connection, is_valid in TEST_HOSTS if not is_valid])
def test_remote_init_fails_on_input_validation(tmp_path, connection):
runner = CliRunner()

with cwd(tmp_path):
result = runner.invoke(entrypoints.remote_init, ["host:path:path"])
result = runner.invoke(entrypoints.remote_init, [connection])

assert result.exit_code == 2

Expand All @@ -309,11 +315,12 @@ def test_remote_commands_fail_on_no_workspace(tmp_path):
assert result.output == f"Cannot resolve the remote workspace in {tmp_path}\n"


def test_remote_add_fails_on_input_validation(tmp_path):
@pytest.mark.parametrize("connection", [connection for connection, is_valid in TEST_HOSTS if not is_valid])
def test_remote_add_fails_on_input_validation(tmp_path, connection):
runner = CliRunner()

with cwd(tmp_path):
result = runner.invoke(entrypoints.remote_add, ["host:path:path"])
result = runner.invoke(entrypoints.remote_add, [connection])

assert result.exit_code == 2

Expand Down
Loading