Files
langflow/scripts/ci/update_pyproject_combined.py
Eric Hare dbe482c800 fix(ci): lockstep langflow-nightly and langflow-base-nightly versions (#13413)
* fix(ci): lockstep langflow-nightly and langflow-base-nightly versions

The nightly pipeline computed each package's .devN number independently
(pypi_nightly_tag.py queried each package's own PyPI history and
incremented separately), while langflow-nightly pins an exact
langflow-base-nightly[complete]==X.Y.Z.devN dependency. Because
langflow-nightly publishes on more nights than langflow-base-nightly,
the two counters drift (live: dev54 vs dev48). Each time base lags a
publish while main succeeds, the newest langflow-nightly pins a base
dev that does not exist yet, so 'uv pip install langflow-nightly'
becomes unsatisfiable and silently falls back to a weeks-old version.

Fix: version the two packages in lockstep. pypi_nightly_tag.py now
derives a single shared dev number from max(dev across BOTH packages'
PyPI histories) + 1 (restricted to the root base_version), so main and
base always get the identical tag and main's exact pin always
references a base version built and published in the same run. The tag
is computed in a single invocation ('both' mode) so the release and
base tags cannot drift across separate calls.

Also hardens tag computation: 404 / network / malformed responses for
either package now contribute nothing instead of crashing, a
base-version bump resets the dev counter cleanly, and non-dev/final
releases never advance it.

Adds scripts/ci/test_pypi_nightly_tag.py (unit tests) and a
ci-scripts-test.yml workflow to run scripts/ci tests on PRs.

* fix(ci): fail closed on non-404 PyPI errors in nightly tag computation

_all_dev_numbers() previously returned [] for ANY failure (network error,
non-404 HTTP status, malformed 200), which is unsafe: _shared_nightly_version()
takes max(dev)+1 across both packages, so a transient lookup failure on the
higher-versioned package silently LOWERS the next tag. E.g. if the langflow-nightly
lookup fails while langflow-base-nightly reports dev48, the script would emit
v1.10.0.dev49 even though langflow-nightly already published through dev54 — the
workflow then force-recreates an old git tag and fails publishing an already-existing
PyPI version.

Fail closed: only a true 404 (package has no releases) contributes []; network
errors, 5xx/non-404 statuses, and malformed 200s now raise so the nightly job aborts
before mutating tags. Adds tests for malformed/5xx/network failures and a direct
regression test for the higher-package-lookup-failure scenario.

* Update scripts/ci/test_pypi_nightly_tag.py

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Update test_pypi_nightly_tag.py

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2026-05-29 10:14:46 -07:00

65 lines
2.5 KiB
Python
Executable File

#!/usr/bin/env python
# scripts/ci/update_pyproject_combined.py
import sys
from pathlib import Path
from update_lf_base_dependency import update_lfx_dep_in_base
from update_pyproject_name import update_pyproject_name
from update_pyproject_name import update_uv_dep as update_name_uv_dep
from update_pyproject_version import update_pyproject_version
from update_uv_dependency import update_uv_dep as update_version_uv_dep
# 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))
def main():
"""Universal update script that handles both base and main updates in a single run.
Usage:
update_pyproject_combined.py main <main_tag> <base_tag> <lfx_tag>
"""
arg_count = 5
if len(sys.argv) != arg_count:
print("Usage:")
print(" update_pyproject_combined.py main <main_tag> <base_tag> <lfx_tag>")
sys.exit(1)
mode = sys.argv[1]
if mode != "main":
print("Only 'main' mode is supported")
print("Usage: update_pyproject_combined.py main <main_tag> <base_tag> <lfx_tag>")
sys.exit(1)
main_tag = sys.argv[2]
base_tag = sys.argv[3]
lfx_tag = sys.argv[4]
# Lockstep invariant: langflow-base-nightly's published version (set here from `base_tag`)
# and langflow-nightly's exact `==` pin on it (set below from the same `base_tag`) MUST come
# from the same value, so the latest langflow-nightly always pins a base version published in
# the same run. `pypi_nightly_tag.py` makes the main and base tags identical; keep both writes
# sourced from `base_tag` or the pin can reference a version that was never published.
# First handle base package updates
update_pyproject_name("src/backend/base/pyproject.toml", "langflow-base-nightly")
update_name_uv_dep("pyproject.toml", "langflow-base-nightly")
update_pyproject_version("src/backend/base/pyproject.toml", base_tag)
# Update LFX dependency in langflow-base
lfx_version = lfx_tag.lstrip("v")
update_lfx_dep_in_base("src/backend/base/pyproject.toml", lfx_version)
# Then handle main package updates
update_pyproject_name("pyproject.toml", "langflow-nightly")
update_name_uv_dep("pyproject.toml", "langflow-nightly")
update_pyproject_version("pyproject.toml", main_tag)
# Update dependency version (strip 'v' prefix if present)
base_version = base_tag.lstrip("v")
update_version_uv_dep(base_version)
if __name__ == "__main__":
main()