mirror of
https://github.com/langflow-ai/langflow.git
synced 2026-03-27 12:11:40 +00:00
fix: gate PyPI publish jobs on CI completion The publish-base, publish-main, and publish-lfx jobs were not waiting for the CI workflow to complete before publishing to PyPI. This could result in broken packages being published if CI fails. Docker builds already correctly depend on CI. Add 'ci' to the needs array of all three publish jobs so packages are only published after the full test suite passes.
767 lines
28 KiB
YAML
767 lines
28 KiB
YAML
name: Langflow Release
|
|
run-name: Langflow Release by @${{ github.actor }}
|
|
|
|
on:
|
|
workflow_dispatch:
|
|
inputs:
|
|
release_tag:
|
|
description: "Tag to release from. This is the tag that contains the source code for the release."
|
|
required: true
|
|
type: string
|
|
release_package_base:
|
|
description: "Release Langflow Base"
|
|
required: true
|
|
type: boolean
|
|
default: false
|
|
release_package_main:
|
|
description: "Release Langflow"
|
|
required: true
|
|
type: boolean
|
|
default: false
|
|
release_lfx:
|
|
description: "Release LFX package (manually triggered)"
|
|
required: false
|
|
type: boolean
|
|
default: false
|
|
build_docker_base:
|
|
description: "Build Docker Image for Langflow Base"
|
|
required: true
|
|
type: boolean
|
|
default: false
|
|
build_docker_main:
|
|
description: "Build Docker Image for Langflow"
|
|
required: true
|
|
type: boolean
|
|
default: false
|
|
pre_release:
|
|
description: "Pre-release"
|
|
required: false
|
|
type: boolean
|
|
default: false
|
|
create_release:
|
|
description: "Whether to create a gh release"
|
|
required: false
|
|
type: boolean
|
|
default: false
|
|
dry_run:
|
|
description: "Dry run mode - disables all pushes to external services (PyPI, Docker, GitHub releases)"
|
|
required: false
|
|
type: boolean
|
|
default: true
|
|
|
|
jobs:
|
|
echo-inputs:
|
|
name: Echo Workflow Inputs
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Echo workflow inputs
|
|
run: |
|
|
echo "release_tag: ${{ inputs.release_tag }}"
|
|
echo "release_package_base: ${{ inputs.release_package_base }}"
|
|
echo "release_package_main: ${{ inputs.release_package_main }}"
|
|
echo "release_lfx: ${{ inputs.release_lfx }}"
|
|
echo "build_docker_base: ${{ inputs.build_docker_base }}"
|
|
echo "build_docker_main: ${{ inputs.build_docker_main }}"
|
|
echo "pre_release: ${{ inputs.pre_release }}"
|
|
echo "create_release: ${{ inputs.create_release }}"
|
|
echo "dry_run: ${{ inputs.dry_run }}"
|
|
|
|
validate-tag:
|
|
name: Validate Tag Input
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Checkout code
|
|
uses: actions/checkout@v6
|
|
with:
|
|
fetch-depth: 0 # Fetch all history - required for tags (?)
|
|
- name: Validate that input is a tag, not a branch
|
|
run: |
|
|
# Check if the input exists as a tag
|
|
if ! git tag -l | grep -q "^${{ inputs.release_tag }}$"; then
|
|
echo "Error: '${{ inputs.release_tag }}' is not a valid tag."
|
|
echo "Available tags:"
|
|
git tag -l | head -20
|
|
exit 1
|
|
fi
|
|
|
|
# Check if the input also exists as a branch (warn if so, but don't fail)
|
|
if git branch -r | grep -q "origin/${{ inputs.release_tag }}$"; then
|
|
echo "Tag '${{ inputs.release_tag }}' also exists as a branch. Exiting out of caution."
|
|
exit 1
|
|
fi
|
|
|
|
echo "Validated: '${{ inputs.release_tag }}' is a valid tag."
|
|
|
|
validate-dependencies:
|
|
name: Validate Release Dependencies
|
|
runs-on: ubuntu-latest
|
|
if: ${{ inputs.release_package_base || inputs.release_package_main || inputs.release_lfx || inputs.build_docker_base || inputs.build_docker_main }}
|
|
needs: [validate-tag]
|
|
steps:
|
|
- name: Validate that build-base is enabled if build-main is enabled
|
|
run: |
|
|
if [ "${{ inputs.release_package_main }}" = "true" ] && [ "${{ inputs.release_package_base }}" = "false" ]; then
|
|
echo "Error: Cannot release Langflow Main without releasing Langflow Base."
|
|
echo "Please enable 'release_package_base' or disable 'release_package_main'."
|
|
exit 1
|
|
fi
|
|
|
|
echo "✅ Release dependencies validated successfully."
|
|
|
|
determine-base-version:
|
|
name: Determine Base Version
|
|
needs: [validate-tag, validate-dependencies]
|
|
runs-on: ubuntu-latest
|
|
outputs:
|
|
version: ${{ steps.version.outputs.version }}
|
|
skipped: ${{ steps.version.outputs.skipped }}
|
|
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: Determine version
|
|
id: version
|
|
run: |
|
|
version=$(python3 -c "
|
|
import tomllib, pathlib
|
|
data = tomllib.loads(pathlib.Path('src/backend/base/pyproject.toml').read_text())
|
|
print(data['project']['version'])
|
|
")
|
|
echo "Base version from pyproject.toml: $version"
|
|
|
|
if [ ${{inputs.pre_release}} == "true" ]; then
|
|
last_released_version=$(curl -s "https://pypi.org/pypi/langflow-base/json" | jq -r '.releases | keys | .[]' | grep -E '(a|b|rc|dev|alpha|beta)' | sort -V | tail -n 1)
|
|
version="$(uv run ./scripts/ci/langflow_pre_release_tag.py "$version" "$last_released_version")"
|
|
echo "Latest base pre-release version: $last_released_version"
|
|
echo "Base pre-release version to be released: $version"
|
|
else
|
|
last_released_version=$(curl -s "https://pypi.org/pypi/langflow-base/json" | jq -r '.releases | keys | .[]' | grep -vE '(a|b|rc|dev|alpha|beta)' | sort -V | tail -n 1)
|
|
echo "Latest base release version: $last_released_version"
|
|
fi
|
|
|
|
if [ "$version" = "$last_released_version" ]; then
|
|
echo "Base pypi version $version is already released. Skipping release."
|
|
echo skipped=true >> $GITHUB_OUTPUT
|
|
exit 1
|
|
else
|
|
echo version=$version >> $GITHUB_OUTPUT
|
|
echo skipped=false >> $GITHUB_OUTPUT
|
|
fi
|
|
|
|
determine-main-version:
|
|
name: Determine Main Version
|
|
needs: [validate-tag, validate-dependencies]
|
|
runs-on: ubuntu-latest
|
|
outputs:
|
|
version: ${{ steps.version.outputs.version }}
|
|
skipped: ${{ steps.version.outputs.skipped }}
|
|
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: Determine version
|
|
id: version
|
|
run: |
|
|
version=$(uv tree | grep 'langflow' | grep -v 'langflow-base' | awk '{print $2}' | sed 's/^v//')
|
|
echo "Main version from pyproject.toml: $version"
|
|
|
|
if [ ${{inputs.pre_release}} == "true" ]; then
|
|
last_released_version=$(curl -s "https://pypi.org/pypi/langflow/json" | jq -r '.releases | keys | .[]' | grep -E '(a|b|rc|dev|alpha|beta)' | sort -V | tail -n 1)
|
|
version="$(uv run ./scripts/ci/langflow_pre_release_tag.py "$version" "$last_released_version")"
|
|
echo "Latest main pre-release version: $last_released_version"
|
|
echo "Main pre-release version to be released: $version"
|
|
else
|
|
last_released_version=$(curl -s "https://pypi.org/pypi/langflow/json" | jq -r '.releases | keys | .[]' | grep -vE '(a|b|rc|dev|alpha|beta)' | sort -V | tail -n 1)
|
|
echo "Latest main release version: $last_released_version"
|
|
fi
|
|
|
|
if [ "$version" = "$last_released_version" ]; then
|
|
echo "Main pypi version $version is already released. Skipping release."
|
|
echo skipped=true >> $GITHUB_OUTPUT
|
|
exit 1
|
|
else
|
|
echo version=$version >> $GITHUB_OUTPUT
|
|
echo skipped=false >> $GITHUB_OUTPUT
|
|
fi
|
|
|
|
determine-lfx-version:
|
|
name: Determine LFX Version
|
|
needs: [validate-tag, validate-dependencies]
|
|
runs-on: ubuntu-latest
|
|
outputs:
|
|
version: ${{ steps.version.outputs.version }}
|
|
skipped: ${{ steps.version.outputs.skipped }}
|
|
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 LFX dependencies
|
|
run: uv sync --dev --package lfx
|
|
|
|
- name: Determine version
|
|
id: version
|
|
run: |
|
|
version=$(uv tree | grep 'lfx' | awk '{print $3}' | sed 's/^v//' | head -n 2 | xargs)
|
|
echo "LFX version from pyproject.toml: $version"
|
|
|
|
if [ ${{inputs.pre_release}} == "true" ]; then
|
|
last_released_version=$(curl -s "https://pypi.org/pypi/lfx/json" | jq -r '.releases | keys | .[]' | grep -E '(a|b|rc|dev|alpha|beta)' | sort -V | tail -n 1)
|
|
version="$(uv run ./scripts/ci/langflow_pre_release_tag.py "$version" "$last_released_version")"
|
|
echo "Latest LFX pre-release version: $last_released_version"
|
|
echo "LFX pre-release version to be released: $version"
|
|
else
|
|
last_released_version=$(curl -s "https://pypi.org/pypi/lfx/json" | jq -r '.releases | keys | .[]' | grep -vE '(a|b|rc|dev|alpha|beta)' | sort -V | tail -n 1)
|
|
echo "Latest LFX release version: $last_released_version"
|
|
fi
|
|
|
|
if [ "$version" = "$last_released_version" ]; then
|
|
echo "LFX pypi version $version is already released. Skipping release."
|
|
echo skipped=true >> $GITHUB_OUTPUT
|
|
exit 1
|
|
else
|
|
echo version=$version >> $GITHUB_OUTPUT
|
|
echo skipped=false >> $GITHUB_OUTPUT
|
|
fi
|
|
|
|
ci:
|
|
name: CI
|
|
needs: [validate-tag, validate-dependencies]
|
|
uses: ./.github/workflows/ci.yml
|
|
with:
|
|
ref: ${{ inputs.release_tag }}
|
|
python-versions: "['3.10', '3.11', '3.12', '3.13']"
|
|
frontend-tests-folder: "tests"
|
|
release: true
|
|
run-all-tests: true
|
|
runs-on: ubuntu-latest
|
|
secrets: inherit
|
|
|
|
build-lfx:
|
|
name: Build LFX
|
|
needs: [determine-lfx-version]
|
|
if: ${{ needs.determine-lfx-version.outputs.skipped == 'false' }}
|
|
runs-on: ubuntu-latest
|
|
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 LFX dependencies
|
|
run: uv sync --dev --package lfx
|
|
- name: Set version for pre-release
|
|
if: ${{ inputs.pre_release }}
|
|
run: |
|
|
VERSION="${{ needs.determine-lfx-version.outputs.version }}"
|
|
echo "Setting pre-release version to: $VERSION"
|
|
cd src/lfx
|
|
|
|
# Update version in lfx pyproject.toml
|
|
sed -i.bak "s/^version = .*/version = \"$VERSION\"/" pyproject.toml
|
|
|
|
# Verify the change
|
|
echo "Updated pyproject.toml version:"
|
|
grep "^version" pyproject.toml
|
|
- name: Build project for distribution
|
|
run: |
|
|
cd src/lfx
|
|
rm -rf dist/
|
|
uv build --wheel --out-dir dist
|
|
- name: Verify built version
|
|
run: |
|
|
EXPECTED_VERSION="${{ needs.determine-lfx-version.outputs.version }}"
|
|
WHEEL_FILE=$(ls src/lfx/dist/*.whl)
|
|
echo "Built wheel: $WHEEL_FILE"
|
|
|
|
NORMALIZED_VERSION=$(echo "$EXPECTED_VERSION" | sed 's/\.rc/rc/g; s/\.a/a/g; s/\.b/b/g; s/\.dev/dev/g')
|
|
echo "Expected version: $EXPECTED_VERSION"
|
|
echo "Normalized for wheel: $NORMALIZED_VERSION"
|
|
|
|
if [[ ! "$WHEEL_FILE" =~ $NORMALIZED_VERSION ]]; then
|
|
echo "❌ Error: Wheel version doesn't match expected version"
|
|
echo "Expected: $EXPECTED_VERSION (normalized: $NORMALIZED_VERSION)"
|
|
echo "Wheel file: $WHEEL_FILE"
|
|
exit 1
|
|
fi
|
|
echo "✅ Version verified: $EXPECTED_VERSION"
|
|
- name: Test CLI
|
|
run: |
|
|
cd src/lfx
|
|
uv pip install dist/*.whl --force-reinstall
|
|
uv run lfx --help
|
|
- name: Upload Artifact
|
|
uses: actions/upload-artifact@v6
|
|
with:
|
|
name: dist-lfx
|
|
path: src/lfx/dist
|
|
|
|
build-base:
|
|
name: Build Langflow Base
|
|
needs: [build-lfx, determine-base-version, determine-lfx-version]
|
|
if: ${{ inputs.release_package_base && needs.determine-base-version.outputs.skipped == 'false' }}
|
|
runs-on: ubuntu-latest
|
|
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.12"
|
|
prune-cache: false
|
|
- name: Download LFX artifact
|
|
uses: actions/download-artifact@v7
|
|
with:
|
|
name: dist-lfx
|
|
path: ./lfx-dist
|
|
- name: Install dependencies with local LFX wheel
|
|
run: |
|
|
# Create virtual environment
|
|
uv venv
|
|
# Install using pip with local wheel directory as find-links
|
|
uv pip install --find-links ./lfx-dist --prerelease=allow -e src/backend/base
|
|
- name: Check for dependency incompatibility
|
|
run: uv pip check
|
|
- name: Set version for pre-release
|
|
if: ${{ inputs.pre_release }}
|
|
run: |
|
|
VERSION="${{ needs.determine-base-version.outputs.version }}"
|
|
echo "Setting pre-release version to: $VERSION"
|
|
cd src/backend/base
|
|
|
|
# Update version in pyproject.toml
|
|
sed -i.bak "s/^version = .*/version = \"$VERSION\"/" pyproject.toml
|
|
|
|
# Verify the change
|
|
echo "Updated pyproject.toml version:"
|
|
grep "^version" pyproject.toml
|
|
- name: Update lfx dependency for pre-release
|
|
if: ${{ inputs.pre_release }}
|
|
run: |
|
|
LFX_VERSION="${{ needs.determine-lfx-version.outputs.version }}"
|
|
echo "Updating lfx dependency to allow pre-release version: $LFX_VERSION"
|
|
cd src/backend/base
|
|
|
|
# Extract current lfx constraint from pyproject.toml
|
|
CURRENT_CONSTRAINT=$(grep -E '^\s*"lfx' pyproject.toml | head -1)
|
|
echo "Current constraint: $CURRENT_CONSTRAINT"
|
|
|
|
# Extract the major.minor version (e.g., "0.3" from "~=0.3.0")
|
|
MAJOR_MINOR=$(echo "$CURRENT_CONSTRAINT" | sed -E 's/.*[~>=<]+([0-9]+\.[0-9]+).*/\1/')
|
|
NEXT_MAJOR=$((${MAJOR_MINOR%.*} + 1))
|
|
|
|
# Create new constraint: >=LFX_VERSION,<NEXT_MAJOR.dev0
|
|
NEW_CONSTRAINT="\"lfx>=$LFX_VERSION,<$NEXT_MAJOR.dev0\""
|
|
|
|
echo "New constraint: $NEW_CONSTRAINT"
|
|
|
|
# Replace the constraint
|
|
sed -i.bak "s|\"lfx[^\"]*\"|$NEW_CONSTRAINT|" pyproject.toml
|
|
|
|
# Verify the change
|
|
echo "Updated lfx dependency:"
|
|
grep "lfx" pyproject.toml
|
|
- name: Build project for distribution
|
|
run: make build base=true args="--wheel"
|
|
- name: Verify built version
|
|
run: |
|
|
EXPECTED_VERSION="${{ needs.determine-base-version.outputs.version }}"
|
|
WHEEL_FILE=$(ls dist/*.whl 2>/dev/null || ls src/backend/base/dist/*.whl)
|
|
echo "Built wheel: $WHEEL_FILE"
|
|
|
|
NORMALIZED_VERSION=$(echo "$EXPECTED_VERSION" | sed 's/\.rc/rc/g; s/\.a/a/g; s/\.b/b/g; s/\.dev/dev/g')
|
|
echo "Expected version: $EXPECTED_VERSION"
|
|
echo "Normalized for wheel: $NORMALIZED_VERSION"
|
|
|
|
if [[ ! "$WHEEL_FILE" =~ $NORMALIZED_VERSION ]]; then
|
|
echo "❌ Error: Wheel version doesn't match expected version"
|
|
echo "Expected: $EXPECTED_VERSION (normalized: $NORMALIZED_VERSION)"
|
|
echo "Wheel file: $WHEEL_FILE"
|
|
exit 1
|
|
fi
|
|
echo "✅ Version verified: $EXPECTED_VERSION"
|
|
- name: Test CLI
|
|
run: |
|
|
# TODO: Unsure why the whl is not built in src/backend/base/dist
|
|
mkdir src/backend/base/dist
|
|
mv dist/*.whl src/backend/base/dist
|
|
uv pip install src/backend/base/dist/*.whl
|
|
uv run python -m langflow run --host localhost --port 7860 --backend-only &
|
|
SERVER_PID=$!
|
|
# Wait for the server to start
|
|
timeout 120 bash -c 'until curl -f http://localhost:7860/api/v1/auto_login; do sleep 2; done' || (echo "Server did not start in time" && kill $SERVER_PID && exit 1)
|
|
# Terminate the server
|
|
kill $SERVER_PID || (echo "Failed to terminate the server" && exit 1)
|
|
sleep 20 # give the server some time to terminate
|
|
# Check if the server is still running
|
|
if kill -0 $SERVER_PID 2>/dev/null; then
|
|
echo "Failed to terminate the server"
|
|
exit 0
|
|
else
|
|
echo "Server terminated successfully"
|
|
fi
|
|
|
|
# PyPI publishing moved to after cross-platform testing
|
|
|
|
- name: Upload Artifact
|
|
uses: actions/upload-artifact@v6
|
|
with:
|
|
name: dist-base
|
|
path: src/backend/base/dist
|
|
|
|
build-main:
|
|
name: Build Langflow Main
|
|
needs:
|
|
[
|
|
build-base,
|
|
build-lfx,
|
|
determine-base-version,
|
|
determine-main-version,
|
|
determine-lfx-version,
|
|
]
|
|
if: ${{ inputs.release_package_main && needs.determine-main-version.outputs.skipped == 'false' }}
|
|
runs-on: ubuntu-latest
|
|
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.12"
|
|
prune-cache: false
|
|
- name: Download LFX artifact
|
|
uses: actions/download-artifact@v7
|
|
with:
|
|
name: dist-lfx
|
|
path: ./lfx-dist
|
|
- name: Download base artifact
|
|
uses: actions/download-artifact@v7
|
|
with:
|
|
name: dist-base
|
|
path: ./base-dist
|
|
- name: Install dependencies with local wheels
|
|
run: |
|
|
# Create virtual environment
|
|
uv venv
|
|
# Install using pip with local wheel directories as find-links
|
|
uv pip install --find-links ./lfx-dist --find-links ./base-dist --prerelease=allow -e .
|
|
- name: Check for dependency incompatibility
|
|
run: uv pip check
|
|
- name: Set version for pre-release
|
|
if: ${{ inputs.pre_release }}
|
|
run: |
|
|
VERSION="${{ needs.determine-main-version.outputs.version }}"
|
|
echo "Setting main pre-release version to: $VERSION"
|
|
|
|
# Update version in main pyproject.toml
|
|
sed -i.bak "s/^version = .*/version = \"$VERSION\"/" pyproject.toml
|
|
|
|
# Verify the change
|
|
echo "Updated pyproject.toml version:"
|
|
grep "^version" pyproject.toml
|
|
- name: Update langflow-base dependency for pre-release
|
|
if: ${{ inputs.pre_release }}
|
|
run: |
|
|
BASE_VERSION="${{ needs.determine-base-version.outputs.version }}"
|
|
echo "Base version for pre-release: $BASE_VERSION"
|
|
|
|
# Extract current langflow-base constraint from pyproject.toml
|
|
CURRENT_CONSTRAINT=$(grep -E '^\s*"langflow-base' pyproject.toml | head -1)
|
|
echo "Current constraint: $CURRENT_CONSTRAINT"
|
|
|
|
# Extract the major.minor version (e.g., "0.8" from "~=0.8.0")
|
|
MAJOR_MINOR=$(echo "$CURRENT_CONSTRAINT" | sed -E 's/.*[~>=<]+([0-9]+\.[0-9]+).*/\1/')
|
|
NEXT_MAJOR=$((${MAJOR_MINOR%.*} + 1))
|
|
|
|
# Create new constraint: >=BASE_VERSION,<NEXT_MAJOR.dev0 (preserve [complete] extra)
|
|
NEW_CONSTRAINT="\"langflow-base[complete]>=$BASE_VERSION,<$NEXT_MAJOR.dev0\""
|
|
|
|
echo "New constraint: $NEW_CONSTRAINT"
|
|
|
|
# Replace the constraint
|
|
sed -i.bak "s|\"langflow-base[^\"]*\"|$NEW_CONSTRAINT|" pyproject.toml
|
|
|
|
# Verify the change
|
|
echo "Updated dependency:"
|
|
grep "langflow-base" pyproject.toml
|
|
- name: Build project for pre-release distribution
|
|
if: ${{ inputs.pre_release }}
|
|
run: make build pre=true args="--no-sources --wheel"
|
|
- name: Build project for distribution
|
|
if: ${{ !inputs.pre_release }}
|
|
run: make build main=true args="--no-sources --wheel"
|
|
- name: Verify built version
|
|
run: |
|
|
EXPECTED_VERSION="${{ needs.determine-main-version.outputs.version }}"
|
|
WHEEL_FILE=$(ls dist/*.whl)
|
|
echo "Built wheel: $WHEEL_FILE"
|
|
|
|
NORMALIZED_VERSION=$(echo "$EXPECTED_VERSION" | sed 's/\.rc/rc/g; s/\.a/a/g; s/\.b/b/g; s/\.dev/dev/g')
|
|
echo "Expected version: $EXPECTED_VERSION"
|
|
echo "Normalized for wheel: $NORMALIZED_VERSION"
|
|
|
|
if [[ ! "$WHEEL_FILE" =~ $NORMALIZED_VERSION ]]; then
|
|
echo "❌ Error: Wheel version doesn't match expected version"
|
|
echo "Expected: $EXPECTED_VERSION (normalized: $NORMALIZED_VERSION)"
|
|
echo "Wheel file: $WHEEL_FILE"
|
|
exit 1
|
|
fi
|
|
echo "✅ Version verified: $EXPECTED_VERSION"
|
|
- name: Test CLI
|
|
run: |
|
|
uv pip install dist/*.whl
|
|
uv run python -m langflow run --host localhost --port 7860 --backend-only &
|
|
SERVER_PID=$!
|
|
# Wait for the server to start
|
|
timeout 120 bash -c 'until curl -f http://localhost:7860/health_check; do sleep 2; done' || (echo "Server did not start in time" && kill $SERVER_PID && exit 1)
|
|
# Terminate the server
|
|
kill $SERVER_PID || (echo "Failed to terminate the server" && exit 1)
|
|
sleep 20 # give the server some time to terminate
|
|
# Check if the server is still running
|
|
if kill -0 $SERVER_PID 2>/dev/null; then
|
|
echo "Failed to terminate the server"
|
|
exit 0
|
|
else
|
|
echo "Server terminated successfully"
|
|
fi
|
|
|
|
# PyPI publishing moved to after cross-platform testing
|
|
|
|
- name: Upload Artifact
|
|
uses: actions/upload-artifact@v6
|
|
with:
|
|
name: dist-main
|
|
path: dist
|
|
|
|
test-cross-platform:
|
|
name: Test Cross-Platform Installation
|
|
needs: [build-base, build-main, build-lfx]
|
|
if: |
|
|
always() &&
|
|
!cancelled() &&
|
|
(needs.build-base.result == 'success' || needs.build-main.result == 'success' || needs.build-lfx.result == 'success')
|
|
uses: ./.github/workflows/cross-platform-test.yml
|
|
with:
|
|
base-artifact-name: "dist-base"
|
|
main-artifact-name: "dist-main"
|
|
lfx-artifact-name: "dist-lfx"
|
|
pre_release: ${{ inputs.pre_release }}
|
|
|
|
publish-base:
|
|
name: Publish Langflow Base to PyPI
|
|
if: ${{ inputs.release_package_base }}
|
|
needs: [build-base, test-cross-platform, ci]
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Download base artifact
|
|
uses: actions/download-artifact@v7
|
|
with:
|
|
name: dist-base
|
|
path: src/backend/base/dist
|
|
- name: Setup Environment
|
|
uses: astral-sh/setup-uv@v6
|
|
with:
|
|
enable-cache: false
|
|
python-version: "3.13"
|
|
- name: Publish base to PyPI
|
|
if: ${{ !inputs.dry_run }}
|
|
env:
|
|
UV_PUBLISH_TOKEN: ${{ secrets.PYPI_API_TOKEN }}
|
|
run: |
|
|
cd src/backend/base && uv publish dist/*.whl
|
|
|
|
publish-main:
|
|
name: Publish Langflow Main to PyPI
|
|
if: ${{ inputs.release_package_main }}
|
|
needs: [build-main, test-cross-platform, publish-base, ci]
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Download main artifact
|
|
uses: actions/download-artifact@v7
|
|
with:
|
|
name: dist-main
|
|
path: dist
|
|
- name: Setup Environment
|
|
uses: astral-sh/setup-uv@v6
|
|
with:
|
|
enable-cache: false
|
|
python-version: "3.13"
|
|
- name: Publish main to PyPI
|
|
if: ${{ !inputs.dry_run }}
|
|
env:
|
|
UV_PUBLISH_TOKEN: ${{ secrets.PYPI_API_TOKEN }}
|
|
run: |
|
|
uv publish dist/*.whl
|
|
|
|
publish-lfx:
|
|
name: Publish LFX to PyPI
|
|
if: ${{ inputs.release_lfx }}
|
|
needs: [build-lfx, test-cross-platform, ci]
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Download LFX artifact
|
|
uses: actions/download-artifact@v7
|
|
with:
|
|
name: dist-lfx
|
|
path: src/lfx/dist
|
|
- name: Setup Environment
|
|
uses: astral-sh/setup-uv@v6
|
|
with:
|
|
enable-cache: false
|
|
python-version: "3.13"
|
|
- name: Publish LFX to PyPI
|
|
if: ${{ !inputs.dry_run }}
|
|
env:
|
|
UV_PUBLISH_TOKEN: ${{ secrets.PYPI_API_TOKEN }}
|
|
run: |
|
|
cd src/lfx && uv publish dist/*.whl
|
|
|
|
call_docker_build_base:
|
|
name: Call Docker Build Workflow for Langflow Base
|
|
if: ${{ inputs.build_docker_base }}
|
|
needs: [ci]
|
|
uses: ./.github/workflows/docker-build-v2.yml
|
|
with:
|
|
ref: ${{ inputs.release_tag }}
|
|
release_type: base
|
|
pre_release: ${{ inputs.pre_release }}
|
|
push_to_registry: ${{ !inputs.dry_run }}
|
|
secrets: inherit
|
|
|
|
call_docker_build_main:
|
|
name: Call Docker Build Workflow for Langflow
|
|
if: ${{ inputs.build_docker_main }}
|
|
needs: [ci]
|
|
uses: ./.github/workflows/docker-build-v2.yml
|
|
with:
|
|
ref: ${{ inputs.release_tag }}
|
|
release_type: main
|
|
pre_release: ${{ inputs.pre_release }}
|
|
push_to_registry: ${{ !inputs.dry_run }}
|
|
secrets: inherit
|
|
|
|
call_docker_build_main_backend:
|
|
name: Call Docker Build Workflow for Langflow Backend
|
|
if: ${{ inputs.build_docker_main && !inputs.dry_run }}
|
|
needs: [call_docker_build_main]
|
|
uses: ./.github/workflows/docker-build-v2.yml
|
|
with:
|
|
ref: ${{ inputs.release_tag }}
|
|
release_type: main-backend
|
|
pre_release: ${{ inputs.pre_release }}
|
|
push_to_registry: ${{ !inputs.dry_run }}
|
|
secrets: inherit
|
|
|
|
call_docker_build_main_frontend:
|
|
name: Call Docker Build Workflow for Langflow Frontend
|
|
if: ${{ inputs.build_docker_main && !inputs.dry_run }}
|
|
needs: [call_docker_build_main]
|
|
uses: ./.github/workflows/docker-build-v2.yml
|
|
with:
|
|
ref: ${{ inputs.release_tag }}
|
|
release_type: main-frontend
|
|
pre_release: ${{ inputs.pre_release }}
|
|
push_to_registry: ${{ !inputs.dry_run }}
|
|
secrets: inherit
|
|
|
|
call_docker_build_main_ep:
|
|
name: Call Docker Build Workflow for Langflow with Entrypoint
|
|
if: ${{ inputs.build_docker_main }}
|
|
needs: [ci]
|
|
uses: ./.github/workflows/docker-build-v2.yml
|
|
with:
|
|
ref: ${{ inputs.release_tag }}
|
|
release_type: main-ep
|
|
pre_release: ${{ inputs.pre_release }}
|
|
push_to_registry: ${{ !inputs.dry_run }}
|
|
secrets: inherit
|
|
|
|
call_docker_build_main_all:
|
|
name: Call Docker Build Workflow for langflow-all
|
|
if: ${{ inputs.build_docker_main }}
|
|
needs: [ci]
|
|
uses: ./.github/workflows/docker-build-v2.yml
|
|
with:
|
|
ref: ${{ inputs.release_tag }}
|
|
release_type: main-all
|
|
pre_release: ${{ inputs.pre_release }}
|
|
push_to_registry: ${{ !inputs.dry_run }}
|
|
secrets: inherit
|
|
|
|
create_release:
|
|
name: Create Release
|
|
runs-on: ubuntu-latest
|
|
needs: [determine-main-version, build-main, publish-main]
|
|
if: |
|
|
always() &&
|
|
!cancelled() &&
|
|
!inputs.dry_run &&
|
|
inputs.create_release &&
|
|
needs.build-main.result == 'success' &&
|
|
needs.publish-main.result == 'success'
|
|
steps:
|
|
- uses: actions/download-artifact@v4
|
|
with:
|
|
name: dist-main
|
|
path: dist
|
|
- name: Create Release
|
|
uses: ncipollo/release-action@v1
|
|
with:
|
|
artifacts: dist/*
|
|
token: ${{ secrets.GITHUB_TOKEN }}
|
|
draft: false
|
|
generateReleaseNotes: true
|
|
prerelease: ${{ inputs.pre_release }}
|
|
tag: ${{ needs.determine-main-version.outputs.version }}
|
|
allowUpdates: true
|
|
updateOnlyUnreleased: false
|