Automated Test Triggers API
Automated test triggers allow izri to run E2E tests automatically in response to GitHub push and pull request events. The system integrates with the existing webhook infrastructure and reports results back to GitHub via Commit Status Checks.
Overview
GitHub Push / PR Event
│
▼
Webhook Handler (apps/api — Hono route)
│
▼
testTriggers.dispatchPush / dispatchPR (tRPC)
│
├─ Evaluate trigger conditions (branch, file patterns)
├─ Deduplicate (same commit SHA + event type)
├─ Debounce (optional, configurable window)
│
▼
TestTriggerService.runTrigger
│
├─ Create test_runs record (status=PENDING)
└─ Post GitHub commit status: pending
│
▼
Test runner executes tests
│
testTriggers.reportResult
│
├─ Update test_runs record (PASSED / FAILED / ERROR)
└─ Post GitHub commit status: success / failure / error
tRPC Procedures
All procedures require authentication (protected).
testTriggers.dispatchPush
Dispatch a test trigger from a GitHub push payload.
Input:
| Field | Type | Required | Description |
|---|---|---|---|
projectId |
string (ULID) |
✅ | Target project |
payload |
GitHubPushPayload |
✅ | Raw push webhook payload |
configOverride |
TriggerConfig |
❌ | Override stored config for this call |
Output:
// Triggered
{ triggered: true; testRunId: string; reason: string }
// Not triggered
{ triggered: false; reason: string }
Example:
const result = await trpc.testTriggers.dispatchPush.mutate({
projectId: '01HX...',
payload: githubPushPayload,
configOverride: {
branches: ['main', 'develop'],
filePatterns: ['src/**/*.ts', 'packages/**'],
debounceMs: 5000,
prEvents: true,
},
})
testTriggers.dispatchPR
Dispatch a test trigger from a GitHub pull_request payload.
Only fires on actions: opened, synchronize, reopened.
Input:
| Field | Type | Required | Description |
|---|---|---|---|
projectId |
string (ULID) |
✅ | Target project |
payload |
GitHubPRPayload |
✅ | Raw pull_request webhook payload |
changedFiles |
string[] |
❌ | Files changed in the PR (from GitHub Files API) |
configOverride |
TriggerConfig |
❌ | Override stored config |
Output: Same shape as dispatchPush.
Example:
const result = await trpc.testTriggers.dispatchPR.mutate({
projectId: '01HX...',
payload: githubPRPayload,
changedFiles: ['src/api/handler.ts', 'packages/trpc/src/router.ts'],
})
testTriggers.reportResult
Update a test run's result and post the outcome to GitHub's Commit Status API.
Input:
| Field | Type | Required | Description |
|---|---|---|---|
testRunId |
string |
✅ | ID of the test run to update |
status |
'PASSED' | 'FAILED' | 'ERROR' |
✅ | Final outcome |
passedTests |
number |
❌ | Number of passing tests |
failedTests |
number |
❌ | Number of failing tests |
totalTests |
number |
❌ | Total test count |
error |
string |
❌ | Error message (for ERROR status) |
repoFullName |
string |
✅ | e.g. "org/repo" |
commitSha |
string |
✅ | Commit SHA to update status on |
githubToken |
string |
❌ | GitHub token with repo:status scope |
targetUrl |
string |
❌ | URL for the "Details" link in GitHub UI |
Output: { ok: true }
testTriggers.getTestRun
Retrieve a single test run by ID.
Input: { testRunId: string }
Output: Full TestRun record or null.
testTriggers.listTestRuns
List test runs for a project with optional filtering.
Input:
| Field | Type | Default | Description |
|---|---|---|---|
projectId |
string (ULID) |
— | Required |
limit |
number |
20 |
Max 100 |
status |
enum |
— | PENDING | RUNNING | PASSED | FAILED | ERROR |
type |
enum |
— | push | pr | manual |
Output: { runs: TestRun[] }
Trigger Configuration
Trigger conditions can be configured per project via the webhooks config (stored in webhook_configs) or overridden per-call with configOverride.
TriggerConfig shape
interface TriggerConfig {
/** Branches to watch on push events. Empty = all branches. */
branches: string[]
/**
* Glob patterns for changed files. Empty = any change triggers.
*
* Evaluated using minimatch with `dot: true`.
* Supports: `*`, `**`, `?`, `{a,b}` brace expansion, `[abc]` character classes.
*/
filePatterns: string[]
/** Trigger on PR opened/synchronize/reopened events. Default: true */
prEvents: boolean
/** Debounce window in ms. 0 = immediate. Max: 30,000 (30s). Default: 0 */
debounceMs: number
/** GitHub token with repo:status scope for posting commit statuses. */
githubToken?: string
}
Branch patterns
Exact string match against the branch name (after stripping refs/heads/).
branches: ["main", "develop", "release/v2"]
File patterns
Glob patterns evaluated using minimatch. Full glob syntax is supported:
| Pattern | Description | Example match |
|---|---|---|
* |
Any chars within one path segment | src/*.ts → src/index.ts |
** |
Zero or more path segments | src/**/*.ts → src/api/auth.ts |
? |
Any single character | src/?.ts → src/a.ts |
{a,b} |
Brace expansion | *.{ts,tsx} → Button.tsx |
[abc] |
Character class | src/[abc]*.ts → src/auth.ts |
Examples:
filePatterns: ["**/*.test.ts"] // any test file anywhere in the repo
filePatterns: ["src/**/*.e2e.ts"] // E2E tests under src/
filePatterns: ["packages/**", "apps/**"] // monorepo source changes
filePatterns: ["*.{json,yaml}"] // config files at repo root
filePatterns: ["src/**/*.{ts,tsx}"] // all TypeScript source files
filePatterns: [".github/**"] // CI/workflow changes
Debouncing & Deduplication
Deduplication
Before creating a test run, the system checks for an existing run scoped to:
- Same
projectId - Same
commitSha - Same event type (
pushorpull_request)
within the last 1 hour. Duplicate triggers are rejected with a descriptive reason.
Debouncing
When debounceMs > 0, rapid triggers for the same projectId + branch + eventType are coalesced: each new trigger resets the timer, and only the final event (after the quiet period) is executed.
This prevents flooding the test runner when developers push multiple commits quickly.
configOverride: {
debounceMs: 10_000, // wait 10s after last push before starting tests
}
GitHub Status Checks
When a githubToken is configured, the system automatically posts commit statuses at two points:
| Lifecycle point | State | Description |
|---|---|---|
| Test run created | pending |
"E2E tests are running…" |
reportResult with PASSED |
success |
"X/Y tests passed" |
reportResult with FAILED |
failure |
"X/Y tests failed" |
reportResult with ERROR |
error |
Error message (truncated to 140 chars) |
The default status context is izri/e2e. This can be customised per integration.
Required GitHub token scopes
repo:status— post commit statuses on public and private repos
Integration with Webhook System
The test trigger system extends the webhook pipeline (issue #22):
- GitHub sends a push/PR event to the Hono webhook endpoint (
apps/api) - The endpoint validates the HMAC-SHA256 signature and stores a
webhook_eventsrecord - The webhook service calls
testTriggers.dispatchPushortestTriggers.dispatchPR - Test trigger service evaluates conditions, creates a
test_runsrecord, and posts apendingGitHub status - An external test runner (CI agent / queue worker) picks up the pending run, executes tests, and calls
testTriggers.reportResult
Data Model
Test runs are stored in the test_runs table:
id VARCHAR(191) PRIMARY KEY -- ULID
project_id VARCHAR(191) NOT NULL
type VARCHAR(32) NOT NULL -- 'push' | 'pr' | 'manual'
status VARCHAR(32) NOT NULL -- 'PENDING' | 'RUNNING' | 'PASSED' | 'FAILED' | 'ERROR'
passed_tests INTEGER DEFAULT 0
failed_tests INTEGER DEFAULT 0
total_tests INTEGER DEFAULT 0
skipped_tests INTEGER DEFAULT 0
coverage REAL
duration INTEGER -- ms
error TEXT
logs TEXT
results JSONB -- includes commitSha for dedup
started_at TIMESTAMP
completed_at TIMESTAMP
created_at TIMESTAMP DEFAULT now()
user_id VARCHAR(191) NOT NULL -- triggering actor
Error Handling
- Signature invalid: rejected at webhook layer before reaching trigger service
- Unknown project:
dispatchPush/dispatchPRreturnstriggered: falsewith reason - Duplicate trigger: returns
triggered: false— not an error - GitHub Status API failure: logged but does not fail the test run creation (best-effort)
- Debounced trigger: returns
triggered: falsewith debounce reason — the real trigger fires asynchronously after the window
Related
- Webhooks API — underlying event pipeline
packages/trpc/src/routers/testTriggers.ts— router implementationpackages/trpc/src/services/testTriggerService.ts— orchestration servicepackages/trpc/src/utils/testTriggers.ts— pure utility functionspackages/trpc/src/types/testTriggers.ts— type definitions