Form Monitor #5
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: Form Monitor | |
| # Weekly production check that the public lead-capture forms on scanopy.net | |
| # actually submit to Brevo and show their success state. These forms are the | |
| # inbound channel for the highest-value deals and have silently broken before | |
| # (June 2026: /commercial "Request a Quote" did nothing on click). | |
| # See README "Form Monitoring" for the sentinel convention and coverage. | |
| on: | |
| schedule: | |
| - cron: '0 6 * * 1' # Mondays 06:00 UTC | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| issues: write | |
| jobs: | |
| monitor: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 20 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version-file: .nvmrc | |
| # --ignore-scripts skips the heavy docs-site postinstall; the E2E suite | |
| # only needs @playwright/test. | |
| - name: Install dependencies | |
| run: npm ci --ignore-scripts | |
| - name: Install Playwright browser | |
| run: npx playwright install --with-deps chromium | |
| - name: Run form monitor suite against production | |
| run: npx playwright test | |
| - name: Upload Playwright report | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: playwright-report | |
| path: playwright-report/ | |
| retention-days: 30 | |
| - name: Create or update alert issue | |
| if: failure() | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const label = 'form-monitor'; | |
| const title = 'Form monitor: production lead form check failed'; | |
| const runUrl = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`; | |
| try { | |
| await github.rest.issues.getLabel({ ...context.repo, name: label }); | |
| } catch { | |
| await github.rest.issues.createLabel({ | |
| ...context.repo, | |
| name: label, | |
| color: 'd73a4a', | |
| description: 'Automated production form monitoring alerts', | |
| }); | |
| } | |
| const { data: open } = await github.rest.issues.listForRepo({ | |
| ...context.repo, | |
| state: 'open', | |
| labels: label, | |
| }); | |
| if (open.length > 0) { | |
| await github.rest.issues.createComment({ | |
| ...context.repo, | |
| issue_number: open[0].number, | |
| body: `Still failing as of the latest run: ${runUrl}`, | |
| }); | |
| } else { | |
| await github.rest.issues.create({ | |
| ...context.repo, | |
| title, | |
| labels: [label], | |
| body: [ | |
| `The weekly production form check failed: ${runUrl}`, | |
| '', | |
| 'A lead-capture form on scanopy.net may be silently broken — a prospect', | |
| 'filling it out could be getting no response. Treat as revenue-impacting.', | |
| '', | |
| 'The Playwright report (screenshots + traces) is attached to the run as an artifact.', | |
| 'Failure messages decode as follows:', | |
| '', | |
| '- `FORM DID NOT SUBMIT` — clicking submit fired no request to Brevo.', | |
| ' Broken page JS or unwired handler (the June 2026 failure mode).', | |
| '- `BREVO HTTP ERROR <status>` — the request fired but Brevo returned an error.', | |
| ' Check whether the Brevo form still exists / the endpoint URL changed.', | |
| '- `BREVO RESPONSE NOT JSON` — endpoint is not behaving like a Brevo form', | |
| ' (redirect, HTML error page, moved URL).', | |
| '- `BREVO REJECTED SUBMISSION` — 2xx but `success:false`; body included in the message.', | |
| '- A success-UI assertion failure — Brevo accepted the data but the user-visible', | |
| ' confirmation never appeared.', | |
| '', | |
| 'See the "Form Monitoring" section of the README.', | |
| ].join('\n'), | |
| }); | |
| } |