Files
langflow/RELEASE.md
Jordan Frazier 1ab6251164 feat: sync langflow and lfx versions (#13176)
* feat(lfx): synchronize LFX onto Langflow major.minor version line (Phase 1)

- Bump src/lfx/pyproject.toml from 0.5.0 to 1.10.0
- Tighten langflow-base lfx pin: ~=0.5.0 → ~=1.10.0
- Fix release.yml pre-release boundary from next-major to next-minor (<X.(Y+1).dev0)
- Add minor-parity soft check to scripts/release-lfx.sh
- Extend make patch to sync lfx version alongside langflow/base/frontend
- Document LFX compatibility contract in RELEASE.md

Contract: LFX X.Y.N is compatible with any Flow from Langflow X.Y.M.

* feat(lfx/upgrade): add compatibility checker (Phase 2)

* fix(lfx/upgrade): fix registry_code guard and types order-sensitivity in checker

* feat(lfx/upgrade): implement safe-upgrade applier (Phase 2)

* feat(lfx): add lfx upgrade command (Phase 2)

* feat(lfx/run): add --upgrade-flow option (Phase 2)

* feat(lfx/serve): add --upgrade-flow option (Phase 2)

* update lfx pyproject version

* [autofix.ci] apply automated fixes

* test(lfx/upgrade): add v1.9.0 starter flow fixtures for upgrade integration tests

* test(lfx/upgrade): add v1.9.0 starter flow fixtures and real-flow integration tests

* [autofix.ci] apply automated fixes

* fix(lfx/upgrade): use alias-aware registry lookup so renamed components are not falsely blocked

* fix(lfx/upgrade): handle outer flow envelope and file-path inputs in upgrade checks

* fix(lfx/upgrade): fail-fast on upgrade_flow errors; add regression tests for Bugs 1-3

* chore(tests): remove bug-number labels from test comments

* [autofix.ci] apply automated fixes

* fix(lfx/upgrade): address PR review comments

- Reject .py files early in serve --upgrade-flow with a clear error message
- Preserve outer flow envelope metadata (name, description, etc.) when lfx upgrade --write rewrites a file
- Move Mapping import under TYPE_CHECKING to satisfy Ruff TC003
- Assert fixture flows and registry are non-empty so parametrized tests cannot pass vacuously
- Remove unused capsys arg and replace print with sys.stderr.write (Ruff T201/ARG001)
- Add upgrade_flow param to run command docstring

* fix(lfx/upgrade): preserve envelope on run, apply nested upgrades (#13200)

* fix(lfx/upgrade): preserve envelope on run, apply nested upgrades

Three follow-ups on top of the upgrade tooling:

- run --upgrade-flow: re-attach the inner graph to the outer envelope before handing the flow to aload_flow_from_json. Previously the upgrade path unwrapped {"data": ...} and passed the inner dict to the loader, which raised KeyError: 'data'. Adds happy-path tests for envelope and flat file inputs.
- applier: recurse into one level of nested flow nodes (node.data.node.flow.data.nodes), matching the checker. Without this, outdated_safe nodes inside grouped components were reported but never written. Adds a regression test.
- upgrade --write test: assert the envelope is preserved (name, description, endpoint_name, etc.) instead of the previous unwrapped-output expectation, so the test matches the actual fix in this PR.
- Drive-by ruff cleanup: D417 docstring, SIM103, SIM108, E501, RUF059.

* [autofix.ci] apply automated fixes

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>

* fix(lfx/upgrade): fix loader KeyError, outer-envelope checker gap, and nested-node applier

Three bugs from ogabrielluiz's PR review:

1. run/base.py loader KeyError: file-path branch unwrapped the outer envelope
   for the checker but passed the inner dict to aload_flow_from_json, which does
   flow_graph["data"] unconditionally. Re-wrap as {"data": flow_dict} before the
   loader call. Applied consistently to all three input paths (--flow-json,
   --stdin, file-path).

2. run/base.py zero-node checker for --flow-json/--stdin: those paths stored the
   raw parsed JSON without unwrapping, so a caller passing an exported flow
   {"name":..., "data":{...}} caused the checker to see zero nodes and silently
   pass. Now unwrap with raw.get("data", raw) matching the file-path branch.

3. applier.py nested flows never upgraded: the early `continue` on top-level
   nodes not in safe_ids prevented the nested-node loop from running. Restructured
   so the nested check fires unconditionally for every top-level node, mirroring
   checker.py:160-165.

* fix(lfx/run): unify outer-envelope unwrap across all --upgrade-flow input paths

File-path reads now call raw.get("data", raw) matching --flow-json and
--stdin, so the upgrade checker always sees the inner graph. This also
removes the has_envelope/re-wrap machinery that was double-wrapping
outer-envelope files when passing to aload_flow_from_json.

Fix two test assertions that described the old double-wrapped shape.

* fix(lfx/upgrade): inject registry into upgrade_command; fix Makefile lfx pin regex

- upgrade_command now accepts an optional registry parameter so tests
  can pass the dict directly instead of mocking load_registry_from_index
- Makefile sed regex broadened from \"lfx~=.*\" to match both ~= and >=
  forms so make patch works after release.yml rewrites the pin
- Echo label changed to LFX (synced) to clarify the variable is the
  shared Langflow version, not a separate LFX-specific value

* fix(make/patch): fix langflow-base sed regex and validation grep

The dependency in pyproject.toml is "langflow-base[complete]>=X.Y.Z",
not the "langflow-base==X" form the original regex expected, so make
patch silently left the pin unchanged and the validation step always
failed. The sed pattern now matches any extras/operator combination and
rewrites to the canonical [complete]>= form; the grep uses -F so the
[complete] brackets are treated literally.

* test(lfx): add patch regex tests and symmetric safe-mode envelope tests

test_patch_regexes.py — 15 tests covering the Python regexes embedded in
the Makefile patch target. Exercises all three substitutions (langflow-base
pin, lfx pin, version field) against every realistic pin format including
the >=X.Y.Z,<dev0 form that release.yml writes. Would have caught the
langflow-base==.* vs [complete]>= mismatch before manual testing.

test_base.py — two new TestUpgradeFlowOption tests:
  test_upgrade_flow_safe_envelope_inline_json_loads_successfully
  test_upgrade_flow_safe_envelope_stdin_loads_successfully
Symmetric to the existing file-path envelope test; verifies that --flow-json
and --stdin with an outer-envelope flow also pass {"data": inner} to the
loader after safe upgrades, not a double-wrapped {"data": outer_envelope}.

* fix(lfx/upgrade): address review: compat checker, shared gate, fail-fast registry

Checker correctness:
- _outputs_are_compatible: drop cosmetic display_name from the breaking check;
  treat widened output types as safe (flow types must be a subset of registry
  types), only narrowing breaks downstream edges.
- _input_types_contained: stop flagging widened input_types as breaking; keep
  narrowing as the only breaking case; fix misleading comments.
- check_flow_compatibility now recurses fully into nested grouped components
  (symmetric with the applier) and accepts a pre-built registry lookup.

CLI/run/serve:
- New lfx.upgrade.cli_gate (UpgradeFlowMode enum, UpgradeFlowError,
  apply_upgrade_gate) shared by run_flow and serve_command so the two
  --upgrade-flow paths can't diverge.
- run_flow: extract _materialize_flow_dict and route gating through the shared
  helper.
- run/serve --upgrade-flow options typed as UpgradeFlowMode (check|safe choices).
- lfx upgrade: load_registry_from_index fails fast when the bundled registry is
  empty/missing instead of silently marking every node blocked; ASCII-only report
  output; new --strict flag; build the registry lookup once and reuse it.

Docs/tests:
- RELEASE.md: migration note for the lfx 0.5.0 -> 1.10.0 version jump.
- Regression tests for the checker fixes, the shared gate, fail-fast registry,
  --strict, and serve --upgrade-flow parity.

* [autofix.ci] apply automated fixes

* [autofix.ci] apply automated fixes (attempt 2/3)

* fix(lfx): consolidate flow-envelope handling and fix version-ceiling regex

Extract the outer-envelope unwrap/rewrap logic (previously hand-rolled as
raw.get("data", raw) with subtly different rules across serve, run, and
upgrade) into a single lfx.utils.flow_envelope module with split/merge
helpers. This fixes a serve bug where an enveloped flow had its inner graph
written bare to the temp file, making the loader's flow_graph["data"] raise
KeyError.

Also fix release.yml's major.minor extraction: the greedy sed grabbed the
upper bound from a range pin (>=1.10.0,<1.11.dev0), drifting the version
ceiling up one minor each release cycle. Anchor to the first version instead.

* fix(lfx): upgrade-flow gate reads bundled component index, not empty cache

The --upgrade-flow=check|safe gate on lfx run and lfx serve rejected every
flow as 'blocked'. Both call sites passed component_cache.all_types_dict to
apply_upgrade_gate, but that cache is populated lazily after services start,
so at gate time it is empty -- an empty registry classifies every node as
blocked. The standalone lfx upgrade command was unaffected because it reads
the bundled _assets/component_index.json instead.

Make the gate own registry loading: apply_upgrade_gate now defaults
all_types_dict to None and loads the bundled index (the same source lfx
upgrade uses) via a new _load_bundled_registry helper, raising
UpgradeFlowError on a missing/empty index so a broken install fails loudly
instead of silently blocking every component. Both call sites pass mode=
and let the gate load the registry.

Existing gate tests mocked component_cache with a populated registry, which
is exactly what hid the bug; repoint them at the new _load_bundled_registry
seam and add regression tests that do not mock the registry, including an
end-to-end run_flow check against a real clean v1.9.0 starter flow.

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Gabriel Luiz Freitas Almeida <gabriel@logspace.ai>
2026-06-05 01:30:59 +00:00

192 lines
8.0 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Releasing Langflow
Langflow follows a **release-when-ready** cadence, with each cycle typically lasting 46 weeks depending on QA and stabilization needs.
## Goals
* Keep `main` fast-moving for everyday work while ensuring stable release builds when features mature.
* Provide an isolated branch for QA and last-minute fixes (the release candidate, RC).
* Preserve a linear, readable history wherever possible.
* Ensure released code is extensively tested before publication.
* Minimize time to resolution of critical bugs.
## Process Overview
### 1. OSS QA
Create an OSS release candidate (RC) branch containing `langflow` and any associated PyPI packages (e.g. `lfx`).
During this period:
* QA is performed manually.
* Bug fixes are merged into the RC branch.
* New features continue development on `main`.
This step usually lasts about a week.
### 2. Desktop QA
Once OSS QA and bugfixing are complete, create a Desktop release candidate.
* The Desktop RC is based on the final OSS RC.
* Manual QA is performed.
* Bug fixes are merged into the Desktop RC.
* New features continue on `main`.
This step also usually lasts about a week.
### 3. Release
After QA and bugfixing are complete for both OSS and Desktop:
* Final releases are cut from their respective RC branches.
* Release timing is coordinated with Langflow's DevRel team.
* For at least 24 hours after release, Discord, GitHub, and other support channels should be monitored for critical bug reports.
### 4. Release Artifacts
The release workflow automatically publishes the following artifacts:
* **PyPI Packages:**
* `langflow` - Main package with all integrations
* `langflow-base` - Core framework without integrations
* `lfx` - Lightweight executor CLI
* `langflow-sdk` - SDK for programmatic access (when updated)
* **Docker Images:**
* `langflowai/langflow` - Full Langflow image
* `langflowai/langflow-backend` - Backend-only image (published independently)
* `langflowai/langflow-frontend` - Frontend-only image (published independently)
* `langflowai/langflow-ep` - Enterprise edition image (published independently)
* `langflowai/langflow-base` - Base image without integrations
**Note:** Backend, frontend, and enterprise images are published separately from the main image and will be built even if the main version already exists on Docker Hub.
## Branch Model
| Branch | Purpose | Merge Policy |
| --------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ |
| **`main`** | Integration branch. All feature PRs target this by default. | **Squash & Merge** (linear history) |
| **`release-X.Y.Z`**<br>(e.g. `release-1.4.3`) | Temporary RC branch. Active only for the release cycle. Accepts QA and blocking-bug PRs labeled `type:release`. | **Squash & Merge** within the branch.<br>Rebased onto **`main`** before final merge. |
## Release Steps
### 1. Cut Release Candidate
```sh
git checkout main && git pull # Ensure local main is up to date
git checkout -b release-X.Y.Z # Create new release candidate branch
git push -u origin release-X.Y.Z # Push RC branch to remote
```
### 2. Apply a Bugfix to RC
1. Create a feature branch as usual.
2. Open a GitHub PR targeting `release-X.Y.Z`.
3. Review and approve as normal.
4. Merge into the RC branch after review.
### 3. Review Regression Log
Before tagging, review `regressions/X.Y.x.yaml` to confirm no unresolved `blocking` entries exist.
If `blocking` entries exist, they should be signed off on.
See [regressions/README.md](./regressions/README.md) for the full schema and entry instructions.
### 4. Final Release
```sh
git checkout release-X.Y.Z && git pull # Ensure RC branch is up to date
git tag vX.Y.Z # Create final release tag
git push origin vX.Y.Z # Push tag to remote
```
### 5. Merge RC Back into Main
```sh
git checkout main
git merge --ff-only release-X.Y.Z # Fast-forward main to include RC changes
```
## Merge Strategy
1. **Squash & Merge** everywhere for atomic commits and clean history.
2. While RC is open, periodically re-sync with main:
```sh
git checkout release-X.Y.Z
git fetch origin
git rebase origin/main
```
*This resolves conflicts early while keeping history linear.*
3. Final merge back must be fast-forward only. If not possible, rebase the RC onto `main` before merging.
## Versioning & Tags
* Follows [Semantic Versioning](https://semver.org): `MAJOR.MINOR.PATCH`.
* RC tags use `-rc.N`, e.g. `v1.8.0-rc.1`.
* **All tags MUST start with `v` prefix** (e.g., `v1.9.1`, not `1.9.1`).
* The release workflow validates this format and rejects tags without the `v` prefix.
* Duplicate tags (e.g., both `1.8.3` and `v1.8.3`) cause GitHub's release notes generation to use the wrong base comparison, resulting in incomplete changelogs.
* The workflow automatically checks for and prevents duplicate tags.
## LFX Compatibility
Langflow and LFX share a **major.minor version line**. The compatibility contract is:
> **LFX X.Y.N is guaranteed compatible with any Flow exported from Langflow X.Y.M.**
Patch releases (`N` and `M`) are independent — a patch to LFX does not require a Langflow patch release, and vice versa.
### Version management
`make patch v=X.Y.Z` updates all four artifacts together:
| Artifact | Version set |
|---|---|
| `langflow` | `X.Y.Z` |
| `langflow-base` | `0.Y.Z` |
| `lfx` | `X.Y.Z` |
| frontend | `X.Y.Z` |
### Cutting an LFX patch release
Use `scripts/release-lfx.sh <version>`. The script warns if the LFX minor version does not match the current Langflow minor version, which would violate the compatibility contract. A warning is not a hard block — patch-only LFX releases within the same minor are expected and fine.
### Implications for users
Users can pin `lfx~=X.Y.0` in their `requirements.txt` to receive all compatible LFX patch releases for a given Langflow minor.
### Migrating from lfx 0.5.x to 1.10.0
LFX was realigned from its standalone `0.5.x` line onto Langflow's `major.minor` line, so the version jumps from `0.5.0` to `1.10.0` in a single step. This is a version-numbering change, not 95 minors of feature churn. The jump affects downstream pins, and neither pip nor uv will flag it — so it must be called out in the release announcement, not just here:
- `lfx==0.5.x` or `lfx<1.0` pins will **not** upgrade (intentional — those deployments stay put).
- `lfx>=0.5,<1` pins will **not** upgrade.
- `lfx>=0.5` with no upper bound **will** pull `1.10.0` on the next install — a major jump with no warning.
Going forward, pin `lfx~=X.Y.0` (e.g. `lfx~=1.10.0`) so you track compatible patches for a given Langflow minor without silently crossing minor lines.
## Roles
| Role | Responsibility |
| --------------------------------------- | ----------------------------------------------------------------- |
| **Release Captain** (rotates per cycle) | Owns timeline, branch cut, tagging, merge-back. |
| **PR Author** | Ensures tests pass; flags PR with `type:release` if needed in RC. |
| **CI** | Blocks merges on failing tests or missing labels. |
## FAQ
### Do we ever merge main into the RC?
No. Always rebase the RC onto `main` to preserve linear history.
### Can we automate branch deletion?
Not yet — merge-back and cleanup are manual.
### How flexible is the timeline?
Very flexible. QA and stabilization phases can be extended as needed for quality.