Files
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

258 lines
11 KiB
YAML

name: Compose Published-Image Smoke
# Tests "main's docker-compose.yml + the currently-published Docker Hub image"
# end-to-end. Catches drift between compose changes that have landed on main
# and the image artefact that users actually pull when they follow the README
# quickstart:
#
# curl -O .../docker-compose.yml && docker compose up -d
#
# This complements the release-pipeline test in compose-integration-test.yml,
# which builds the image from the working tree (validating "this code's
# compose with this code's image") and runs only at release time + manual
# dispatch. That test cannot catch the drift case — the published image
# always lags main between releases.
#
# Triggers: weekly schedule + manual dispatch only. No pull_request trigger
# because PRs don't change the published image — by definition this test
# can only fail on drift that's already landed on main.
#
# On failure, opens a tracking issue (or comments on the existing one) so a
# scheduled-job failure isn't lost in the noise.
on:
workflow_dispatch:
schedule:
# 05:00 UTC every Monday — offset from release-gate's 02:00 daily cron
# to avoid runner contention. Weekly cadence is enough since the failure
# modes are slow-moving (compose-vs-published-image drift) and the test
# itself burns CI minutes.
- cron: '0 5 * * 1'
permissions:
contents: read
jobs:
compose-up:
name: docker compose up + healthcheck (published image)
runs-on: ubuntu-latest
timeout-minutes: 25
permissions:
contents: read
issues: write # for the failure-issue step
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
with:
egress-policy: audit
- name: Checkout (for docker-compose.yml on default branch)
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
# Pull all three services from their registries, including the LDR
# image. This is the whole point of this workflow — exercise the
# exact artefact users get from `docker compose up -d`.
- name: Pull all images
run: docker compose pull
- name: Bring up the stack
# `--no-build` defends against a future docker-compose.yml change
# adding a `build:` directive — this workflow specifically tests the
# *published* image artifact, not a fresh build from source.
run: docker compose up -d --no-build
- name: Wait for the stack to be healthy and serving
# Same wait logic as compose-integration-test.yml. 6 min budget
# covers cold daemon startup + LDR migrations + flask boot.
# Container resolution: always via `docker compose ps -q <service>`
# so we don't couple to compose's `container_name:` values.
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.
if [ "$ollama_h" = "healthy" ] && [ "$searxng_h" = "healthy" ] && [ "$ldr_h" = "healthy" ]; then
echo "All services healthy."
break
fi
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
# Capture image digests for both the workflow log (every run, via
# the cat below) and the auto-failure-issue body (failure runs
# only, via the next step). Always-logging gives us audit and
# bisection signal — "what was the published image SHA when this
# passed on date X?" — without needing to re-run the workflow.
# Resolve every container by service name through `docker compose
# ps -q` so we don't depend on `container_name:` values.
cid_for() { docker compose ps -q "$1" 2>/dev/null || true; }
{
echo "## Image digests"
for svc in ollama searxng local-deep-research; do
cid=$(cid_for "$svc")
if [ -n "$cid" ]; then
img=$(docker inspect -f '{{.Image}}' "$cid" 2>/dev/null || echo missing)
echo "- $svc: image=$img container=$cid"
else
echo "- $svc: container=missing"
fi
done
} > /tmp/digests.md
echo "::group::Image digests"
cat /tmp/digests.md
echo "::endgroup::"
- name: Open or update failure-tracking issue
if: failure() && github.event_name == 'schedule'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REPO: ${{ github.repository }}
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
run: |
set -euo pipefail
# Stable title prefix so we dedup across weeks. If an issue with
# this title is already open, comment on it; otherwise create a
# fresh one. We don't auto-close on success — let a human triage.
TITLE="[compose-published-smoke] published-image drift detected"
DATE=$(date -u +%Y-%m-%d)
DIGESTS=$(cat /tmp/digests.md 2>/dev/null || echo "(digests not captured)")
BODY=$(cat <<MARKDOWN
The weekly **published-image smoke test** failed on $DATE.
This usually means \`main\`'s \`docker-compose.yml\` and the currently-published
\`localdeepresearch/local-deep-research:latest\` on Docker Hub have drifted —
either a compose change landed without an image republish, or the
published image (or one of its dependencies) regressed.
**Workflow run:** $RUN_URL
**What to check first:**
- Compose changes since the last release: \`git log --oneline <last-release-tag>..main -- docker-compose.yml\`
- Whether the LDR image needs a republish (cut a release, or the image was repointed)
- Upstream image regressions: ollama, searxng image digests below
$DIGESTS
*This issue is auto-managed by \`.github/workflows/compose-published-smoke.yml\`.
Subsequent failures will add comments here. Close it when the underlying drift is fixed
and a successful run rolls in.*
MARKDOWN
)
# Find existing open issue with the same title
NUM=$(gh issue list -R "$REPO" \
--state open \
--search "in:title \"[compose-published-smoke] published-image drift detected\"" \
--json number,title \
--jq '.[0].number // empty' \
--limit 1)
if [ -n "$NUM" ]; then
echo "Commenting on existing issue #$NUM"
# `--body=` (= form) prevents gh from interpreting body content
# as flags if it ever starts with `-`. Same below.
gh issue comment "$NUM" -R "$REPO" --body="$BODY"
else
echo "Creating new issue"
gh issue create -R "$REPO" \
--title="$TITLE" \
--label="bug,docker,ci-cd" \
--body="$BODY"
fi
- name: Tear down
# `|| true` so a teardown flake (daemon hiccup, hung container) can't
# flip the job to failure() after the smoke test itself passed —
# which would falsely trigger the auto-issue step. CI runners are
# ephemeral, so any leftover containers/volumes vanish with the
# runner regardless.
if: always()
run: docker compose down -v --remove-orphans || true