diff --git a/libs/cli/langgraph_cli/cli.py b/libs/cli/langgraph_cli/cli.py index 727b04a0e..f5b114ce4 100644 --- a/libs/cli/langgraph_cli/cli.py +++ b/libs/cli/langgraph_cli/cli.py @@ -410,7 +410,6 @@ For production use, requires a license key in env var LANGGRAPH_CLOUD_LICENSE_KE ) @click.option( "--flat", - "single_user_layer", is_flag=True, envvar="LANGGRAPH_CLI_FLAT_IMAGES", help="Flatten generated Python build steps into one image layer when supported.", @@ -433,7 +432,7 @@ def build( tag: str, install_command: str | None, build_command: str | None, - single_user_layer: bool, + flat: bool, ): if install_command and langgraph_cli.config.has_disallowed_build_command_content( install_command @@ -469,7 +468,7 @@ def build( docker_build_args, install_command, build_command, - single_user_layer=single_user_layer, + flat=flat, ) diff --git a/libs/cli/langgraph_cli/config.py b/libs/cli/langgraph_cli/config.py index 7dfa8ae1f..fb6b742db 100644 --- a/libs/cli/langgraph_cli/config.py +++ b/libs/cli/langgraph_cli/config.py @@ -1162,9 +1162,7 @@ def _build_runtime_env_vars(config: Config) -> list[str]: return env_vars -def _supports_single_user_layer( - config: Config, local_deps: LocalDeps, escape_variables: bool -) -> bool: +def _can_flatten(config: Config, local_deps: LocalDeps, escape_variables: bool) -> bool: return not ( config["dockerfile_lines"] or local_deps.additional_contexts @@ -1188,7 +1186,7 @@ build-backend = "setuptools.build_meta" PYPROJECT""" -def _build_single_user_layer_dockerfile( +def _build_flat_dockerfile( config_path: pathlib.Path, config: Config, local_deps: LocalDeps, @@ -1211,12 +1209,8 @@ def _build_single_user_layer_dockerfile( for reqpath, destination in local_deps.pip_reqs: source = context / reqpath.relative_to(config_path.parent) destination_parent = pathlib.PurePosixPath(destination).parent - commands.extend( - [ - f"mkdir -p {shlex.quote(str(destination_parent))}", - f"cp {shlex.quote(str(source))} {shlex.quote(destination)}", - ] - ) + commands.append(f"""mkdir -p {shlex.quote(str(destination_parent))} +cp {shlex.quote(str(source))} {shlex.quote(destination)}""") if local_deps.pip_reqs: requirements = " ".join( f"-r {destination}" for _, destination in local_deps.pip_reqs @@ -1226,22 +1220,15 @@ def _build_single_user_layer_dockerfile( for _, (relative_path, name) in local_deps.real_pkgs.items(): source = f"{context / relative_path}/." destination = f"/deps/{name}" - commands.extend( - [ - f"mkdir -p {shlex.quote(destination)}", - f"cp -a {shlex.quote(source)} {shlex.quote(destination)}/", - ] - ) + commands.append(f"""mkdir -p {shlex.quote(destination)} +cp -a {shlex.quote(source)} {shlex.quote(destination)}/""") for full_path, (relative_path, destination) in local_deps.faux_pkgs.items(): source = f"{context / relative_path}/." - commands.extend( - [ - f"mkdir -p {shlex.quote(destination)}", - f"cp -a {shlex.quote(source)} {shlex.quote(destination)}/", - _faux_package_pyproject_command(full_path.name), - ] - ) + pyproject = _faux_package_pyproject_command(full_path.name) + commands.append(f"""mkdir -p {shlex.quote(destination)} +cp -a {shlex.quote(source)} {shlex.quote(destination)}/ +{pyproject}""") commands.append(local_deps_install_command) commands.extend( @@ -1368,7 +1355,7 @@ def python_config_to_docker( api_version: str | None = None, *, escape_variables: bool = False, - single_user_layer: bool = False, + flat: bool = False, ) -> tuple[str, dict[str, str]]: """Generate a Dockerfile from the configuration.""" source_kind = _get_source_kind(config) @@ -1544,11 +1531,9 @@ ADD {relpath} /deps/{name} pip_installer=pip_installer, ) - if single_user_layer and _supports_single_user_layer( - config, local_deps, escape_variables - ): + if flat and _can_flatten(config, local_deps, escape_variables): return ( - _build_single_user_layer_dockerfile( + _build_flat_dockerfile( config_path=config_path, config=config, local_deps=local_deps, @@ -1746,7 +1731,7 @@ def config_to_docker( build_command: str | None = None, build_context: str | None = None, escape_variables: bool = False, - single_user_layer: bool = False, + flat: bool = False, ) -> tuple[str, dict[str, str]]: base_image = base_image or default_base_image(config) @@ -1767,7 +1752,7 @@ def config_to_docker( base_image=base_image, api_version=api_version, escape_variables=escape_variables, - single_user_layer=single_user_layer, + flat=flat, ) diff --git a/libs/cli/langgraph_cli/docker.py b/libs/cli/langgraph_cli/docker.py index f9376398a..ddc050199 100644 --- a/libs/cli/langgraph_cli/docker.py +++ b/libs/cli/langgraph_cli/docker.py @@ -345,7 +345,7 @@ def build_docker_image( docker_command: Sequence[str] | None = None, extra_flags: Sequence[str] = (), verbose: bool = True, - single_user_layer: bool = False, + flat: bool = False, ): """Build a Docker image from a LangGraph config.""" # pull latest images @@ -388,7 +388,7 @@ def build_docker_image( install_command=install_command, build_command=build_command, build_context=build_context, - single_user_layer=single_user_layer, + flat=flat, ) # add additional_contexts if additional_contexts: diff --git a/libs/cli/tests/unit_tests/test_config.py b/libs/cli/tests/unit_tests/test_config.py index 018b086c4..8879ce4da 100644 --- a/libs/cli/tests/unit_tests/test_config.py +++ b/libs/cli/tests/unit_tests/test_config.py @@ -723,14 +723,14 @@ WORKDIR /deps/outer-unit_tests/unit_tests\ } -def test_config_to_docker_single_user_layer(): +def test_config_to_docker_flat(): dockerfile, additional_contexts = config_to_docker( PATH_TO_CONFIG, validate_config( {"dependencies": ["."], "graphs": {"agent": "./agent.py:graph"}} ), base_image="langchain/langgraph-api", - single_user_layer=True, + flat=True, ) assert additional_contexts == {}