refactor(cli): simplify flat image plumbing

This commit is contained in:
Parker Rule
2026-08-28 11:43:55 -04:00
parent fc70ba12fb
commit 2b5cd95263
4 changed files with 21 additions and 37 deletions
+2 -3
View File
@@ -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,
)
+15 -30
View File
@@ -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,
)
+2 -2
View File
@@ -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:
+2 -2
View File
@@ -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 == {}