|
| 1 | +# GitHub Webhook Integration Setup and Testing Guide |
| 2 | + |
| 3 | +## Overview |
| 4 | + |
| 5 | +The Topcoder Review API includes a secure GitHub webhook integration that receives webhook events from GitHub repositories, validates them using HMAC-SHA256 signature verification, and stores them in the database for audit and future processing. |
| 6 | + |
| 7 | +## Table of Contents |
| 8 | + |
| 9 | +1. [Quick Start](#quick-start) |
| 10 | +2. [Environment Setup](#environment-setup) |
| 11 | +3. [GitHub Repository Configuration](#github-repository-configuration) |
| 12 | +4. [Local Development Setup](#local-development-setup) |
| 13 | +5. [Testing the Integration](#testing-the-integration) |
| 14 | +6. [API Endpoint Reference](#api-endpoint-reference) |
| 15 | +7. [Database Schema](#database-schema) |
| 16 | +8. [Security Considerations](#security-considerations) |
| 17 | +9. [Troubleshooting](#troubleshooting) |
| 18 | +10. [Monitoring and Maintenance](#monitoring-and-maintenance) |
| 19 | + |
| 20 | +## Quick Start |
| 21 | + |
| 22 | +For immediate setup, follow these steps: |
| 23 | + |
| 24 | +1. Generate a secure webhook secret |
| 25 | +2. Configure environment variables |
| 26 | +3. Set up GitHub webhook in repository settings |
| 27 | +4. Test with a sample event |
| 28 | + |
| 29 | +## Environment Setup |
| 30 | + |
| 31 | +### Required Environment Variables |
| 32 | + |
| 33 | +Add the following environment variable to your application configuration: |
| 34 | + |
| 35 | +```bash |
| 36 | +# .env file |
| 37 | +GITHUB_WEBHOOK_SECRET=your_generated_secret_here |
| 38 | +``` |
| 39 | + |
| 40 | +### Generate Webhook Secret |
| 41 | + |
| 42 | +**Using OpenSSL:** |
| 43 | +```bash |
| 44 | +openssl rand -hex 32 |
| 45 | +``` |
| 46 | + |
| 47 | +**Example Output:** |
| 48 | +``` |
| 49 | +a1b2c3d4e5f6789012345678901234567890abcdef1234567890abcdef123456 |
| 50 | +``` |
| 51 | + |
| 52 | +⚠️ **Important:** Store this secret securely and use the same value in both your application environment and GitHub webhook configuration. |
| 53 | + |
| 54 | +### Database Setup |
| 55 | + |
| 56 | +The webhook integration requires the `gitWebhookLog` table. If not already created, run the database migration: |
| 57 | + |
| 58 | +```bash |
| 59 | +npx prisma migrate dev |
| 60 | +``` |
| 61 | + |
| 62 | +## GitHub Repository Configuration |
| 63 | + |
| 64 | +### Step 1: Access Repository Settings |
| 65 | + |
| 66 | +1. Navigate to your GitHub repository |
| 67 | +2. Click on the **Settings** tab (requires admin permissions) |
| 68 | +3. In the left sidebar, click **Webhooks** |
| 69 | +4. Click **Add webhook** |
| 70 | + |
| 71 | +### Step 2: Configure Webhook Settings |
| 72 | + |
| 73 | +#### Payload URL |
| 74 | + |
| 75 | +**Production/Staging Environment:** |
| 76 | +``` |
| 77 | +https://your-api-domain.com/v6/review/webhooks/git |
| 78 | +``` |
| 79 | + |
| 80 | +**Development Environment:** |
| 81 | +``` |
| 82 | +https://your-dev-domain.com/webhooks/git |
| 83 | +``` |
| 84 | + |
| 85 | +Note: The `/v6/review` prefix is only added in production when `NODE_ENV=production`. |
| 86 | + |
| 87 | +#### Content Type |
| 88 | +- Select `application/json` |
| 89 | + |
| 90 | +#### Secret |
| 91 | +- Enter the webhook secret you generated earlier |
| 92 | +- This must exactly match your `GITHUB_WEBHOOK_SECRET` environment variable |
| 93 | + |
| 94 | +#### SSL Verification |
| 95 | +- Keep **Enable SSL verification** checked (recommended for production) |
| 96 | +- For development with proper HTTPS setup, this should remain enabled |
| 97 | + |
| 98 | +### Step 3: Select Events |
| 99 | + |
| 100 | +Choose one of the following options: |
| 101 | + |
| 102 | +**Option A: Send Everything (Recommended for Testing)** |
| 103 | +- Select "Send me everything" to receive all GitHub event types |
| 104 | + |
| 105 | +**Option B: Select Individual Events** |
| 106 | +Common events for development workflows: |
| 107 | +- **Pushes** - Code pushes to repository |
| 108 | +- **Pull requests** - PR creation, updates, merges |
| 109 | +- **Issues** - Issue creation, updates, comments |
| 110 | +- **Issue comments** - Comments on issues and PRs |
| 111 | +- **Releases** - Release creation and updates |
| 112 | +- **Create** - Branch or tag creation |
| 113 | +- **Delete** - Branch or tag deletion |
| 114 | + |
| 115 | +### Step 4: Activate and Create |
| 116 | + |
| 117 | +1. Ensure **Active** checkbox is checked |
| 118 | +2. Click **Add webhook** |
| 119 | +3. GitHub will automatically send a `ping` event to test the webhook |
| 120 | + |
| 121 | +## Local Development Setup |
| 122 | + |
| 123 | +Since GitHub webhooks require a publicly accessible URL, local development requires exposing your local server to the internet. |
| 124 | + |
| 125 | +**Install ngrok:** |
| 126 | +```bash |
| 127 | +npm install -g ngrok |
| 128 | +``` |
| 129 | + |
| 130 | +**Setup process:** |
| 131 | +```bash |
| 132 | +# 1. Start your local API server |
| 133 | +pnpm run start:dev |
| 134 | + |
| 135 | +# 2. In another terminal, expose your local server |
| 136 | +ngrok http 3000 |
| 137 | + |
| 138 | +# 3. Copy the HTTPS URL from ngrok output |
| 139 | +# Example: https://abc123.ngrok.io |
| 140 | + |
| 141 | +# 4. Use this URL in GitHub webhook settings |
| 142 | +# https://abc123.ngrok.io/webhooks/git |
| 143 | +``` |
| 144 | +## Testing the Integration |
| 145 | + |
| 146 | +### Manual Testing |
| 147 | + |
| 148 | +#### 1. Verify Initial Setup |
| 149 | + |
| 150 | +After creating the webhook, GitHub automatically sends a `ping` event: |
| 151 | + |
| 152 | +1. Go to your repository's webhook settings |
| 153 | +2. Click on your webhook |
| 154 | +3. Check **Recent Deliveries** section |
| 155 | +4. Look for the `ping` event with status 200 OK |
| 156 | + |
| 157 | +#### 2. Trigger Test Events |
| 158 | + |
| 159 | +**Create a Push Event:** |
| 160 | +```bash |
| 161 | +# Make a small change |
| 162 | +echo "webhook test" >> test-webhook.txt |
| 163 | +git add test-webhook.txt |
| 164 | +git commit -m "Test webhook integration" |
| 165 | +git push origin main |
| 166 | +``` |
| 167 | + |
| 168 | +**Create an Issue:** |
| 169 | +1. Go to your repository on GitHub |
| 170 | +2. Click **Issues** tab |
| 171 | +3. Click **New issue** |
| 172 | +4. Create a test issue |
| 173 | + |
| 174 | +**Create a Pull Request:** |
| 175 | +1. Create a new branch: `git checkout -b test-webhook` |
| 176 | +2. Make changes and commit |
| 177 | +3. Push branch: `git push origin test-webhook` |
| 178 | +4. Open pull request on GitHub |
| 179 | + |
| 180 | +### Testing with curl |
| 181 | + |
| 182 | +You can test the webhook endpoint directly using curl with proper signature generation: |
| 183 | + |
| 184 | +```bash |
| 185 | +#!/bin/bash |
| 186 | + |
| 187 | +# Configuration |
| 188 | +WEBHOOK_URL="http://localhost:3000/webhooks/git" # Adjust for your environment |
| 189 | +WEBHOOK_SECRET="your_webhook_secret_here" |
| 190 | +PAYLOAD='{"test": "data", "repository": {"name": "test-repo"}}' |
| 191 | +DELIVERY_ID="test-delivery-$(date +%s)" |
| 192 | +EVENT_TYPE="push" |
| 193 | + |
| 194 | +# Generate signature |
| 195 | +SIGNATURE="sha256=$(echo -n "$PAYLOAD" | openssl dgst -sha256 -hmac "$WEBHOOK_SECRET" | sed 's/^.* //')" |
| 196 | + |
| 197 | +# Send test webhook |
| 198 | +curl -X POST "$WEBHOOK_URL" \ |
| 199 | + -H "Content-Type: application/json" \ |
| 200 | + -H "X-GitHub-Event: $EVENT_TYPE" \ |
| 201 | + -H "X-GitHub-Delivery: $DELIVERY_ID" \ |
| 202 | + -H "X-Hub-Signature-256: $SIGNATURE" \ |
| 203 | + -d "$PAYLOAD" |
| 204 | +``` |
| 205 | + |
| 206 | +## API Endpoint Reference |
| 207 | + |
| 208 | +### Webhook Endpoint |
| 209 | + |
| 210 | +**URL:** `POST /webhooks/git` (development) or `POST /v6/review/webhooks/git` (production) |
| 211 | + |
| 212 | +**Required Headers:** |
| 213 | +- `Content-Type: application/json` |
| 214 | +- `X-GitHub-Event: {event_type}` - GitHub event type (push, pull_request, etc.) |
| 215 | +- `X-GitHub-Delivery: {delivery_id}` - Unique delivery identifier from GitHub |
| 216 | +- `X-Hub-Signature-256: sha256={signature}` - HMAC-SHA256 signature for verification |
| 217 | + |
| 218 | +**Request Body:** |
| 219 | +- GitHub webhook payload (varies by event type) |
| 220 | + |
| 221 | +**Response Codes:** |
| 222 | +- `200 OK` - Webhook processed successfully |
| 223 | +- `400 Bad Request` - Missing required headers or invalid payload |
| 224 | +- `403 Forbidden` - Invalid signature verification |
| 225 | +- `500 Internal Server Error` - Processing error or configuration issue |
| 226 | + |
| 227 | +**Success Response:** |
| 228 | +```json |
| 229 | +{ |
| 230 | + "success": true, |
| 231 | + "message": "Webhook processed successfully" |
| 232 | +} |
| 233 | +``` |
| 234 | + |
| 235 | +**Error Response:** |
| 236 | +```json |
| 237 | +{ |
| 238 | + "statusCode": 403, |
| 239 | + "message": "Invalid signature", |
| 240 | + "error": "Forbidden", |
| 241 | + "timestamp": "2024-01-01T00:00:00.000Z", |
| 242 | + "path": "/webhooks/git" |
| 243 | +} |
| 244 | +``` |
| 245 | + |
| 246 | +## Database Schema |
| 247 | + |
| 248 | +Webhook events are stored in the `gitWebhookLog` table: |
| 249 | + |
| 250 | +```sql |
| 251 | +CREATE TABLE "gitWebhookLog" ( |
| 252 | + "id" VARCHAR(14) NOT NULL DEFAULT nanoid(), |
| 253 | + "eventId" TEXT NOT NULL, |
| 254 | + "event" TEXT NOT NULL, |
| 255 | + "eventPayload" JSONB NOT NULL, |
| 256 | + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, |
| 257 | + |
| 258 | + CONSTRAINT "gitWebhookLog_pkey" PRIMARY KEY ("id") |
| 259 | +); |
| 260 | + |
| 261 | +-- Indexes for efficient querying |
| 262 | +CREATE INDEX "gitWebhookLog_eventId_idx" ON "gitWebhookLog"("eventId"); |
| 263 | +CREATE INDEX "gitWebhookLog_event_idx" ON "gitWebhookLog"("event"); |
| 264 | +CREATE INDEX "gitWebhookLog_createdAt_idx" ON "gitWebhookLog"("createdAt"); |
| 265 | +``` |
| 266 | + |
| 267 | +### Query Examples |
| 268 | + |
| 269 | +**View recent webhook events:** |
| 270 | +```sql |
| 271 | +SELECT |
| 272 | + id, |
| 273 | + "eventId", |
| 274 | + event, |
| 275 | + "createdAt" |
| 276 | +FROM "gitWebhookLog" |
| 277 | +ORDER BY "createdAt" DESC |
| 278 | +LIMIT 10; |
| 279 | +``` |
| 280 | + |
| 281 | +**Filter by event type:** |
| 282 | +```sql |
| 283 | +SELECT * FROM "gitWebhookLog" |
| 284 | +WHERE event = 'push' |
| 285 | +ORDER BY "createdAt" DESC; |
| 286 | +``` |
| 287 | + |
| 288 | +**View specific webhook payload:** |
| 289 | +```sql |
| 290 | +SELECT |
| 291 | + event, |
| 292 | + "eventPayload" |
| 293 | +FROM "gitWebhookLog" |
| 294 | +WHERE "eventId" = 'your-delivery-id'; |
| 295 | +``` |
| 296 | + |
| 297 | +## Security Considerations |
| 298 | + |
| 299 | +### Signature Verification |
| 300 | + |
| 301 | +The webhook implementation uses GitHub's recommended security practices: |
| 302 | + |
| 303 | +1. **HMAC-SHA256 Signature:** All incoming webhooks are verified using HMAC-SHA256 |
| 304 | +2. **Timing-Safe Comparison:** Uses `crypto.timingSafeEqual()` to prevent timing attacks |
| 305 | +3. **Secret Protection:** Webhook secrets are stored as environment variables |
| 306 | +4. **Header Validation:** Validates all required GitHub headers |
| 307 | + |
| 308 | +### Best Practices |
| 309 | + |
| 310 | +1. **Use HTTPS:** Always use HTTPS URLs for production webhooks |
| 311 | +2. **Rotate Secrets:** Periodically rotate webhook secrets |
| 312 | +3. **Monitor Access:** Regularly review webhook delivery logs |
| 313 | +4. **Limit Events:** Only subscribe to events you actually need |
| 314 | +5. **Access Control:** Restrict webhook configuration to repository administrators |
| 315 | + |
| 316 | +### Environment Security |
| 317 | + |
| 318 | +- Store `GITHUB_WEBHOOK_SECRET` securely using your deployment platform's secret management |
| 319 | +- Never commit secrets to version control |
| 320 | +- Use different secrets for different environments |
| 321 | +- Implement proper secret rotation procedures |
| 322 | + |
| 323 | +### Log Analysis |
| 324 | + |
| 325 | +Key log messages to monitor: |
| 326 | + |
| 327 | +``` |
| 328 | +# Successful webhook processing |
| 329 | +[WebhookController] Successfully processed GitHub webhook |
| 330 | +
|
| 331 | +# Signature validation failures |
| 332 | +[GitHubSignatureGuard] Invalid webhook signature for delivery |
| 333 | +
|
| 334 | +# Configuration errors |
| 335 | +[GitHubSignatureGuard] GITHUB_WEBHOOK_SECRET environment variable is not configured |
| 336 | +``` |
| 337 | + |
| 338 | +Example |
| 339 | + |
| 340 | +``` |
| 341 | +[2025-08-02T01:06:48.312Z] [LOG] [Bootstrap] Server started on port 3000 |
| 342 | +[2025-08-02T01:07:15.700Z] [LOG] [HttpRequest] {"type":"request","method":"POST","url":"/webhooks/git","ip":"::1","userAgent":"GitHub-Hookshot/4f8bd7a"} |
| 343 | +[2025-08-02T01:07:15.739Z] [LOG] [GitHubSignatureGuard] Valid webhook signature verified for delivery 0722d0bc-6f3d-11f0-8a2d-6cc18966c098, event push |
| 344 | +[2025-08-02T01:07:15.740Z] [LOG] [WebhookController] {"message":"Received GitHub webhook","delivery":"0722d0bc-6f3d-11f0-8a2d-6cc18966c098","event":"push","timestamp":"2025-08-02T01:07:15.740Z"} |
| 345 | +[2025-08-02T01:07:15.740Z] [LOG] [WebhookService] {"message":"Processing GitHub webhook event","eventId":"0722d0bc-6f3d-11f0-8a2d-6cc18966c098","event":"push","timestamp":"2025-08-02T01:07:15.740Z"} |
| 346 | +[2025-08-02T01:07:15.804Z] [LOG] [WebhookService] {"message":"Successfully stored webhook event","eventId":"0722d0bc-6f3d-11f0-8a2d-6cc18966c098","event":"push","storedId":"9aHvEgDYPCYYnU","createdAt":"2025-08-02T01:07:15.747Z"} |
| 347 | +[2025-08-02T01:07:15.804Z] [LOG] [WebhookService] {"message":"Event-specific processing placeholder","event":"push","payloadSize":7979} |
| 348 | +[2025-08-02T01:07:15.804Z] [LOG] [WebhookController] {"message":"Successfully processed GitHub webhook","delivery":"0722d0bc-6f3d-11f0-8a2d-6cc18966c098","event":"push","success":true} |
| 349 | +[2025-08-02T01:07:15.804Z] [LOG] [HttpRequest] {"type":"response","statusCode":200,"method":"POST","url":"/webhooks/git","responseTime":"104ms"} |
| 350 | +``` |
0 commit comments