langgraph-cli 0.4.23__tar.gz → 0.4.24__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (148) hide show
  1. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/PKG-INFO +1 -1
  2. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/examples/graph_prerelease_reqs/deps/zuper_deps/pyproject.toml +1 -1
  3. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/examples/graph_prerelease_reqs/pyproject.toml +1 -1
  4. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/examples/graph_prerelease_reqs_fail/pyproject.toml +1 -1
  5. langgraph_cli-0.4.24/langgraph_cli/__init__.py +1 -0
  6. langgraph_cli-0.4.24/langgraph_cli/_ignore.py +124 -0
  7. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/langgraph_cli/archive.py +1 -24
  8. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/langgraph_cli/uv_lock.py +50 -11
  9. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/tests/unit_tests/test_archive.py +7 -0
  10. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/tests/unit_tests/test_config.py +359 -0
  11. langgraph_cli-0.4.23/langgraph_cli/__init__.py +0 -1
  12. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/.gitignore +0 -0
  13. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/LICENSE +0 -0
  14. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/Makefile +0 -0
  15. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/README.md +0 -0
  16. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/examples/.env.example +0 -0
  17. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/examples/.gitignore +0 -0
  18. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/examples/Makefile +0 -0
  19. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/examples/graph_prerelease_reqs/agent.py +0 -0
  20. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/examples/graph_prerelease_reqs/deps/additional_deps/pyproject.toml +0 -0
  21. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/examples/graph_prerelease_reqs/langgraph.json +0 -0
  22. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/examples/graph_prerelease_reqs_fail/agent.py +0 -0
  23. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/examples/graph_prerelease_reqs_fail/langgraph.json +0 -0
  24. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/examples/graphs/agent.py +0 -0
  25. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/examples/graphs/langgraph.json +0 -0
  26. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/examples/graphs/storm.py +0 -0
  27. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/examples/graphs_reqs_a/__init__.py +0 -0
  28. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/examples/graphs_reqs_a/graphs_submod/__init__.py +0 -0
  29. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/examples/graphs_reqs_a/graphs_submod/agent.py +0 -0
  30. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/examples/graphs_reqs_a/graphs_submod/subprompt.txt +0 -0
  31. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/examples/graphs_reqs_a/hello.py +0 -0
  32. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/examples/graphs_reqs_a/langgraph.json +0 -0
  33. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/examples/graphs_reqs_a/prompt.txt +0 -0
  34. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/examples/graphs_reqs_a/requirements.txt +0 -0
  35. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/examples/graphs_reqs_b/graphs_submod/agent.py +0 -0
  36. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/examples/graphs_reqs_b/graphs_submod/subprompt.txt +0 -0
  37. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/examples/graphs_reqs_b/hello.py +0 -0
  38. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/examples/graphs_reqs_b/langgraph.json +0 -0
  39. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/examples/graphs_reqs_b/prompt.txt +0 -0
  40. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/examples/graphs_reqs_b/requirements.txt +0 -0
  41. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/examples/graphs_reqs_b/utils/__init__.py +0 -0
  42. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/examples/graphs_reqs_b/utils/greeter.py +0 -0
  43. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/examples/langgraph.json +0 -0
  44. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/examples/my_app.py +0 -0
  45. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/examples/pipconf.txt +0 -0
  46. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/examples/poetry.lock +0 -0
  47. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/examples/pyproject.toml +0 -0
  48. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/generate_schema.py +0 -0
  49. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/js-examples/.dockerignore +0 -0
  50. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/js-examples/.editorconfig +0 -0
  51. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/js-examples/.env.example +0 -0
  52. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/js-examples/.eslintrc.cjs +0 -0
  53. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/js-examples/.gitignore +0 -0
  54. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/js-examples/LICENSE +0 -0
  55. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/js-examples/README.md +0 -0
  56. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/js-examples/jest.config.js +0 -0
  57. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/js-examples/langgraph.json +0 -0
  58. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/js-examples/package.json +0 -0
  59. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/js-examples/src/agent/graph.ts +0 -0
  60. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/js-examples/src/agent/state.ts +0 -0
  61. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/js-examples/static/studio.png +0 -0
  62. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/js-examples/tests/agent.test.ts +0 -0
  63. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/js-examples/tests/graph.int.test.ts +0 -0
  64. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/js-examples/tsconfig.json +0 -0
  65. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/js-examples/yarn.lock +0 -0
  66. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/js-monorepo-example/.eslintrc.cjs +0 -0
  67. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/js-monorepo-example/apps/agent/langgraph.json +0 -0
  68. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/js-monorepo-example/apps/agent/package.json +0 -0
  69. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/js-monorepo-example/apps/agent/src/graph.ts +0 -0
  70. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/js-monorepo-example/apps/agent/src/state.ts +0 -0
  71. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/js-monorepo-example/apps/agent/tsconfig.json +0 -0
  72. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/js-monorepo-example/libs/shared/package.json +0 -0
  73. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/js-monorepo-example/libs/shared/src/index.ts +0 -0
  74. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/js-monorepo-example/libs/shared/tsconfig.json +0 -0
  75. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/js-monorepo-example/package.json +0 -0
  76. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/js-monorepo-example/tsconfig.json +0 -0
  77. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/js-monorepo-example/turbo.json +0 -0
  78. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/js-monorepo-example/yarn.lock +0 -0
  79. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/langgraph_cli/__main__.py +0 -0
  80. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/langgraph_cli/analytics.py +0 -0
  81. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/langgraph_cli/cli.py +0 -0
  82. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/langgraph_cli/config.py +0 -0
  83. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/langgraph_cli/constants.py +0 -0
  84. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/langgraph_cli/deploy.py +0 -0
  85. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/langgraph_cli/docker.py +0 -0
  86. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/langgraph_cli/exec.py +0 -0
  87. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/langgraph_cli/host_backend.py +0 -0
  88. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/langgraph_cli/progress.py +0 -0
  89. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/langgraph_cli/py.typed +0 -0
  90. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/langgraph_cli/schemas.py +0 -0
  91. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/langgraph_cli/templates.py +0 -0
  92. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/langgraph_cli/util.py +0 -0
  93. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/langgraph_cli/version.py +0 -0
  94. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/pyproject.toml +0 -0
  95. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/python-monorepo-example/apps/agent/.env.example +0 -0
  96. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/python-monorepo-example/apps/agent/langgraph.json +0 -0
  97. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/python-monorepo-example/apps/agent/pyproject.toml +0 -0
  98. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/python-monorepo-example/apps/agent/src/agent/__init__.py +0 -0
  99. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/python-monorepo-example/apps/agent/src/agent/graph.py +0 -0
  100. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/python-monorepo-example/apps/agent/src/agent/state.py +0 -0
  101. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/python-monorepo-example/libs/common/__init__.py +0 -0
  102. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/python-monorepo-example/libs/common/helpers.py +0 -0
  103. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/python-monorepo-example/libs/shared/pyproject.toml +0 -0
  104. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/python-monorepo-example/libs/shared/src/shared/__init__.py +0 -0
  105. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/python-monorepo-example/libs/shared/src/shared/utils.py +0 -0
  106. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/python-monorepo-example/pyproject.toml +0 -0
  107. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/schemas/schema.json +0 -0
  108. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/schemas/schema.v0.json +0 -0
  109. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/schemas/version.schema.json +0 -0
  110. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/tests/__init__.py +0 -0
  111. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/tests/integration_tests/__init__.py +0 -0
  112. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/tests/integration_tests/test_cli.py +0 -0
  113. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/tests/unit_tests/__init__.py +0 -0
  114. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/tests/unit_tests/agent.py +0 -0
  115. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/tests/unit_tests/cli/__init__.py +0 -0
  116. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/tests/unit_tests/cli/langgraph.json +0 -0
  117. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/tests/unit_tests/cli/pyproject.toml +0 -0
  118. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/tests/unit_tests/cli/test_cli.py +0 -0
  119. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/tests/unit_tests/cli/test_templates.py +0 -0
  120. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/tests/unit_tests/conftest.py +0 -0
  121. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/tests/unit_tests/graphs/agent.py +0 -0
  122. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/tests/unit_tests/helpers.py +0 -0
  123. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/tests/unit_tests/multiplatform/js.mts +0 -0
  124. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/tests/unit_tests/multiplatform/python.py +0 -0
  125. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/tests/unit_tests/pipconfig.txt +0 -0
  126. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/tests/unit_tests/test_config.json +0 -0
  127. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/tests/unit_tests/test_deploy_helpers.py +0 -0
  128. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/tests/unit_tests/test_docker.py +0 -0
  129. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/tests/unit_tests/test_host_backend.py +0 -0
  130. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/tests/unit_tests/test_logs_helpers.py +0 -0
  131. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/tests/unit_tests/test_util.py +0 -0
  132. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/uv-examples/monorepo/apps/agent/.env.example +0 -0
  133. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/uv-examples/monorepo/apps/agent/langgraph.json +0 -0
  134. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/uv-examples/monorepo/apps/agent/pyproject.toml +0 -0
  135. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/uv-examples/monorepo/apps/agent/src/agent/__init__.py +0 -0
  136. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/uv-examples/monorepo/apps/agent/src/agent/graph.py +0 -0
  137. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/uv-examples/monorepo/libs/shared/pyproject.toml +0 -0
  138. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/uv-examples/monorepo/libs/shared/src/shared/__init__.py +0 -0
  139. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/uv-examples/monorepo/libs/shared/src/shared/utils.py +0 -0
  140. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/uv-examples/monorepo/pyproject.toml +0 -0
  141. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/uv-examples/monorepo/uv.lock +0 -0
  142. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/uv-examples/simple/.env.example +0 -0
  143. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/uv-examples/simple/langgraph.json +0 -0
  144. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/uv-examples/simple/pyproject.toml +0 -0
  145. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/uv-examples/simple/src/agent/__init__.py +0 -0
  146. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/uv-examples/simple/src/agent/graph.py +0 -0
  147. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/uv-examples/simple/uv.lock +0 -0
  148. {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: langgraph-cli
3
- Version: 0.4.23
3
+ Version: 0.4.24
4
4
  Summary: CLI for interacting with LangGraph API
5
5
  Project-URL: Source, https://github.com/langchain-ai/langgraph/tree/main/libs/cli
6
6
  Project-URL: Twitter, https://x.com/LangChain
@@ -5,5 +5,5 @@ description = "Test for prerelease stuff"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
7
7
  dependencies = [
8
- "langchain-openai==1.0.1"
8
+ "langchain-openai==1.1.14"
9
9
  ]
@@ -5,7 +5,7 @@ description = "Test for prerelease stuff"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
7
7
  dependencies = [
8
- "langchain-openai==1.0.0a2",
8
+ "langchain-openai==1.1.14",
9
9
  "langchain-anthropic==1.0.0a5",
10
10
  "langgraph==1.1.5"
11
11
  ]
@@ -5,7 +5,7 @@ description = "Test for prerelease stuff"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
7
7
  dependencies = [
8
- "langchain-openai==1.0.0a2",
8
+ "langchain-openai==1.1.14",
9
9
  "langgraph==1.1.2",
10
10
  "langchain_community>=0.3.0",
11
11
  ]
@@ -0,0 +1 @@
1
+ __version__ = "0.4.24"
@@ -0,0 +1,124 @@
1
+ """Shared ignore-file handling for local source filtering."""
2
+
3
+ import pathlib
4
+ from dataclasses import dataclass
5
+
6
+ import pathspec
7
+
8
+ _ALWAYS_EXCLUDE = [
9
+ "__pycache__/",
10
+ ".git/",
11
+ ".venv/",
12
+ "venv/",
13
+ "node_modules/",
14
+ ".tox/",
15
+ ".mypy_cache/",
16
+ ]
17
+ _ALWAYS_EXCLUDE_NAMES = frozenset(
18
+ pattern.rstrip("/").split("/")[-1] for pattern in _ALWAYS_EXCLUDE
19
+ )
20
+ _GLOB_CHARS = frozenset("*?[")
21
+
22
+
23
+ @dataclass(frozen=True, slots=True)
24
+ class _NegatedDockerignoreHints:
25
+ exact_dirs: frozenset[pathlib.PurePosixPath] = frozenset()
26
+ wildcard_prefixes: frozenset[pathlib.PurePosixPath] = frozenset()
27
+ recurse_all: bool = False
28
+
29
+ def requires_dir_walk(self, path: pathlib.PurePosixPath) -> bool:
30
+ if self.recurse_all or path in self.exact_dirs:
31
+ return True
32
+ return any(
33
+ path == prefix or path in prefix.parents or prefix in path.parents
34
+ for prefix in self.wildcard_prefixes
35
+ )
36
+
37
+
38
+ def _build_ignore_spec(
39
+ directory: pathlib.Path, *, include_gitignore: bool = True
40
+ ) -> pathspec.PathSpec:
41
+ """Build a PathSpec combining built-in exclusions with ignore files.
42
+
43
+ Always excludes common non-source directories (`_ALWAYS_EXCLUDE`). On top
44
+ of that, patterns from `.dockerignore` are merged in. `.gitignore` patterns
45
+ are optional because some callers need Docker build-context semantics,
46
+ while archive creation wants both files.
47
+ """
48
+ lines: list[str] = list(_ALWAYS_EXCLUDE)
49
+ ignore_files = [".dockerignore"]
50
+ if include_gitignore:
51
+ ignore_files.append(".gitignore")
52
+ for name in ignore_files:
53
+ ignore_file = directory / name
54
+ if ignore_file.is_file():
55
+ lines.extend(ignore_file.read_text(encoding="utf-8").splitlines())
56
+ return pathspec.PathSpec.from_lines("gitwildmatch", lines)
57
+
58
+
59
+ def _is_always_excluded(path: pathlib.PurePosixPath, *, is_dir: bool) -> bool:
60
+ """Whether `path` lives inside a built-in excluded directory."""
61
+ parent_parts = path.parts if is_dir else path.parts[:-1]
62
+ return any(part in _ALWAYS_EXCLUDE_NAMES for part in parent_parts)
63
+
64
+
65
+ def _build_dockerignore_negation_hints(
66
+ directory: pathlib.Path,
67
+ ) -> _NegatedDockerignoreHints:
68
+ """Summarize which ignored directories must still be traversed.
69
+
70
+ Most negations only require walking a small, concrete chain of parent
71
+ directories (for example `!assets/keep.txt` requires entering `assets/`).
72
+ Broader glob negations may force a wider walk.
73
+ """
74
+ ignore_file = directory / ".dockerignore"
75
+ if not ignore_file.is_file():
76
+ return _NegatedDockerignoreHints()
77
+
78
+ exact_dirs: set[pathlib.PurePosixPath] = set()
79
+ wildcard_prefixes: set[pathlib.PurePosixPath] = set()
80
+ recurse_all = False
81
+
82
+ for raw_line in ignore_file.read_text(encoding="utf-8").splitlines():
83
+ line = raw_line.strip()
84
+ if not line or line.startswith("#") or line.startswith("\\!"):
85
+ continue
86
+ if line.startswith("\\#"):
87
+ line = line[1:]
88
+ if not line.startswith("!"):
89
+ continue
90
+
91
+ pattern = line[1:].lstrip("/")
92
+ while pattern.startswith("./"):
93
+ pattern = pattern[2:]
94
+ pattern = pattern.rstrip("/")
95
+ parts = [part for part in pattern.split("/") if part and part != "."]
96
+ if not parts:
97
+ recurse_all = True
98
+ continue
99
+
100
+ wildcard_index = next(
101
+ (
102
+ idx
103
+ for idx, part in enumerate(parts)
104
+ if any(char in part for char in _GLOB_CHARS)
105
+ ),
106
+ None,
107
+ )
108
+ if wildcard_index is not None:
109
+ literal_parts = parts[:wildcard_index]
110
+ if not literal_parts:
111
+ recurse_all = True
112
+ continue
113
+ wildcard_prefixes.add(pathlib.PurePosixPath(*literal_parts))
114
+ continue
115
+
116
+ parent_parts = parts[:-1]
117
+ for idx in range(1, len(parent_parts) + 1):
118
+ exact_dirs.add(pathlib.PurePosixPath(*parent_parts[:idx]))
119
+
120
+ return _NegatedDockerignoreHints(
121
+ exact_dirs=frozenset(exact_dirs),
122
+ wildcard_prefixes=frozenset(wildcard_prefixes),
123
+ recurse_all=recurse_all,
124
+ )
@@ -9,35 +9,12 @@ from contextlib import contextmanager
9
9
  import click
10
10
  import pathspec
11
11
 
12
+ from langgraph_cli._ignore import _build_ignore_spec
12
13
  from langgraph_cli.config import Config, _assemble_local_deps
13
14
 
14
15
  _WARN_SIZE = 50 * 1024 * 1024 # 50 MB
15
16
  _MAX_SIZE = 200 * 1024 * 1024 # 200 MB
16
17
 
17
- _ALWAYS_EXCLUDE = [
18
- "__pycache__/",
19
- ".git/",
20
- ".venv/",
21
- "venv/",
22
- "node_modules/",
23
- ".tox/",
24
- ".mypy_cache/",
25
- ]
26
-
27
-
28
- def _build_ignore_spec(directory: pathlib.Path) -> pathspec.PathSpec:
29
- """Build a PathSpec combining built-in exclusions with .dockerignore and .gitignore.
30
-
31
- Always excludes common non-source directories (_ALWAYS_EXCLUDE). On top of
32
- that, patterns from .dockerignore and .gitignore (if present) are merged in.
33
- """
34
- lines: list[str] = list(_ALWAYS_EXCLUDE)
35
- for name in (".dockerignore", ".gitignore"):
36
- ignore_file = directory / name
37
- if ignore_file.is_file():
38
- lines.extend(ignore_file.read_text(encoding="utf-8").splitlines())
39
- return pathspec.PathSpec.from_lines("gitwildmatch", lines)
40
-
41
18
 
42
19
  def _tar_filter(tarinfo: tarfile.TarInfo) -> tarfile.TarInfo | None:
43
20
  """Strip symlinks, hardlinks, and traversal paths from archive."""
@@ -10,7 +10,13 @@ except ModuleNotFoundError: # pragma: no cover - exercised on Python 3.10.
10
10
  import tomli as tomllib
11
11
 
12
12
  import click
13
+ import pathspec
13
14
 
15
+ from langgraph_cli._ignore import (
16
+ _build_dockerignore_negation_hints,
17
+ _build_ignore_spec,
18
+ _is_always_excluded,
19
+ )
14
20
  from langgraph_cli.schemas import Config
15
21
 
16
22
 
@@ -440,16 +446,32 @@ def _container_root_for_uv_lock_package(
440
446
 
441
447
 
442
448
  def _uv_lock_package_copy_items(
443
- package: UvLockPackage, plan: UvLockPlan
449
+ package: UvLockPackage,
450
+ plan: UvLockPlan,
451
+ ignore_spec: pathspec.PathSpec,
444
452
  ) -> tuple[tuple[pathlib.PurePosixPath, pathlib.PurePosixPath], ...]:
453
+ # Skip entries that .dockerignore / built-in exclusions would strip from
454
+ # the build context. Emitting `ADD <path>` for a file that Docker has
455
+ # filtered out causes the build to fail with
456
+ # "failed to compute cache key: <path> not found".
445
457
  if package.root != plan.project_root:
446
458
  relative_root = pathlib.PurePosixPath(
447
459
  *package.root.relative_to(plan.project_root).parts
448
460
  )
461
+ if _is_always_excluded(relative_root, is_dir=True) or ignore_spec.match_file(
462
+ f"{relative_root.as_posix()}/"
463
+ ):
464
+ raise click.UsageError(
465
+ f"Workspace member '{package.name}' at {relative_root} is "
466
+ "excluded from the Docker build context, but uv.lock requires "
467
+ "it to be copied into the build context. Remove the matching "
468
+ "pattern or drop the member from [tool.uv.workspace].members."
469
+ )
449
470
  return ((relative_root, plan.container_roots[package.root]),)
450
471
 
451
472
  root_container = plan.container_roots[package.root]
452
473
  workspace_member_roots = plan.all_workspace_roots - {plan.project_root}
474
+ negated_dockerignore_hints = _build_dockerignore_negation_hints(plan.project_root)
453
475
 
454
476
  def iter_entries(
455
477
  current_dir: pathlib.Path,
@@ -461,18 +483,32 @@ def _uv_lock_package_copy_items(
461
483
  # and excluded entirely otherwise.
462
484
  continue
463
485
 
464
- descendant_member_roots = [
465
- ws_root
466
- for ws_root in workspace_member_roots
467
- if child in ws_root.parents
468
- ]
469
- if child.is_dir() and descendant_member_roots:
470
- entries.extend(iter_entries(child))
471
- continue
472
-
473
486
  relative_child = pathlib.PurePosixPath(
474
487
  *child.relative_to(plan.project_root).parts
475
488
  )
489
+ is_dir = child.is_dir()
490
+ if _is_always_excluded(relative_child, is_dir=is_dir):
491
+ continue
492
+ ignored = ignore_spec.match_file(
493
+ f"{relative_child.as_posix()}/" if is_dir else relative_child.as_posix()
494
+ )
495
+ is_workspace_parent = is_dir and any(
496
+ child in ws_root.parents for ws_root in workspace_member_roots
497
+ )
498
+
499
+ if is_workspace_parent:
500
+ entries.extend(iter_entries(child))
501
+ continue
502
+ if (
503
+ is_dir
504
+ and ignored
505
+ and negated_dockerignore_hints.requires_dir_walk(relative_child)
506
+ ):
507
+ entries.extend(iter_entries(child))
508
+ continue
509
+ if ignored:
510
+ continue
511
+
476
512
  entries.append(
477
513
  (relative_child, root_container.joinpath(*relative_child.parts))
478
514
  )
@@ -956,10 +992,13 @@ def python_config_to_docker_uv_lock(
956
992
  docker_plan.add_raw("# -- End of uv.lock dependencies install --")
957
993
  docker_plan.add_blank()
958
994
 
995
+ ignore_spec = _build_ignore_spec(plan.project_root, include_gitignore=False)
959
996
  for package in plan.install_order:
960
997
  package_label = package.root.relative_to(plan.project_root).as_posix() or "."
961
998
  docker_plan.add_raw(f"# -- Adding workspace package {package_label} --")
962
- for source, destination in _uv_lock_package_copy_items(package, plan):
999
+ for source, destination in _uv_lock_package_copy_items(
1000
+ package, plan, ignore_spec
1001
+ ):
963
1002
  docker_plan.add_raw(copy_from_project_root(source, destination.as_posix()))
964
1003
  docker_plan.add_instruction(
965
1004
  "WORKDIR", plan.container_roots[package.root].as_posix()
@@ -99,6 +99,13 @@ class TestBuildIgnoreSpec:
99
99
  assert spec.match_file("app.log")
100
100
  assert spec.match_file("mod.pyc")
101
101
 
102
+ def test_can_skip_gitignore(self, tmp_path):
103
+ (tmp_path / ".dockerignore").write_text("*.log\n")
104
+ (tmp_path / ".gitignore").write_text("*.pyc\n")
105
+ spec = _build_ignore_spec(tmp_path, include_gitignore=False)
106
+ assert spec.match_file("app.log")
107
+ assert not spec.match_file("mod.pyc")
108
+
102
109
  def test_no_ignore_files_only_builtins(self, tmp_path):
103
110
  spec = _build_ignore_spec(tmp_path)
104
111
  assert spec.match_file("__pycache__/")