Skip to content

Commit 4c4444a

Browse files
authored
Merge pull request #329 from QuantStrategyLab/fix/nested-service-target-controls
fix: normalize nested Cloud Run target controls
2 parents 8bc7a38 + 6bb35fc commit 4c4444a

2 files changed

Lines changed: 74 additions & 0 deletions

File tree

python/scripts/build_runtime_switch.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -962,6 +962,25 @@ def _patch_service_targets(
962962
current_entry=current_entry,
963963
replacement=replacement,
964964
)
965+
current_env = current_entry.get("env")
966+
if isinstance(current_env, dict):
967+
# Preserve the source shape of migrated inventories. A top-level
968+
# override would take precedence over env.* at deploy time while
969+
# leaving a stale nested value for consoles and future edits.
970+
nested_env = dict(current_env)
971+
structural_fields = {
972+
"service",
973+
"service_name",
974+
"cloud_run_service",
975+
"runtime_target",
976+
"ACCOUNT_GROUP",
977+
}
978+
for name, value in tuple(replacement.items()):
979+
if name in structural_fields:
980+
continue
981+
nested_env[name] = value
982+
replacement.pop(name)
983+
current_entry = {**current_entry, "env": nested_env}
965984
entries[matched_index] = {**current_entry, **replacement}
966985
elif not allow_create:
967986
platform_label = "IBKR " if platform == "ibkr" else ""

python/tests/test_runtime_settings.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3363,6 +3363,61 @@ def test_build_switch_target_patches_ibkr_service_targets_with_soxl_plugin_mount
33633363
self.assertEqual(selected["runtime_target"]["strategy_profile"], "soxl_soxx_trend_income")
33643364
self.assertEqual(selected["IBKR_STRATEGY_PLUGIN_MOUNTS_JSON"]["strategy_plugins"], [])
33653365

3366+
def test_build_switch_target_updates_nested_service_controls_without_stale_overrides(self):
3367+
existing = {
3368+
"targets": [
3369+
{
3370+
"service": "interactive-brokers-demo-ibkr-soxl-service",
3371+
"ACCOUNT_GROUP": "demo-ibkr-soxl",
3372+
"env": {
3373+
"RUNTIME_TARGET_ENABLED": "false",
3374+
"IBKR_DRY_RUN_ONLY": "false",
3375+
},
3376+
"runtime_target": {
3377+
"platform_id": "ibkr",
3378+
"strategy_profile": "soxl_soxx_trend_income",
3379+
"dry_run_only": False,
3380+
"deployment_selector": "demo-ibkr-soxl",
3381+
"account_selector": ["DEMO_IBKR_SOXL"],
3382+
"account_scope": "demo-ibkr-soxl",
3383+
"service_name": "interactive-brokers-demo-ibkr-soxl-service",
3384+
"execution_mode": "live",
3385+
},
3386+
},
3387+
],
3388+
}
3389+
path = ROOT / ".pytest_runtime_service_targets_nested_controls.json"
3390+
path.write_text(runtime_settings.compact_json(existing), encoding="utf-8")
3391+
self.addCleanup(lambda: path.unlink(missing_ok=True))
3392+
parser = build_runtime_switch.build_parser()
3393+
args = parser.parse_args(
3394+
[
3395+
"--platform",
3396+
"ibkr",
3397+
"--target-name",
3398+
"demo-ibkr-soxl",
3399+
"--strategy-profile",
3400+
"soxl_soxx_trend_income",
3401+
"--account-selector",
3402+
"DEMO_IBKR_SOXL",
3403+
"--service-name",
3404+
"interactive-brokers-demo-ibkr-soxl-service",
3405+
"--existing-service-targets-json-file",
3406+
str(path),
3407+
"--extra-variables-json",
3408+
'{"RUNTIME_TARGET_ENABLED":"true"}',
3409+
]
3410+
)
3411+
3412+
target = build_runtime_switch.build_switch_target(args)
3413+
assignments = {item.name: item.value for item in runtime_settings.build_assignments(target)}
3414+
selected = json.loads(assignments["CLOUD_RUN_SERVICE_TARGETS_JSON"])["targets"][0]
3415+
3416+
self.assertEqual(selected["env"]["RUNTIME_TARGET_ENABLED"], "true")
3417+
self.assertEqual(selected["env"]["IBKR_DRY_RUN_ONLY"], "false")
3418+
self.assertNotIn("RUNTIME_TARGET_ENABLED", selected)
3419+
self.assertNotIn("IBKR_DRY_RUN_ONLY", selected)
3420+
33663421
def test_build_switch_target_rejects_unknown_ibkr_service_target_by_default(self):
33673422
path = ROOT / ".pytest_runtime_service_targets_unknown.json"
33683423
path.write_text('{"targets":[]}', encoding="utf-8")

0 commit comments

Comments
 (0)