Skip to content
Merged
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
7 changes: 4 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,10 @@ haystack-enterprise deploy pipeline.py SERVICE
Redirect with `--output`, never with `>`. The CLI's logger writes to stdout, so a shell redirect
interleaves warning lines into the YAML or JSON and the result no longer parses.

Exit codes worth knowing: 1 for validation, transform, and API errors; 130 when `run` is interrupted;
and 0 when a `deploy` rollout is detached or times out. A zero exit from `deploy` does not mean the
service is serving — confirm with `haystack-enterprise service-status SERVICE`.
Exit codes worth knowing: 1 for validation, transform, and API errors, and for a `deploy` rollout that
fails or does not finish within the wait limit; 130 when `run` is interrupted; and 0 when you detach from
a `deploy` on purpose (Ctrl-C or `--skip-activation`). A rollout that timed out may still come up —
confirm with `haystack-enterprise service-status SERVICE`.

Set `CI=true` to replace spinners and progress bars with plain line output.

Expand Down
17 changes: 15 additions & 2 deletions docs/guides/deploy-a-pipeline.md
Original file line number Diff line number Diff line change
Expand Up @@ -228,8 +228,8 @@ A pipeline with conventionally named sockets (`query`, `answers`, `replies`, `do
inference and asks nothing. Pin the mapping in `<pipeline>.io.yaml` (or `--io-config`) to skip the
questions entirely; `--share` reviews the whole mapping instead (see below).

If you interrupt the command (Ctrl-C) during rollout, the rollout continues on the platform. Check
progress any time with:
If you interrupt the command (Ctrl-C) during rollout, the rollout continues on the platform and the
command exits 0, since you detached on purpose. Check progress any time with:

```shell
haystack-enterprise service-status my-service
Expand Down Expand Up @@ -407,6 +407,19 @@ appends a new line per repaint, so a multi-minute rollout becomes thousands of l
Set `CI=false` (or unset it) to force the animations back on. Independently of `CI`, `run` never
animates when its stdout is redirected, so a piped JSON payload stays parseable.

### What the job result means

The exit code of `deploy` answers "is the new revision serving?", so a CI job that passes can be trusted:

- **Exit 0**: the revision is serving (a serverless service activated it, or a managed rollout reached
`DEPLOYED` or `IDLE`), or you chose not to activate it with `--skip-activation`, which pushes the revision
and says so. Interrupting with Ctrl-C also exits 0.
- **Exit 1**: the rollout failed, **or it did not finish within the wait limit** (10 minutes). In the second
case the rollout continues on the platform and the service may still come up; check with
`haystack-enterprise service-status my-service`, then re-run the job or fix the service.

To push a revision without waiting for a rollout, use `--skip-activation` and activate it later.

To pass CI secrets to the service, expose them to the step's environment and name them with a bare
`--env`. A GitHub Actions example:

Expand Down
7 changes: 6 additions & 1 deletion haystack_enterprise_sdk/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -1010,7 +1010,8 @@ def _on_status(status: object) -> None:
)
elif result.timed_out:
typer.echo(
f"Activation of '{service_name}' is still in progress. Detached; the rollout continues. "
f"The rollout of '{service_name}' did not finish within the wait limit, so the new revision is "
f"not confirmed to be serving. The rollout continues on the platform. "
f"Check with `haystack-enterprise service-status {service_name}`."
)
elif result.deployment.deployment_mode is DeploymentMode.SERVERLESS:
Expand All @@ -1030,6 +1031,10 @@ def _on_status(status: object) -> None:
)
_echo_endpoint(client, service_name, result, shared=bool(share))

# A job result answers "is the new revision serving?", so an unfinished rollout must not look like success.
if result.timed_out:
raise typer.Exit(1)


@cli_app.command()
def validate(
Expand Down
8 changes: 4 additions & 4 deletions tests/unit/test_cli_deploy.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,8 +146,8 @@ def test_deploy_activates_by_default(self, client_cls: Mock) -> None:
def test_deploy_activate_timed_out(self, client_cls: Mock) -> None:
client_cls.return_value.deploy.return_value = _result(activated=True, timed_out=True)
result = runner.invoke(cli_app, ["deploy", FIXTURE, "svc"])
assert result.exit_code == 0
assert "still in progress" in result.stdout
assert result.exit_code == 1
assert "did not finish within the wait limit" in result.stdout

@patch("haystack_enterprise_sdk.cli.DeploymentClient")
def test_deploy_failure_exits_1(self, client_cls: Mock) -> None:
Expand Down Expand Up @@ -455,7 +455,7 @@ def test_deploy_share_skipped_when_not_deployed(self, client_cls: Mock) -> None:
# Rollout timed out: the service never reached DEPLOYED, so the prototype must not be attempted.
client_cls.return_value.deploy.return_value = _result(activated=True, timed_out=True)
result = runner.invoke(cli_app, ["deploy", FIXTURE, "svc", "--share"])
assert result.exit_code == 0
assert result.exit_code == 1
assert "Skipped shared prototype" in result.stdout
client_cls.return_value.create_shared_prototype.assert_not_called()

Expand Down Expand Up @@ -496,7 +496,7 @@ def test_no_endpoint_when_rollout_timed_out(self, client_cls: Mock) -> None:
_stub_endpoint(client_cls)
client_cls.return_value.deploy.return_value = _result(activated=True, timed_out=True)
result = runner.invoke(cli_app, ["deploy", FIXTURE, "svc"])
assert result.exit_code == 0
assert result.exit_code == 1
assert "chat/completions" not in result.stdout

@patch("haystack_enterprise_sdk.cli.DeploymentClient")
Expand Down
Loading