name: GoChat CI/CD Pipeline on: pull_request: branches: [main] push: branches: [main] permissions: contents: read jobs: backend: name: Backend (SQLite) runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: go-version-file: backend/go.mod cache-dependency-path: backend/go.sum - uses: actions/setup-python@v5 with: python-version: "3.12" - uses: browser-actions/setup-chrome@v2 id: chrome - name: Install browser harness run: python -m pip install browser-harness==0.1.9 - name: Test working-directory: backend env: GOCHAT_TEST_DB: sqlite GOCHAT_BROWSER_SMOKE: "1" BU_CDP_URL: http://127.0.0.1:9222 run: | "${{ steps.chrome.outputs.chrome-path }}" --headless=new --no-sandbox --disable-dev-shm-usage --remote-debugging-port=9222 --user-data-dir="$RUNNER_TEMP/chrome" about:blank & chrome_pid=$! trap 'kill "$chrome_pid" 2>/dev/null || true; browser-harness --reload > /dev/null 2>&1 || true' EXIT curl --fail --silent --show-error --retry 10 --retry-connrefused --retry-delay 1 http://127.0.0.1:9222/json/version > /dev/null browser-harness <<'PY' print(page_info()) PY go test ./internal/... ./pkg/... ./cmd/... - name: Build working-directory: backend run: go build ./... - name: Vet working-directory: backend run: go vet ./... backend-postgres: name: Backend (PostgreSQL) runs-on: ubuntu-latest services: postgres: image: pgvector/pgvector:pg16 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 steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: go-version-file: backend/go.mod cache-dependency-path: backend/go.sum - name: Test auto-assignment concurrency 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 output="$RUNNER_TEMP/autoassignment-concurrency.json" go test -json ./internal/autoassignment -run '^TestAssignmentServiceOnlyOneConcurrentWorkerWinsPostgres$' -count=3 | tee "$output" grep -Eq '"Action":"pass".*"Test":"TestAssignmentServiceOnlyOneConcurrentWorkerWinsPostgres"' "$output" || { echo "PostgreSQL auto-assignment concurrency test did not run" >&2 exit 1 } - name: Test PostgreSQL E2E 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 output="$RUNNER_TEMP/postgres-e2e.json" go test -json -count=1 -timeout 10m ./tests/e2e/... | tee "$output" python3 - "$output" <<'PY' import json import sys required = { "TestAuthE2ESuite", "TestAccountCRUDE2ESuite", "TestConversationE2ESuite", "TestCRME2ESuite", "TestCSRFE2ETestSuite", "TestRBACE2ESuite", } results = {} with open(sys.argv[1], encoding="utf-8") as events: for line in events: event = json.loads(line) if event.get("Test") and event.get("Action") in {"pass", "skip", "fail"}: results[event["Test"]] = event["Action"] missing = sorted(suite for suite in required if results.get(suite) != "pass") passed_subtests = {suite: 0 for suite in required} skipped_subtests = {suite: 0 for suite in required} for test, action in results.items(): suite, separator, _ = test.partition("/") if separator and suite in required: if action == "pass": passed_subtests[suite] += 1 elif action == "skip": skipped_subtests[suite] += 1 empty = sorted(suite for suite, count in passed_subtests.items() if count == 0) for suite in sorted(required): print(f"{suite}: {passed_subtests[suite]} passed, {skipped_subtests[suite]} skipped") print( "E2E totals: " f"{sum(action == 'pass' for action in results.values())} passed, " f"{sum(action == 'skip' for action in results.values())} skipped" ) if missing: raise SystemExit(f"required E2E suites did not pass: {', '.join(missing)}") if empty: raise SystemExit(f"required E2E suites ran no passing subtests: {', '.join(empty)}") PY frontend: name: Frontend Build runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 20 - run: corepack enable - run: pnpm install --frozen-lockfile - name: ESLint representative JS/Vue files run: pnpm --dir frontend exec eslint app/javascript/dashboard/components-next/captain/assistant/AssistantPlayground.spec.js app/javascript/dashboard/components-next/captain/assistant/AssistantPlayground.vue - name: Prettier representative JS/TS/Vue files run: pnpm --dir frontend exec prettier --check app/javascript/dashboard/components-next/captain/assistant/AssistantPlayground.spec.js app/javascript/dashboard/components-next/captain/assistant/AssistantPlayground.vue vite.config.ts app/javascript/histoire.setup.ts - run: pnpm --dir frontend build