From 363f429c7a9c22354bc07d499eef5f92fdcf263f Mon Sep 17 00:00:00 2001 From: mwiegand Date: Sun, 10 May 2026 22:46:21 +0200 Subject: [PATCH] l4d2host: LEFT4ME_STEAMCMD env var for steamcmd path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SteamInstaller defaults to steamcmd="steamcmd" (bare name), which relies on PATH lookup. Deployments that don't have steamcmd on PATH — or where steamcmd.sh's `cd "$(dirname "$0")"` breaks under PATH-symlink invocation (observed with the Valve-shipped script) — can now pin an absolute path via LEFT4ME_STEAMCMD. Default keeps bare-name lookup for dev/tests. Co-Authored-By: Claude Opus 4.7 (1M context) --- l4d2host/cli.py | 8 +++++++- l4d2host/tests/test_cli.py | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/l4d2host/cli.py b/l4d2host/cli.py index f55807d..ae61197 100644 --- a/l4d2host/cli.py +++ b/l4d2host/cli.py @@ -1,5 +1,6 @@ from pathlib import Path import json +import os import subprocess import typer @@ -21,8 +22,13 @@ def _exit_from_subprocess_error(exc: subprocess.CalledProcessError) -> None: @app.command() def install() -> None: + # LEFT4ME_STEAMCMD lets deployment pin an absolute path (e.g. when the + # binary lives outside PATH or its steamcmd.sh resolves $0 via dirname, + # which breaks under PATH-symlink invocation). Default keeps PATH lookup + # for dev/tests. + steamcmd = os.environ.get("LEFT4ME_STEAMCMD", "steamcmd") try: - SteamInstaller().install_or_update(passthrough=True) + SteamInstaller(steamcmd=steamcmd).install_or_update(passthrough=True) except subprocess.CalledProcessError as exc: _exit_from_subprocess_error(exc) diff --git a/l4d2host/tests/test_cli.py b/l4d2host/tests/test_cli.py index 5028ad5..a57ce29 100644 --- a/l4d2host/tests/test_cli.py +++ b/l4d2host/tests/test_cli.py @@ -14,6 +14,44 @@ def test_help_lists_v1_commands() -> None: assert command in result.output +def test_install_uses_left4me_steamcmd_env_var(monkeypatch) -> None: + captured: dict[str, str] = {} + + class FakeInstaller: + def __init__(self, *, steamcmd): + captured["steamcmd"] = steamcmd + + def install_or_update(self, **kwargs): + del kwargs + + monkeypatch.setattr("l4d2host.cli.SteamInstaller", FakeInstaller) + monkeypatch.setenv("LEFT4ME_STEAMCMD", "/opt/left4me/steam/steamcmd.sh") + + result = CliRunner().invoke(app, ["install"]) + + assert result.exit_code == 0 + assert captured["steamcmd"] == "/opt/left4me/steam/steamcmd.sh" + + +def test_install_defaults_to_bare_steamcmd_when_env_unset(monkeypatch) -> None: + captured: dict[str, str] = {} + + class FakeInstaller: + def __init__(self, *, steamcmd): + captured["steamcmd"] = steamcmd + + def install_or_update(self, **kwargs): + del kwargs + + monkeypatch.setattr("l4d2host.cli.SteamInstaller", FakeInstaller) + monkeypatch.delenv("LEFT4ME_STEAMCMD", raising=False) + + result = CliRunner().invoke(app, ["install"]) + + assert result.exit_code == 0 + assert captured["steamcmd"] == "steamcmd" + + def test_cli_propagates_subprocess_return_code(monkeypatch) -> None: def fail(*args, **kwargs): del args