mirror of
https://github.com/langchain-ai/langgraph.git
synced 2026-09-07 02:07:52 +02:00
fix(cli): persist deployment name to .env after prompt (#7323)
## Summary - When `langgraph deploy` prompts for the deployment name interactively, persist it as `LANGSMITH_DEPLOYMENT_NAME` in the `.env` file so subsequent deploys pick it up automatically - Extracted `_resolve_env_path()` helper from `_parse_env_from_config()` to share env file path resolution logic - Skips writing when env vars are configured as an inline dict in `langgraph.json` (no file to write to) - **CI fix**: CLI integration tests were using `pip install` but `setup-uv` with caching enabled, so the post-step cache save failed on a non-existent directory — switched to `uv pip install` ## Test plan - [x] All 207 existing unit tests pass - [ ] Manual: run `langgraph deploy` without `--name`, enter a name at prompt, verify `.env` now contains `LANGSMITH_DEPLOYMENT_NAME=<name>` - [ ] Manual: run `langgraph deploy` again, verify the name is picked up from `.env` without re-prompting - [x] CI integration test cache fix (broken on main since Feb 2026)
This commit is contained in:
@@ -18,7 +18,7 @@ from datetime import datetime, timezone
|
||||
import click
|
||||
import click.exceptions
|
||||
from click import secho
|
||||
from dotenv import dotenv_values
|
||||
from dotenv import dotenv_values, set_key
|
||||
|
||||
import langgraph_cli.config
|
||||
import langgraph_cli.docker
|
||||
@@ -94,14 +94,13 @@ _API_KEY_ENV_NAMES = (
|
||||
_DEPLOYMENT_NAME_ENV = "LANGSMITH_DEPLOYMENT_NAME"
|
||||
|
||||
|
||||
def _parse_env_from_config(
|
||||
def _resolve_env_path(
|
||||
config_json: dict, config_path: pathlib.Path
|
||||
) -> dict[str, str]:
|
||||
"""Resolve env vars from langgraph.json 'env' field or a .env fallback."""
|
||||
) -> pathlib.Path | None:
|
||||
"""Return the .env file path implied by the config, or None for inline dicts."""
|
||||
env_field = config_json.get("env")
|
||||
# validate_config_file will default env to {}
|
||||
if isinstance(env_field, dict) and env_field:
|
||||
return {str(k): str(v) for k, v in env_field.items()}
|
||||
return None
|
||||
if isinstance(env_field, str):
|
||||
env_path = (config_path.parent / env_field).resolve()
|
||||
if not env_path.exists():
|
||||
@@ -109,9 +108,21 @@ def _parse_env_from_config(
|
||||
f"Warning: env file '{env_field}' specified in langgraph.json not found.",
|
||||
fg="yellow",
|
||||
)
|
||||
return {}
|
||||
else:
|
||||
env_path = pathlib.Path.cwd() / ".env"
|
||||
return None
|
||||
return env_path
|
||||
return pathlib.Path.cwd() / ".env"
|
||||
|
||||
|
||||
def _parse_env_from_config(
|
||||
config_json: dict, config_path: pathlib.Path
|
||||
) -> dict[str, str]:
|
||||
"""Resolve env vars from langgraph.json 'env' field or a .env fallback."""
|
||||
env_field = config_json.get("env")
|
||||
if isinstance(env_field, dict) and env_field:
|
||||
return {str(k): str(v) for k, v in env_field.items()}
|
||||
env_path = _resolve_env_path(config_json, config_path)
|
||||
if env_path is None:
|
||||
return {}
|
||||
return {k: v for k, v in dotenv_values(env_path).items() if v is not None}
|
||||
|
||||
|
||||
@@ -851,7 +862,15 @@ def _deploy(
|
||||
if not deployment_id and not name:
|
||||
default_name = _normalize_image_name(pathlib.Path.cwd().name)
|
||||
name = click.prompt("Deployment name", default=default_name)
|
||||
# Persist so the user doesn't have to type it again next time
|
||||
env_path = _resolve_env_path(config_json, config)
|
||||
if env_path is not None:
|
||||
set_key(str(env_path), _DEPLOYMENT_NAME_ENV, name)
|
||||
click.echo(f"Saved deployment name to {env_path}")
|
||||
|
||||
# Remove deployment name before converting to secrets — it's consumed above
|
||||
# and is in RESERVED_ENV_VARS, so _secrets_from_env would warn about it.
|
||||
env_vars.pop(_DEPLOYMENT_NAME_ENV, None)
|
||||
secrets = _secrets_from_env(env_vars)
|
||||
|
||||
# Use buildx to cross-compile for amd64 when running on a non-x86_64 host
|
||||
|
||||
Reference in New Issue
Block a user