ci: sync workflow fixes from main into release-1.10.0 (fix broken nightly) (#13400)

ci: sync workflow fixes from main into release-1.10.0

Brings the latest CI/workflow fixes from main into release-1.10.0 so the
nightly build is unblocked and the two branches share the same hardening.

Most important fix: release_nightly.yml's "Verify Nightly Name and Version"
step now uses the anchored grep '^langflow(-nightly)?[[:space:]]' instead of
'grep langflow | grep -v langflow-base | grep -v langflow-sdk'. The old
filter started matching two packages once the langflow-stepflow workspace
package was added, so the name became multi-line and the check failed
(this is what broke the nightly build).

Also carries main's fixes for cross-platform install
(--prerelease=if-necessary-or-explicit), docker build workflows,
db-migration-validation, and the backend test timeout bump (30 -> 40).
3-way merged from the common ancestor, so release-only changes are
preserved; no release-only feature workflows are modified.
This commit is contained in:
Eric Hare
2026-05-28 15:38:29 -07:00
committed by GitHub
parent b70acb9f9a
commit fb4dfd49fd
10 changed files with 148 additions and 40 deletions

View File

@ -293,7 +293,12 @@ jobs:
printf 'Installing %s wheel(s):\n' "$label"
printf '%s\n' "$@"
refresh_find_links
uv pip install --prerelease=allow --python "$PYTHON" "${FIND_LINKS[@]}" "$@"
# Use `if-necessary-or-explicit` (not `allow`): nightly langflow* wheels
# are .dev pre-releases so we need to allow them explicitly, but `allow`
# leaks to transitive deps and resolves pre-release pydantic alphas
# (e.g. 2.14.0a1) which break langchain-core's module-level
# `RunnablePassthrough()` call at import time.
uv pip install --prerelease=if-necessary-or-explicit --python "$PYTHON" "${FIND_LINKS[@]}" "$@"
}
SDK_WHEELS=(./sdk-dist/*.whl)
@ -581,7 +586,8 @@ jobs:
printf 'Installing %s wheel(s):\n' "$label"
printf '%s\n' "$@"
refresh_find_links
uv pip install --prerelease=allow --python "$PYTHON" "${FIND_LINKS[@]}" "$@"
# See stable job above for rationale on --prerelease=if-necessary-or-explicit.
uv pip install --prerelease=if-necessary-or-explicit --python "$PYTHON" "${FIND_LINKS[@]}" "$@"
}
SDK_WHEELS=(./sdk-dist/*.whl)
@ -631,7 +637,7 @@ jobs:
if [ "${{ inputs.pre_release }}" = "true" ]; then
NO_DEPS=""
fi
uv pip install --force-reinstall $NO_DEPS --prerelease=allow --python ./test-env/Scripts/python.exe "${INSTALL_WHEELS[@]}"
uv pip install --force-reinstall $NO_DEPS --prerelease=if-necessary-or-explicit --python ./test-env/Scripts/python.exe "${INSTALL_WHEELS[@]}"
fi
fi
@ -643,7 +649,7 @@ jobs:
if [ "${{ inputs.pre_release }}" = "true" ]; then
NO_DEPS=""
fi
uv pip install --force-reinstall $NO_DEPS --prerelease=allow --python ./test-env/Scripts/python.exe "$BASE_WHEEL"
uv pip install --force-reinstall $NO_DEPS --prerelease=if-necessary-or-explicit --python ./test-env/Scripts/python.exe "$BASE_WHEEL"
fi
shell: bash
@ -668,7 +674,7 @@ jobs:
if [ "${{ inputs.pre_release }}" = "true" ]; then
NO_DEPS=""
fi
uv pip install --force-reinstall $NO_DEPS --prerelease=allow --python ./test-env/bin/python "${INSTALL_WHEELS[@]}"
uv pip install --force-reinstall $NO_DEPS --prerelease=if-necessary-or-explicit --python ./test-env/bin/python "${INSTALL_WHEELS[@]}"
fi
fi
@ -680,7 +686,7 @@ jobs:
if [ "${{ inputs.pre_release }}" = "true" ]; then
NO_DEPS=""
fi
uv pip install --force-reinstall $NO_DEPS --prerelease=allow --python ./test-env/bin/python "$BASE_WHEEL"
uv pip install --force-reinstall $NO_DEPS --prerelease=if-necessary-or-explicit --python ./test-env/bin/python "$BASE_WHEEL"
fi
shell: bash

View File

@ -302,7 +302,7 @@ jobs:
- name: Create Docker Compose file for stable
working-directory: docker-migration-test
run: |
run: | # pragma: allowlist secret
cat > docker-compose.yml <<'EOF'
services:
langflow:

View File

@ -124,7 +124,7 @@ jobs:
- name: Determine version
id: version
run: |
version=$(uv tree 2>/dev/null | grep '^langflow' | grep -v '^langflow-base' | grep -v '^langflow-sdk' | cut -d' ' -f2 | sed 's/^v//')
version=$(uv tree 2>/dev/null | grep -E '^langflow(-nightly)?[[:space:]]' | cut -d' ' -f2 | sed 's/^v//')
echo "Main version from pyproject.toml: $version"
if [ ${{inputs.pre_release}} == "true" ]; then
@ -604,7 +604,7 @@ jobs:
if [[ "${{ inputs.release_type }}" == "base" ]]; then
version=$(uv tree 2>/dev/null | grep 'langflow-base' | awk '{print $3}' | sed 's/^v//' | head -n 1)
else
version=$(uv tree 2>/dev/null | grep '^langflow' | grep -v '^langflow-base' | grep -v '^langflow-sdk' | cut -d' ' -f2 | sed 's/^v//')
version=$(uv tree 2>/dev/null | grep -E '^langflow(-nightly)?[[:space:]]' | cut -d' ' -f2 | sed 's/^v//')
fi
echo "Using version: $version"
echo version=$version >> $GITHUB_OUTPUT

View File

@ -150,7 +150,7 @@ jobs:
if: ${{ inputs.main_version == '' && (inputs.release_type == 'main' || inputs.release_type == 'main-ep' || inputs.release_type == 'nightly-main' || inputs.release_type == 'main-all' || inputs.release_type == 'nightly-main-all') }}
id: get-version-main
run: |
version=$(uv tree | grep 'langflow' | grep -v 'langflow-base' | grep -v 'langflow-sdk' | awk '{print $2}' | sed 's/^v//')
version=$(uv tree | grep -E '^langflow(-nightly)?[[:space:]]' | awk '{print $2}' | sed 's/^v//')
echo version=$version
echo version=$version >> $GITHUB_OUTPUT
setup:

View File

@ -168,7 +168,7 @@ jobs:
id: version
run: |
echo "Extracting main version from pyproject.toml"
version=$(uv tree 2>/dev/null | grep '^langflow' | grep -v '^langflow-base' | grep -v '^langflow-sdk' | cut -d' ' -f2 | sed 's/^v//')
version=$(uv tree 2>/dev/null | grep -E '^langflow(-nightly)?[[:space:]]' | cut -d' ' -f2 | sed 's/^v//')
# Verify nightly tag format
if [[ ! "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.dev[0-9]+$ ]]; then
@ -263,7 +263,7 @@ jobs:
id: version
run: |
echo "Extracting main version from pyproject.toml"
version=$(uv tree 2>/dev/null | grep '^langflow' | grep -v '^langflow-base' | grep -v '^langflow-sdk' | cut -d' ' -f2 | sed 's/^v//')
version=$(uv tree 2>/dev/null | grep -E '^langflow(-nightly)?[[:space:]]' | cut -d' ' -f2 | sed 's/^v//')
# Verify nightly tag format
if [[ ! "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.dev[0-9]+$ ]]; then
@ -353,7 +353,7 @@ jobs:
if [[ "${{ inputs.release_type }}" == "nightly-base" ]]; then
version=$(uv tree 2>/dev/null | grep 'langflow-base' | awk '{print $3}' | sed 's/^v//' | head -n 1)
else
version=$(uv tree 2>/dev/null | grep '^langflow' | grep -v '^langflow-base' | grep -v '^langflow-sdk' | cut -d' ' -f2 | sed 's/^v//')
version=$(uv tree 2>/dev/null | grep -E '^langflow(-nightly)?[[:space:]]' | cut -d' ' -f2 | sed 's/^v//')
fi
echo "Using version: $version"
echo version=$version >> $GITHUB_OUTPUT

View File

@ -283,7 +283,7 @@ jobs:
# ref: ${{ needs.create-nightly-tag.outputs.tag }}
release-nightly-build:
if: github.repository == 'langflow-ai/langflow' && always() && needs.frontend-tests-linux.result != 'failure' && needs.backend-unit-tests.result != 'failure'
if: github.repository == 'langflow-ai/langflow' && always() && needs.create-nightly-tag.result == 'success' && needs.frontend-tests-linux.result != 'failure' && needs.backend-unit-tests.result != 'failure'
name: Run Nightly Langflow Build
needs:
[validate-inputs, create-nightly-tag, frontend-tests-linux, frontend-tests-windows, backend-unit-tests]

View File

@ -93,7 +93,7 @@ jobs:
- name: Run unit tests
uses: nick-fields/retry@v3
with:
timeout_minutes: 30
timeout_minutes: 40
max_attempts: 2
command: make unit_tests args="-x -vv --splits ${{ matrix.splitCount }} --group ${{ matrix.group }} --reruns 5 --cov --cov-config=src/backend/.coveragerc --cov-report=xml --cov-report=html"

View File

@ -28,6 +28,11 @@ on:
required: false
type: boolean
default: false
release_bundles:
description: "Release every src/bundles/* package (required when releasing main, since main pins exact bundle versions)"
required: false
type: boolean
default: false
build_docker_base:
description: "Build Docker Image for Langflow Base"
required: true
@ -71,6 +76,7 @@ jobs:
echo "release_package_main: ${{ inputs.release_package_main }}"
echo "release_lfx: ${{ inputs.release_lfx }}"
echo "release_sdk: ${{ inputs.release_sdk }}"
echo "release_bundles: ${{ inputs.release_bundles }}"
echo "build_docker_base: ${{ inputs.build_docker_base }}"
echo "build_docker_main: ${{ inputs.build_docker_main }}"
echo "pre_release: ${{ inputs.pre_release }}"
@ -163,6 +169,12 @@ jobs:
exit 1
fi
if [ "${{ inputs.release_package_main }}" = "true" ] && [ "${{ inputs.release_bundles }}" = "false" ]; then
echo "Error: Cannot release Langflow Main without releasing bundles."
echo "Langflow Main pins exact bundle versions (lfx-arxiv, lfx-duckduckgo, ...). Please enable 'release_bundles' or disable 'release_package_main'."
exit 1
fi
echo "✅ Release dependencies validated successfully."
determine-base-version:
@ -245,7 +257,7 @@ jobs:
- name: Determine version
id: version
run: |
version=$(uv tree | grep 'langflow' | grep -v 'langflow-base' | grep -v 'langflow-sdk' | awk '{print $2}' | sed 's/^v//')
version=$(uv tree | grep -E '^langflow(-nightly)?[[:space:]]' | awk '{print $2}' | sed 's/^v//')
echo "Main version from pyproject.toml: $version"
if [ ${{inputs.pre_release}} == "true" ]; then
@ -844,9 +856,63 @@ jobs:
name: dist-main
path: dist
build-bundles:
name: Build Langflow Bundles
needs: [build-lfx, determine-lfx-version]
if: |
always() &&
!cancelled() &&
inputs.release_bundles &&
needs.build-lfx.result == 'success'
runs-on: ubuntu-latest
defaults:
run:
shell: bash
outputs:
bundles-found: ${{ steps.build.outputs.bundles-found }}
steps:
- name: Checkout code
uses: actions/checkout@v6
with:
ref: ${{ inputs.release_tag }}
- name: Setup Environment
uses: astral-sh/setup-uv@v6
with:
enable-cache: true
cache-dependency-glob: "uv.lock"
python-version: "3.13"
prune-cache: false
- name: Install the project
run: uv sync
- name: Build bundle wheels
id: build
run: |
shopt -s nullglob
bundles=(src/bundles/*/pyproject.toml)
if [ ${#bundles[@]} -eq 0 ]; then
echo "No bundles found under src/bundles/*/"
echo "bundles-found=0" >> $GITHUB_OUTPUT
exit 0
fi
mkdir -p bundles-dist
BUNDLES_DIST="$PWD/bundles-dist"
for bundle_pyproject in "${bundles[@]}"; do
bundle_dir=$(dirname "$bundle_pyproject")
echo "Building wheel for $bundle_dir"
(cd "$bundle_dir" && uv build --wheel --out-dir "$BUNDLES_DIST")
done
echo "bundles-found=1" >> $GITHUB_OUTPUT
ls -la bundles-dist/
- name: Upload bundles artifact
if: steps.build.outputs.bundles-found == '1'
uses: actions/upload-artifact@v6
with:
name: dist-bundles
path: bundles-dist
test-cross-platform:
name: Test Cross-Platform Installation
needs: [build-base, build-main, build-lfx, build-sdk]
needs: [build-base, build-main, build-lfx, build-sdk, build-bundles]
if: |
always() &&
!cancelled() &&
@ -857,6 +923,7 @@ jobs:
main-artifact-name: "dist-main"
lfx-artifact-name: "dist-lfx"
sdk-artifact-name: ${{ needs.build-sdk.result == 'success' && 'dist-sdk' || '' }}
bundles-artifact-name: ${{ needs.build-bundles.result == 'success' && needs.build-bundles.outputs.bundles-found == '1' && 'dist-bundles' || '' }}
pre_release: ${{ inputs.pre_release }}
publish-base:
@ -889,10 +956,58 @@ jobs:
run: |
cd src/backend/base && uv publish dist/*.whl
publish-bundles:
name: Publish Bundles to PyPI
needs: [build-bundles, test-cross-platform, ci, publish-lfx]
if: |
always() &&
!cancelled() &&
inputs.release_bundles &&
needs.build-bundles.result == 'success' &&
needs.build-bundles.outputs.bundles-found == '1' &&
needs.test-cross-platform.result == 'success' &&
needs.ci.result == 'success' &&
(needs.publish-lfx.result == 'success' || needs.publish-lfx.result == 'skipped')
runs-on: ubuntu-latest
steps:
- name: Download bundles artifact
uses: actions/download-artifact@v7
with:
name: dist-bundles
path: bundles-dist
- name: Setup Environment
uses: astral-sh/setup-uv@v6
with:
enable-cache: false
python-version: "3.13"
- name: Publish bundles to PyPI
if: ${{ !inputs.dry_run }}
env:
UV_PUBLISH_TOKEN: ${{ secrets.PYPI_API_TOKEN }}
run: |
shopt -s nullglob
wheels=(bundles-dist/*.whl)
if [ ${#wheels[@]} -eq 0 ]; then
echo "No bundle wheels to publish."
exit 0
fi
for wheel in "${wheels[@]}"; do
echo "Publishing $wheel"
uv publish "$wheel"
done
publish-main:
name: Publish Langflow Main to PyPI
if: ${{ inputs.release_package_main }}
needs: [build-main, test-cross-platform, publish-base, ci]
if: |
always() &&
!cancelled() &&
inputs.release_package_main &&
needs.build-main.result == 'success' &&
needs.test-cross-platform.result == 'success' &&
needs.publish-base.result == 'success' &&
needs.ci.result == 'success' &&
(needs.publish-bundles.result == 'success' || needs.publish-bundles.result == 'skipped')
needs: [build-main, test-cross-platform, publish-base, publish-bundles, ci]
runs-on: ubuntu-latest
steps:
- name: Download main artifact

View File

@ -194,15 +194,18 @@ jobs:
name_without_extras=$(echo $name | sed 's/\[.*\]//')
if [ "$name_without_extras" != "langflow-base-nightly" ]; then
echo "Name $name_without_extras does not match langflow-base-nightly. Exiting the workflow."
echo "skipped=true" >> $GITHUB_OUTPUT
exit 1
fi
if [ "$version" != "${{ inputs.nightly_tag_base }}" ]; then
echo "Version $version does not match nightly tag ${{ inputs.nightly_tag_base }}. Exiting the workflow."
echo "skipped=true" >> $GITHUB_OUTPUT
exit 1
fi
# Strip the leading `v` from the version
version=$(echo $version | sed 's/^v//')
echo "version=$version" >> $GITHUB_OUTPUT
echo "skipped=false" >> $GITHUB_OUTPUT
- name: Build Langflow Base for distribution
run: |
@ -294,8 +297,8 @@ jobs:
- name: Verify Nightly Name and Version
id: verify
run: |
name=$(uv tree | grep 'langflow' | grep -v 'langflow-base' | grep -v 'langflow-sdk' | awk '{print $1}')
version=$(uv tree | grep 'langflow' | grep -v 'langflow-base' | grep -v 'langflow-sdk' | awk '{print $2}')
name=$(uv tree | grep -E '^langflow(-nightly)?[[:space:]]' | awk '{print $1}')
version=$(uv tree | grep -E '^langflow(-nightly)?[[:space:]]' | awk '{print $2}')
if [ "$name" != "langflow-nightly" ]; then
echo "Name $name does not match langflow-nightly. Exiting the workflow."
exit 1

View File

@ -127,22 +127,6 @@
}
],
"results": {
".github/workflows/db-migration-validation.yml": [
{
"type": "Secret Keyword",
"filename": ".github/workflows/db-migration-validation.yml",
"hashed_secret": "86267c394a92ee1c619541c3e399bb0ef3c461a9",
"is_verified": false,
"line_number": 305
},
{
"type": "Basic Auth Credentials",
"filename": ".github/workflows/db-migration-validation.yml",
"hashed_secret": "e80c4f90316c87b6b24d03890493c8d1c7c1c99d",
"is_verified": false,
"line_number": 305
}
],
".github/workflows/nightly_build.yml": [
{
"type": "Secret Keyword",
@ -159,7 +143,7 @@
"filename": ".github/workflows/release.yml",
"hashed_secret": "3e26d6750975d678acb8fa35a0f69237881576b0",
"is_verified": false,
"line_number": 391,
"line_number": 403,
"is_secret": false
}
],
@ -169,7 +153,7 @@
"filename": ".github/workflows/release_nightly.yml",
"hashed_secret": "3e26d6750975d678acb8fa35a0f69237881576b0",
"is_verified": false,
"line_number": 512,
"line_number": 515,
"is_secret": false
}
],
@ -9297,5 +9281,5 @@
}
]
},
"generated_at": "2026-05-28T15:17:56Z"
"generated_at": "2026-05-28T22:21:26Z"
}