From 993bf4ce812a37802f2a578d3cfbaee42061b796 Mon Sep 17 00:00:00 2001 From: Juan Sugg Date: Sun, 29 Mar 2026 03:25:42 -0300 Subject: [PATCH] Add isolation regressions and nightly profile validation --- .../workflows/memory-plugin-verification.yml | 1 + .github/workflows/nightly.yml | 2 + tests/scripts/compatibility_matrix.py | 14 +++ tests/scripts/memory_plugin_verification.py | 18 ++++ .../repo/test_ci_workflow_surfaces.py | 6 ++ .../unit/ci/test_compatibility_matrix.py | 71 ++++++++++++++ tests/suites/unit/ci/test_fresh_host.py | 95 ++++++++++++++++++- .../ci/test_memory_plugin_verification.py | 80 ++++++++++++++++ .../unit/clawops/test_strongclaw_ops.py | 50 ++++++++++ .../helpers/_ci_workflows/compatibility.py | 34 ++++++- .../helpers/_ci_workflows/memory_plugin.py | 36 +++++++ tests/utils/helpers/_fresh_host/shell.py | 16 ++++ tests/utils/helpers/ci_workflows.py | 4 + 13 files changed, 425 insertions(+), 2 deletions(-) diff --git a/.github/workflows/memory-plugin-verification.yml b/.github/workflows/memory-plugin-verification.yml index b541168..c679b3b 100644 --- a/.github/workflows/memory-plugin-verification.yml +++ b/.github/workflows/memory-plugin-verification.yml @@ -63,6 +63,7 @@ jobs: enable-cache: true - run: uv sync --locked - run: PYTHONPATH=src uv run python -m clawops config --asset-root . memory --set-profile memory-lancedb-pro --output "$RUNNER_TEMP/openclaw.json" + - run: python3 ./tests/scripts/memory_plugin_verification.py run-clawops-memory-migration --repo-root . --runner-temp "$RUNNER_TEMP" - run: python3 ./tests/scripts/memory_plugin_verification.py run-vendored-host-checks --repo-root . --package-spec "openclaw@2026.3.13" verify-hypermemory-qdrant: name: Run Hypermemory Qdrant Checks diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index b143778..47af112 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -32,6 +32,8 @@ jobs: run: PYTHONPATH=src uv run python -m compileall -q src tests - name: Create nightly artifact directory run: mkdir -p "$RUNNER_TEMP/strongclaw/nightly" + - name: Render nightly OpenClaw profile matrix + run: ./tests/scripts/compatibility_matrix.py assert-openclaw-profiles --repo-root . --runner-temp "${RUNNER_TEMP}" - name: Run nightly security harness run: PYTHONPATH=src uv run python -m clawops harness --suite platform/configs/harness/security_regressions.yaml --output "$RUNNER_TEMP/strongclaw/nightly/security.jsonl" - name: Run nightly policy harness diff --git a/tests/scripts/compatibility_matrix.py b/tests/scripts/compatibility_matrix.py index 6364086..40b6cb8 100644 --- a/tests/scripts/compatibility_matrix.py +++ b/tests/scripts/compatibility_matrix.py @@ -17,6 +17,7 @@ from tests.utils.helpers._ci_workflows.compatibility import ( # noqa: E402 assert_hypermemory_config, assert_lossless_claw_installed, + assert_openclaw_profiles_render, prepare_setup_smoke, ) @@ -46,6 +47,13 @@ def _parse_args(argv: list[str] | None = None) -> argparse.Namespace: ) config_parser.add_argument("--tmp-root", type=Path, required=True) + profile_parser = subparsers.add_parser( + "assert-openclaw-profiles", + help="Render all OpenClaw profiles for nightly validation.", + ) + profile_parser.add_argument("--repo-root", type=Path, default=REPO_ROOT) + profile_parser.add_argument("--runner-temp", type=Path, required=True) + return parser.parse_args(argv) @@ -70,6 +78,12 @@ def main(argv: list[str] | None = None) -> int: if args.command == "assert-hypermemory-config": assert_hypermemory_config(Path(args.tmp_root).expanduser().resolve()) return 0 + if args.command == "assert-openclaw-profiles": + assert_openclaw_profiles_render( + Path(args.repo_root).expanduser().resolve(), + Path(args.runner_temp).expanduser().resolve(), + ) + return 0 except CiWorkflowError as exc: print(f"compatibility-matrix error: {exc}", file=sys.stderr) return 1 diff --git a/tests/scripts/memory_plugin_verification.py b/tests/scripts/memory_plugin_verification.py index 139cff3..aa17653 100644 --- a/tests/scripts/memory_plugin_verification.py +++ b/tests/scripts/memory_plugin_verification.py @@ -16,6 +16,7 @@ from tests.utils.helpers._ci_workflows.common import CiWorkflowError # noqa: E402 from tests.utils.helpers._ci_workflows.memory_plugin import ( # noqa: E402 DEFAULT_OPENCLAW_PACKAGE_SPEC, + run_clawops_memory_migration, run_vendored_host_checks, wait_for_qdrant, ) @@ -33,6 +34,13 @@ def _parse_args(argv: list[str] | None = None) -> argparse.Namespace: vendored_parser.add_argument("--repo-root", type=Path, default=REPO_ROOT) vendored_parser.add_argument("--package-spec", default=DEFAULT_OPENCLAW_PACKAGE_SPEC) + migration_parser = subparsers.add_parser( + "run-clawops-memory-migration", + help="Run clawops memory migration in dry-run mode.", + ) + migration_parser.add_argument("--repo-root", type=Path, default=REPO_ROOT) + migration_parser.add_argument("--runner-temp", type=Path) + qdrant_parser = subparsers.add_parser("wait-for-qdrant", help="Wait for Qdrant readiness.") qdrant_parser.add_argument("--url", required=True) qdrant_parser.add_argument("--attempts", type=int, default=30) @@ -51,6 +59,16 @@ def main(argv: list[str] | None = None) -> int: package_spec=str(args.package_spec), ) return 0 + if args.command == "run-clawops-memory-migration": + run_clawops_memory_migration( + Path(args.repo_root).expanduser().resolve(), + runner_temp=( + Path(args.runner_temp).expanduser().resolve() + if args.runner_temp is not None + else None + ), + ) + return 0 if args.command == "wait-for-qdrant": wait_for_qdrant( str(args.url), diff --git a/tests/suites/contracts/repo/test_ci_workflow_surfaces.py b/tests/suites/contracts/repo/test_ci_workflow_surfaces.py index 731ec6a..63cacbd 100644 --- a/tests/suites/contracts/repo/test_ci_workflow_surfaces.py +++ b/tests/suites/contracts/repo/test_ci_workflow_surfaces.py @@ -251,12 +251,18 @@ def test_remaining_workflow_logic_routes_through_semantic_scripts() -> None: """Refactored workflow lanes should route operational logic through semantic scripts.""" compatibility = _workflow_text("compatibility-matrix.yml") memory_plugin = _workflow_text("memory-plugin-verification.yml") + nightly = _workflow_text("nightly.yml") security = _workflow_text("security.yml") release = _workflow_text("release.yml") assert "./tests/scripts/compatibility_matrix.py prepare-setup-smoke" in compatibility assert "./tests/scripts/compatibility_matrix.py assert-lossless-claw" in compatibility assert "./tests/scripts/compatibility_matrix.py assert-hypermemory-config" in compatibility + assert "./tests/scripts/compatibility_matrix.py assert-openclaw-profiles" in nightly + assert ( + "./tests/scripts/memory_plugin_verification.py run-clawops-memory-migration" + in memory_plugin + ) assert "./tests/scripts/memory_plugin_verification.py run-vendored-host-checks" in memory_plugin assert "./tests/scripts/memory_plugin_verification.py wait-for-qdrant" in memory_plugin assert "./tests/scripts/security_workflow.py write-coverage-summary" in security diff --git a/tests/suites/unit/ci/test_compatibility_matrix.py b/tests/suites/unit/ci/test_compatibility_matrix.py index 6f60e4a..1c64676 100644 --- a/tests/suites/unit/ci/test_compatibility_matrix.py +++ b/tests/suites/unit/ci/test_compatibility_matrix.py @@ -177,3 +177,74 @@ def fake_prepare_setup_smoke( github_env_file.resolve(), ) ] + + +def test_assert_openclaw_profiles_render_writes_one_file_per_profile( + test_context: TestContext, + tmp_path: Path, +) -> None: + """Nightly profile validation should render every OpenClaw profile to disk.""" + repo_root = tmp_path / "repo" + repo_root.mkdir() + runner_temp = tmp_path / "runner-temp" + seen_calls: list[tuple[str, Path, Path]] = [] + + def fake_render_openclaw_profile( + *, + profile_name: str, + repo_root: Path, + home_dir: Path, + ) -> dict[str, str]: + seen_calls.append((profile_name, repo_root, home_dir)) + return {"profile": profile_name} + + test_context.patch.patch_object( + compatibility_helpers, + "render_openclaw_profile", + new=fake_render_openclaw_profile, + ) + + rendered_profiles = ci_workflows.assert_openclaw_profiles_render(repo_root, runner_temp) + + expected_profiles = sorted(compatibility_helpers.PROFILES) + expected_home = (runner_temp / "strongclaw" / "nightly" / "profile-home").resolve() + expected_output_dir = (runner_temp / "strongclaw" / "nightly" / "openclaw-profiles").resolve() + assert rendered_profiles == expected_profiles + assert [profile for profile, _, _ in seen_calls] == expected_profiles + assert all(resolved_repo_root == repo_root.resolve() for _, resolved_repo_root, _ in seen_calls) + assert all(home_dir == expected_home for _, _, home_dir in seen_calls) + for profile_name in expected_profiles: + payload = json.loads( + (expected_output_dir / f"{profile_name}.json").read_text(encoding="utf-8") + ) + assert payload == {"profile": profile_name} + + +def test_main_dispatches_assert_openclaw_profiles( + test_context: TestContext, + tmp_path: Path, +) -> None: + """The CLI should dispatch all-profile rendering checks.""" + seen_calls: list[tuple[Path, Path]] = [] + + def fake_assert_openclaw_profiles_render(repo_root: Path, runner_temp: Path) -> None: + seen_calls.append((repo_root, runner_temp)) + + test_context.patch.patch_object( + compatibility_matrix_script, + "assert_openclaw_profiles_render", + new=fake_assert_openclaw_profiles_render, + ) + + exit_code = compatibility_matrix_script.main( + [ + "assert-openclaw-profiles", + "--repo-root", + str(tmp_path / "repo"), + "--runner-temp", + str(tmp_path / "runner-temp"), + ] + ) + + assert exit_code == 0 + assert seen_calls == [((tmp_path / "repo").resolve(), (tmp_path / "runner-temp").resolve())] diff --git a/tests/suites/unit/ci/test_fresh_host.py b/tests/suites/unit/ci/test_fresh_host.py index 71f2d4c..0b7f26e 100644 --- a/tests/suites/unit/ci/test_fresh_host.py +++ b/tests/suites/unit/ci/test_fresh_host.py @@ -5,7 +5,7 @@ import argparse import json import subprocess -from collections.abc import Callable +from collections.abc import Callable, Mapping from pathlib import Path from typing import Any, cast @@ -613,6 +613,99 @@ def fake_run( ) +def test_verify_compose_services_running_ignores_isolated_runtime_keys_from_local_env( + tmp_path: Path, + test_context: TestContext, +) -> None: + """Compose probes should not accept legacy runtime-path keys when isolation is active.""" + repo_root = tmp_path / "repo" + compose_dir = repo_root / "platform" / "compose" + compose_dir.mkdir(parents=True) + compose_file = compose_dir / "docker-compose.browser-lab.yaml" + compose_file.write_text("services: {}\n", encoding="utf-8") + home_dir = tmp_path / "home" + home_dir.mkdir() + runtime_root = home_dir / "runtime-root" + local_env_file = tmp_path / "legacy.env.local" + local_env_file.write_text( + "\n".join( + ( + "OPENCLAW_STATE_DIR=/tmp/legacy-openclaw-state", + "OPENCLAW_CONFIG_PATH=/tmp/legacy-openclaw.json", + "OPENCLAW_PROFILE=legacy-profile", + "NEO4J_PASSWORD=repo-secret", + ) + ) + + "\n", + encoding="utf-8", + ) + captured_env: dict[str, str] = {} + payload = json.dumps( + [ + {"Service": "browserlab-proxy", "State": "running"}, + {"Service": "browserlab-playwright", "State": "running"}, + ] + ) + + def fake_run( + args: list[str], + *, + cwd: Path | None = None, + env: dict[str, str] | None = None, + check: bool = False, + timeout: float | None = None, + text: bool = False, + capture_output: bool = False, + ) -> subprocess.CompletedProcess[str]: + del args, cwd, check, timeout, text, capture_output + assert env is not None + captured_env.update(env) + return subprocess.CompletedProcess( + args=["docker", "compose"], + returncode=0, + stdout=payload, + stderr="", + ) + + def _varlock_local_env_file( + _repo_root: Path, + *, + home_dir: Path | None = None, + environ: Mapping[str, str] | None = None, + ) -> Path: + del home_dir, environ + return local_env_file + + test_context.patch.patch_object(fresh_host_shell.subprocess, "run", new=fake_run) + test_context.patch.patch_object( + fresh_host_shell, + "varlock_local_env_file", + new=_varlock_local_env_file, + ) + + fresh_host_shell.verify_compose_services_running( + compose_file, + cwd=compose_dir, + env={ + "HOME": str(home_dir), + "PATH": "/usr/bin", + "STRONGCLAW_RUNTIME_ROOT": str(runtime_root), + }, + expected_services=("browserlab-proxy", "browserlab-playwright"), + repo_root_path=repo_root, + repo_local_state=True, + ) + + expected_state_dir = (runtime_root / ".openclaw").resolve() + expected_config_path = expected_state_dir / "openclaw.json" + assert captured_env["OPENCLAW_HOME"] == str(runtime_root.resolve()) + assert captured_env["OPENCLAW_STATE_DIR"] == str(expected_state_dir) + assert captured_env["OPENCLAW_CONFIG_PATH"] == str(expected_config_path) + assert captured_env["OPENCLAW_CONFIG"] == str(expected_config_path) + assert captured_env["OPENCLAW_PROFILE"] == "strongclaw-dev" + assert captured_env["NEO4J_PASSWORD"] == "repo-secret" + + def test_verify_compose_services_running_honors_repo_local_state_override_for_variants( tmp_path: Path, test_context: TestContext, diff --git a/tests/suites/unit/ci/test_memory_plugin_verification.py b/tests/suites/unit/ci/test_memory_plugin_verification.py index 322eae3..080797c 100644 --- a/tests/suites/unit/ci/test_memory_plugin_verification.py +++ b/tests/suites/unit/ci/test_memory_plugin_verification.py @@ -88,6 +88,51 @@ def fake_urlopen(url: str, timeout: int) -> _Response: assert sleeps == [1.5, 1.5] +def test_run_clawops_memory_migration_invokes_dry_run_cli( + test_context: TestContext, + tmp_path: Path, +) -> None: + """Memory migration checks should execute the dry-run clawops migration CLI.""" + repo_root = tmp_path / "repo" + repo_root.mkdir() + runner_temp = tmp_path / "runner-temp" + seen_calls: list[tuple[list[str], Path | None, dict[str, str] | None]] = [] + + def fake_run_checked( + command: list[str], + *, + cwd: Path | None = None, + env: dict[str, str] | None = None, + timeout_seconds: int | None = None, + capture_output: bool = False, + ) -> Any: + del timeout_seconds, capture_output + seen_calls.append((command, cwd, env)) + return None + + test_context.patch.patch_object(memory_plugin_helpers, "run_checked", new=fake_run_checked) + test_context.env.remove("PYTHONPATH") + + report_path = ci_workflows.run_clawops_memory_migration(repo_root, runner_temp=runner_temp) + + assert report_path == runner_temp.resolve() / "clawops-memory-migration-report.json" + command, command_cwd, command_env = seen_calls[0] + assert command[:8] == [ + "uv", + "run", + "python", + "-m", + "clawops", + "memory", + "migrate-hypermemory-to-pro", + "--dry-run", + ] + assert command[-2:] == ["--report", str(report_path)] + assert command_cwd == repo_root.resolve() + assert command_env is not None + assert command_env["PYTHONPATH"] == "src" + + def test_main_dispatches_wait_for_qdrant(test_context: TestContext) -> None: """The CLI should dispatch Qdrant readiness checks.""" seen_calls: list[tuple[str, int, float]] = [] @@ -107,3 +152,38 @@ def fake_wait_for_qdrant(url: str, *, attempts: int = 30, sleep_seconds: float = assert exit_code == 0 assert seen_calls == [("http://127.0.0.1:6333/healthz", 12, 2.0)] + + +def test_main_dispatches_run_clawops_memory_migration( + test_context: TestContext, + tmp_path: Path, +) -> None: + """The CLI should dispatch dry-run clawops memory migration checks.""" + seen_calls: list[tuple[Path, Path | None]] = [] + + def fake_run_clawops_memory_migration( + repo_root: Path, + *, + runner_temp: Path | None = None, + ) -> Path: + seen_calls.append((repo_root, runner_temp)) + return tmp_path / "report.json" + + test_context.patch.patch_object( + memory_plugin_script, + "run_clawops_memory_migration", + new=fake_run_clawops_memory_migration, + ) + + exit_code = memory_plugin_script.main( + [ + "run-clawops-memory-migration", + "--repo-root", + str(tmp_path / "repo"), + "--runner-temp", + str(tmp_path / "runner-temp"), + ] + ) + + assert exit_code == 0 + assert seen_calls == [((tmp_path / "repo").resolve(), (tmp_path / "runner-temp").resolve())] diff --git a/tests/suites/unit/clawops/test_strongclaw_ops.py b/tests/suites/unit/clawops/test_strongclaw_ops.py index 15cc4c0..58ed2dc 100644 --- a/tests/suites/unit/clawops/test_strongclaw_ops.py +++ b/tests/suites/unit/clawops/test_strongclaw_ops.py @@ -190,6 +190,56 @@ def test_compose_env_exports_isolated_runtime_contract( assert env["STRONGCLAW_RUNTIME_ROOT"] == str(runtime_root) +def test_compose_env_ignores_isolated_runtime_keys_from_local_env( + monkeypatch: pytest.MonkeyPatch, + tmp_path: pathlib.Path, +) -> None: + """Compose env should keep runtime isolation even if local env files define legacy paths.""" + runtime_root = tmp_path / "dev-runtime" + local_env_file = tmp_path / "legacy.env.local" + local_env_file.write_text( + "\n".join( + ( + "OPENCLAW_STATE_DIR=/tmp/legacy-openclaw-state", + "OPENCLAW_CONFIG_PATH=/tmp/legacy-openclaw.json", + "OPENCLAW_CONFIG=/tmp/legacy-openclaw-config.json", + "OPENCLAW_HOME=/tmp/legacy-openclaw-home", + "OPENCLAW_PROFILE=legacy-profile", + "NEO4J_PASSWORD=repo-secret", + ) + ) + + "\n", + encoding="utf-8", + ) + + def _varlock_local_env_file( + _repo_root: pathlib.Path, + *, + home_dir: pathlib.Path | None = None, + environ: Mapping[str, str] | None = None, + ) -> pathlib.Path: + del home_dir, environ + return local_env_file + + monkeypatch.setenv("STRONGCLAW_RUNTIME_ROOT", str(runtime_root)) + monkeypatch.setattr(strongclaw_ops, "varlock_local_env_file", _varlock_local_env_file) + + env = _compose_env( + REPO_ROOT, + repo_local_state=False, + compose_name="docker-compose.aux-stack.yaml", + ) + + expected_state_dir = runtime_root / ".openclaw" + expected_config_path = expected_state_dir / "openclaw.json" + assert env["OPENCLAW_HOME"] == str(runtime_root) + assert env["OPENCLAW_STATE_DIR"] == str(expected_state_dir) + assert env["OPENCLAW_CONFIG_PATH"] == str(expected_config_path) + assert env["OPENCLAW_CONFIG"] == str(expected_config_path) + assert env["OPENCLAW_PROFILE"] == "strongclaw-dev" + assert env["NEO4J_PASSWORD"] == "repo-secret" + + def test_compose_env_inherits_repo_local_varlock_assignments( monkeypatch: pytest.MonkeyPatch, tmp_path: pathlib.Path ) -> None: diff --git a/tests/utils/helpers/_ci_workflows/compatibility.py b/tests/utils/helpers/_ci_workflows/compatibility.py index 5a4ac7f..86e6132 100644 --- a/tests/utils/helpers/_ci_workflows/compatibility.py +++ b/tests/utils/helpers/_ci_workflows/compatibility.py @@ -9,7 +9,7 @@ from typing import cast from clawops.common import write_json -from clawops.openclaw_config import render_openclaw_profile +from clawops.openclaw_config import PROFILES, render_openclaw_profile from clawops.strongclaw_bootstrap import ensure_varlock_installed, install_lossless_claw_asset from tests.utils.helpers._ci_workflows.common import ( CiWorkflowError, @@ -136,6 +136,38 @@ def assert_hypermemory_config(tmp_root: Path) -> None: raise CiWorkflowError(f"unexpected autoRecall setting: expected True, got {auto_recall!r}") +def assert_openclaw_profiles_render( + repo_root: Path, + runner_temp: Path, +) -> list[str]: + """Render every OpenClaw profile and persist artifacts for nightly inspection.""" + resolved_repo_root = repo_root.expanduser().resolve() + resolved_runner_temp = runner_temp.expanduser().resolve() + nightly_root = resolved_runner_temp / "strongclaw" / "nightly" + home_dir = nightly_root / "profile-home" + runtime_root = nightly_root / "profile-runtime-root" + output_dir = nightly_root / "openclaw-profiles" + for directory in (home_dir, runtime_root, output_dir): + directory.mkdir(parents=True, exist_ok=True) + + rendered_profiles: list[str] = [] + with patched_environment( + { + "HOME": str(home_dir), + "STRONGCLAW_RUNTIME_ROOT": str(runtime_root), + } + ): + for profile_name in sorted(PROFILES): + rendered = render_openclaw_profile( + profile_name=profile_name, + repo_root=resolved_repo_root, + home_dir=home_dir, + ) + write_json(output_dir / f"{profile_name}.json", rendered) + rendered_profiles.append(profile_name) + return rendered_profiles + + def _require_str_object_dict(value: object, *, label: str) -> dict[str, object]: """Validate that *value* is a string-keyed mapping.""" if not isinstance(value, dict): diff --git a/tests/utils/helpers/_ci_workflows/memory_plugin.py b/tests/utils/helpers/_ci_workflows/memory_plugin.py index b71469d..49f5b33 100644 --- a/tests/utils/helpers/_ci_workflows/memory_plugin.py +++ b/tests/utils/helpers/_ci_workflows/memory_plugin.py @@ -73,3 +73,39 @@ def wait_for_qdrant(url: str, *, attempts: int = 30, sleep_seconds: float = 2.0) break time.sleep(sleep_seconds) raise CiWorkflowError(f"Qdrant failed to become ready at {url} after {attempts} attempts") + + +def run_clawops_memory_migration( + repo_root: Path, + *, + runner_temp: Path | None = None, +) -> Path: + """Run a dry-run clawops memory migration and return the report path.""" + resolved_repo_root = repo_root.expanduser().resolve() + report_root = ( + runner_temp.expanduser().resolve() + if runner_temp is not None + else Path(tempfile.mkdtemp(prefix="strongclaw-memory-migration.")) + ) + report_root.mkdir(parents=True, exist_ok=True) + report_path = report_root / "clawops-memory-migration-report.json" + env = dict(os.environ) + if not env.get("PYTHONPATH", "").strip(): + env["PYTHONPATH"] = "src" + run_checked( + [ + "uv", + "run", + "python", + "-m", + "clawops", + "memory", + "migrate-hypermemory-to-pro", + "--dry-run", + "--report", + str(report_path), + ], + cwd=resolved_repo_root, + env=env, + ) + return report_path diff --git a/tests/utils/helpers/_fresh_host/shell.py b/tests/utils/helpers/_fresh_host/shell.py index f2a4dcd..3123dc4 100644 --- a/tests/utils/helpers/_fresh_host/shell.py +++ b/tests/utils/helpers/_fresh_host/shell.py @@ -19,6 +19,7 @@ resolve_openclaw_config_path, resolve_openclaw_state_dir, resolve_repo_local_compose_state_dir, + resolve_runtime_layout, varlock_local_env_file, ) from tests.utils.helpers._fresh_host.models import FreshHostContext, FreshHostError @@ -192,10 +193,25 @@ def _compose_probe_env( """Build the compose env used by the runtime probe.""" probe_env = dict(base_env) home_dir = Path(probe_env.get("HOME", Path.home().as_posix())).expanduser().resolve() + layout = resolve_runtime_layout( + repo_root=repo_root_path, + home_dir=home_dir, + environ=probe_env, + ) + isolated_runtime_keys = { + "OPENCLAW_HOME", + "OPENCLAW_STATE_DIR", + "OPENCLAW_CONFIG_PATH", + "OPENCLAW_CONFIG", + "OPENCLAW_PROFILE", + "STRONGCLAW_RUNTIME_ROOT", + } local_env = load_env_assignments( varlock_local_env_file(repo_root_path, home_dir=home_dir, environ=probe_env) ) for key, value in local_env.items(): + if layout.uses_isolated_runtime and key in isolated_runtime_keys: + continue if value and not probe_env.get(key, "").strip(): probe_env[key] = value openclaw_state_dir = resolve_openclaw_state_dir( diff --git a/tests/utils/helpers/ci_workflows.py b/tests/utils/helpers/ci_workflows.py index fd6f708..d3417f7 100644 --- a/tests/utils/helpers/ci_workflows.py +++ b/tests/utils/helpers/ci_workflows.py @@ -5,12 +5,14 @@ SetupSmokePaths, assert_hypermemory_config, assert_lossless_claw_installed, + assert_openclaw_profiles_render, prepare_setup_smoke, resolve_setup_smoke_paths, ) from tests.utils.helpers._ci_workflows.memory_plugin import ( AWS_CREDENTIAL_ENV_VARS, DEFAULT_OPENCLAW_PACKAGE_SPEC, + run_clawops_memory_migration, run_vendored_host_checks, wait_for_qdrant, ) @@ -34,12 +36,14 @@ "append_coverage_summary", "assert_hypermemory_config", "assert_lossless_claw_installed", + "assert_openclaw_profiles_render", "clean_artifact_directories", "install_gitleaks", "install_syft", "prepare_setup_smoke", "publish_github_release", "resolve_setup_smoke_paths", + "run_clawops_memory_migration", "run_vendored_host_checks", "verify_release_artifacts", "wait_for_qdrant",