mirror of
https://github.com/langflow-ai/langflow.git
synced 2026-07-25 22:19:40 +08:00
* feat: upgrade Docker images to Python 3.14 (experimental)
- Update pyproject.toml: requires-python from <3.14 to <3.15
- Update all 5 Dockerfiles to use Python 3.14:
- Builder stage: python3.12-trixie-slim → python3.14-trixie-slim
- Runtime stage: python:3.12-slim-trixie → python:3.14-slim-trixie
- src/backend/base/pyproject.toml
- docker/build_and_push_base.Dockerfile
- docker/build_and_push.Dockerfile
- docker/build_and_push_backend.Dockerfile
- docker/build_and_push_with_extras.Dockerfile
- docker/build_and_push_ep.Dockerfile
Python 3.14.5 was released on May 10, 2026. Upgrading to the latest
Python version should help reduce CVE vulnerabilities in Docker images.
This is an experimental change to test Python 3.14 compatibility:
- Docker images will use Python 3.14
- CI/CD workflows still test on Python 3.10-3.13
- Nightly build will validate if dependencies work with 3.14
- Can be reverted quickly if issues are found
- Monitor nightly build for failures
- If successful, update CI/CD workflows to add Python 3.14 to test matrix
- If failures occur, revert and investigate compatibility issues
* chore: support Python 3.14
Bump requires-python upper bound to <3.15 across langflow, langflow-base,
lfx, and langflow-sdk, and add 3.14 to CI test matrices so PRs are gated
on 3.14 compatibility.
Conditional pins for transitive deps without 3.14 wheels at the existing
caps:
- onnxruntime: >=1.26 on 3.14 (existing <1.24 cap retained for 3.10)
- faiss-cpu: >=1.13.2 on 3.14 (existing ==1.9.0.post1 retained for <3.14)
* chore: marker-gate IBM watsonx packages for Python 3.14
ibm-watsonx-ai 1.3.x cannot import on Python 3.14: its StrEnum subclasses
override __init__ in a way that conflicts with 3.14's reworked enum
__set_name__ path (TypeError on KnowledgeBaseFieldRole class creation).
Cap ibm-watsonx-ai, langchain-ibm, and the ibm-watsonx-orchestrate-*
extras at python_version<'3.14' until upstream adapts. Watsonx component
imports are already lazy, so this surfaces only at component-access time
on 3.14, where the existing ImportError handler in
lfx.components.ibm.__init__ degrades it gracefully.
test_model_utils.py imports ChatWatsonx at module top to verify
get_model_name resolves model_id; guard that import so the test
module skips on 3.14 instead of breaking collection.
* [autofix.ci] apply automated fixes
* chore: upgrade remaining Docker images to Python 3.14
build_and_push*.Dockerfile already moved to 3.14 in the merge from
feat/upgrade-python-3.14-docker-images. Apply the same bump to the
dev, devcontainer, and lfx Docker images so all in-repo Dockerfiles
share one Python version.
* [autofix.ci] apply automated fixes
* chore: gate 3.14-broken extras to python_version<'3.14'
cuga (and its transitive fastembed -> py-rust-stemmers source build),
altk/agent-lifecycle-toolkit (re-pulls ibm-watsonx-ai which was already
gated), langchain-pinecone, langwatch, ragstack-ai-knowledge-store, and
OpenDsStar all pin themselves below 3.14 upstream. Without a marker
guard, the workspace lock still tries to install them and a Docker
`uv sync` then attempts source builds that require Rust (CI Docker
test failure with py-rust-stemmers 0.1.5).
Add python_version<'3.14' (or <'3.13' for ragstack) to each pin so
those packages are simply omitted from the 3.14 install set until
upstream catches up. uv pip check is now clean on 3.14.2.
* test: skip test_altk_agent on Python 3.14
altk (agent-lifecycle-toolkit) is gated to python_version<'3.14'
upstream and now via the langflow-base [altk] extra marker. The test
imports altk at module top to exercise ALTKAgentComponent; guard the
import so collection skips on 3.14 instead of failing.
* test: skip remaining altk tests on Python 3.14
Three more test modules import lfx.base.agents.altk_* at module top,
which transitively imports altk. Add the same module-level skip guard
as test_altk_agent.py:
- test_altk_agent_logic.py
- test_altk_agent_tool_conversion.py
- test_conversation_context_ordering.py
* fix(calculator): replace removed ast.Num with ast.Constant for Python 3.14
ast.Num was deprecated in Python 3.8 in favor of ast.Constant and
removed entirely in 3.14. The calculator tool and its core walker
crashed on 3.14 with 'module ast has no attribute Num'.
Switch the isinstance checks to ast.Constant + isinstance(node.value,
(int, float)), and drop the now-dead ast.Num backwards-compat branch
in calculator_core.py (the ast.Constant branch already handles every
input it would have matched). Update the parser unit-test fixtures
to construct ast.Constant nodes.
* [autofix.ci] apply automated fixes
* [autofix.ci] apply automated fixes (attempt 2/3)
* test: skip LangWatch HTTP instrumentation tests on Python 3.14
langwatch is gated to python_version<'3.14' upstream. The
TestLangWatchHttpInstrumentation class patches langwatch.setup and
langwatch.trace inside a fixture, which requires langwatch to be
importable; on 3.14 the test errors with ModuleNotFoundError.
Guard the class with a skipif on langwatch availability.
* test: allow IBM and altk components to be missing on Python 3.14
test_all_modules_importable enforces that every component in __all__
imports cleanly. On 3.14 the ibm-watsonx-ai, langchain-ibm, and altk
packages are gated upstream and intentionally not installed, so the
WatsonxAI, WatsonxEmbeddings, and ALTKAgent components fail to import
as designed.
- test_all_components_in_categories_importable: accept a known-gated
deny-list on 3.14 instead of failing.
- test_all_lfx_component_modules_directly_importable: extend the
existing 'missing optional dependency' allowlist with altk,
langchain_ibm, and ibm_watsonx_ai.
* fix(lfx): pass ensure_exists to user_cache_dir for Python 3.14
Python 3.14 tightened PurePath.__init__ to reject unknown keyword
arguments. The KeyedWorkerLockManager constructor was passing
ensure_exists=True to Path() instead of user_cache_dir(), which
silently worked on 3.10-3.13 but raises TypeError on 3.14 and also
meant the cache directory was never actually being created.
Move the kwarg to user_cache_dir() where it belongs and update the
two unit tests that asserted the buggy call shape.
* [autofix.ci] apply automated fixes
* test: accept either ZIP or JSON error path on Python 3.14
Python 3.14's zipfile.is_zipfile() now validates the central-directory
signature in addition to EOCD, so the garbage+EOCD payload no longer
passes the is_zipfile() dispatch in the /flows/upload/ route. The
endpoint still returns 400 with a descriptive detail (now from the JSON
branch); only the message wording differs, which the test was asserting
verbatim.
* fix(lfx): normalize cors_origins ['*'] back to '*' on Python 3.14
Pydantic-settings on Python 3.14 parses the env var '*' into ['*']
before the cors_origins field validator runs (the list[str] | str union
resolves differently than on 3.10-3.13). Collapse that back to the
bare-string wildcard so downstream consumers — including
warn_about_future_cors_changes and the test suite — see the same shape
on every supported Python version.
---------
Co-authored-by: vijay kumar katuri <vijay.katuri@ibm.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
(cherry picked from commit 1955f8fae5)
541 lines
20 KiB
YAML
541 lines
20 KiB
YAML
name: CI
|
|
|
|
on:
|
|
workflow_call:
|
|
inputs:
|
|
ref:
|
|
description: "(Optional) Ref to checkout"
|
|
required: false
|
|
type: string
|
|
python-versions:
|
|
description: "Python Versions"
|
|
required: false
|
|
type: string
|
|
default: "['3.10', '3.14']"
|
|
frontend-tests-folder:
|
|
description: "Frontend Tests Folder"
|
|
required: false
|
|
type: string
|
|
default: "tests/core"
|
|
release:
|
|
description: "Release"
|
|
required: false
|
|
type: boolean
|
|
default: false
|
|
run-all-tests:
|
|
description: "Run all tests regardless of file changes (skips path filtering)"
|
|
required: false
|
|
type: boolean
|
|
default: false
|
|
runs-on:
|
|
description: "Runner to use for the tests"
|
|
required: false
|
|
type: string
|
|
default: "ubuntu-latest"
|
|
workflow_dispatch:
|
|
inputs:
|
|
ref:
|
|
description: "(Optional) Ref to checkout"
|
|
required: false
|
|
type: string
|
|
openai_api_key:
|
|
description: "OpenAI API Key"
|
|
required: false
|
|
type: string
|
|
store_api_key:
|
|
description: "Store API Key"
|
|
required: false
|
|
type: string
|
|
python-versions:
|
|
description: "Python Versions"
|
|
required: false
|
|
type: string
|
|
default: "['3.10', '3.14']"
|
|
runs-on:
|
|
description: "Runner to use for the tests"
|
|
required: false
|
|
type: choice
|
|
options:
|
|
- ubuntu-latest
|
|
- self-hosted
|
|
- '["self-hosted", "linux", "ARM64", "langflow-ai-arm64-40gb-ephemeral"]'
|
|
default: ubuntu-latest
|
|
pull_request:
|
|
types: [opened, synchronize, labeled]
|
|
merge_group:
|
|
concurrency:
|
|
group: ${{ github.workflow }}-${{ github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
env:
|
|
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
|
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
STORE_API_KEY: ${{ secrets.STORE_API_KEY }}
|
|
TAVILY_API_KEY: ${{ secrets.TAVILY_API_KEY }}
|
|
|
|
jobs:
|
|
echo-inputs:
|
|
name: Echo Inputs
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Echo inputs
|
|
run: |
|
|
echo "Inputs:"
|
|
echo " ref: ${{ inputs.ref }}"
|
|
echo " python-versions: ${{ inputs.python-versions }}"
|
|
echo " frontend-tests-folder: ${{ inputs.frontend-tests-folder }}"
|
|
echo " release: ${{ inputs.release }}"
|
|
echo " run-all-tests: ${{ inputs.run-all-tests }}"
|
|
echo " runs-on: ${{ inputs.runs-on }}"
|
|
|
|
check-nightly-status:
|
|
name: Check PyPI Version Update
|
|
runs-on: ubuntu-latest
|
|
outputs:
|
|
should-proceed: ${{ steps.check-pypi.outputs.success }}
|
|
steps:
|
|
- name: Check PyPI package update
|
|
id: check-pypi
|
|
run: |
|
|
# Get today's date in ISO format for comparison
|
|
TODAY=$(date -u +"%Y-%m-%d")
|
|
echo "Today's date: $TODAY"
|
|
|
|
# Query PyPI API for the langflow package
|
|
HTTP_STATUS=$(curl -s -o response.json -w "%{http_code}" https://pypi.org/pypi/langflow-nightly/json)
|
|
|
|
# Check HTTP status code first
|
|
if [ "$HTTP_STATUS" -ne 200 ]; then
|
|
echo "Error: PyPI API returned HTTP status $HTTP_STATUS"
|
|
echo "success=false" >> $GITHUB_OUTPUT
|
|
exit 0
|
|
fi
|
|
|
|
# Check if response is valid JSON before proceeding
|
|
if ! jq -e . response.json >/dev/null 2>&1; then
|
|
echo "Error: Invalid JSON response from PyPI API"
|
|
echo "Response preview:"
|
|
head -n 10 response.json
|
|
echo "success=false" >> $GITHUB_OUTPUT
|
|
exit 0
|
|
fi
|
|
|
|
# Extract the latest version
|
|
LATEST_VERSION=$(jq -r '.info.version // empty' response.json)
|
|
|
|
if [ -z "$LATEST_VERSION" ]; then
|
|
echo "Could not extract latest version"
|
|
echo "success=false" >> $GITHUB_OUTPUT
|
|
exit 0
|
|
fi
|
|
|
|
# Extract the release date of the latest version
|
|
RELEASE_DATE=$(jq -r --arg ver "$LATEST_VERSION" '.releases[$ver][0].upload_time_iso_8601 // empty' response.json | cut -d'T' -f1)
|
|
|
|
if [ -z "$RELEASE_DATE" ]; then
|
|
echo "Could not extract release date"
|
|
echo "success=false" >> $GITHUB_OUTPUT
|
|
exit 0
|
|
fi
|
|
|
|
echo "Latest version: $LATEST_VERSION"
|
|
echo "Release date: $RELEASE_DATE"
|
|
|
|
# Check if the release date is today
|
|
if [[ "$RELEASE_DATE" == "$TODAY" ]]; then
|
|
echo "Package was updated today"
|
|
echo "success=true" >> $GITHUB_OUTPUT
|
|
else
|
|
echo "Package was not updated today"
|
|
echo "success=false" >> $GITHUB_OUTPUT
|
|
fi
|
|
|
|
# Clean up
|
|
rm -f response.json
|
|
|
|
set-ci-condition:
|
|
name: Should Run CI
|
|
runs-on: ubuntu-latest
|
|
outputs:
|
|
should-run-ci: ${{ github.event.pull_request.draft == false || (github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call' || github.event_name == 'merge_group') }}
|
|
should-run-tests: ${{ !contains(github.event.pull_request.labels.*.name, 'fast-track') || github.event_name == 'workflow_call' || github.event_name == 'workflow_dispatch' || github.event_name == 'merge_group' }}
|
|
steps:
|
|
# Do anything just to make the job run
|
|
- run: echo "Debug CI Condition"
|
|
- run: echo "Labels -> ${{ join(github.event.pull_request.labels.*.name, ',') }}"
|
|
- run: echo "IsDraft -> ${{ github.event.pull_request.draft }}"
|
|
- run: echo "Event name -> ${{ github.event_name }}"
|
|
- run: echo "Should run ci -> ${{ (github.event.pull_request.draft == false) || (github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call' || github.event_name == 'merge_group') }}"
|
|
- run: echo "Should run tests -> ${{ !contains(github.event.pull_request.labels.*.name, 'fast-track') || github.event_name == 'workflow_call' || github.event_name == 'workflow_dispatch' || github.event_name == 'merge_group' }}"
|
|
|
|
path-filter:
|
|
needs: set-ci-condition
|
|
if: ${{ needs.set-ci-condition.outputs.should-run-ci == 'true' && !inputs.run-all-tests }}
|
|
name: Filter Paths
|
|
runs-on: ubuntu-latest
|
|
outputs:
|
|
python: ${{ steps.filter.outputs.python }}
|
|
frontend: ${{ steps.filter.outputs.frontend }}
|
|
docs: ${{ steps.filter.outputs.docs }}
|
|
frontend-tests: ${{ steps.filter.outputs.frontend-tests }}
|
|
components-changes: ${{ steps.filter.outputs.components-changes }}
|
|
starter-projects-changes: ${{ steps.filter.outputs.starter-projects-changes }}
|
|
starter-projects: ${{ steps.filter.outputs.starter-projects }}
|
|
components: ${{ steps.filter.outputs.components }}
|
|
workspace: ${{ steps.filter.outputs.workspace }}
|
|
api: ${{ steps.filter.outputs.api }}
|
|
database: ${{ steps.filter.outputs.database }}
|
|
docker: ${{ steps.filter.outputs.docker }}
|
|
design-md: ${{ steps.filter.outputs.design-md }}
|
|
docs-only: ${{
|
|
steps.filter.outputs.docs == 'true' &&
|
|
steps.filter.outputs.python != 'true' &&
|
|
steps.filter.outputs.frontend != 'true' &&
|
|
steps.filter.outputs['frontend-tests'] != 'true' &&
|
|
steps.filter.outputs['components-changes'] != 'true' &&
|
|
steps.filter.outputs['starter-projects-changes'] != 'true' &&
|
|
steps.filter.outputs['starter-projects'] != 'true' &&
|
|
steps.filter.outputs.components != 'true' &&
|
|
steps.filter.outputs.workspace != 'true' &&
|
|
steps.filter.outputs.api != 'true' &&
|
|
steps.filter.outputs.database != 'true' &&
|
|
steps.filter.outputs.docker != 'true'
|
|
}}
|
|
|
|
steps:
|
|
- name: Checkout code
|
|
uses: actions/checkout@v6
|
|
with:
|
|
ref: ${{ inputs.ref || github.ref }}
|
|
- name: Filter Paths
|
|
id: filter
|
|
uses: dorny/paths-filter@v3
|
|
with:
|
|
filters: ./.github/changes-filter.yaml
|
|
- name: Set up Python
|
|
uses: actions/setup-python@v6
|
|
with:
|
|
python-version: "3.12"
|
|
- name: Install deps for coverage check
|
|
run: |
|
|
python -m pip install --upgrade pip
|
|
pip install --disable-pip-version-check --no-cache-dir pyyaml
|
|
- name: Validate Filter Coverage
|
|
continue-on-error: true
|
|
shell: bash
|
|
run: |
|
|
BASE_SHA="${{ github.event.pull_request.base.sha }}"
|
|
BASE_REF="${{ github.event.pull_request.base.ref || 'main' }}"
|
|
# Ensure base commit is present locally
|
|
git fetch --no-tags --depth=1 origin "$BASE_REF" || true
|
|
git fetch --no-tags --depth=1 origin "$BASE_SHA" || true
|
|
git diff --name-only "${BASE_SHA:-origin/$BASE_REF}"...HEAD | python scripts/check_changes_filter.py
|
|
|
|
test-backend:
|
|
needs: [path-filter, set-ci-condition]
|
|
name: Run Backend Tests
|
|
if: |
|
|
always() &&
|
|
!cancelled() &&
|
|
needs.set-ci-condition.outputs.should-run-tests == 'true' &&
|
|
(
|
|
inputs.run-all-tests ||
|
|
(needs.path-filter.result != 'skipped' &&
|
|
needs.path-filter.outputs.docs-only != 'true')
|
|
)
|
|
uses: ./.github/workflows/python_test.yml
|
|
with:
|
|
python-versions: ${{ inputs.python-versions || '["3.10", "3.14"]' }}
|
|
runs-on: ${{ inputs['runs-on'] || github.event.inputs['runs-on'] || 'ubuntu-latest' }}
|
|
ref: ${{ inputs.ref || github.ref }}
|
|
secrets:
|
|
OPENAI_API_KEY: "${{ secrets.OPENAI_API_KEY }}"
|
|
ANTHROPIC_API_KEY: "${{ secrets.ANTHROPIC_API_KEY }}"
|
|
CODECOV_TOKEN: "${{ secrets.CODECOV_TOKEN }}"
|
|
|
|
test-frontend-unit:
|
|
needs: [path-filter, set-ci-condition]
|
|
name: Run Frontend Unit Tests
|
|
if: |
|
|
always() &&
|
|
!cancelled() &&
|
|
needs.set-ci-condition.outputs.should-run-tests == 'true' &&
|
|
(
|
|
inputs.run-all-tests ||
|
|
(needs.path-filter.result != 'skipped' &&
|
|
needs.path-filter.outputs.docs-only != 'true')
|
|
)
|
|
uses: ./.github/workflows/jest_test.yml
|
|
with:
|
|
ref: ${{ inputs.ref || github.ref }}
|
|
secrets:
|
|
CODECOV_TOKEN: "${{ secrets.CODECOV_TOKEN }}"
|
|
|
|
test-frontend:
|
|
needs: [path-filter, set-ci-condition]
|
|
name: Run Frontend Tests
|
|
if: |
|
|
always() &&
|
|
!cancelled() &&
|
|
needs.set-ci-condition.outputs.should-run-tests == 'true' &&
|
|
(
|
|
inputs.run-all-tests ||
|
|
(needs.path-filter.result != 'skipped' &&
|
|
needs.path-filter.outputs.docs-only != 'true')
|
|
)
|
|
uses: ./.github/workflows/typescript_test.yml
|
|
with:
|
|
tests_folder: ${{ inputs.frontend-tests-folder }}
|
|
release: ${{ inputs.release || false }}
|
|
runs-on: ${{ inputs['runs-on'] || github.event.inputs['runs-on'] || 'ubuntu-latest' }}
|
|
ref: ${{ inputs.ref || github.ref }}
|
|
secrets:
|
|
OPENAI_API_KEY: "${{ secrets.OPENAI_API_KEY }}"
|
|
STORE_API_KEY: "${{ secrets.STORE_API_KEY }}"
|
|
ANTHROPIC_API_KEY: "${{ secrets.ANTHROPIC_API_KEY }}"
|
|
TAVILY_API_KEY: "${{ secrets.TAVILY_API_KEY }}"
|
|
|
|
test-docs-build:
|
|
needs: path-filter
|
|
if: |
|
|
always() &&
|
|
!cancelled() &&
|
|
(inputs.run-all-tests || needs.path-filter.outputs.docs == 'true')
|
|
name: Test Docs Build
|
|
uses: ./.github/workflows/docs_test.yml
|
|
|
|
test-templates:
|
|
needs: [path-filter, set-ci-condition]
|
|
name: Test Starter Templates
|
|
if: |
|
|
always() &&
|
|
!cancelled() &&
|
|
needs.set-ci-condition.outputs.should-run-tests == 'true' &&
|
|
(inputs.run-all-tests || needs.path-filter.outputs.python == 'true' || needs.path-filter.outputs.frontend == 'true')
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Checkout code
|
|
uses: actions/checkout@v6
|
|
with:
|
|
ref: ${{ inputs.ref || github.ref }}
|
|
|
|
- name: Set up Python 3.12
|
|
uses: actions/setup-python@v6
|
|
with:
|
|
python-version: 3.12
|
|
|
|
- name: Install uv
|
|
uses: astral-sh/setup-uv@v6
|
|
with:
|
|
version: "latest"
|
|
|
|
- name: Install dependencies
|
|
run: |
|
|
uv sync --dev
|
|
|
|
- name: Test all starter project templates
|
|
run: |
|
|
uv run pytest src/backend/tests/unit/template/test_starter_projects.py -v -n auto
|
|
|
|
test-docker:
|
|
needs: [path-filter, set-ci-condition]
|
|
name: Test Docker Images
|
|
if: ${{ needs.path-filter.outputs.docker == 'true' && needs.set-ci-condition.outputs.should-run-tests == 'true' }}
|
|
uses: ./.github/workflows/docker_test.yml
|
|
secrets:
|
|
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
|
|
lint-design-md:
|
|
needs: path-filter
|
|
name: Lint DESIGN.md
|
|
if: ${{ inputs.run-all-tests || needs.path-filter.outputs.design-md == 'true' }}
|
|
runs-on: ubuntu-latest
|
|
env:
|
|
DESIGN_MD_CLI_VERSION: "0.1.1"
|
|
steps:
|
|
- name: Checkout code
|
|
uses: actions/checkout@v6
|
|
with:
|
|
ref: ${{ inputs.ref || github.ref }}
|
|
|
|
- name: Setup Node.js
|
|
uses: actions/setup-node@v6
|
|
with:
|
|
node-version: "22"
|
|
|
|
- name: Lint DESIGN.md
|
|
run: npx --yes @google/design.md@${{ env.DESIGN_MD_CLI_VERSION }} lint DESIGN.md
|
|
|
|
merge-frontend-coverage:
|
|
name: Merge Frontend Jest + Playwright Coverage Reports
|
|
needs: [path-filter, set-ci-condition, test-frontend-unit, test-frontend]
|
|
if: |
|
|
always() &&
|
|
!cancelled() &&
|
|
needs.set-ci-condition.outputs.should-run-tests == 'true' &&
|
|
(
|
|
inputs.run-all-tests ||
|
|
(needs.path-filter.result != 'skipped' &&
|
|
needs.path-filter.outputs.docs-only != 'true')
|
|
)
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v6
|
|
|
|
- name: Download Jest Coverage
|
|
uses: actions/download-artifact@v4
|
|
continue-on-error: true
|
|
with:
|
|
name: jest-coverage
|
|
path: coverage/jest
|
|
|
|
- name: Download Playwright Coverage
|
|
uses: actions/download-artifact@v4
|
|
continue-on-error: true
|
|
with:
|
|
pattern: playwright-coverage-*
|
|
path: coverage/playwright
|
|
merge-multiple: true
|
|
|
|
- name: Merge Coverage
|
|
run: |
|
|
mkdir -p coverage/combined
|
|
npx nyc merge coverage/playwright/individual-test coverage/combined/playwright.json || echo "Failed to merge Playwright coverage"
|
|
npx nyc merge coverage/jest coverage/combined/jest.json || echo "Failed to merge Jest coverage"
|
|
npx nyc merge coverage/combined coverage/combined/total-frontend-coverage.json || echo "Failed to combine Playwright + Jest coverage"
|
|
|
|
- name: Generate Combined Report
|
|
run: |
|
|
npx nyc report \
|
|
--reporter=html \
|
|
--reporter=text \
|
|
--temp-dir coverage/combined \
|
|
--report-dir coverage/final
|
|
|
|
- name: Upload Merged Frontend Coverage to Codecov
|
|
uses: codecov/codecov-action@v5
|
|
with:
|
|
token: ${{ secrets.CODECOV_TOKEN }}
|
|
files: coverage/combined/total-frontend-coverage.json
|
|
flags: frontend
|
|
fail_ci_if_error: true
|
|
|
|
# https://github.com/langchain-ai/langchain/blob/master/.github/workflows/check_diffs.yml
|
|
ci_success:
|
|
name: "CI Success"
|
|
needs:
|
|
[
|
|
test-backend,
|
|
test-frontend-unit,
|
|
test-frontend,
|
|
test-docs-build,
|
|
test-templates,
|
|
test-docker,
|
|
lint-design-md,
|
|
set-ci-condition,
|
|
path-filter,
|
|
check-nightly-status,
|
|
merge-frontend-coverage,
|
|
]
|
|
|
|
if: ${{ always() }}
|
|
runs-on: ubuntu-latest
|
|
env:
|
|
JOBS_JSON: ${{ toJSON(needs) }}
|
|
RESULTS_JSON: ${{ toJSON(needs.*.result) }}
|
|
# Skip nightly build check if only docs files changed
|
|
DOCS_ONLY: ${{ needs.path-filter.outputs.docs-only }}
|
|
EXIT_CODE: ${{ (contains(needs.*.result, 'failure') ||
|
|
contains(needs.*.result, 'cancelled') ||
|
|
(needs.check-nightly-status.outputs.should-proceed != 'true' && github.event_name != 'workflow_dispatch' && github.event_name != 'merge_group' && needs.path-filter.outputs.docs-only != 'true' && !contains(github.event.pull_request.labels.*.name, 'skip-nightly-check')))
|
|
&& '1' || '0' }}
|
|
steps:
|
|
- name: "CI Success"
|
|
run: |
|
|
echo "=== CI Status Summary ==="
|
|
echo "Should run tests: ${{ needs.set-ci-condition.outputs.should-run-tests }}"
|
|
echo "Should run CI: ${{ needs.set-ci-condition.outputs.should-run-ci }}"
|
|
echo "Nightly build status: ${{ needs.check-nightly-status.outputs.should-proceed }}"
|
|
echo "Event type: ${{ github.event_name }}"
|
|
echo "Docs only changes: $DOCS_ONLY"
|
|
echo "Python changes: ${{ needs.path-filter.outputs.python }}"
|
|
echo "Frontend changes: ${{ needs.path-filter.outputs.frontend }}"
|
|
echo "Docs changes: ${{ needs.path-filter.outputs.docs }}"
|
|
echo ""
|
|
|
|
# Check for job failures
|
|
if [[ "${{ contains(needs.*.result, 'failure') }}" == "true" ]]; then
|
|
echo "❌ CI FAILED: One or more jobs failed"
|
|
echo ""
|
|
echo "Failed jobs:"
|
|
|
|
# Dynamically list failed jobs with helpful descriptions
|
|
echo "$JOBS_JSON" | jq -r '
|
|
to_entries[]
|
|
| select(.value.result=="failure")
|
|
| .key as $job
|
|
| if $job == "test-backend" then
|
|
" - Backend Tests: Check Python code, tests, and dependencies"
|
|
elif $job == "test-frontend-unit" then
|
|
" - Frontend Unit Tests: Check React components and unit test logic"
|
|
elif $job == "test-frontend" then
|
|
" - Frontend E2E Tests: Check integration tests and UI functionality"
|
|
elif $job == "test-docs-build" then
|
|
" - Documentation Build: Check documentation syntax and build process"
|
|
elif $job == "test-templates" then
|
|
" - Template Tests: Check starter project templates"
|
|
elif $job == "test-docker" then
|
|
" - Docker Tests: Check Docker image builds and version verification"
|
|
elif $job == "lint-design-md" then
|
|
" - DESIGN.md Lint: Check token references, WCAG contrast, and spec compliance"
|
|
elif $job == "path-filter" then
|
|
" - Path Filter: File path filtering failed"
|
|
elif $job == "set-ci-condition" then
|
|
" - CI Condition Check: CI condition evaluation failed"
|
|
elif $job == "check-nightly-status" then
|
|
" - Nightly Status Check: PyPI package status check failed"
|
|
else
|
|
" - \($job): See job log for details"
|
|
end
|
|
'
|
|
|
|
echo ""
|
|
echo "🔧 Next steps:"
|
|
echo " 1. Review the failed job logs above"
|
|
echo " 2. Fix the identified issues in your code"
|
|
echo " 3. Push your changes to re-run the tests"
|
|
|
|
elif [[ "${{ contains(needs.*.result, 'cancelled') }}" == "true" ]]; then
|
|
echo "⚠️ CI CANCELLED: One or more jobs were cancelled"
|
|
echo ""
|
|
echo "🔧 Next steps:"
|
|
echo " 1. Check if the cancellation was intentional"
|
|
echo " 2. Re-run the workflow if needed"
|
|
|
|
elif [[ "${{ needs.check-nightly-status.outputs.should-proceed }}" != "true" && "${{ github.event_name }}" != "workflow_dispatch" && "$DOCS_ONLY" != "true" ]]; then
|
|
echo "🚫 CI BLOCKED: Nightly build is broken"
|
|
echo ""
|
|
echo "The nightly PyPI package was not updated today, indicating the nightly build failed."
|
|
echo ""
|
|
echo "🔧 Next steps:"
|
|
echo " 1. Work with the team to investigate and fix the nightly build"
|
|
echo " 2. Check the nightly build logs for errors"
|
|
echo " 3. Once the nightly build is fixed and publishes successfully, re-run this workflow"
|
|
echo " 4. Alternatively, use 'workflow_dispatch' to manually override this check if needed"
|
|
echo " 5. Note: PRs with only documentation changes can bypass this check"
|
|
|
|
else
|
|
echo "✅ CI SUCCESS: All checks passed!"
|
|
echo ""
|
|
echo "🎉 Your changes are ready:"
|
|
echo " - All tests passed"
|
|
echo " - Code quality checks passed"
|
|
echo " - Nightly build is healthy"
|
|
fi
|
|
|
|
echo ""
|
|
echo "Exit code: $EXIT_CODE"
|
|
exit $EXIT_CODE
|