From 03a464e3b7fae5de90abd560ace6239bd6cd74c1 Mon Sep 17 00:00:00 2001 From: hordunlarmy Date: Thu, 29 Jan 2026 20:05:23 +0100 Subject: [PATCH] feat(connection): Add use_shell_profile option to run_command --- changelogs/2026-01-29_19-47-44.md | 2 +- changelogs/2026-01-29_19-48-25.md | 2 +- changelogs/2026-01-29_20-04-22.md | 15 +++++++++++++++ src/connection.py | 12 +++++++----- src/env_manager.py | 14 ++++++++------ tests/unit/test_connection.py | 9 +++++++++ tests/unit/test_env_manager.py | 19 ++++++++++--------- 7 files changed, 51 insertions(+), 22 deletions(-) create mode 100644 changelogs/2026-01-29_20-04-22.md diff --git a/changelogs/2026-01-29_19-47-44.md b/changelogs/2026-01-29_19-47-44.md index 4ee83ea..3f501e5 100644 --- a/changelogs/2026-01-29_19-47-44.md +++ b/changelogs/2026-01-29_19-47-44.md @@ -13,4 +13,4 @@ - **Documentation Updates**: The `README.md` and `docs/env-generation.md` have been updated to reflect the new environment variable management approach and the removal of the `env_blob` input. ### Removed -- The `env_blob` input from `action.yml` has been removed. Its functionality is now superseded by the enhanced capabilities of the `ENV` secret for passing raw environment variable blocks and templating. \ No newline at end of file +- The `env_blob` input from `action.yml` has been removed. Its functionality is now superseded by the enhanced capabilities of the `ENV` secret for passing raw environment variable blocks and templating. diff --git a/changelogs/2026-01-29_19-48-25.md b/changelogs/2026-01-29_19-48-25.md index ee1b1d4..a44ecca 100644 --- a/changelogs/2026-01-29_19-48-25.md +++ b/changelogs/2026-01-29_19-48-25.md @@ -8,4 +8,4 @@ - **Refined Secret Parsing**: Further internal refinements to the environment variable parsing logic improve robustness when handling various formats and merging into base templates, ensuring more reliable processing of complex environment variable strings. ### Removed -- The `env_blob` input and its associated functionality for bulk secret injection have been removed. Users should now leverage the `ENV` secret for providing raw blocks of variables that serve as a base template for generated environment files. \ No newline at end of file +- The `env_blob` input and its associated functionality for bulk secret injection have been removed. Users should now leverage the `ENV` secret for providing raw blocks of variables that serve as a base template for generated environment files. diff --git a/changelogs/2026-01-29_20-04-22.md b/changelogs/2026-01-29_20-04-22.md new file mode 100644 index 0000000..67bb950 --- /dev/null +++ b/changelogs/2026-01-29_20-04-22.md @@ -0,0 +1,15 @@ +# Changelog + +## [Unreleased] + +### Added +- **`use_shell_profile` Option for Remote Commands**: Introduced a new `use_shell_profile` parameter in the `run_command` utility, allowing users to execute commands on remote hosts without sourcing the user's shell profile. This can be used for commands that do not require shell environment setup, potentially improving performance. + +### Changed +- **Optimized Environment File Creation**: The process of creating and managing environment files on remote hosts has been significantly optimized. Multiple operations (directory creation, file content writing, and permissions setting) are now batched into a single SSH command, and shell profile loading is skipped for these specific tasks, leading to faster and more efficient environment file deployments. +- **Enhanced `ENV` Secret Functionality**: The `ENV` secret now offers more robust capabilities for passing raw blocks of environment variables and supports advanced templating, providing a more powerful and flexible method for managing deployment configurations. +- **Improved Environment Variable Parsing**: Internal parsing logic for environment variables has been refined to enhance robustness when handling diverse formats and merging them into base templates, ensuring more reliable processing of complex environment strings. +- **Documentation Updates**: The `README.md` and `docs/env-generation.md` have been updated to reflect the new environment variable management approach and the removal of the `env_blob` input. + +### Removed +- The `env_blob` input from `action.yml` has been removed. Its functionality is now superseded by the enhanced capabilities of the `ENV` secret for passing raw environment variable blocks and templating. \ No newline at end of file diff --git a/src/connection.py b/src/connection.py index a2ce865..f428d45 100644 --- a/src/connection.py +++ b/src/connection.py @@ -23,15 +23,17 @@ def setup_ssh_key(): os.chmod(config.SSH_KEY_PATH, 0o600) -def run_command(conn, command, force_sudo=False): - """ - Helper function to run commands with optional sudo support. - """ +def run_command(conn, command: str, force_sudo: bool = False, use_shell_profile: bool = True): + """Run a command on the remote host with environment setup and sudo support""" use_sudo_for_this = config.USE_SUDO or force_sudo - if not use_sudo_for_this: + if not use_sudo_for_this and not use_shell_profile: return conn.run(command, warn=False) + if not use_shell_profile: + # Just sudo without the expensive profile loading + return conn.run(f"sudo {command}", warn=False) + if config.REMOTE_USER == "root": home_dir = "/root" else: diff --git a/src/env_manager.py b/src/env_manager.py index c92eaa5..3827f27 100644 --- a/src/env_manager.py +++ b/src/env_manager.py @@ -416,14 +416,16 @@ def create_env_file(conn, file_path: str, env_content: str) -> None: if not env_content: return dir_path = os.path.dirname(file_path) - if dir_path and dir_path != file_path: - # Only mkdir, skip chmod on directory to avoid permission errors - run_command(conn, f"mkdir -p {dir_path}") - # Use base64 to avoid shell character/newline mangling issues encoded = base64.b64encode(env_content.encode("utf-8")).decode("utf-8") - run_command(conn, f"echo '{encoded}' | base64 -d | tee \"{file_path}\" > /dev/null") - run_command(conn, f'chmod 600 "{file_path}"') + + # Batch commands into one SSH round-trip and skip expensive shell profile sourcing + batch_cmd = ( + f'mkdir -p "{dir_path}" && ' + f'echo "{encoded}" | base64 -d | tee "{file_path}" > /dev/null && ' + f'chmod 600 "{file_path}"' + ) + run_command(conn, batch_cmd, use_shell_profile=False) def generate_env_files(conn) -> None: diff --git a/tests/unit/test_connection.py b/tests/unit/test_connection.py index 87a369f..16d3c8a 100644 --- a/tests/unit/test_connection.py +++ b/tests/unit/test_connection.py @@ -48,6 +48,15 @@ def test_run_command_sudo_wrapping(mock_conn, monkeypatch): assert "source /home/deploy/.bashrc" in call_args +def test_run_command_no_profile(mock_conn, monkeypatch): + monkeypatch.setattr(config, "USE_SUDO", True) + run_command(mock_conn, "simple-cmd", use_shell_profile=False) + + call_args = mock_conn.run.call_args[0][0] + assert call_args == "sudo simple-cmd" + assert "bash -l -c" not in call_args + + def test_run_command_password_sudo(mock_conn, monkeypatch): monkeypatch.setattr(config, "USE_SUDO", True) monkeypatch.setattr(config, "REMOTE_PASSWORD", "mypass") diff --git a/tests/unit/test_env_manager.py b/tests/unit/test_env_manager.py index c20d8e0..fe83ea6 100644 --- a/tests/unit/test_env_manager.py +++ b/tests/unit/test_env_manager.py @@ -81,15 +81,16 @@ def test_root_mega_file_creation(mock_conn, monkeypatch): def test_heredoc_escaping(mock_conn): # We need to mock run_command because create_env_file now uses it with patch("src.env_manager.run_command") as mock_run: - create_env_file(mock_conn, ".env", "PORT=3000\nDEBUG=true") - - # Check that it was called with base64 encoded content - found_base64_tee = False - for call in mock_run.call_args_list: - cmd = call[0][1] - if "base64 -d" in cmd and "tee" in cmd: - found_base64_tee = True - assert found_base64_tee + create_env_file(mock_conn, "/testing/.env", "PORT=3000\nDEBUG=true") + + # Check that it was called with a BATCHED command + assert mock_run.call_count == 1 + cmd = mock_run.call_args[0][1] + assert "mkdir -p" in cmd + assert "echo" in cmd + assert "chmod 600" in cmd + assert "base64 -d" in cmd + assert mock_run.call_args[1].get("use_shell_profile") is False def test_mixed_blob_and_raw_bucketing(mocker):