Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion truss-chains/truss_chains/deployment/deployment_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -625,7 +625,7 @@ def __init__(
dev_version = b10_core.get_dev_version(self._remote_provider.api, model_name)
if not dev_version:
raise b10_errors.RemoteError(
"No development model found. Run `truss push` then try again."
"No development model found. Run `truss push --watch` then try again."
)

def _patch(self) -> None:
Expand Down
30 changes: 27 additions & 3 deletions truss/api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ def push(
progress_bar: Optional[Type["progress.Progress"]] = None,
include_git_info: bool = False,
preserve_env_instance_type: bool = True,
watch: bool = False,
) -> definitions.ModelDeployment:
"""
Pushes a Truss to Baseten.
Expand All @@ -74,16 +75,16 @@ def push(
remote: Name of the remote in .trussrc to patch changes to.
model_name: The name of the model, if different from the one in the config.yaml.
publish: Push the truss as a published deployment. If no production deployment exists,
promote the truss to production after deploy completes.
promote the truss to production after deploy completes. Default is True.
promote: Push the truss as a published deployment. Even if a production deployment exists,
promote the truss to production after deploy completes.
preserve_previous_production_deployment: Preserve the previous production deployments autoscaling
preserve_previous_production_deployment: Preserve the previous production deployment's autoscaling
setting. When not specified, the previous production deployment will be updated to allow it to
scale to zero. Can only be use in combination with `promote` option.
trusted: [DEPRECATED]
deployment_name: Name of the deployment created by the push. Can only be
used in combination with `publish` or `promote`. Deployment name must
only contain alphanumeric, ’.’, ’-’ or ’_’ characters.
only contain alphanumeric, '.', '-' or '_' characters.
environment: Name of stable environment on baseten.
progress_bar: Optional `rich.progress.Progress` if output is desired.
include_git_info: Whether to attach git versioning info (sha, branch, tag) to
Expand All @@ -92,10 +93,33 @@ def push(
preserve_env_instance_type: When pushing a truss to an environment, whether to use the resources
specified in the truss config to resolve the instance type or preserve the instance type
configured in the specified environment.
watch: Push the truss as a development deployment with hot reload support.
Development models allow you to iterate quickly during the deployment process.

Returns:
The newly created ModelDeployment.
"""
# Handle the new logic: --watch creates development deployment, default is published
if watch and publish:
raise ValueError(
"Cannot use both --watch and --publish flags. Use --watch for development deployments or --publish for published deployments."
)

if watch and promote:
raise ValueError(
"Cannot use both --watch and --promote flags. Use --watch for development deployments or --promote for production deployments."
)

# Determine the deployment type based on flags
if watch:
# --watch explicitly creates development deployment
publish = False
elif publish or promote:
# --publish or --promote explicitly creates published deployment
publish = True
else:
# Default behavior: create published deployment
publish = True
if trusted is not None:
warnings.warn(
"`trusted` is deprecated and will be ignored, all models are "
Expand Down
4 changes: 2 additions & 2 deletions truss/cli/chains_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ def _create_chains_table(service) -> Tuple[rich.table.Table, List[str]]:
)
@click.option(
"--publish/--no-publish",
default=False,
default=True,
help="Create chainlets as published deployments.",
)
@click.option(
Expand Down Expand Up @@ -178,7 +178,7 @@ def _create_chains_table(service) -> Tuple[rich.table.Table, List[str]]:
"Watches the chains source code and applies live patches. Using this option "
"will wait for the chain to be deployed (i.e. `--wait` flag is applied), "
"before starting to watch for changes. This option required the deployment "
"to be a development deployment (i.e. `--no-promote` and `--no-publish`."
"to be a development deployment (i.e. use `--watch` flag when pushing)."
),
)
@click.option(
Expand Down
39 changes: 36 additions & 3 deletions truss/cli/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,16 @@ def run_python(script, target_directory):
"after deploy completes."
),
)
@click.option(
"--watch",
is_flag=True,
required=False,
default=False,
help=(
"Push the truss as a development deployment with hot reload support. "
"Development models allow you to iterate quickly during the deployment process."
),
)
@click.option(
"--promote",
is_flag=True,
Expand Down Expand Up @@ -518,21 +528,44 @@ def push(
include_git_info: bool = False,
tail: bool = False,
preserve_env_instance_type: bool = True,
watch: bool = False,
) -> None:
"""
Pushes a truss to a TrussRemote.

TARGET_DIRECTORY: A Truss directory. If none, use current directory.

"""
# Handle the new logic: --watch creates development deployment, default is published
if watch and publish:
raise click.UsageError(
"Cannot use both --watch and --publish flags. Use --watch for development deployments or --publish for published deployments."
)

if watch and promote:
raise click.UsageError(
"Cannot use both --watch and --promote flags. Use --watch for development deployments or --promote for production deployments."
)

# Determine the deployment type based on flags
if watch:
# --watch explicitly creates development deployment
publish = False
elif publish or promote:
# --publish or --promote explicitly creates published deployment
publish = True
else:
# Default behavior: create published deployment
publish = True

tr = _get_truss_from_directory(target_directory=target_directory)
if (
tr.spec.config.runtime.transport.kind == TransportKind.GRPC
and not publish
and not promote
):
raise click.UsageError(
"Truss with gRPC transport cannot be used as a development deployment. Please rerun the command with --publish or --promote."
"Truss with gRPC transport cannot be used as a development deployment. Please rerun the command without --watch, or with --promote."
)

if not remote:
Expand Down Expand Up @@ -635,9 +668,9 @@ def push(
| Your model is deploying as a development model. Development models allow you to |
| iterate quickly during the deployment process. |
| |
| To monitor changes to your model and rapidly iterate, run the 'truss watch' command.|
| When you are ready to publish your deployed model as a new deployment, |
| pass '--publish' to the 'truss push' command. To monitor changes to your model and |
| rapidly iterate, run the 'truss watch' command. |
| run 'truss push' without the --watch flag. |
| |
|---------------------------------------------------------------------------------------|
"""
Expand Down
4 changes: 2 additions & 2 deletions truss/remote/baseten/remote.py
Original file line number Diff line number Diff line change
Expand Up @@ -319,15 +319,15 @@ def _get_matching_version(model_versions: List[dict], published: bool) -> dict:
dev_version = get_dev_version_from_versions(model_versions)
if not dev_version:
raise RemoteError(
"No development model found. Run `truss push` then try again."
"No development model found. Run `truss push --watch` then try again."
)
return dev_version

# Return the production deployment version.
prod_version = get_prod_version_from_versions(model_versions)
if not prod_version:
raise RemoteError(
"No production model found. Run `truss push --publish` then try again."
"No production model found. Run `truss push` then try again."
)
return prod_version

Expand Down
26 changes: 25 additions & 1 deletion truss/tests/cli/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,39 @@ def test_push_with_grpc_transport_fails_for_development_deployment():

runner = CliRunner()

# Test that gRPC transport fails with --watch (development deployment)
with patch("truss.cli.cli._get_truss_from_directory", return_value=mock_truss):
with patch("truss.cli.remote_cli.inquire_remote_name", return_value="remote1"):
result = runner.invoke(
truss_cli,
["push", "test_truss", "--remote", "remote1", "--model-name", "name"],
["push", "test_truss", "--remote", "remote1", "--model-name", "name", "--watch"],
)

assert result.exit_code == 2
assert (
"Truss with gRPC transport cannot be used as a development deployment"
in result.output
)


def test_push_with_grpc_transport_succeeds_by_default():
"""Test that gRPC transport succeeds by default (published deployment)"""
mock_truss = Mock()
mock_truss.spec.config.runtime.transport.kind = "grpc"
mock_remote_provider = Mock()
mock_service = Mock()
mock_service.is_draft = False
mock_remote_provider.push.return_value = mock_service

runner = CliRunner()

with patch("truss.cli.cli._get_truss_from_directory", return_value=mock_truss):
with patch("truss.cli.remote_cli.inquire_remote_name", return_value="remote1"):
with patch("truss.cli.cli.RemoteFactory.create", return_value=mock_remote_provider):
result = runner.invoke(
truss_cli,
["push", "test_truss", "--remote", "remote1", "--model-name", "name"],
)

# Should succeed now since default is published deployment
assert result.exit_code == 0
Loading