HH-446: restore auditable quality gates from current main (#102)
* HH-446: restore auditable quality gates * test(HH-446): restore help center smoke contract --------- Co-authored-by: Rogee <rogee@ipao.vip>
This commit is contained in:
+166
-2
@@ -41,6 +41,9 @@ jobs:
|
||||
run: python -m pip install --require-hashes -r .github/requirements-browser-harness.txt
|
||||
- name: Test Prometheus alert rules
|
||||
run: docker run --rm --entrypoint promtool -v "$PWD/backend/configs:/configs:ro" prom/prometheus:v3.5.0@sha256:63805ebb8d2b3920190daf1cb14a60871b16fd38bed42b857a3182bc621f4996 test rules /configs/prometheus_alerts_test.yml
|
||||
- name: Test quality gate failure contracts
|
||||
working-directory: backend
|
||||
run: python3 scripts/quality_gate_test.py
|
||||
- name: Test
|
||||
working-directory: backend
|
||||
env:
|
||||
@@ -55,7 +58,30 @@ jobs:
|
||||
browser-harness <<'PY'
|
||||
print(page_info())
|
||||
PY
|
||||
go test ./internal/... ./pkg/... ./cmd/...
|
||||
GOCHAT_RACE=false \
|
||||
GOCHAT_COLD_CACHE=true \
|
||||
GOCHAT_MAX_DURATION_SECONDS=480 \
|
||||
GOCHAT_QUALITY_DIR="$GITHUB_WORKSPACE/backend/quality/sqlite/cold-cache" \
|
||||
./scripts/ci_quality_gate.sh
|
||||
- name: Test critical race suites
|
||||
working-directory: backend
|
||||
env:
|
||||
GOCHAT_TEST_DB: sqlite
|
||||
run: |
|
||||
GOCHAT_RACE=true \
|
||||
GOCHAT_COLD_CACHE=true \
|
||||
GOCHAT_TEST_PACKAGES='./internal/ws ./internal/handler/ws ./internal/worker ./internal/channel/...' \
|
||||
GOCHAT_TEST_TIMEOUT=180s \
|
||||
GOCHAT_MAX_DURATION_SECONDS=180 \
|
||||
GOCHAT_QUALITY_DIR="$GITHUB_WORKSPACE/backend/quality/sqlite/race" \
|
||||
./scripts/ci_quality_gate.sh
|
||||
- name: Upload SQLite quality evidence
|
||||
if: always()
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
with:
|
||||
name: backend-quality-sqlite
|
||||
path: backend/quality/sqlite
|
||||
if-no-files-found: error
|
||||
- name: Build
|
||||
working-directory: backend
|
||||
run: go build ./...
|
||||
@@ -105,6 +131,58 @@ jobs:
|
||||
GOCHAT_TEST_DB: postgres
|
||||
GOCHAT_TEST_DB_URL: postgres://postgres:postgres@localhost:5432/gochat_test?sslmode=disable
|
||||
run: go test -count=1 -timeout 10m ./internal/... ./pkg/... ./cmd/...
|
||||
- name: Test controlled service race budget
|
||||
working-directory: backend
|
||||
env:
|
||||
GOCHAT_TEST_DB: postgres
|
||||
GOCHAT_TEST_DB_URL: postgres://postgres:postgres@localhost:5432/gochat_test?sslmode=disable
|
||||
run: |
|
||||
set -euo pipefail
|
||||
evidence="$GITHUB_WORKSPACE/backend/quality/postgres/service-race"
|
||||
mkdir -p "$evidence"
|
||||
start=$SECONDS
|
||||
set +e
|
||||
timeout 180s go test -race -count=1 -json -timeout 170s \
|
||||
-run '^(TestCaptainPreferenceService_(Create|Create_DuplicateAccount|Get|Get_NotFound|Update|Delete)|TestEnsureCaptainAgentBotBindingConcurrentCallsStayUnique|TestTagCreate7_Cov35)$' \
|
||||
./internal/service | tee "$evidence/test.jsonl"
|
||||
test_status=${PIPESTATUS[0]}
|
||||
set -e
|
||||
elapsed=$((SECONDS - start))
|
||||
python3 - "$evidence/test.jsonl" "$evidence/summary.json" "$test_status" "$elapsed" <<'PY'
|
||||
import json, sys
|
||||
|
||||
required = {
|
||||
"TestCaptainPreferenceService_Create",
|
||||
"TestCaptainPreferenceService_Create_DuplicateAccount",
|
||||
"TestCaptainPreferenceService_Get",
|
||||
"TestCaptainPreferenceService_Get_NotFound",
|
||||
"TestCaptainPreferenceService_Update",
|
||||
"TestCaptainPreferenceService_Delete",
|
||||
"TestEnsureCaptainAgentBotBindingConcurrentCallsStayUnique",
|
||||
"TestTagCreate7_Cov35",
|
||||
}
|
||||
results = {}
|
||||
with open(sys.argv[1], encoding="utf-8") as events:
|
||||
for line in events:
|
||||
event = json.loads(line)
|
||||
if event.get("Test") in required and event.get("Action") in {"pass", "fail", "skip"}:
|
||||
results[event["Test"]] = event["Action"]
|
||||
status, elapsed = int(sys.argv[3]), int(sys.argv[4])
|
||||
failures = [name for name in sorted(required) if results.get(name) != "pass"]
|
||||
report = {
|
||||
"status": "passed" if status == 0 and elapsed <= 180 and not failures else "failed",
|
||||
"duration_seconds": elapsed,
|
||||
"budget_seconds": 180,
|
||||
"test_exit_code": status,
|
||||
"required_tests": {name: results.get(name, "missing") for name in sorted(required)},
|
||||
}
|
||||
with open(sys.argv[2], "w", encoding="utf-8") as output:
|
||||
json.dump(report, output, indent=2, sort_keys=True)
|
||||
output.write("\n")
|
||||
print(json.dumps(report, sort_keys=True))
|
||||
if report["status"] != "passed":
|
||||
raise SystemExit(1)
|
||||
PY
|
||||
- name: Test production upload migrations
|
||||
working-directory: backend
|
||||
env:
|
||||
@@ -164,6 +242,13 @@ jobs:
|
||||
if empty:
|
||||
raise SystemExit(f"required E2E suites ran no passing subtests: {', '.join(empty)}")
|
||||
PY
|
||||
- name: Upload PostgreSQL quality evidence
|
||||
if: always()
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
with:
|
||||
name: backend-quality-postgres
|
||||
path: backend/quality/postgres
|
||||
if-no-files-found: error
|
||||
|
||||
frontend:
|
||||
name: Frontend Build
|
||||
@@ -184,8 +269,85 @@ jobs:
|
||||
- name: Build and verify artifacts
|
||||
run: pnpm --dir frontend test:build
|
||||
|
||||
frontend-smoke:
|
||||
name: Browser core smoke
|
||||
needs: [frontend]
|
||||
runs-on: ubuntu-latest
|
||||
services:
|
||||
postgres:
|
||||
image: pgvector/pgvector:pg16@sha256:ccc6e83d6e35e931dc7c5def2022729d5a6c370318d099181995567ff1fb4d6b
|
||||
env:
|
||||
POSTGRES_DB: gochat_test
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
ports:
|
||||
- 5432:5432
|
||||
options: >-
|
||||
--health-cmd "pg_isready -U postgres -d gochat_test"
|
||||
--health-interval 5s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
redis:
|
||||
image: redis:7.4.2-alpine@sha256:02419de7eddf55aa5bcf49efb74e88fa8d931b4d77c07eff8a6b2144472b6952
|
||||
ports:
|
||||
- 6379:6379
|
||||
options: >-
|
||||
--health-cmd "redis-cli ping"
|
||||
--health-interval 5s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
steps:
|
||||
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
||||
- uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5
|
||||
with:
|
||||
go-version: 1.25.13
|
||||
cache-dependency-path: backend/go.sum
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
||||
with:
|
||||
node-version: 20.19.5
|
||||
- uses: browser-actions/setup-chrome@48ad923757ca74d66703209fe939badbdf80f2f4 # v2
|
||||
id: chrome
|
||||
with:
|
||||
chrome-version: 152.0.7977.54
|
||||
- name: Test browser smoke failure contracts
|
||||
run: node backend/scripts/parity_frontend_browser_smoke.mjs --self-test
|
||||
- run: corepack enable && corepack prepare pnpm@10.2.0 --activate
|
||||
- run: pnpm install --frozen-lockfile
|
||||
- name: Run browser smoke
|
||||
env:
|
||||
CI: "true"
|
||||
GOCHAT_ENV: development
|
||||
GOCHAT_DATABASE_DSN: postgres://postgres:postgres@localhost:5432/gochat_test?sslmode=disable
|
||||
GOCHAT_REDIS_DSN: redis://localhost:6379
|
||||
GOCHAT_JWT_SECRET: gochat-smoke-jwt-secret-at-least-32-characters
|
||||
CHATWOOT_DIR: ${{ github.workspace }}/frontend
|
||||
GOCHAT_SMOKE_SEARCH_ENGINE: db
|
||||
GOCHAT_SMOKE_LOG_DIR: ${{ github.workspace }}/backend/.tmp/frontend-smoke
|
||||
GOCHAT_SMOKE_REPORT: ${{ github.workspace }}/docs/parity/frontend-smoke-report.md
|
||||
GOCHAT_SMOKE_CHROME: ${{ steps.chrome.outputs.chrome-path }}
|
||||
run: |
|
||||
mkdir -p "$GOCHAT_SMOKE_LOG_DIR"
|
||||
(cd backend && go run ./cmd/gochat serve >"$GOCHAT_SMOKE_LOG_DIR/gochat.log" 2>&1) &
|
||||
backend_pid=$!
|
||||
(cd frontend && VITE_API_HOST=http://127.0.0.1:3000 pnpm exec vite --host localhost --port 3036 >"$GOCHAT_SMOKE_LOG_DIR/vite.log" 2>&1) &
|
||||
frontend_pid=$!
|
||||
trap 'kill "$backend_pid" "$frontend_pid" 2>/dev/null || true' EXIT
|
||||
curl --fail --silent --show-error --retry 60 --retry-connrefused --retry-delay 1 \
|
||||
http://127.0.0.1:3000/health > /dev/null
|
||||
backend/scripts/parity_frontend_smoke.sh --browser-smoke
|
||||
- name: Upload browser smoke evidence
|
||||
if: always()
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
with:
|
||||
name: frontend-browser-smoke
|
||||
path: |
|
||||
backend/.tmp/frontend-smoke
|
||||
docs/parity/frontend-smoke-report.md
|
||||
if-no-files-found: error
|
||||
|
||||
security:
|
||||
name: Security (SAST/SCA)
|
||||
needs: [frontend-smoke]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
||||
@@ -221,6 +383,7 @@ jobs:
|
||||
production-smoke:
|
||||
name: Production image smoke
|
||||
if: github.event_name == 'pull_request' || startsWith(github.ref, 'refs/tags/v')
|
||||
needs: [frontend-smoke]
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
GOCHAT_IMAGE_REF: gochat:production-smoke
|
||||
@@ -308,7 +471,8 @@ jobs:
|
||||
container_id="$("${compose[@]}" ps -q "$service")"
|
||||
test "$(docker inspect --format '{{.State.Health.Status}}' "$container_id")" = healthy
|
||||
done
|
||||
curl -fsS "http://127.0.0.1:$GOCHAT_PORT/health" | grep -q '"status":"healthy"'
|
||||
curl -fsS "http://127.0.0.1:$GOCHAT_PORT/health" > "$RUNNER_TEMP/health.json"
|
||||
python3 backend/scripts/validate_health_json.py "$RUNNER_TEMP/health.json"
|
||||
curl -fsS "http://127.0.0.1:$GOCHAT_PORT/app" | grep -q '/assets/'
|
||||
curl -fsS "http://127.0.0.1:$GOCHAT_PORT/runtime-config.js" | grep -q 'window.__GOCHAT_CONFIG__'
|
||||
curl -fsS -o "$RUNNER_TEMP/favicon-32x32.png" "http://127.0.0.1:$GOCHAT_PORT/favicon-32x32.png"
|
||||
|
||||
Reference in New Issue
Block a user