GitHub App Integration
Overview
This document describes the implementation of GitHub App integration for Anchorpipe, allowing users to install the app on their repositories and receive webhook events for test runs.
Story: ST-301
Status: Implemented
Priority: P0
Features
- GitHub App Installation: Users can install the anchorpipe GitHub App on their repositories
- Webhook Event Handling: Automatically receives and processes installation, uninstallation, and repository events
- Installation Management: API endpoints for listing and managing installations
- Audit Logging: All installation events are logged for security monitoring
- Signature Verification: Webhook signatures are verified using HMAC-SHA256
Architecture
Flow Diagram
GitHub → Webhook Event → Signature Verification → Event Processing → Database Update → Audit Log
Components
-
Webhook Handler (
/api/webhooks/github-app)- Receives GitHub App webhook events
- Verifies webhook signatures
- Processes installation/uninstallation events
- Updates installation records
-
GitHub App Service (
github-app-service.ts)- Manages installation lifecycle (create, update, delete)
- Provides functions for querying installations
- Handles repository list updates
-
Installation API (
/api/github-app/installations)- Lists all installations
- Gets installation by ID
- Filters by account login
- Uninstalls installations (DELETE endpoint)
- Checks installation health
-
Database Schema (
GitHubAppInstallation)- Stores installation metadata
- Tracks repository access
- Records suspension status
-
Test Report Parsing (
test-report-parsers)- Automatically parses test reports from workflow artifacts
- Supports Jest, PyTest, Playwright, and JUnit formats
- Converts to standardized ingestion format
Setup
1. Create GitHub App
- Go to GitHub Settings → Developer settings → GitHub Apps
- Click "New GitHub App"
- Fill in:
- GitHub App name: Anchorpipe (or your app name)
- Homepage URL:
https://anchorpipe.dev - Webhook URL:
https://api.anchorpipe.dev/api/webhooks/github-app - Webhook secret: Generate a secure random string (store in
GITHUB_APP_WEBHOOK_SECRET) - Permissions:
- Metadata: Read-only
- Contents: Read-only (if needed for test file access)
- Pull requests: Read-only (if needed for PR comments)
- Subscribe to events:
installationinstallation_repositories
- Click "Create GitHub App"
- Note the App ID and generate a Private key (store securely)
2. Configure Environment Variables
Add the following to your .env file:
# GitHub App Configuration
GITHUB_APP_ID=your_app_id
GITHUB_APP_WEBHOOK_SECRET=your_webhook_secret
GITHUB_APP_PRIVATE_KEY=your_private_key_pem
3. Run Database Migration
npx prisma migrate dev --name add_github_app_installations --schema libs/database/prisma/schema.prisma
4. Configure Webhook in GitHub
- Go to your GitHub App settings
- Navigate to "Webhooks"
- Ensure the webhook URL is set to:
https://api.anchorpipe.dev/api/webhooks/github-app - Verify the webhook secret matches
GITHUB_APP_WEBHOOK_SECRET
Usage
Installing the App
- Users navigate to the GitHub App installation page
- Click "Install" on the desired repository or organization
- GitHub sends an
installation.createdwebhook event - The webhook handler processes the event and creates an installation record
Listing Installations
# Get all installations
curl -H "Authorization: Bearer $TOKEN" \
https://api.anchorpipe.dev/api/github-app/installations
# Get installations for a specific account
curl -H "Authorization: Bearer $TOKEN" \
"https://api.anchorpipe.dev/api/github-app/installations?account=testuser"
# Get a specific installation
curl -H "Authorization: Bearer $TOKEN" \
https://api.anchorpipe.dev/api/github-app/installations/123456
# Check installation health
curl -H "Authorization: Bearer $TOKEN" \
https://api.anchorpipe.dev/api/github-app/installations/123456/health
# Uninstall an installation
curl -X DELETE -H "Authorization: Bearer $TOKEN" \
https://api.anchorpipe.dev/api/github-app/installations/123456
# Update repository selection
curl -X PUT -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"repositoryIds": [123456, 789012]}' \
https://api.anchorpipe.dev/api/github-app/installations/123456/repositories
# Refresh installation permissions
curl -X POST -H "Authorization: Bearer $TOKEN" \
https://api.anchorpipe.dev/api/github-app/installations/123456/permissions/refresh
Webhook Events
The app handles the following webhook events:
-
installation- Installation lifecycle events (created, deleted, suspended, unsuspended, new_permissions_accepted) -
installation.created: New installation created -
installation.deleted: Installation removed -
installation.suspend: Installation suspended -
installation.unsuspend: Installation unsuspended -
installation_repositories.added: Repositories added to installation -
installation_repositories.removed: Repositories removed from installation
API Reference
GET /api/github-app/installations
List all GitHub App installations.
Query Parameters:
account(optional): Filter by account login
Response:
{
"installations": [
{
"id": "uuid",
"installationId": "123456",
"accountLogin": "testuser",
"accountType": "User",
"repositoryCount": 2,
"suspendedAt": null,
"createdAt": "2025-11-09T00:00:00Z"
}
]
}
GET /api/github-app/installations/[installationId]
Get a specific GitHub App installation.
Response:
{
"installation": {
"id": "uuid",
"installationId": "123456",
"accountId": "789",
"accountType": "User",
"accountLogin": "testuser",
"targetType": "User",
"targetId": null,
"repositoryIds": ["1", "2"],
"permissions": {
"metadata": "read",
"contents": "read"
},
"events": ["push", "pull_request"],
"suspendedAt": null,
"suspendedBy": null,
"suspendedReason": null,
"createdAt": "2025-11-09T00:00:00Z",
"updatedAt": "2025-11-09T00:00:00Z"
},
"permissions": {
"valid": true,
"missing": [],
"warnings": []
}
}
DELETE /api/github-app/installations/[installationId]
Uninstall a GitHub App installation. This removes the installation from the database and clears any cached tokens.
Response:
{
"message": "Installation uninstalled successfully",
"installationId": "123456"
}
Status Codes:
200: Installation uninstalled successfully401: Unauthorized404: Installation not found500: Internal server error
GET /api/github-app/installations/[installationId]/health
Check health of a GitHub App installation. Performs comprehensive checks to verify the installation is functioning correctly.
Response:
{
"healthy": true,
"installationId": "123456",
"accountLogin": "testuser",
"checks": {
"exists": { "status": "pass" },
"notSuspended": { "status": "pass" },
"tokenGeneration": { "status": "pass" },
"permissions": {
"status": "pass",
"details": {
"valid": true,
"missing": [],
"warnings": []
}
},
"repositoryAccess": {
"status": "pass",
"accessibleRepos": 2
}
},
"summary": "Installation is healthy"
}
Status Codes:
200: Installation is healthy401: Unauthorized503: Installation has critical issues (unhealthy)500: Internal server error
Health Checks:
- exists: Installation exists in database
- notSuspended: Installation is not suspended
- tokenGeneration: Can generate installation access tokens (GitHub API access)
- permissions: Installation has required permissions
- repositoryAccess: Can access repositories (if repositories are configured)
PUT /api/github-app/installations/[installationId]/repositories
Update repository selection for a GitHub App installation. This endpoint allows you to add or remove repositories from an installation when repository_selection: 'selected'.
Request Body:
{
"repositoryIds": [123456, 789012, 345678]
}
Response:
{
"message": "Repository selection updated successfully",
"installationId": "123456",
"repositoryCount": 3
}
Status Codes:
200: Repository selection updated successfully400: Invalid request (missing or invalid repositoryIds)401: Unauthorized404: Installation not found500: Internal server error
Notes:
- This endpoint updates the repository selection via GitHub API
- The database record is automatically updated
- Repositories are automatically synced to the local database
- An audit log entry is created for the update
POST /api/github-app/installations/[installationId]/permissions/refresh
Refresh and validate installation permissions from GitHub API. This endpoint fetches the latest permissions from GitHub and validates them against required permissions.
Response:
{
"message": "Permissions refreshed successfully",
"installationId": "123456",
"validation": {
"valid": true,
"missing": [],
"warnings": []
}
}
Status Codes:
200: Permissions refreshed successfully401: Unauthorized404: Installation not found500: Internal server error
Notes:
- This endpoint fetches current permissions from GitHub API
- The database record is automatically updated with latest permissions
- Permissions are validated against required permissions
- An audit log entry is created for the refresh
- Webhook events for
new_permissions_acceptedautomatically trigger permission validation
Test Report Parsing
When workflow runs or check runs complete, the GitHub App integration automatically:
- Downloads test artifacts from the workflow run
- Detects the test framework from artifact names (Jest, PyTest, Playwright, JUnit, etc.)
- Parses test reports using the appropriate parser:
- JUnit XML - Parsed using fast-xml-parser
- Jest JSON - Native JSON parsing
- PyTest JSON - Native JSON parsing
- Playwright JSON - Native JSON parsing
- Converts to standardized format - All test reports are converted to the Anchorpipe ingestion format
- Submits to ingestion service - Parsed test results are submitted via the ingestion API
Supported Test Report Formats
The following test report formats are automatically parsed:
- Jest (
jest) - JavaScript/TypeScript test results (JSON format) - PyTest (
pytest) - Python test results (JSON format) - Playwright (
playwright) - End-to-end test results (JSON format) - JUnit (
junit) - Java/XML test results (XML format)
Framework Detection
Frameworks are detected from artifact names using pattern matching:
- Files containing
junitor ending in.xml→ JUnit - Files containing
jest→ Jest - Files containing
pytest→ PyTest - Files containing
playwright→ Playwright
Error Handling
If a test report cannot be parsed:
- The error is logged for debugging
- Other artifacts continue to be processed
- The workflow run ingestion continues with successfully parsed reports
Security
Webhook Signature Verification
All webhook events are verified using HMAC-SHA256 signatures:
- GitHub computes
HMAC-SHA256(body, webhook_secret) - Sends signature in
X-Hub-Signature-256header - Server verifies signature before processing events
Authentication
All API endpoints require authentication via session cookies.
Testing
Unit Tests
Run unit tests for the GitHub App service:
npm test -- github-app-service.test.ts
Integration Tests
To test webhook handling:
- Use GitHub's webhook testing tool
- Send test events to the webhook endpoint
- Verify installation records are created/updated
Troubleshooting
Webhook Not Received
- Verify webhook URL is correct in GitHub App settings
- Check webhook secret matches
GITHUB_APP_WEBHOOK_SECRET - Ensure webhook endpoint is publicly accessible
- Check server logs for signature verification errors
Installation Not Created
- Verify webhook signature is valid
- Check database connection
- Review audit logs for errors
- Ensure Prisma migration has been run
Signature Verification Fails
- Verify
GITHUB_APP_WEBHOOK_SECRETmatches GitHub App settings - Check webhook payload is not modified (e.g., by proxy)
- Ensure raw request body is used for signature verification