ci(replatform): tail Harper's internal log (not just stdout) on failure #15
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Replatform Parity | |
| # Regression gate for the Harper replatform (app/). Scoped to the replatform | |
| # branch only — does not run on the team's normal PRs. Builds the Docusaurus | |
| # site (ground truth), stands up Harper with the app, ingests the corpus, and | |
| # runs the parity harness in --strict mode. | |
| on: | |
| push: | |
| branches: [replatform] | |
| pull_request: | |
| branches: [replatform] | |
| concurrency: | |
| group: replatform-parity-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| parity: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: actions/setup-node@v6 | |
| with: | |
| node-version: '24' | |
| - name: Build Docusaurus (ground truth) | |
| run: | | |
| npm ci | |
| npm run build | |
| - name: Install app deps + Harper | |
| run: | | |
| npm --prefix app ci | |
| npm install -g harper | |
| - name: Typecheck | |
| run: npm --prefix app run typecheck | |
| - name: Unit tests | |
| run: npm --prefix app test | |
| - name: Install & start Harper | |
| env: | |
| TC_AGREEMENT: 'yes' | |
| HDB_ADMIN_USERNAME: HDB_ADMIN | |
| HDB_ADMIN_PASSWORD: ${{ github.run_id }}-parity | |
| ROOTPATH: ${{ runner.temp }}/hdb | |
| HTTP_PORT: '9936' | |
| OPERATIONSAPI_NETWORK_PORT: '9935' | |
| # Enables the /admin/dev-login affordance so the integration + e2e | |
| # tests can establish an admin session without the Google round-trip. | |
| ADMIN_DEV_LOGIN: 'true' | |
| # Trust X-Forwarded-For so the chat tests can isolate their per-IP quota | |
| # (this runner is a trusted environment). | |
| CHAT_TRUST_PROXY: 'true' | |
| # Generation model for chat answers, the multi-turn condenser, and the | |
| # faithfulness monitor. OPTIONAL: without the ANTHROPIC_API_KEY repo | |
| # secret the server runs the deterministic dev stub — chat still streams, | |
| # but the model-gated cache tests self-skip and the grounding eval's | |
| # multi-turn cases exercise a no-op condenser. Add the secret to run | |
| # those for real (each CI run then makes a modest number of API calls). | |
| ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} | |
| # Embedding model for @embed / the semantic lane. Requires the | |
| # GEMINI_API_KEY repo secret; HARPER_CONFIG merges the models section | |
| # into the instance config at startup. Without it, @embed writes fail | |
| # and ingest aborts (the @embed ingest-time coupling — see #1594). | |
| GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }} | |
| HARPER_CONFIG: >- | |
| {"models":{"embedding":{"gemini-embedding-001":{"backend":"openai", | |
| "baseUrl":"https://generativelanguage.googleapis.com/v1beta/openai/", | |
| "apiKey":"${GEMINI_API_KEY}","model":"gemini-embedding-001"}}}} | |
| run: | | |
| harper install --TC_AGREEMENT=yes --HDB_ADMIN_USERNAME="$HDB_ADMIN_USERNAME" \ | |
| --HDB_ADMIN_PASSWORD="$HDB_ADMIN_PASSWORD" --ROOTPATH="$ROOTPATH" \ | |
| --HTTP_PORT=9936 --OPERATIONSAPI_NETWORK_PORT=9935 | |
| # Run the app as a COMPONENT — `harper run .` loads app/ (its custom | |
| # resources: /Ingest, /api/*, /admin/*), whereas `harper start` boots a | |
| # bare instance with none of them (so ingest 404'd). Backgrounded so the | |
| # later steps run against it; env (incl. the API keys) is inherited. | |
| (cd app && ROOTPATH="$ROOTPATH" nohup harper run . > "$ROOTPATH/harper-run.log" 2>&1 &) | |
| for i in $(seq 1 45); do | |
| curl -sf -o /dev/null "http://127.0.0.1:9936/" && break || sleep 2 | |
| done | |
| curl -s -o /dev/null -w "Harper up: HTTP %{http_code} on /\n" "http://127.0.0.1:9936/" || true | |
| # Fail fast (with the app log) if the component didn't register its routes: | |
| # a 404 on /Ingest means the app didn't load; any other code means it did. | |
| ingest_code=$(curl -s -o /dev/null -w '%{http_code}' "http://127.0.0.1:9936/Ingest") | |
| echo "/Ingest probe: HTTP $ingest_code" | |
| if [ "$ingest_code" = "404" ]; then | |
| echo "::error::/Ingest 404 — app component did not load its routes" | |
| tail -80 "$ROOTPATH/harper-run.log" || true | |
| exit 1 | |
| fi | |
| - name: Ingest corpus | |
| env: | |
| HARPER_CLI_USERNAME: HDB_ADMIN | |
| HARPER_CLI_PASSWORD: ${{ github.run_id }}-parity | |
| run: node app/scripts/ingest.ts --target http://127.0.0.1:9936 | |
| # Surface the server-side error when a step above fails (e.g. an ingest 500 | |
| # from an @embed write when GEMINI_API_KEY is missing) instead of leaving | |
| # only the client's opaque "500 after 5 retries". | |
| - name: Harper server log (on failure) | |
| if: failure() | |
| run: | | |
| echo "=== harper run stdout ===" | |
| tail -40 "${{ runner.temp }}/hdb/harper-run.log" 2>/dev/null || echo "(none)" | |
| echo "=== Harper internal log(s) — the real @embed / request errors ===" | |
| find "${{ runner.temp }}/hdb" -name '*.log' -not -name harper-run.log \ | |
| -exec sh -c 'echo "── $1 ──"; tail -120 "$1"' _ {} \; 2>/dev/null || echo "(no logs found)" | |
| - name: Integration tests | |
| env: | |
| HARPER_TARGET: http://127.0.0.1:9936 | |
| HARPER_CLI_USERNAME: HDB_ADMIN | |
| HARPER_CLI_PASSWORD: ${{ github.run_id }}-parity | |
| run: npm --prefix app run test:integration | |
| - name: Parity gate (strict) | |
| # Also records a ParityRun row for the admin Validation trend — needs the | |
| # admin credentials, or recordMetric() silently no-ops. | |
| env: | |
| HARPER_CLI_USERNAME: HDB_ADMIN | |
| HARPER_CLI_PASSWORD: ${{ github.run_id }}-parity | |
| run: node app/scripts/parity.ts --strict --target http://127.0.0.1:9936 | |
| - name: Search relevance gate | |
| # Keyword-primary ranking makes the eval deterministic (MRR 0.907, | |
| # stable across rebuilds), so a real floor holds. 0.85 leaves headroom | |
| # for golden-set edits without flaking. Also records an EvalRun row. | |
| env: | |
| HARPER_CLI_USERNAME: HDB_ADMIN | |
| HARPER_CLI_PASSWORD: ${{ github.run_id }}-parity | |
| run: node app/scripts/search-eval.ts --min-mrr 0.85 --target http://127.0.0.1:9936 | |
| - name: Chat grounding eval | |
| # Retrieval-only (retrieveOnly) — no model key or quota needed. Gates that | |
| # chat retrieval still surfaces the right doc page for NL questions. | |
| run: node app/scripts/chat-grounding.ts --min-recall 0.6 --target http://127.0.0.1:9936 | |
| - name: Install Playwright browser | |
| run: npx --prefix app playwright install --with-deps chromium | |
| - name: E2E tests | |
| env: | |
| HARPER_TARGET: http://127.0.0.1:9936 | |
| HARPER_CLI_USERNAME: HDB_ADMIN | |
| HARPER_CLI_PASSWORD: ${{ github.run_id }}-parity | |
| run: npm --prefix app run test:e2e | |
| - name: Upload parity report | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: parity-report | |
| path: app/parity-report.json |