Files
langgraph/.github/workflows/_integration_test.yml
T
51f6cee1b1 chore: uv lock resolution (#7342)
## Summary

Adds native uv workspace/lockfile support to the LangGraph CLI's Docker
build pipeline. Instead of listing dependencies manually, users can
point at their existing `uv.lock` and the CLI will:

1. Discover workspace packages and their dependency graph
2. Export locked requirements via `uv export --package <name> --frozen`
3. Copy only the necessary workspace closure into the container
4. Install packages in dependency order with `--no-deps` for
reproducibility
5. Rewrite all import paths (graphs, auth, encryption, etc.) to
container paths

### New config field: `source`

Rather than using `pip` or `uv pip`, we add a new `uv_lock` installer.
The previous installers should still remain unchanged.

To avoid ambiguity, we discriminate by "source" field and **do not
permit** other arbitrary "dependencies". In this mode, we will treat the
provided root (defaults to the current directory) as the source of
truth.

This also would natively support uv workspaces, so you can specify the
target package within a larger workspace.

**Simple single-package project:**
```json
{
  "python_version": "3.11",
  "graphs": {
    "agent": "./agent.py:graph"
  },
  "source": {
    "kind": "uv"
  }
}
```

**Multi-package workspace with explicit package:**
```json
{
  "python_version": "3.11",
  "graphs": {
    "agent": "../../apps/agent/src/agent/graph.py:graph"
  },
  "source": {
    "kind": "uv",
    "root": "../..",
    "package": "agent"
  }
}
```

**Traditional pip deployment (unchanged):**
```json
{
  "python_version": "3.11",
  "dependencies": ["langgraph", "my-package"],
  "graphs": {
    "agent": "./agent.py:graph"
  }
}
```

Config validation enforces mutual exclusivity. you must use either
`dependencies` or `source`, not both.

---------

Co-authored-by: Will Fu-Hinthorn <will@langchain.dev>
2026-04-07 17:17:54 -07:00

169 lines
8.4 KiB
YAML

name: CLI integration test
on:
workflow_call:
secrets:
LANGSMITH_API_KEY:
required: false
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
python-version:
- "3.10"
- "3.14"
example:
- name: A
workdir: libs/cli/examples
tag: langgraph-test-a
- name: B
workdir: libs/cli/examples/graphs
tag: langgraph-test-b
- name: C
workdir: libs/cli/examples/graphs_reqs_a
tag: langgraph-test-c
- name: D
workdir: libs/cli/examples/graphs_reqs_b
tag: langgraph-test-d
name: "CLI integration test"
env:
HAS_LANGSMITH_API_KEY: ${{ secrets.LANGSMITH_API_KEY != '' }}
defaults:
run:
working-directory: libs/cli
steps:
- uses: actions/checkout@v6
- name: Get changed files
id: changed-files
if: github.event_name != 'workflow_dispatch'
uses: Ana06/get-changed-files@25f79e676e7ea1868813e21465014798211fad8c # v2.3.0
with:
filter: "libs/cli/**"
- name: Set up Python ${{ matrix.python-version }}
if: (steps.changed-files.outputs.all || github.event_name == 'workflow_dispatch')
uses: ./.github/actions/uv_setup
with:
python-version: ${{ matrix.python-version }}
enable-cache: "false"
working-directory: libs/cli
- name: Install cli globally
if: (steps.changed-files.outputs.all || github.event_name == 'workflow_dispatch')
run: pip install -e .
- name: Build service ${{ matrix.example.name }}
if: (steps.changed-files.outputs.all || github.event_name == 'workflow_dispatch')
working-directory: ${{ matrix.example.workdir }}
run: |
langgraph build -t ${{ matrix.example.tag }}
- name: Test service ${{ matrix.example.name }}
if: ${{ (steps.changed-files.outputs.all || github.event_name == 'workflow_dispatch') &&env.HAS_LANGSMITH_API_KEY == 'true' }}
working-directory: ${{ matrix.example.workdir }}
env:
LANGSMITH_API_KEY: ${{ secrets.LANGSMITH_API_KEY }}
run: |
# Prepare environment file from local or parent example directory
if [ -f .env.example ]; then cp .env.example .env; elif [ -f ../.env.example ]; then cp ../.env.example .env && cp ../.env.example ../.env; fi
echo "LANGSMITH_API_KEY=${{ secrets.LANGSMITH_API_KEY }}" >> .env
if [ -f ../.env ]; then echo "LANGSMITH_API_KEY=${{ secrets.LANGSMITH_API_KEY }}" >> ../.env; fi
# Run the integration test using the built tag
REPO_ROOT=$(git rev-parse --show-toplevel)
timeout 60 python "$REPO_ROOT/.github/scripts/run_langgraph_cli_test.py" -t ${{ matrix.example.tag }}
- name: Build JS service
if: ${{ (steps.changed-files.outputs.all || github.event_name == 'workflow_dispatch') &&matrix.example.name == 'A' }}
working-directory: libs/cli/js-examples
run: |
langgraph build -t langgraph-test-e
- name: Build JS monorepo service
if: ${{ (steps.changed-files.outputs.all || github.event_name == 'workflow_dispatch') &&matrix.example.name == 'A' }}
working-directory: libs/cli/js-monorepo-example
run: |
langgraph build -t langgraph-test-f -c apps/agent/langgraph.json --build-command "yarn run turbo build" --install-command "yarn install"
- name: Build Python monorepo service
if: ${{ (steps.changed-files.outputs.all || github.event_name == 'workflow_dispatch') &&matrix.example.name == 'A' }}
working-directory: libs/cli/python-monorepo-example
run: |
langgraph build -t langgraph-test-g -c apps/agent/langgraph.json
- name: Test Python monorepo service
if: ${{ (steps.changed-files.outputs.all || github.event_name == 'workflow_dispatch') &&matrix.example.name == 'A' && env.HAS_LANGSMITH_API_KEY == 'true' }}
working-directory: libs/cli/python-monorepo-example
env:
LANGSMITH_API_KEY: ${{ secrets.LANGSMITH_API_KEY }}
run: |
cp apps/agent/.env.example apps/agent/.env
echo "LANGSMITH_API_KEY=${{ secrets.LANGSMITH_API_KEY }}" >> apps/agent/.env
timeout 60 python ../../../.github/scripts/run_langgraph_cli_test.py -t langgraph-test-g -c apps/agent/langgraph.json
- name: Build prerelease reqs service
if: ${{ (steps.changed-files.outputs.all || github.event_name == 'workflow_dispatch') &&matrix.example.name == 'A' }}
working-directory: libs/cli/examples/graph_prerelease_reqs
run: |
langgraph build -t langgraph-test-h
- name: Test prerelease reqs service
if: ${{ (steps.changed-files.outputs.all || github.event_name == 'workflow_dispatch') &&matrix.example.name == 'A' && env.HAS_LANGSMITH_API_KEY == 'true' }}
working-directory: libs/cli/examples/graph_prerelease_reqs
env:
LANGSMITH_API_KEY: ${{ secrets.LANGSMITH_API_KEY }}
run: |
cp ../.env.example .env
echo "LANGSMITH_API_KEY=${{ secrets.LANGSMITH_API_KEY }}" >> .env
timeout 60 python ../../../../.github/scripts/run_langgraph_cli_test.py -t langgraph-test-h
echo "Finished starting up langgraph-test-h"
LANGGRAPH_VERSION=$(docker run --rm --entrypoint "" langgraph-test-h python -c "import sys; from importlib.metadata import version; v = version('langgraph'); print(v);")
if [ "$LANGGRAPH_VERSION" != "1.1.5" ]; then
echo "LANGGRAPH_VERSION != 1.1.5; $LANGGRAPH_VERSION"
exit 1
fi
LANGCHAIN_OPENAI_VERSION=$(docker run --rm --entrypoint "" langgraph-test-h python -c "import sys; from importlib.metadata import version; v = version('langchain-openai'); print(v);")
if [ "$LANGCHAIN_OPENAI_VERSION" != "1.0.1" ]; then
echo "LANGCHAIN_OPENAI_VERSION != 1.0.1; $LANGCHAIN_OPENAI_VERSION"
exit 1
fi
LANGCHAIN_ANTHROPIC_VERSION=$(docker run --rm --entrypoint "" langgraph-test-h python -c "import sys; from importlib.metadata import version; v = version('langchain-anthropic'); print(v);")
if [ "$LANGCHAIN_ANTHROPIC_VERSION" != "1.0.0a5" ]; then
echo "LANGCHAIN_ANTHROPIC_VERSION != 1.0.0a5; $LANGCHAIN_ANTHROPIC_VERSION"
exit 1
fi
- name: Build and test prerelease reqs fail service
if: ${{ (steps.changed-files.outputs.all || github.event_name == 'workflow_dispatch') &&matrix.example.name == 'A' }}
working-directory: libs/cli/examples/graph_prerelease_reqs_fail
run: |
langgraph build -t langgraph-test-i || [ $? -eq 1 ]
- name: Build uv simple service
if: ${{ (steps.changed-files.outputs.all || github.event_name == 'workflow_dispatch') &&matrix.example.name == 'A' }}
working-directory: libs/cli/uv-examples/simple
run: |
langgraph build -t langgraph-test-uv-simple
- name: Test uv simple service
if: ${{ (steps.changed-files.outputs.all || github.event_name == 'workflow_dispatch') &&matrix.example.name == 'A' && env.HAS_LANGSMITH_API_KEY == 'true' }}
working-directory: libs/cli/uv-examples/simple
env:
LANGSMITH_API_KEY: ${{ secrets.LANGSMITH_API_KEY }}
run: |
cp .env.example .env
echo "LANGSMITH_API_KEY=${{ secrets.LANGSMITH_API_KEY }}" >> .env
timeout 60 python ../../../../.github/scripts/run_langgraph_cli_test.py -t langgraph-test-uv-simple
- name: Build uv monorepo service
if: ${{ (steps.changed-files.outputs.all || github.event_name == 'workflow_dispatch') &&matrix.example.name == 'A' }}
working-directory: libs/cli/uv-examples/monorepo/apps/agent
run: |
langgraph build -t langgraph-test-uv-monorepo
- name: Test uv monorepo service
if: ${{ (steps.changed-files.outputs.all || github.event_name == 'workflow_dispatch') &&matrix.example.name == 'A' && env.HAS_LANGSMITH_API_KEY == 'true' }}
working-directory: libs/cli/uv-examples/monorepo/apps/agent
env:
LANGSMITH_API_KEY: ${{ secrets.LANGSMITH_API_KEY }}
run: |
cp .env.example .env
echo "LANGSMITH_API_KEY=${{ secrets.LANGSMITH_API_KEY }}" >> .env
timeout 60 python ../../../../../../.github/scripts/run_langgraph_cli_test.py -t langgraph-test-uv-monorepo