Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
1f46056
feat(extension): add manifest-less lfx.bundles discovery + precedence…
erichare Jun 9, 2026
dbedb8f
Merge remote-tracking branch 'origin/release-1.11.0' into bundles/fou…
erichare Jun 9, 2026
8afc5ad
docs(bundles): changelog entry for the lfx.bundles discovery surface
erichare Jun 10, 2026
5bec743
Merge remote-tracking branch 'origin/release-1.11.0' into bundles/fou…
erichare Jun 10, 2026
8a4e311
fix(extension): reject plain-module lfx.bundles targets; correct vali…
erichare Jun 10, 2026
1aa104a
fix(extension): review fixes — shadowed metapackage providers never i…
erichare Jun 10, 2026
883789c
fix(extension): harden lfx.bundles discovery per review — broad find_…
erichare Jun 10, 2026
7cd1673
feat(bundles): create lfx-bundles metapackage skeleton (#13564)
erichare Jun 11, 2026
51f8ecf
ci(bundles): cross-bundle test matrix (lfx contract axis) (#13566)
erichare Jun 11, 2026
8788f9c
feat(lfx): add lfx[bundles] extra and keep lfx engine-only (#13565)
erichare Jun 11, 2026
2b5faf2
ci(bundles): freeze lfx/components against new top-level providers (#…
erichare Jun 11, 2026
7139d07
feat(bundles): move 45 long-tail providers into lfx-bundles + graduat…
erichare Jun 11, 2026
9df9475
Frontend test updates
erichare Jun 11, 2026
5ec49d8
fix: restore loading.py and tableAutoCellRender to base-branch state
erichare Jun 11, 2026
cbe8d23
ci(bundles): make freeze gate reachable on PRs; review follow-ups
erichare Jun 11, 2026
98cd335
fix(bundles): restore legacy-name resolution for ext components; sync…
erichare Jun 11, 2026
f550c6d
fix(bundles): keep importable module paths in starter projects
erichare Jun 11, 2026
031ca0a
Merge branch 'release-1.11.0' into bundles/foundation-discovery
erichare Jun 12, 2026
5fbac3d
feat: add Oracle integration (#13502)
fileames Jun 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
162 changes: 162 additions & 0 deletions .github/workflows/cross-bundle-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
name: Cross-Bundle Test

# Tests every extracted bundle (the lfx-bundles metapackage + each graduated
# lfx-<provider>) against the lfx contract surface it depends on. lfx is the
# PRIMARY axis because the BUNDLE_API contract lives in lfx: a bundle that
# imports cleanly, validates, and passes its tests against a given lfx is
# compatible with that lfx minor.
#
# Cost-control shape (from the bundle-separation epic):
# - contract-smoke (install + import + discover + validate): every bundle,
# on the oldest and latest supported Python.
# - bundle tests (pytest the bundle's own tests/): same matrix.
# - scheduled run: weekly, the exhaustive grid.
#
# The lfx-minor axis is currently a single entry -- the IN-REPO lfx -- because
# the 1.10 line is not yet published to PyPI. When lfx minors publish, add an
# explicit lfx-version matrix dimension here (oldest + latest get the full
# tests; every supported minor gets contract-smoke), and wire langflow RCs as
# the secondary axis via workflow_call from the release pipeline.

on:
workflow_call:
workflow_dispatch:
pull_request:
paths:
- "src/bundles/**"
- "src/lfx/**"
- ".github/workflows/cross-bundle-test.yml"
schedule:
- cron: "0 6 * * 1" # Monday 06:00 UTC -- exhaustive grid

# Stacked bundle PRs would otherwise queue N-bundles x N-pythons jobs per push.
concurrency:
group: cross-bundle-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

jobs:
discover:
name: Discover bundles
runs-on: ubuntu-latest
timeout-minutes: 5
outputs:
bundles: ${{ steps.find.outputs.bundles }}
steps:
- uses: actions/checkout@v6
with:
persist-credentials: false
- id: find
name: Enumerate src/bundles/*/pyproject.toml
run: |
shopt -s nullglob
dirs=()
for p in src/bundles/*/pyproject.toml; do
dirs+=("$(dirname "$p")")
done
if [ ${#dirs[@]} -eq 0 ]; then
echo "No bundles found under src/bundles/*/" >&2
echo "bundles=[]" >> "$GITHUB_OUTPUT"
exit 0
fi
json=$(printf '%s\n' "${dirs[@]}" | sort | jq -R . | jq -cs .)
echo "bundles=$json" >> "$GITHUB_OUTPUT"
echo "Discovered bundles: $json"

bundle:
name: ${{ matrix.bundle }} (py${{ matrix.python-version }})
needs: discover
if: needs.discover.outputs.bundles != '[]'
runs-on: ubuntu-latest
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
bundle: ${{ fromJson(needs.discover.outputs.bundles) }}
# PR runs: oldest + latest supported Python (cost-control smoke).
# Scheduled runs: every supported Python (the exhaustive grid).
# The lfx-minor axis collapses to the in-repo lfx until the 1.10 line
# publishes (see header).
python-version: ${{ github.event_name == 'schedule' && fromJson('["3.10", "3.11", "3.12", "3.13", "3.14"]') || fromJson('["3.10", "3.13"]') }}
steps:
- uses: actions/checkout@v6
with:
persist-credentials: false
- uses: astral-sh/setup-uv@v6
with:
enable-cache: true
cache-dependency-glob: "uv.lock"
python-version: ${{ matrix.python-version }}

- name: Install in-repo lfx + the bundle into a clean venv
run: |
uv venv
# The in-repo lfx satisfies the bundle's `lfx>=X,<Y` pin and is the
# contract surface under test; pytest is for the bundle's own tests.
# tomli backports stdlib tomllib for the py3.10 matrix leg.
uv pip install ./src/lfx "./${{ matrix.bundle }}" pytest pytest-asyncio tomli

- name: Contract smoke -- import + discovery
run: |
.venv/bin/python - "${{ matrix.bundle }}" <<'PY'
import importlib
import sys
from pathlib import Path

try:
import tomllib # stdlib from 3.11
except ModuleNotFoundError:
import tomli as tomllib # py3.10 backport, installed above

bundle_dir = Path(sys.argv[1])
meta = tomllib.loads((bundle_dir / "pyproject.toml").read_text())
name = meta["project"]["name"]
eps = meta["project"].get("entry-points", {})

# Manifest bundles declare langflow.extensions; the manifest-less
# metapackage declares lfx.bundles. Either way the declared package
# must import.
modules = list(eps.get("langflow.extensions", {}).values()) or list(eps.get("lfx.bundles", {}).values())
assert modules, f"{name}: no langflow.extensions or lfx.bundles entry-point declared"
for value in modules:
importlib.import_module(value.split(":", 1)[0])
print(f" imported {value}")

# The manifest-less metapackage must be discoverable by the loader.
# This venv installs the bundle WITHOUT its per-provider extras, so
# providers whose modules import their SDK at top level degrade with
# `module-import-failed` -- that is the expected graceful-degradation
# contract, not a failure. Structural errors (bundle-empty,
# path-escape, invalid names, ...) still fail the smoke. A provider
# whose every module failed import also reports no components, which
# is fine here; the bundle's own tests cover behavior with SDKs.
if "lfx.bundles" in eps:
from lfx.extension import load_lfx_bundles_extensions

EXPECTED_WITHOUT_EXTRAS = {"module-import-failed"}
results = load_lfx_bundles_extensions()
bad = [e.code for r in results for e in r.errors if e.code not in EXPECTED_WITHOUT_EXTRAS]
assert not bad, f"{name}: lfx.bundles structural discovery errors: {bad}"
providers = sum(1 for r in results if r.bundle)
degraded = sum(1 for r in results for e in r.errors if e.code in EXPECTED_WITHOUT_EXTRAS)
print(f" lfx.bundles discovery OK ({providers} providers; {degraded} module(s) degraded sans extras)")
print(f"{name}: contract smoke OK")
PY

- name: Validate manifest (manifest-shipping bundles only)
run: |
# Manifest bundles ship extension.json; the manifest-less metapackage
# has none and is exempt from `lfx extension validate`.
manifest=$(ls "${{ matrix.bundle }}"/src/*/extension.json 2>/dev/null | head -1 || true)
if [ -n "$manifest" ]; then
.venv/bin/lfx extension validate "$(dirname "$manifest")"
else
echo "manifest-less bundle; skipping extension validate"
fi

- name: Run the bundle's own tests
run: |
if [ -d "${{ matrix.bundle }}/tests" ]; then
.venv/bin/python -m pytest "${{ matrix.bundle }}/tests" -q
else
echo "no tests/ directory in ${{ matrix.bundle }}; skipping"
fi
17 changes: 17 additions & 0 deletions .github/workflows/extension-migration-checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ on:
- "src/lfx/src/lfx/components/**"
- "src/bundles/**"
- "src/backend/base/langflow/api/**.py"
- "scripts/ci/check_components_frozen.py"
- "scripts/ci/frozen_component_dirs.txt"
- ".github/workflows/extension-migration-checks.yml"

jobs:
Expand Down Expand Up @@ -84,3 +86,18 @@ jobs:

- name: Run check_router_trust.py
run: python scripts/migrate/check_router_trust.py

freeze-components:
name: Freeze lfx/components
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Checkout code
uses: actions/checkout@v6
with:
persist-credentials: false

# Stdlib-only gate; no uv sync needed. After the bundle metapackage
# split, new providers go to lfx-bundles, not src/lfx/src/lfx/components/.
- name: Forbid new top-level directories under lfx/components (post-freeze)
run: python3 scripts/ci/check_components_frozen.py
Loading
Loading