Files
learningcircuit--local-deep…/.github/workflows/compose-integration-test.yml
T
wehub-resource-sync 7a0da7932b
Backwards Compatibility / Verify Encryption Constants (push) Waiting to run
Backwards Compatibility / PyPI Version Compatibility (push) Waiting to run
Backwards Compatibility / Database Migration Tests (push) Waiting to run
CodeQL Advanced / Analyze (javascript-typescript) (push) Waiting to run
CodeQL Advanced / Analyze (python) (push) Waiting to run
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-form] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-metrics] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-workflow] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-core] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-pages] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [history-news] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [library] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [link-analytics] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [mobile] (push) Blocked by required conditions
Docker Tests (Consolidated) / detect-changes (push) Waiting to run
Docker Tests (Consolidated) / Build Test Image (push) Waiting to run
Docker Tests (Consolidated) / All Pytest Tests + Coverage (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [accessibility] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [api-crud] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-login] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-pages] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-register] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-core] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-lifecycle] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [error-benchmark] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) (push) Blocked by required conditions
Docker Tests (Consolidated) / Accessibility Tests (push) Blocked by required conditions
Docker Tests (Consolidated) / LLM Unit Tests (push) Blocked by required conditions
Docker Tests (Consolidated) / LLM Example Tests (push) Blocked by required conditions
Docker Tests (Consolidated) / Production Image Smoke Test (push) Blocked by required conditions
Docker Tests (Consolidated) / Infrastructure Tests (push) Blocked by required conditions
OSSF Scorecard / OSSF Security Scorecard Analysis (push) Waiting to run
OSV-Scanner (Scheduled) / scan-scheduled (push) Failing after 0s
Create Release / test-gate (push) Has been cancelled
Create Release / release-gate (push) Has been cancelled
Create Release / ci-gate (push) Has been cancelled
Create Release / version-check (push) Has been cancelled
Create Release / e2e-test-gate (push) Has been cancelled
Create Release / responsive-test-gate (push) Has been cancelled
Create Release / compat-test-gate (push) Has been cancelled
Create Release / compose-integration-gate (push) Has been cancelled
Create Release / vulture-gate (push) Has been cancelled
Create Release / build (push) Has been cancelled
Create Release / provenance (push) Has been cancelled
Create Release / prerelease-docker (push) Has been cancelled
Create Release / publish-docker (push) Has been cancelled
Create Release / create-release (push) Has been cancelled
Create Release / cleanup-changelog (push) Has been cancelled
Create Release / trigger-pypi (push) Has been cancelled
Create Release / monitor-pypi (push) Has been cancelled
Create Release / Clean up orphan prerelease tags and signatures (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:08:55 +08:00

196 lines
8.3 KiB
YAML

name: Compose Integration Test
# Brings up the bundled docker-compose.yml end-to-end in CI and verifies the
# whole stack (searxng, ollama, local-deep-research) reaches a healthy state.
# This is the test that would have caught #3874 (broken cap_drop on searxng)
# before users hit it. See #3886 for the rationale.
#
# Cost: ~3-6 min per run depending on cache state. Runs only via the release
# pipeline (`release.yml` includes it as `compose-integration-gate`) and on
# manual dispatch. NOT inside `release-gate.yml`, because release-gate runs
# daily and the failure modes here (compose/image changes) are tied to
# actual release events, not time. NOT on pull_request — too expensive for
# per-PR feedback latency.
#
# Do not move this into release-gate's daily cron. Ongoing compose drift
# between releases is already covered by `compose-published-smoke.yml`,
# which runs weekly against main's compose.yml + the *published* Docker Hub
# image — the more meaningful drift to catch, since users pull the
# published image, not main's build override. PR #3962 tried adding this
# to the daily cron and was closed for that reason.
#
# Tests the docker-compose.yml as users actually run it — no test-only
# overrides. The model pre-pull was removed from the compose itself in this
# same PR so the stack starts in seconds rather than waiting on a multi-GB
# download that the test doesn't need.
on:
workflow_call:
workflow_dispatch:
permissions:
contents: read
jobs:
compose-up:
name: docker compose up + healthcheck
runs-on: ubuntu-latest
timeout-minutes: 25
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
with:
egress-policy: audit
- name: Checkout
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0
# Build the LDR image from the working tree and tag it as the name the
# bundled compose references. Compose then uses the local image instead
# of pulling the published one — so we test the current code path, not
# whatever's on Docker Hub.
- name: Build LDR image with the published tag
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0
with:
context: .
# Same target as docker-tests.yml's `ldr-prod` build, so its cache
# layers (scope=ldr-prod, populated by ci-gate during the same
# release pipeline) are reusable here. Explicit target guards
# against future Dockerfile reorderings making the default stage
# something other than `ldr`.
target: ldr
load: true
tags: localdeepresearch/local-deep-research:latest
# Read from both our own scope and the shared `ldr-prod` scope —
# whichever has the layers wins. Falls back to a fresh build if
# neither does (e.g. on a brand-new branch). Only write to our
# own scope so we don't poison the cross-workflow cache.
cache-from: |
type=gha,scope=compose-integration
type=gha,scope=ldr-prod
cache-to: type=gha,mode=max,scope=compose-integration
- name: Bring up the stack
# `docker compose up -d` pulls any image it doesn't have locally
# (default pull_policy: missing), so ollama and searxng are pulled
# inline. The LDR image is the locally-built one from the previous
# step — compose sees it's already present and uses it as-is.
# `--no-build` defends against a future docker-compose.yml change
# adding a `build:` directive — we want the image we tagged above,
# not a fresh build that bypasses the cache strategy.
run: docker compose up -d --no-build
- name: Wait for the stack to be healthy and serving
# Budget: 6 min covers cold daemon startup + LDR migrations + flask
# boot + slow CI runners. Without the model pull this completes in
# ~1-2 min on a warm runner.
#
# Container resolution: always via `docker compose ps -q <service>`
# so we don't couple to compose's `container_name:` values. If those
# drift, this still works.
run: |
set -euo pipefail
deadline=$(( $(date +%s) + 360 ))
cid_for() { docker compose ps -q "$1" 2>/dev/null || true; }
status() {
local cid=$1
[ -n "$cid" ] || { echo missing; return; }
docker inspect -f "{{if .State.Health}}{{.State.Health.Status}}{{else}}{{.State.Status}}{{end}}" "$cid" 2>/dev/null || echo missing
}
while :; do
now=$(date +%s)
if [ "$now" -ge "$deadline" ]; then
echo "::error::Timed out after 6 min waiting for stack to be healthy"
docker compose ps
exit 1
fi
ollama_id=$(cid_for ollama)
searxng_id=$(cid_for searxng)
ldr_id=$(cid_for local-deep-research)
ollama_h=$(status "$ollama_id")
searxng_h=$(status "$searxng_id")
ldr_h=$(status "$ldr_id")
echo "[$(date -u +%H:%M:%S)] ollama=$ollama_h searxng=$searxng_h ldr=$ldr_h"
# All three services have healthchecks: ollama and searxng via
# docker-compose.yml, LDR via the Dockerfile (HEALTHCHECK at
# Dockerfile:306, probing /api/v1/health). status() returns the
# health status when one is defined, so require "healthy" for
# all three — strictly stronger signal than "running".
if [ "$ollama_h" = "healthy" ] && [ "$searxng_h" = "healthy" ] && [ "$ldr_h" = "healthy" ]; then
echo "All services healthy."
break
fi
# Fail fast on any container exiting non-zero rather than burning
# the whole 6 min budget.
for svc in ollama searxng local-deep-research; do
cid=$(cid_for "$svc")
[ -n "$cid" ] || continue
s=$(docker inspect -f '{{.State.Status}}' "$cid" 2>/dev/null || true)
if [ "$s" = "exited" ] || [ "$s" = "dead" ]; then
echo "::error::Container for service $svc has exited"
exit 1
fi
done
sleep 10
done
- name: Probe LDR HTTP endpoint
# Avoids `curl ... | grep` so we don't need pipefail to surface curl
# failures — the HTTP code is captured directly via -w and checked
# with a case statement. `|| echo "000"` is the sentinel for true
# network failures (connection refused, DNS, etc.) so we can log
# on retry.
#
# Deliberately no `-f`: with `-f`, curl exits non-zero on HTTP
# 4xx/5xx AND suppresses -w output — which would collapse "404",
# "503", and network-error all into "000" and erase the most
# interesting failure signal (LDR up but serving an error page).
# Without -f, every HTTP response gives us its real code; only
# true network failures fall through to "000".
run: |
set -euo pipefail
for i in $(seq 1 30); do
code=$(curl -sS -o /dev/null -w "%{http_code}" http://localhost:5000/ 2>/dev/null || echo "000")
case "$code" in
200|301|302|303|307|308)
echo "LDR is serving on :5000 (HTTP $code)"
exit 0
;;
esac
echo "Waiting for LDR HTTP (attempt $i/30, last code: $code)..."
sleep 5
done
echo "::error::LDR HTTP probe failed after ~150s"
exit 1
- name: Dump compose state and logs
if: always()
run: |
echo "::group::docker compose ps"
docker compose ps || true
echo "::endgroup::"
for svc in ollama searxng local-deep-research; do
echo "::group::$svc logs"
docker compose logs --no-color --tail=500 "$svc" || true
echo "::endgroup::"
done
- name: Tear down
if: always()
run: docker compose down -v --remove-orphans