mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-06-30 00:22:10 -04:00
CI test fixes for dev sync
This commit is contained in:
@@ -2005,6 +2005,7 @@ async def stream_agent_loop(
|
|||||||
and (_casual_low_signal_turn or not active_email)
|
and (_casual_low_signal_turn or not active_email)
|
||||||
and (_casual_low_signal_turn or not workspace)
|
and (_casual_low_signal_turn or not workspace)
|
||||||
and not forced_tools
|
and not forced_tools
|
||||||
|
and not relevant_tools
|
||||||
)
|
)
|
||||||
# Tool retrieval uses the latest message by default. It may inherit recent
|
# Tool retrieval uses the latest message by default. It may inherit recent
|
||||||
# user turns only for explicit continuations ("yes", "do it", "1").
|
# user turns only for explicit continuations ("yes", "do it", "1").
|
||||||
|
|||||||
@@ -43,7 +43,8 @@ def test_background_session_sort_uses_owner_task_endpoint():
|
|||||||
def test_scheduler_fallbacks_and_research_headers_are_owner_scoped():
|
def test_scheduler_fallbacks_and_research_headers_are_owner_scoped():
|
||||||
src = _src("src/task_scheduler.py")
|
src = _src("src/task_scheduler.py")
|
||||||
|
|
||||||
assert "resolve_utility_fallback_candidates(owner=task.owner or None)" in src
|
assert "resolve_task_candidates(" in src
|
||||||
|
assert "owner=task.owner or None" in src
|
||||||
assert 'resolve_endpoint(\n "research",' in src
|
assert 'resolve_endpoint(\n "research",' in src
|
||||||
assert "owner=task.owner or None" in src
|
assert "owner=task.owner or None" in src
|
||||||
assert "headers_from_resolver = False" in src
|
assert "headers_from_resolver = False" in src
|
||||||
|
|||||||
@@ -51,23 +51,19 @@ class _Db:
|
|||||||
self.closed = True
|
self.closed = True
|
||||||
|
|
||||||
|
|
||||||
def _resolver_spy(monkeypatch, utility_result=("", "", {}), default_result=("http://llm", "model", {})):
|
def _resolver_spy(monkeypatch, candidates=None):
|
||||||
from src import endpoint_resolver
|
from src import task_endpoint
|
||||||
|
|
||||||
calls = []
|
calls = []
|
||||||
fallback_calls = []
|
|
||||||
|
|
||||||
def fake_resolve(kind, *args, **kwargs):
|
def fake_candidates(*args, **kwargs):
|
||||||
calls.append((kind, kwargs.get("owner")))
|
calls.append(kwargs.get("owner"))
|
||||||
return utility_result if kind == "utility" else default_result
|
if candidates is None:
|
||||||
|
return [("http://llm", "model", {})]
|
||||||
|
return list(candidates)
|
||||||
|
|
||||||
def fake_fallbacks(*args, **kwargs):
|
monkeypatch.setattr(task_endpoint, "resolve_task_candidates", fake_candidates)
|
||||||
fallback_calls.append(kwargs.get("owner"))
|
return calls
|
||||||
return []
|
|
||||||
|
|
||||||
monkeypatch.setattr(endpoint_resolver, "resolve_endpoint", fake_resolve)
|
|
||||||
monkeypatch.setattr(endpoint_resolver, "resolve_utility_fallback_candidates", fake_fallbacks)
|
|
||||||
return calls, fallback_calls
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
@@ -88,7 +84,7 @@ async def test_classify_events_resolves_llm_for_task_owner(monkeypatch):
|
|||||||
location="",
|
location="",
|
||||||
)
|
)
|
||||||
db = _Db({FakeCalendarEvent: [event]})
|
db = _Db({FakeCalendarEvent: [event]})
|
||||||
calls, _fallback_calls = _resolver_spy(monkeypatch, utility_result=("http://llm", "model", {}))
|
calls = _resolver_spy(monkeypatch)
|
||||||
|
|
||||||
monkeypatch.setattr(database, "CalendarEvent", FakeCalendarEvent)
|
monkeypatch.setattr(database, "CalendarEvent", FakeCalendarEvent)
|
||||||
monkeypatch.setattr(database, "SessionLocal", lambda: db)
|
monkeypatch.setattr(database, "SessionLocal", lambda: db)
|
||||||
@@ -97,7 +93,7 @@ async def test_classify_events_resolves_llm_for_task_owner(monkeypatch):
|
|||||||
|
|
||||||
assert ok is True
|
assert ok is True
|
||||||
assert "Scanned 1 upcoming event" in message
|
assert "Scanned 1 upcoming event" in message
|
||||||
assert calls == [("utility", "alice")]
|
assert calls == ["alice"]
|
||||||
assert db.closed is True
|
assert db.closed is True
|
||||||
|
|
||||||
|
|
||||||
@@ -122,7 +118,7 @@ async def test_learn_sender_signatures_resolves_llm_for_task_owner(monkeypatch):
|
|||||||
def logout(self):
|
def logout(self):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
calls, _fallback_calls = _resolver_spy(monkeypatch, utility_result=("", "", {}), default_result=("", "", {}))
|
calls = _resolver_spy(monkeypatch, candidates=[])
|
||||||
imap_owners = []
|
imap_owners = []
|
||||||
|
|
||||||
def fake_imap_connect(_account_id=None, owner=""):
|
def fake_imap_connect(_account_id=None, owner=""):
|
||||||
@@ -135,14 +131,14 @@ async def test_learn_sender_signatures_resolves_llm_for_task_owner(monkeypatch):
|
|||||||
|
|
||||||
assert ok is False
|
assert ok is False
|
||||||
assert message == "No LLM endpoint available"
|
assert message == "No LLM endpoint available"
|
||||||
assert calls == [("utility", "alice"), ("default", "alice")]
|
assert calls == ["alice"]
|
||||||
assert imap_owners == ["alice"]
|
assert imap_owners == ["alice"]
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_learn_sender_signatures_writes_owner_scoped_cache(monkeypatch, tmp_path):
|
async def test_learn_sender_signatures_writes_owner_scoped_cache(monkeypatch, tmp_path):
|
||||||
from routes import email_helpers
|
from routes import email_helpers
|
||||||
from src import endpoint_resolver, llm_core
|
from src import llm_core, task_endpoint
|
||||||
from src.builtin_actions import action_learn_sender_signatures
|
from src.builtin_actions import action_learn_sender_signatures
|
||||||
|
|
||||||
db_path = tmp_path / "scheduled_emails.db"
|
db_path = tmp_path / "scheduled_emails.db"
|
||||||
@@ -205,15 +201,15 @@ async def test_learn_sender_signatures_writes_owner_scoped_cache(monkeypatch, tm
|
|||||||
|
|
||||||
monkeypatch.setattr(email_helpers, "_imap_connect", fake_imap_connect)
|
monkeypatch.setattr(email_helpers, "_imap_connect", fake_imap_connect)
|
||||||
monkeypatch.setattr(
|
monkeypatch.setattr(
|
||||||
endpoint_resolver,
|
task_endpoint,
|
||||||
"resolve_endpoint",
|
"resolve_task_candidates",
|
||||||
lambda kind, *args, **kwargs: ("http://llm", "alice-model", {}),
|
lambda *args, **kwargs: [("http://llm", "alice-model", {})],
|
||||||
)
|
)
|
||||||
|
|
||||||
async def fake_llm_call_async(**_kwargs):
|
async def fake_llm_call_async(_candidates, **_kwargs):
|
||||||
return "Writer Example\nExample Co.\nwriter@example.com"
|
return "Writer Example\nExample Co.\nwriter@example.com"
|
||||||
|
|
||||||
monkeypatch.setattr(llm_core, "llm_call_async", fake_llm_call_async)
|
monkeypatch.setattr(llm_core, "llm_call_async_with_fallback", fake_llm_call_async)
|
||||||
|
|
||||||
message, ok = await action_learn_sender_signatures("alice")
|
message, ok = await action_learn_sender_signatures("alice")
|
||||||
|
|
||||||
@@ -253,7 +249,7 @@ async def test_check_email_urgency_resolves_llm_candidates_for_task_owner(monkey
|
|||||||
from_address = _Column()
|
from_address = _Column()
|
||||||
|
|
||||||
db = _Db({FakeEmailAccount: []})
|
db = _Db({FakeEmailAccount: []})
|
||||||
calls, fallback_calls = _resolver_spy(monkeypatch, utility_result=("http://llm", "model", {}))
|
calls = _resolver_spy(monkeypatch)
|
||||||
|
|
||||||
monkeypatch.chdir(tmp_path)
|
monkeypatch.chdir(tmp_path)
|
||||||
monkeypatch.setattr(database, "EmailAccount", FakeEmailAccount)
|
monkeypatch.setattr(database, "EmailAccount", FakeEmailAccount)
|
||||||
@@ -262,6 +258,5 @@ async def test_check_email_urgency_resolves_llm_candidates_for_task_owner(monkey
|
|||||||
with pytest.raises(TaskNoop, match="no email accounts configured"):
|
with pytest.raises(TaskNoop, match="no email accounts configured"):
|
||||||
await action_check_email_urgency("alice")
|
await action_check_email_urgency("alice")
|
||||||
|
|
||||||
assert calls == [("utility", "alice")]
|
assert calls == ["alice"]
|
||||||
assert fallback_calls == ["alice"]
|
|
||||||
assert db.closed is True
|
assert db.closed is True
|
||||||
|
|||||||
@@ -29,8 +29,8 @@ def _read_memories(data_dir):
|
|||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_consolidate_memory_empty_owner_treats_each_owner_separately(monkeypatch, tmp_path):
|
async def test_consolidate_memory_empty_owner_treats_each_owner_separately(monkeypatch, tmp_path):
|
||||||
from src import constants
|
from src import constants
|
||||||
from src import endpoint_resolver
|
|
||||||
from src import llm_core
|
from src import llm_core
|
||||||
|
from src import task_endpoint
|
||||||
action_consolidate_memory = _import_consolidate_action()
|
action_consolidate_memory = _import_consolidate_action()
|
||||||
|
|
||||||
long_alice_text = "Alice private project context. " + ("A" * 2200)
|
long_alice_text = "Alice private project context. " + ("A" * 2200)
|
||||||
@@ -44,11 +44,15 @@ async def test_consolidate_memory_empty_owner_treats_each_owner_separately(monke
|
|||||||
],
|
],
|
||||||
)
|
)
|
||||||
monkeypatch.setattr(constants, "DATA_DIR", str(data_dir))
|
monkeypatch.setattr(constants, "DATA_DIR", str(data_dir))
|
||||||
monkeypatch.setattr(endpoint_resolver, "resolve_endpoint", lambda *args, **kwargs: ("http://llm", "model", {}))
|
monkeypatch.setattr(
|
||||||
|
task_endpoint,
|
||||||
|
"resolve_task_candidates",
|
||||||
|
lambda *args, **kwargs: [("http://llm", "model", {})],
|
||||||
|
)
|
||||||
|
|
||||||
prompts = []
|
prompts = []
|
||||||
|
|
||||||
async def fake_llm_call_async(**kwargs):
|
async def fake_llm_call_async(_candidates, **kwargs):
|
||||||
prompt = kwargs["messages"][0]["content"]
|
prompt = kwargs["messages"][0]["content"]
|
||||||
prompts.append(prompt)
|
prompts.append(prompt)
|
||||||
if "alice-long" in prompt:
|
if "alice-long" in prompt:
|
||||||
@@ -71,7 +75,7 @@ async def test_consolidate_memory_empty_owner_treats_each_owner_separately(monke
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
monkeypatch.setattr(llm_core, "llm_call_async", fake_llm_call_async)
|
monkeypatch.setattr(llm_core, "llm_call_async_with_fallback", fake_llm_call_async)
|
||||||
|
|
||||||
message, ok = await action_consolidate_memory("")
|
message, ok = await action_consolidate_memory("")
|
||||||
|
|
||||||
|
|||||||
@@ -29,24 +29,24 @@ class _FakeMM:
|
|||||||
|
|
||||||
def test_omitted_memory_survives_only_explicit_drop(monkeypatch):
|
def test_omitted_memory_survives_only_explicit_drop(monkeypatch):
|
||||||
import src.memory
|
import src.memory
|
||||||
import src.endpoint_resolver
|
|
||||||
import src.llm_core
|
import src.llm_core
|
||||||
|
import src.task_endpoint
|
||||||
|
|
||||||
_FakeMM.saved = None
|
_FakeMM.saved = None
|
||||||
monkeypatch.setattr(src.memory, "MemoryManager", _FakeMM)
|
monkeypatch.setattr(src.memory, "MemoryManager", _FakeMM)
|
||||||
monkeypatch.setattr(
|
monkeypatch.setattr(
|
||||||
src.endpoint_resolver, "resolve_endpoint",
|
src.task_endpoint, "resolve_task_candidates",
|
||||||
lambda kind, owner=None: ("http://x/v1", "model", {}),
|
lambda owner=None: [("http://x/v1", "model", {})],
|
||||||
)
|
)
|
||||||
|
|
||||||
async def fake_llm(**kwargs):
|
async def fake_llm(_candidates, **kwargs):
|
||||||
# Model keeps 'a', drops 'b', and OMITS 'c' entirely.
|
# Model keeps 'a', drops 'b', and OMITS 'c' entirely.
|
||||||
return json.dumps({
|
return json.dumps({
|
||||||
"keep": [{"id": "a", "text": "Likes dark roast coffee", "category": "preference"}],
|
"keep": [{"id": "a", "text": "Likes dark roast coffee", "category": "preference"}],
|
||||||
"drop": [{"id": "b", "reason": "duplicate of a"}],
|
"drop": [{"id": "b", "reason": "duplicate of a"}],
|
||||||
})
|
})
|
||||||
|
|
||||||
monkeypatch.setattr(src.llm_core, "llm_call_async", fake_llm)
|
monkeypatch.setattr(src.llm_core, "llm_call_async_with_fallback", fake_llm)
|
||||||
|
|
||||||
msg, ok = asyncio.run(ba.action_consolidate_memory("alice"))
|
msg, ok = asyncio.run(ba.action_consolidate_memory("alice"))
|
||||||
|
|
||||||
|
|||||||
@@ -68,18 +68,18 @@ def test_vllm_blank_swap_omits_swap_space_flag():
|
|||||||
def test_serve_preflight_uses_selected_server_not_stale_env_host():
|
def test_serve_preflight_uses_selected_server_not_stale_env_host():
|
||||||
text = SERVE_SRC.read_text(encoding="utf-8")
|
text = SERVE_SRC.read_text(encoding="utf-8")
|
||||||
|
|
||||||
assert "const _selectedServeTarget = (() => {" in text
|
assert "function _selectedServeTarget(panel) {" in text
|
||||||
assert "const _hostStr = _selectedServeTarget.host || '';" in text
|
assert "const _hostStr = launchTarget.host || '';" in text
|
||||||
assert "(t.remoteHost || '') === _hostStr" in text
|
assert "(t.remoteHost || '') === _hostStr" in text
|
||||||
assert "const _probeHost = (_selectedServeTarget.host || '').trim();" in text
|
assert "const _probeHost = (launchTarget.host || '').trim();" in text
|
||||||
assert "const _portHost = (_selectedServeTarget.host || '').trim();" in text
|
assert "const _portHost = (launchTarget.host || '').trim();" in text
|
||||||
|
|
||||||
|
|
||||||
def test_vllm_route_strips_swap_space_when_runtime_rejects_it():
|
def test_vllm_route_strips_swap_space_when_runtime_rejects_it():
|
||||||
text = ROUTES_SRC.read_text(encoding="utf-8")
|
text = ROUTES_SRC.read_text(encoding="utf-8")
|
||||||
|
|
||||||
assert "Removing --swap-space 0; off is represented by omitting the vLLM flag." in text
|
assert "Setting vLLM --swap-space 0 so the runtime does not reserve CPU swap per GPU." in text
|
||||||
assert "vLLM serve does not support --swap-space; removing it" in text
|
assert "vLLM serve does not expose --swap-space; removing the flag and patching the runtime default to 0." in text
|
||||||
assert "ODYSSEUS_VLLM_HELP_CMD" in text
|
assert "ODYSSEUS_VLLM_HELP_CMD" in text
|
||||||
assert "print(shlex.join(parts[:serve_i + 1] + [\"--help\"]))" in text
|
assert "print(shlex.join(parts[:serve_i + 1] + [\"--help\"]))" in text
|
||||||
assert "eval \"$ODYSSEUS_VLLM_HELP_CMD\" 2>&1 | grep -q -- \"--swap-space\"" in text
|
assert "eval \"$ODYSSEUS_VLLM_HELP_CMD\" 2>&1 | grep -q -- \"--swap-space\"" in text
|
||||||
|
|||||||
@@ -348,7 +348,7 @@ def test_serve_pip_install_normalizes_llama_cpp_alias_and_adds_wheel_index():
|
|||||||
src = (pathlib.Path(__file__).resolve().parent.parent
|
src = (pathlib.Path(__file__).resolve().parent.parent
|
||||||
/ "routes" / "cookbook_routes.py").read_text(encoding="utf-8")
|
/ "routes" / "cookbook_routes.py").read_text(encoding="utf-8")
|
||||||
|
|
||||||
assert "re.sub(r\"(?<![A-Za-z0-9_.-])llama_cpp(?![A-Za-z0-9_.-])\", \"llama-cpp-python[server]\", req.cmd)" in src
|
assert "re.sub(r\"(?<![A-Za-z0-9_.\\-/])llama_cpp(?![A-Za-z0-9_.\\-/])\", \"llama-cpp-python[server]\", req.cmd)" in src
|
||||||
assert "if \"llama-cpp-python\" in req.cmd and \"--extra-index-url\" not in req.cmd:" in src
|
assert "if \"llama-cpp-python\" in req.cmd and \"--extra-index-url\" not in req.cmd:" in src
|
||||||
assert "https://abetlen.github.io/llama-cpp-python/whl/cpu" in src
|
assert "https://abetlen.github.io/llama-cpp-python/whl/cpu" in src
|
||||||
|
|
||||||
@@ -626,7 +626,7 @@ def test_llama_cpp_linux_bootstrap_prefers_rocm_before_cuda():
|
|||||||
script = "\n".join(runner_lines)
|
script = "\n".join(runner_lines)
|
||||||
|
|
||||||
assert "mkdir -p ~/bin" in script
|
assert "mkdir -p ~/bin" in script
|
||||||
assert script.index("mkdir -p ~/bin") < script.index("cd ~/llama.cpp && rm -rf build")
|
assert script.index("mkdir -p ~/bin") < script.index("cd ~/llama.cpp")
|
||||||
assert 'command -v hipconfig &>/dev/null || [ -d /opt/rocm ] || [ -n "$ROCM_PATH" ] || [ -n "$HIP_PATH" ]' in script
|
assert 'command -v hipconfig &>/dev/null || [ -d /opt/rocm ] || [ -n "$ROCM_PATH" ] || [ -n "$HIP_PATH" ]' in script
|
||||||
assert 'cmake -B build -DCMAKE_BUILD_TYPE=Release -DGGML_HIP=ON' in script
|
assert 'cmake -B build -DCMAKE_BUILD_TYPE=Release -DGGML_HIP=ON' in script
|
||||||
assert 'cmake -B build -DCMAKE_BUILD_TYPE=Release -DGGML_CUDA=ON' in script
|
assert 'cmake -B build -DCMAKE_BUILD_TYPE=Release -DGGML_CUDA=ON' in script
|
||||||
@@ -676,7 +676,7 @@ def test_llama_cpp_linux_bootstrap_nvcc_without_cudart_warns_and_falls_back():
|
|||||||
# outer else that handles no-GPU-toolchain). Verify it appears at least once
|
# outer else that handles no-GPU-toolchain). Verify it appears at least once
|
||||||
# before the outer "no HIP/CUDA toolchain" warning.
|
# before the outer "no HIP/CUDA toolchain" warning.
|
||||||
cpu_cmake = 'cmake -B build -DCMAKE_BUILD_TYPE=Release &&'
|
cpu_cmake = 'cmake -B build -DCMAKE_BUILD_TYPE=Release &&'
|
||||||
no_toolchain_warn = 'WARNING: no HIP/CUDA toolchain found'
|
no_toolchain_warn = 'WARNING: no HIP/CUDA/Vulkan toolchain found'
|
||||||
assert cpu_cmake in script
|
assert cpu_cmake in script
|
||||||
assert script.index(cpu_cmake) < script.index(no_toolchain_warn)
|
assert script.index(cpu_cmake) < script.index(no_toolchain_warn)
|
||||||
|
|
||||||
@@ -693,8 +693,8 @@ def test_llama_cpp_linux_bootstrap_keeps_cpu_fallback_when_no_gpu_toolchain():
|
|||||||
_append_llama_cpp_linux_accel_build_lines(runner_lines)
|
_append_llama_cpp_linux_accel_build_lines(runner_lines)
|
||||||
script = "\n".join(runner_lines)
|
script = "\n".join(runner_lines)
|
||||||
|
|
||||||
assert 'WARNING: no HIP/CUDA toolchain found — building llama-server for CPU only.' in script
|
assert 'WARNING: no HIP/CUDA/Vulkan toolchain found — building llama-server for CPU only.' in script
|
||||||
assert 'Install ROCm for AMD GPUs or vLLM/CUDA tooling for NVIDIA' in script
|
assert 'Install Vulkan (libvulkan-dev) / ROCm for AMD GPUs or CUDA tooling for NVIDIA' in script
|
||||||
|
|
||||||
|
|
||||||
def test_llama_cpp_rebuild_cmd_clears_cached_build_paths():
|
def test_llama_cpp_rebuild_cmd_clears_cached_build_paths():
|
||||||
|
|||||||
@@ -50,14 +50,14 @@ def test_serve_launch_preflights_use_selected_target_and_port():
|
|||||||
assert "if (launchTarget.port) _probeParams.set('ssh_port', launchTarget.port);" in SERVE
|
assert "if (launchTarget.port) _probeParams.set('ssh_port', launchTarget.port);" in SERVE
|
||||||
assert "const _portHost = (launchTarget.host || '').trim();" in SERVE
|
assert "const _portHost = (launchTarget.host || '').trim();" in SERVE
|
||||||
assert "StrictHostKeyChecking=no ${_sshPrefix(launchTarget.port)}${_portHost}" in SERVE
|
assert "StrictHostKeyChecking=no ${_sshPrefix(launchTarget.port)}${_portHost}" in SERVE
|
||||||
assert "let serveHost = launchTarget.host || '';" in SERVE
|
assert "const serveHost = launchTarget.host || '';" in SERVE
|
||||||
assert SERVE.index(launch_target) < SERVE.index("const _runningMod = await import('./cookbookRunning.js');")
|
assert SERVE.index(launch_target) < SERVE.index("const _runningMod = await import('./cookbookRunning.js');")
|
||||||
|
|
||||||
|
|
||||||
def test_running_tab_resolves_profile_key_not_first_host():
|
def test_running_tab_resolves_profile_key_not_first_host():
|
||||||
assert "_serverByVal(_envState.remoteServerKey || _tHost)" in RUNNING
|
assert "_serverByVal(_targetKey)" in RUNNING
|
||||||
assert "_serverByVal(_envState.remoteServerKey || _host)" in RUNNING
|
assert "_serverByVal(_envState.remoteServerKey || _host)" in RUNNING
|
||||||
assert "_serverByVal(_envState.remoteServerKey || host)" in RUNNING
|
assert "_serverByVal(savedKey)" in RUNNING
|
||||||
assert "_serverByVal = shared._serverByVal;" in RUNNING
|
assert "_serverByVal = shared._serverByVal;" in RUNNING
|
||||||
assert "_selectedServer = shared._selectedServer;" in RUNNING
|
assert "_selectedServer = shared._selectedServer;" in RUNNING
|
||||||
|
|
||||||
|
|||||||
@@ -59,8 +59,8 @@ def test_docker_entrypoint_does_not_resolve_root_commands_from_app_local_path():
|
|||||||
path_export = script.index('export PATH="/app/.local/bin:$PATH"')
|
path_export = script.index('export PATH="/app/.local/bin:$PATH"')
|
||||||
gosu_capture = script.index('GOSU_BIN="$(command -v gosu)"')
|
gosu_capture = script.index('GOSU_BIN="$(command -v gosu)"')
|
||||||
python_capture = script.index('PYTHON_BIN="$(command -v python)"')
|
python_capture = script.index('PYTHON_BIN="$(command -v python)"')
|
||||||
setup_call = script.index('"$GOSU_BIN" "$PUID:$PGID" "$PYTHON_BIN" /app/setup.py')
|
setup_call = script.index('"$GOSU_BIN" "$ODY_USER" "$PYTHON_BIN" /app/setup.py')
|
||||||
final_exec = script.index('exec "$GOSU_BIN" "$PUID:$PGID" "$@"')
|
final_exec = script.index('exec "$GOSU_BIN" "$ODY_USER" "$@"')
|
||||||
|
|
||||||
assert gosu_capture < path_export < setup_call
|
assert gosu_capture < path_export < setup_call
|
||||||
assert python_capture < path_export < setup_call
|
assert python_capture < path_export < setup_call
|
||||||
|
|||||||
Reference in New Issue
Block a user