diff --git a/libs/cli/langgraph_cli/config.py b/libs/cli/langgraph_cli/config.py index b48ac7779..a391db4d4 100644 --- a/libs/cli/langgraph_cli/config.py +++ b/libs/cli/langgraph_cli/config.py @@ -1325,7 +1325,10 @@ def docker_tag( base_image: Optional[str] = None, ) -> str: base_image = base_image or default_base_image(config) - wolfi_tag = "-wolfi" if config.get("image_distro") == "wolfi" else "" + + image_distro = config.get("image_distro") + distro_tag = "" if image_distro == DEFAULT_IMAGE_DISTRO else f"-{image_distro}" + if config.get("_INTERNAL_docker_tag"): return f"{base_image}:{config['_INTERNAL_docker_tag']}" @@ -1333,8 +1336,8 @@ def docker_tag( return f"{base_image}-py{config['python_version']}" if config.get("node_version") and not config.get("python_version"): - return f"{base_image}:{config['node_version']}{wolfi_tag}" - return f"{base_image}:{config['python_version']}{wolfi_tag}" + return f"{base_image}:{config['node_version']}{distro_tag}" + return f"{base_image}:{config['python_version']}{distro_tag}" def config_to_docker( diff --git a/libs/cli/tests/unit_tests/test_config.py b/libs/cli/tests/unit_tests/test_config.py index 9d2fc7745..e3aed1bc2 100644 --- a/libs/cli/tests/unit_tests/test_config.py +++ b/libs/cli/tests/unit_tests/test_config.py @@ -11,6 +11,7 @@ from langgraph_cli.config import ( PIP_CLEANUP_LINES, config_to_compose, config_to_docker, + docker_tag, validate_config, validate_config_file, ) @@ -188,7 +189,7 @@ def test_validate_config_image_distro(): ) assert "Invalid image_distro: 'alpine'" in str(exc_info.value) - # Test with Node.js config too + # Test base Node.js config with image distro config = validate_config( { "node_version": "20", @@ -198,7 +199,7 @@ def test_validate_config_image_distro(): ) assert config["image_distro"] == "wolfi" - # Test Node.js config with default + # Test Node.js config with no distro specified config = validate_config( { "node_version": "20", @@ -965,3 +966,129 @@ def test_config_to_compose_end_to_end(): watch=True, ) assert clean_empty_lines(actual_compose_stdin) == expected_compose_stdin + + +def test_docker_tag_image_distro(): + """Test docker_tag function with different image_distro configurations.""" + + # Test 1: Default distro (debian) - no suffix + config = validate_config({ + "python_version": "3.11", + "dependencies": ["."], + "graphs": {"agent": "./agent.py:graph"}, + }) + tag = docker_tag(config) + assert tag == "langchain/langgraph-api:3.11" + + # Test 2: Explicit debian distro - no suffix (same as default) + config = validate_config({ + "python_version": "3.11", + "dependencies": ["."], + "graphs": {"agent": "./agent.py:graph"}, + "image_distro": "debian", + }) + tag = docker_tag(config) + assert tag == "langchain/langgraph-api:3.11" + + # Test 3: Wolfi distro - should add suffix + config = validate_config({ + "python_version": "3.11", + "dependencies": ["."], + "graphs": {"agent": "./agent.py:graph"}, + "image_distro": "wolfi", + }) + tag = docker_tag(config) + assert tag == "langchain/langgraph-api:3.11-wolfi" + + # Test 4: Node.js with default distro + config = validate_config({ + "node_version": "20", + "graphs": {"agent": "./agent.js:graph"}, + }) + tag = docker_tag(config) + assert tag == "langchain/langgraphjs-api:20" + + # Test 5: Node.js with wolfi distro + config = validate_config({ + "node_version": "20", + "graphs": {"agent": "./agent.js:graph"}, + "image_distro": "wolfi", + }) + tag = docker_tag(config) + assert tag == "langchain/langgraphjs-api:20-wolfi" + + # Test 6: Custom base image with wolfi + config = validate_config({ + "python_version": "3.12", + "dependencies": ["."], + "graphs": {"agent": "./agent.py:graph"}, + "image_distro": "wolfi", + "base_image": "my-registry/custom-image", + }) + tag = docker_tag(config, base_image="my-registry/custom-image") + assert tag == "my-registry/custom-image:3.12-wolfi" + + +def test_docker_tag_multiplatform_with_distro(): + """Test docker_tag with multiplatform configs and image_distro.""" + + # Test 1: Multiplatform (Python + Node) with wolfi + config = validate_config({ + "python_version": "3.11", + "node_version": "20", + "dependencies": ["."], + "graphs": {"python": "./agent.py:graph", "js": "./agent.js:graph"}, + "image_distro": "wolfi", + }) + tag = docker_tag(config) + # Should default to Python when both are present + assert tag == "langchain/langgraph-api:3.11-wolfi" + + # Test 2: Node-only multiplatform with wolfi + config = validate_config({ + "node_version": "20", + "graphs": {"js": "./agent.js:graph"}, + "image_distro": "wolfi", + }) + tag = docker_tag(config) + assert tag == "langchain/langgraphjs-api:20-wolfi" + + +def test_docker_tag_different_python_versions_with_distro(): + """Test docker_tag with different Python versions and distros.""" + + versions_and_expected = [ + ("3.11", "langchain/langgraph-api:3.11-wolfi"), + ("3.12", "langchain/langgraph-api:3.12-wolfi"), + ("3.13", "langchain/langgraph-api:3.13-wolfi") + ] + + for python_version, expected_tag in versions_and_expected: + config = validate_config({ + "python_version": python_version, + "dependencies": ["."], + "graphs": {"agent": "./agent.py:graph"}, + "image_distro": "wolfi", + }) + tag = docker_tag(config) + assert tag == expected_tag, f"Failed for Python {python_version}" + + +def test_docker_tag_different_node_versions_with_distro(): + """Test docker_tag with different Node.js versions and distros.""" + + versions_and_expected = [ + ("20", "langchain/langgraphjs-api:20-wolfi"), + ("21", "langchain/langgraphjs-api:21-wolfi"), + ("22", "langchain/langgraphjs-api:22-wolfi") + ] + + for node_version, expected_tag in versions_and_expected: + config = validate_config({ + "node_version": node_version, + "graphs": {"agent": "./agent.js:graph"}, + "image_distro": "wolfi", + }) + tag = docker_tag(config) + assert tag == expected_tag, f"Failed for Node.js {node_version}" +