mirror of
https://github.com/langflow-ai/langflow.git
synced 2026-07-24 04:13:36 +08:00
176 lines
6.4 KiB
YAML
176 lines
6.4 KiB
YAML
# Advisory-only check: flags PRs that change source code (.py / .ts / .tsx)
|
|
# without touching any test file. It NEVER fails and is NOT a required check,
|
|
# so it cannot block other jobs or the merge — it only posts a sticky PR
|
|
# comment + job summary so the author knows tests appear to be missing.
|
|
name: Test Coverage Advisor
|
|
|
|
on:
|
|
pull_request:
|
|
types: [opened, synchronize, reopened, ready_for_review]
|
|
|
|
# Dedicated concurrency group so this never cancels or queues behind CI.
|
|
concurrency:
|
|
group: test-coverage-advisor-${{ github.event.pull_request.number }}
|
|
cancel-in-progress: true
|
|
|
|
permissions:
|
|
contents: read
|
|
pull-requests: write
|
|
|
|
jobs:
|
|
advise:
|
|
name: Test Coverage Advisor
|
|
# Skip drafts to avoid noise; they re-run on "ready_for_review".
|
|
if: github.event.pull_request.draft == false
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Checkout code
|
|
uses: actions/checkout@v6
|
|
with:
|
|
# Full history so merge-base against the PR target works.
|
|
fetch-depth: 0
|
|
|
|
- name: Detect missing tests
|
|
id: detect
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
|
|
BASE_REF="${{ github.event.pull_request.base.ref }}"
|
|
git fetch --no-tags --depth=50 origin "$BASE_REF" || true
|
|
MERGE_BASE=$(git merge-base HEAD "origin/$BASE_REF" || git rev-parse HEAD~1)
|
|
|
|
echo "BASE_REF=$BASE_REF"
|
|
echo "MERGE_BASE=$MERGE_BASE"
|
|
|
|
CHANGED=$(git diff --name-only --diff-filter=ACMR "$MERGE_BASE"...HEAD || true)
|
|
echo "=== Changed files ==="
|
|
echo "$CHANGED"
|
|
|
|
# --- Backend Python ---------------------------------------------
|
|
# Source: .py under src/backend or src/lfx, excluding tests,
|
|
# migrations and dunder/init plumbing.
|
|
PY_SRC=$(echo "$CHANGED" \
|
|
| grep -E '^(src/backend|src/lfx)/.*\.py$' \
|
|
| grep -Ev '(^|/)tests?/' \
|
|
| grep -Ev '(^|/)(test_[^/]+|conftest)\.py$' \
|
|
| grep -Ev '/alembic/versions/' \
|
|
| grep -Ev '/__init__\.py$' \
|
|
|| true)
|
|
PY_TEST=$(echo "$CHANGED" \
|
|
| grep -E '\.py$' \
|
|
| grep -E '((^|/)tests?/|(^|/)test_[^/]+\.py$|(^|/)conftest\.py$)' \
|
|
|| true)
|
|
|
|
# --- Frontend TS/TSX --------------------------------------------
|
|
# Source: .ts/.tsx under src/frontend/src, excluding *.test.* /
|
|
# *.spec.* / __tests__ and type declaration files.
|
|
FE_SRC=$(echo "$CHANGED" \
|
|
| grep -E '^src/frontend/src/.*\.(ts|tsx)$' \
|
|
| grep -Ev '\.(test|spec)\.(ts|tsx)$' \
|
|
| grep -Ev '(^|/)__tests__/' \
|
|
| grep -Ev '\.d\.ts$' \
|
|
|| true)
|
|
FE_TEST=$(echo "$CHANGED" \
|
|
| grep -E '\.(ts|tsx)$' \
|
|
| grep -E '(^src/frontend/tests/|\.(test|spec)\.(ts|tsx)$|(^|/)__tests__/)' \
|
|
|| true)
|
|
|
|
need_be=false
|
|
need_fe=false
|
|
[ -n "$PY_SRC" ] && [ -z "$PY_TEST" ] && need_be=true
|
|
[ -n "$FE_SRC" ] && [ -z "$FE_TEST" ] && need_fe=true
|
|
|
|
{
|
|
echo "need_be=$need_be"
|
|
echo "need_fe=$need_fe"
|
|
} >> "$GITHUB_OUTPUT"
|
|
|
|
# Multiline outputs for the comment body.
|
|
{
|
|
echo "py_src<<EOF"
|
|
echo "$PY_SRC"
|
|
echo "EOF"
|
|
echo "fe_src<<EOF"
|
|
echo "$FE_SRC"
|
|
echo "EOF"
|
|
} >> "$GITHUB_OUTPUT"
|
|
|
|
- name: Build advisory message
|
|
id: msg
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
NEED_BE="${{ steps.detect.outputs.need_be }}"
|
|
NEED_FE="${{ steps.detect.outputs.need_fe }}"
|
|
|
|
BODY_FILE="$(mktemp)"
|
|
echo '<!-- test-coverage-advisor -->' > "$BODY_FILE"
|
|
|
|
if [ "$NEED_BE" != "true" ] && [ "$NEED_FE" != "true" ]; then
|
|
{
|
|
echo '### ✅ Test Coverage Advisor'
|
|
echo
|
|
echo 'No source changes detected without accompanying tests. Thanks for keeping coverage up! 🎉'
|
|
echo
|
|
echo '> _Advisory check only — never blocks merge._'
|
|
} >> "$BODY_FILE"
|
|
else
|
|
{
|
|
echo '### ⚠️ Test Coverage Advisor — tests appear to be missing'
|
|
echo
|
|
echo 'This PR changes source code but does **not** add or modify any test files.'
|
|
echo 'This is an **advisory** check: it will **not** block this PR or any other job —'
|
|
echo 'please add tests if the change is testable.'
|
|
echo
|
|
} >> "$BODY_FILE"
|
|
if [ "$NEED_BE" = "true" ]; then
|
|
{
|
|
echo '#### Backend (Python) — no `test_*.py` / `tests/` change found'
|
|
echo '```'
|
|
echo '${{ steps.detect.outputs.py_src }}'
|
|
echo '```'
|
|
} >> "$BODY_FILE"
|
|
fi
|
|
if [ "$NEED_FE" = "true" ]; then
|
|
{
|
|
echo '#### Frontend (TS/TSX) — no `*.test.*` / `*.spec.*` / `tests/` change found'
|
|
echo '```'
|
|
echo '${{ steps.detect.outputs.fe_src }}'
|
|
echo '```'
|
|
} >> "$BODY_FILE"
|
|
fi
|
|
{
|
|
echo
|
|
echo '> _Advisory check only — it always passes and is never a required status._'
|
|
} >> "$BODY_FILE"
|
|
fi
|
|
|
|
# Mirror into the run's Job Summary (works even on fork PRs).
|
|
cat "$BODY_FILE" >> "$GITHUB_STEP_SUMMARY"
|
|
|
|
echo "body_file=$BODY_FILE" >> "$GITHUB_OUTPUT"
|
|
|
|
- name: Find existing advisory comment
|
|
id: find
|
|
uses: peter-evans/find-comment@v3
|
|
continue-on-error: true
|
|
with:
|
|
issue-number: ${{ github.event.pull_request.number }}
|
|
comment-author: github-actions[bot]
|
|
body-includes: "<!-- test-coverage-advisor -->"
|
|
|
|
- name: Upsert advisory comment
|
|
uses: peter-evans/create-or-update-comment@v4
|
|
# Fork PRs get a read-only token — don't fail the run if so.
|
|
continue-on-error: true
|
|
with:
|
|
comment-id: ${{ steps.find.outputs.comment-id }}
|
|
issue-number: ${{ github.event.pull_request.number }}
|
|
body-path: ${{ steps.msg.outputs.body_file }}
|
|
edit-mode: replace
|
|
|
|
- name: Always succeed
|
|
# Explicit: this job never blocks anything.
|
|
run: 'echo "Advisory check complete — this status is informational only."'
|