mirror of
https://github.com/langflow-ai/langflow.git
synced 2026-07-24 07:34:10 +08:00
* ci: stop publishing -nightly bundle variants; release bundles on demand (#13206) Bundles (lfx-arxiv, lfx-duckduckgo) change infrequently enough that a nightly cadence is overkill. Drop the rename-to-`-nightly` and bundle publish lanes from the nightly pipeline and add a dedicated workflow_dispatch-only release_bundles.yml for purposeful releases. - nightly_build.yml: drop update_bundle_versions.py invocation and the src/bundles/*/pyproject.toml git-add guard. - release_nightly.yml: remove build-nightly-bundles and publish-nightly-bundles jobs; untether test-cross-platform and publish-nightly-main from them. - release_bundles.yml (new): build all src/bundles/* wheels, run a cross-OS install smoke test, then publish to PyPI under their stable names. Tolerates already-published versions so re-runs without a version bump are no-ops. release.yml still owns bundle publishing as part of main releases. Manual follow-up (PyPI admin): delete the lfx-arxiv-nightly and lfx-duckduckgo-nightly projects from PyPI. * ci(release_bundles): build lfx wheel locally for smoke test (#13207) The release_bundles.yml smoke test installs bundle wheels into a fresh venv, which makes uv resolve their `lfx>=X.Y,<Z` pin against PyPI. When bundles are released ahead of a matching lfx (typical case — bundles ship more often than lfx bumps land on PyPI), the resolve fails: Because only lfx<=0.4.3 is available and lfx-arxiv==0.1.0 depends on lfx>=0.5.0,<0.6.0, we can conclude that lfx-arxiv==0.1.0 cannot be used. Build the lfx wheel from the current ref in the build-bundles job and install it alongside the bundle wheels in the smoke test. The lfx wheel ships as a separate `dist-lfx-smoketest` artifact and is NOT included in the publish-bundles step — only the `dist-bundles` artifact gets pushed to PyPI. * ci(nightly): re-pin bundle lfx deps to lfx-nightly during nightly build (#13208) The nightly pipeline renames the workspace `lfx` package to `lfx-nightly` in `src/lfx/pyproject.toml` and the root workspace dep, but leaves each `src/bundles/*/pyproject.toml`'s `"lfx>=X.Y,<Z"` pin unchanged. With the workspace `lfx` gone and PyPI still on lfx 0.4.3, the create-nightly-tag job's `uv lock` fails: × No solution found when resolving dependencies for split [...] ╰─▶ Because only lfx<=0.4.3 is available and lfx-arxiv depends on lfx>=0.5.0,<0.6.0, we can conclude that lfx-arxiv's requirements are unsatisfiable. `update_lfx_version.py` now also rewrites the `lfx` (or already-rewritten `lfx-nightly`) specifier in every `src/bundles/*/pyproject.toml` to `lfx-nightly==<dev version>`, mirroring how `update_lf_base_dependency` re-pins the lfx dep inside `langflow-base`. The lookahead in the regex prevents matching sibling packages like `lfx-arxiv` / `lfx-duckduckgo`. No-op when no bundle pyprojects exist (e.g. on main today), so this is safe to merge ahead of the bundle extraction landing on main. Restored the guarded `git add src/bundles/*/pyproject.toml` step so the modified bundle pyprojects ride the same commit/tag as the rest of the nightly version bumps. Cherry-pick to release-1.10.0 to unblock nightlies cut from that branch.
136 lines
4.8 KiB
Python
136 lines
4.8 KiB
Python
"""Script to update LFX version for nightly builds."""
|
|
|
|
import re
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
from update_pyproject_name import update_pyproject_name
|
|
from update_pyproject_version import update_pyproject_version
|
|
|
|
# Add the current directory to the path so we can import the other scripts
|
|
current_dir = Path(__file__).resolve().parent
|
|
sys.path.append(str(current_dir))
|
|
|
|
BASE_DIR = Path(__file__).parent.parent.parent
|
|
|
|
|
|
def update_lfx_workspace_dep(pyproject_path: str, new_project_name: str) -> None:
|
|
"""Update the LFX workspace dependency in pyproject.toml."""
|
|
filepath = BASE_DIR / pyproject_path
|
|
content = filepath.read_text(encoding="utf-8")
|
|
|
|
if new_project_name == "lfx-nightly":
|
|
pattern = re.compile(r"lfx = \{ workspace = true \}")
|
|
replacement = "lfx-nightly = { workspace = true }"
|
|
else:
|
|
msg = f"Invalid LFX project name: {new_project_name}"
|
|
raise ValueError(msg)
|
|
|
|
# Updates the dependency name for uv
|
|
if not pattern.search(content):
|
|
msg = f"lfx workspace dependency not found in {filepath}"
|
|
raise ValueError(msg)
|
|
content = pattern.sub(replacement, content)
|
|
filepath.write_text(content, encoding="utf-8")
|
|
|
|
|
|
def update_sdk_dependency_in_lfx(pyproject_path: str, sdk_version: str) -> None:
|
|
"""Update the SDK dependency in the LFX pyproject for nightly builds."""
|
|
filepath = BASE_DIR / pyproject_path
|
|
content = filepath.read_text(encoding="utf-8")
|
|
|
|
pattern = re.compile(r'"langflow-sdk(?:-nightly)?(?:==|~=|>=)[\d.]+(?:\.(?:post|dev|a|b|rc)\d+)*"')
|
|
replacement = f'"langflow-sdk-nightly=={sdk_version}"'
|
|
|
|
if not pattern.search(content):
|
|
msg = f"SDK dependency not found in {filepath}"
|
|
raise ValueError(msg)
|
|
|
|
content = pattern.sub(replacement, content)
|
|
filepath.write_text(content, encoding="utf-8")
|
|
|
|
|
|
# Match an `lfx` (or `lfx-nightly`) dependency specifier inside a quoted
|
|
# string. The lookahead enforces a version operator immediately after the
|
|
# name so we don't accidentally match sibling packages like `lfx-arxiv` or
|
|
# `lfx-duckduckgo`.
|
|
_BUNDLE_LFX_DEP_PATTERN = re.compile(r'"lfx(?:-nightly)?(?=[<>=!~])[^"]*"')
|
|
|
|
|
|
def update_lfx_dep_in_bundles(lfx_version: str) -> None:
|
|
"""Pin every bundle's `lfx` dep to the renamed `lfx-nightly==<version>`.
|
|
|
|
Each `src/bundles/*/pyproject.toml` declares an `lfx>=X.Y,<Z` style pin
|
|
against the published `lfx` package. During nightly builds the
|
|
workspace `lfx` package gets renamed to `lfx-nightly`, so those pins
|
|
no longer resolve against the workspace member — and PyPI may not yet
|
|
ship a matching `lfx` either. Rewrite each bundle's pin to
|
|
`lfx-nightly==<exact dev version>` so `uv lock` resolves cleanly.
|
|
|
|
No-op when no bundles exist (e.g. on a branch that hasn't picked up
|
|
the bundle extraction) or when a bundle has no `lfx` dep.
|
|
"""
|
|
bundles_dir = BASE_DIR / "src" / "bundles"
|
|
if not bundles_dir.is_dir():
|
|
return
|
|
|
|
replacement = f'"lfx-nightly=={lfx_version}"'
|
|
for bundle_pyproject in sorted(bundles_dir.glob("*/pyproject.toml")):
|
|
content = bundle_pyproject.read_text(encoding="utf-8")
|
|
if not _BUNDLE_LFX_DEP_PATTERN.search(content):
|
|
continue
|
|
new_content = _BUNDLE_LFX_DEP_PATTERN.sub(replacement, content)
|
|
if new_content == content:
|
|
continue
|
|
bundle_pyproject.write_text(new_content, encoding="utf-8")
|
|
print(f"Updated lfx dep in {bundle_pyproject.relative_to(BASE_DIR)} -> lfx-nightly=={lfx_version}")
|
|
|
|
|
|
def update_lfx_for_nightly(lfx_tag: str, sdk_tag: str):
|
|
"""Update LFX package for nightly build.
|
|
|
|
Args:
|
|
lfx_tag: The nightly tag for LFX (e.g., "v0.1.0.dev0")
|
|
sdk_tag: The nightly tag for the SDK (e.g., "v0.1.0.dev0")
|
|
"""
|
|
lfx_pyproject_path = "src/lfx/pyproject.toml"
|
|
|
|
# Update name to lfx-nightly
|
|
update_pyproject_name(lfx_pyproject_path, "lfx-nightly")
|
|
|
|
# Update version (strip 'v' prefix if present)
|
|
version = lfx_tag.lstrip("v")
|
|
update_pyproject_version(lfx_pyproject_path, version)
|
|
|
|
# Update workspace dependency in root pyproject.toml
|
|
update_lfx_workspace_dep("pyproject.toml", "lfx-nightly")
|
|
|
|
sdk_version = sdk_tag.lstrip("v")
|
|
update_sdk_dependency_in_lfx(lfx_pyproject_path, sdk_version)
|
|
|
|
# Re-pin every bundle's lfx dep to the renamed workspace package so
|
|
# `uv lock` resolves cleanly. No-op when no bundles are present.
|
|
update_lfx_dep_in_bundles(version)
|
|
|
|
print(f"Updated LFX package to lfx-nightly version {version}")
|
|
|
|
|
|
def main():
|
|
"""Update LFX for nightly builds.
|
|
|
|
Usage:
|
|
update_lfx_version.py <lfx_tag> <sdk_tag>
|
|
"""
|
|
expected_args = 3
|
|
if len(sys.argv) != expected_args:
|
|
print("Usage: update_lfx_version.py <lfx_tag> <sdk_tag>")
|
|
sys.exit(1)
|
|
|
|
lfx_tag = sys.argv[1]
|
|
sdk_tag = sys.argv[2]
|
|
update_lfx_for_nightly(lfx_tag, sdk_tag)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|