Skip to content

Commit 6d42abb

Browse files
Selective watch, fixes BT-12924
1 parent 5cf5423 commit 6d42abb

File tree

2 files changed

+67
-22
lines changed

2 files changed

+67
-22
lines changed

truss-chains/truss_chains/deployment/deployment_client.py

+31-4
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,7 @@ class _Watcher:
470470
_console: "rich_console.Console"
471471
_error_console: "rich_console.Console"
472472
_show_stack_trace: bool
473+
_included_chainlets: set[str]
473474

474475
def __init__(
475476
self,
@@ -480,6 +481,7 @@ def __init__(
480481
console: "rich_console.Console",
481482
error_console: "rich_console.Console",
482483
show_stack_trace: bool,
484+
included_chainlets: Optional[list[str]],
483485
) -> None:
484486
self._source = source
485487
self._entrypoint = entrypoint
@@ -498,6 +500,16 @@ def __init__(
498500
for desc in _get_ordered_dependencies([entrypoint_cls])
499501
)
500502

503+
if included_chainlets:
504+
if not_matched := (set(included_chainlets) - chainlet_names):
505+
raise definitions.ChainsDeploymentError(
506+
"Requested to watch specific chainlets, but did not find "
507+
f"{not_matched} among available chainlets {chainlet_names}."
508+
)
509+
self._included_chainlets = set(included_chainlets)
510+
else:
511+
self._included_chainlets = chainlet_names
512+
501513
chain_id = b10_core.get_chain_id_by_name(
502514
self._remote_provider.api, self._deployed_chain_name
503515
)
@@ -594,6 +606,13 @@ def _patch(self, executor: concurrent.futures.Executor) -> None:
594606
)
595607
future_to_display_name = {}
596608
for chainlet_descr in chainlet_descriptors:
609+
if chainlet_descr.display_name not in self._included_chainlets:
610+
self._console.print(
611+
f"⏩ Skipping patching `{chainlet_descr.display_name}`.",
612+
style="grey50",
613+
)
614+
continue
615+
597616
future = executor.submit(
598617
self._code_gen_and_patch_thread,
599618
chainlet_descr,
@@ -658,17 +677,17 @@ def _check_patch_results(
658677

659678
if patch_result.status == b10_remote.PatchStatus.SUCCESS:
660679
self._console.print(
661-
f"Patched Chainlet `{display_name}`.{logs_output}", style="green"
680+
f"Patched Chainlet `{display_name}`.{logs_output}", style="green"
662681
)
663682
elif patch_result.status == b10_remote.PatchStatus.SKIPPED:
664683
self._console.print(
665-
f"Nothing to do for Chainlet `{display_name}`.{logs_output}",
684+
f"💤 Nothing to do for Chainlet `{display_name}`.{logs_output}",
666685
style="grey50",
667686
)
668687
else:
669688
has_errors = True
670689
self._error_console.print(
671-
f"Failed to patch Chainlet `{display_name}`. "
690+
f"Failed to patch Chainlet `{display_name}`. "
672691
f"{patch_result.message}{logs_output}"
673692
)
674693

@@ -702,6 +721,7 @@ def watch(
702721
console: "rich_console.Console",
703722
error_console: "rich_console.Console",
704723
show_stack_trace: bool,
724+
included_chainlets: Optional[list[str]],
705725
) -> None:
706726
console.print(
707727
(
@@ -711,6 +731,13 @@ def watch(
711731
style="blue",
712732
)
713733
patcher = _Watcher(
714-
source, entrypoint, name, remote, console, error_console, show_stack_trace
734+
source,
735+
entrypoint,
736+
name,
737+
remote,
738+
console,
739+
error_console,
740+
show_stack_trace,
741+
included_chainlets,
715742
)
716743
patcher.watch()

truss/cli/cli.py

+36-18
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,6 @@
8686
]
8787
}
8888

89-
9089
console = Console()
9190

9291
error_console = Console(stderr=True, style="bold red")
@@ -555,11 +554,15 @@ def _create_chains_table(service) -> Tuple[rich.table.Table, List[str]]:
555554
help="Name of the remote in .trussrc to push to.",
556555
)
557556
@click.option(
558-
"--user_env",
559-
required=False,
557+
"--experimental-watch-chainlet-names",
560558
type=str,
561-
help="[DEPRECATED], use ``environment`` instead.",
562-
hidden=True,
559+
required=False,
560+
help=(
561+
"Runs `watch`, but only applies patches to specified chainlets. The option is "
562+
"a comma-separated list of chainlet (display) names. This option can give "
563+
"faster dev loops, but also lead to inconsistent deployments. Use with caution "
564+
"and refer to docs."
565+
),
563566
)
564567
@log_level_option
565568
@error_handling
@@ -572,9 +575,9 @@ def push_chain(
572575
wait: bool,
573576
watch: bool,
574577
dryrun: bool,
575-
user_env: Optional[str],
576578
remote: Optional[str],
577579
environment: Optional[str],
580+
experimental_watch_chainlet_names: Optional[str],
578581
) -> None:
579582
"""
580583
Deploys a chain remotely.
@@ -589,6 +592,9 @@ def push_chain(
589592
from truss_chains import framework
590593
from truss_chains.deployment import deployment_client
591594

595+
if experimental_watch_chainlet_names:
596+
watch = True
597+
592598
if watch:
593599
if publish or promote:
594600
raise ValueError(
@@ -600,13 +606,10 @@ def push_chain(
600606
)
601607
wait = True
602608

603-
if user_env:
604-
raise ValueError("`user_env` is deprecated, use `environment` instead.")
605-
606609
if promote and environment:
607610
promote_warning = (
608611
"`promote` flag and `environment` flag were both specified. "
609-
"Ignoring the value of `promote`"
612+
"Ignoring the value of `promote`."
610613
)
611614
console.print(promote_warning, style="yellow")
612615

@@ -674,7 +677,14 @@ def push_chain(
674677
)
675678
console.print(deploy_success_text, style="bold green")
676679
console.print(f"You can run the chain with:\n{curl_snippet}")
680+
677681
if watch: # Note that this command will print a startup message.
682+
if experimental_watch_chainlet_names:
683+
included_chainlets = [
684+
x.strip() for x in experimental_watch_chainlet_names.split(",")
685+
]
686+
else:
687+
included_chainlets = None
678688
deployment_client.watch(
679689
source,
680690
entrypoint,
@@ -683,6 +693,7 @@ def push_chain(
683693
console,
684694
error_console,
685695
show_stack_trace=not is_humanfriendly_log_level,
696+
included_chainlets=included_chainlets,
686697
)
687698
else:
688699
console.print(f"Deployment failed ({num_failed} failures).", style="red")
@@ -710,20 +721,24 @@ def push_chain(
710721
help="Name of the remote in .trussrc to push to.",
711722
)
712723
@click.option(
713-
"--user_env",
714-
required=False,
724+
"--experimental-chainlet-names",
715725
type=str,
716-
help="[DEPRECATED], use `environment` instead.",
717-
hidden=True,
726+
required=False,
727+
help=(
728+
"Runs `watch`, but only applies patches to specified chainlets. The option is "
729+
"a comma-separated list of chainlet (display) names. This option can give "
730+
"faster dev loops, but also lead to inconsistent deployments. Use with caution "
731+
"and refer to docs."
732+
),
718733
)
719734
@log_level_option
720735
@error_handling
721736
def watch_chains(
722737
source: Path,
723738
entrypoint: Optional[str],
724739
name: Optional[str],
725-
user_env: Optional[str],
726740
remote: Optional[str],
741+
experimental_chainlet_names: Optional[str],
727742
) -> None:
728743
"""
729744
Watches the chains source code and applies live patches to a development deployment.
@@ -738,12 +753,14 @@ def watch_chains(
738753
# These imports are delayed, to handle pydantic v1 envs gracefully.
739754
from truss_chains.deployment import deployment_client
740755

741-
if user_env:
742-
raise ValueError("`user_env` is deprecated, use `environment` instead.")
743-
744756
if not remote:
745757
remote = inquire_remote_name(RemoteFactory.get_available_config_names())
746758

759+
if experimental_chainlet_names:
760+
included_chainlets = [x.strip() for x in experimental_chainlet_names.split(",")]
761+
else:
762+
included_chainlets = None
763+
747764
deployment_client.watch(
748765
source,
749766
entrypoint,
@@ -752,6 +769,7 @@ def watch_chains(
752769
console,
753770
error_console,
754771
show_stack_trace=not is_humanfriendly_log_level,
772+
included_chainlets=included_chainlets,
755773
)
756774

757775

0 commit comments

Comments
 (0)