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.
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/PKG-INFO +1 -1
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/examples/graph_prerelease_reqs/deps/zuper_deps/pyproject.toml +1 -1
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/examples/graph_prerelease_reqs/pyproject.toml +1 -1
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/examples/graph_prerelease_reqs_fail/pyproject.toml +1 -1
- langgraph_cli-0.4.24/langgraph_cli/__init__.py +1 -0
- langgraph_cli-0.4.24/langgraph_cli/_ignore.py +124 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/langgraph_cli/archive.py +1 -24
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/langgraph_cli/uv_lock.py +50 -11
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/tests/unit_tests/test_archive.py +7 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/tests/unit_tests/test_config.py +359 -0
- langgraph_cli-0.4.23/langgraph_cli/__init__.py +0 -1
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/.gitignore +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/LICENSE +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/Makefile +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/README.md +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/examples/.env.example +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/examples/.gitignore +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/examples/Makefile +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/examples/graph_prerelease_reqs/agent.py +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/examples/graph_prerelease_reqs/deps/additional_deps/pyproject.toml +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/examples/graph_prerelease_reqs/langgraph.json +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/examples/graph_prerelease_reqs_fail/agent.py +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/examples/graph_prerelease_reqs_fail/langgraph.json +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/examples/graphs/agent.py +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/examples/graphs/langgraph.json +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/examples/graphs/storm.py +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/examples/graphs_reqs_a/__init__.py +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/examples/graphs_reqs_a/graphs_submod/__init__.py +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/examples/graphs_reqs_a/graphs_submod/agent.py +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/examples/graphs_reqs_a/graphs_submod/subprompt.txt +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/examples/graphs_reqs_a/hello.py +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/examples/graphs_reqs_a/langgraph.json +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/examples/graphs_reqs_a/prompt.txt +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/examples/graphs_reqs_a/requirements.txt +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/examples/graphs_reqs_b/graphs_submod/agent.py +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/examples/graphs_reqs_b/graphs_submod/subprompt.txt +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/examples/graphs_reqs_b/hello.py +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/examples/graphs_reqs_b/langgraph.json +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/examples/graphs_reqs_b/prompt.txt +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/examples/graphs_reqs_b/requirements.txt +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/examples/graphs_reqs_b/utils/__init__.py +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/examples/graphs_reqs_b/utils/greeter.py +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/examples/langgraph.json +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/examples/my_app.py +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/examples/pipconf.txt +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/examples/poetry.lock +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/examples/pyproject.toml +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/generate_schema.py +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/js-examples/.dockerignore +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/js-examples/.editorconfig +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/js-examples/.env.example +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/js-examples/.eslintrc.cjs +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/js-examples/.gitignore +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/js-examples/LICENSE +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/js-examples/README.md +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/js-examples/jest.config.js +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/js-examples/langgraph.json +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/js-examples/package.json +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/js-examples/src/agent/graph.ts +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/js-examples/src/agent/state.ts +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/js-examples/static/studio.png +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/js-examples/tests/agent.test.ts +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/js-examples/tests/graph.int.test.ts +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/js-examples/tsconfig.json +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/js-examples/yarn.lock +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/js-monorepo-example/.eslintrc.cjs +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/js-monorepo-example/apps/agent/langgraph.json +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/js-monorepo-example/apps/agent/package.json +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/js-monorepo-example/apps/agent/src/graph.ts +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/js-monorepo-example/apps/agent/src/state.ts +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/js-monorepo-example/apps/agent/tsconfig.json +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/js-monorepo-example/libs/shared/package.json +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/js-monorepo-example/libs/shared/src/index.ts +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/js-monorepo-example/libs/shared/tsconfig.json +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/js-monorepo-example/package.json +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/js-monorepo-example/tsconfig.json +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/js-monorepo-example/turbo.json +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/js-monorepo-example/yarn.lock +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/langgraph_cli/__main__.py +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/langgraph_cli/analytics.py +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/langgraph_cli/cli.py +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/langgraph_cli/config.py +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/langgraph_cli/constants.py +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/langgraph_cli/deploy.py +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/langgraph_cli/docker.py +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/langgraph_cli/exec.py +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/langgraph_cli/host_backend.py +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/langgraph_cli/progress.py +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/langgraph_cli/py.typed +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/langgraph_cli/schemas.py +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/langgraph_cli/templates.py +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/langgraph_cli/util.py +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/langgraph_cli/version.py +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/pyproject.toml +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/python-monorepo-example/apps/agent/.env.example +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/python-monorepo-example/apps/agent/langgraph.json +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/python-monorepo-example/apps/agent/pyproject.toml +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/python-monorepo-example/apps/agent/src/agent/__init__.py +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/python-monorepo-example/apps/agent/src/agent/graph.py +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/python-monorepo-example/apps/agent/src/agent/state.py +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/python-monorepo-example/libs/common/__init__.py +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/python-monorepo-example/libs/common/helpers.py +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/python-monorepo-example/libs/shared/pyproject.toml +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/python-monorepo-example/libs/shared/src/shared/__init__.py +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/python-monorepo-example/libs/shared/src/shared/utils.py +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/python-monorepo-example/pyproject.toml +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/schemas/schema.json +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/schemas/schema.v0.json +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/schemas/version.schema.json +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/tests/__init__.py +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/tests/integration_tests/__init__.py +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/tests/integration_tests/test_cli.py +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/tests/unit_tests/__init__.py +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/tests/unit_tests/agent.py +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/tests/unit_tests/cli/__init__.py +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/tests/unit_tests/cli/langgraph.json +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/tests/unit_tests/cli/pyproject.toml +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/tests/unit_tests/cli/test_cli.py +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/tests/unit_tests/cli/test_templates.py +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/tests/unit_tests/conftest.py +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/tests/unit_tests/graphs/agent.py +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/tests/unit_tests/helpers.py +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/tests/unit_tests/multiplatform/js.mts +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/tests/unit_tests/multiplatform/python.py +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/tests/unit_tests/pipconfig.txt +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/tests/unit_tests/test_config.json +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/tests/unit_tests/test_deploy_helpers.py +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/tests/unit_tests/test_docker.py +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/tests/unit_tests/test_host_backend.py +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/tests/unit_tests/test_logs_helpers.py +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/tests/unit_tests/test_util.py +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/uv-examples/monorepo/apps/agent/.env.example +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/uv-examples/monorepo/apps/agent/langgraph.json +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/uv-examples/monorepo/apps/agent/pyproject.toml +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/uv-examples/monorepo/apps/agent/src/agent/__init__.py +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/uv-examples/monorepo/apps/agent/src/agent/graph.py +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/uv-examples/monorepo/libs/shared/pyproject.toml +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/uv-examples/monorepo/libs/shared/src/shared/__init__.py +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/uv-examples/monorepo/libs/shared/src/shared/utils.py +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/uv-examples/monorepo/pyproject.toml +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/uv-examples/monorepo/uv.lock +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/uv-examples/simple/.env.example +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/uv-examples/simple/langgraph.json +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/uv-examples/simple/pyproject.toml +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/uv-examples/simple/src/agent/__init__.py +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/uv-examples/simple/src/agent/graph.py +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/uv-examples/simple/uv.lock +0 -0
- {langgraph_cli-0.4.23 → langgraph_cli-0.4.24}/uv.lock +0 -0
|
@@ -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,
|
|
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(
|
|
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__/")
|