Skip to content

EmadMirzaeiR/webhook-deploy

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

5 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

πŸš€ Webhook Deploy

TypeScript Node.js Express.js Jest License: MIT

A production-ready, secure webhook server for automating CI/CD deployments from GitHub

Features β€’ Quick Start β€’ API β€’ Configuration β€’ Contributing


πŸ“‹ Table of Contents


✨ Features

πŸ”’ Security First

  • GitHub Signature Verification - Cryptographic webhook validation
  • Rate Limiting - Configurable request throttling (default: 100 req/min)
  • CORS Protection - Cross-origin request security
  • Helmet Security Headers - Additional HTTP security layers
  • Input Validation - Joi schema validation for all inputs
  • Script Sanitization - Path traversal protection

πŸ—οΈ Production Ready

  • TypeScript - Full type safety and IntelliSense support
  • Modular Architecture - Clean separation of concerns
  • Graceful Shutdown - Proper cleanup of resources and active deployments
  • Error Handling - Custom error classes with proper HTTP status codes
  • Request Tracing - Unique request IDs for debugging

πŸ“Š Monitoring & Observability

  • Health Checks - /health endpoint with deployment statistics
  • Metrics API - /metrics endpoint for monitoring tools
  • Structured Logging - Winston-powered JSON logging with rotation
  • Deployment Tracking - Success/failure statistics and timing

🎯 Developer Experience

  • Hot Reload - Development mode with automatic restarts
  • Test Suite - Jest testing with coverage reporting
  • Code Quality - ESLint + Prettier for consistent code style
  • Comprehensive Documentation - API docs, examples, and guides

πŸš€ Quick Start

Get your webhook server running in under 5 minutes:

# 1. Clone the repository
git clone https://github.com/EmadMirzaieR/webhook-deploy.git
cd webhook-deploy

# 2. Install dependencies
pnpm install

# 3. Set up environment
cp env-example .env
# Edit .env with your webhook secret

# 4. Configure projects
# Edit config.json with your repositories

# 5. Start development server
pnpm run dev

Your webhook server is now running at http://localhost:6521! πŸŽ‰

⚑ Quick Test

# Check server health
curl http://localhost:6521/health

# View metrics
curl http://localhost:6521/metrics

πŸ“¦ Installation

Prerequisites

  • Node.js >= 14.x
  • pnpm (recommended) or npm
  • Git (for deployment scripts)

Option 1: Development Setup

git clone https://github.com/EmadMirzaieR/webhook-deploy.git
cd webhook-deploy
pnpm install

Option 2: Production Installation

# Clone and build
git clone https://github.com/EmadMirzaieR/webhook-deploy.git
cd webhook-deploy
pnpm install --prod
pnpm run build

# Start production server
pnpm start

Option 3: Global Installation

npm install -g webhook-deploy
webhook-deploy --config ./config.json

βš™οΈ Configuration

Environment Variables

Create a .env file in the project root:

# Required: GitHub webhook secret
WEBHOOK_SECRET=your_super_secret_webhook_key

# Optional: Environment settings
NODE_ENV=production                    # development | production
LOG_LEVEL=info                         # error | warn | info | debug
CONFIG_PATH=./config.json              # Path to configuration file
PORT=3000                             # Override config.json port

Configuration File

Create or edit config.json:

{
  "port": 3000,
  "logDirectory": "./logs",
  "maxLogFiles": 10,
  "maxLogSize": "20m",
  
  "rateLimit": {
    "windowMs": 60000,
    "max": 100
  },
  
  "cors": {
    "origin": ["https://yourdomain.com", "https://api.yourdomain.com"],
    "credentials": false
  },
  
  "projects": [
    {
      "repo": "username/frontend-app",
      "branch": "main",
      "script": "./scripts/deploy-frontend.sh",
      "workingDirectory": "/var/www/frontend",
      "timeout": 300000,
      "enabled": true
    },
    {
      "repo": "username/api-service",
      "branch": "production",
      "script": "npm run deploy:prod && pm2 restart api",
      "workingDirectory": "/home/user/api",
      "timeout": 600000,
      "enabled": true
    }
  ]
}

Configuration Options Reference

Server Settings

Option Type Default Description
port number 3000 Server port
logDirectory string "./logs" Log files directory
maxLogFiles number 10 Max log files to keep
maxLogSize string "20m" Max size per log file

Security Settings

Option Type Default Description
rateLimit.windowMs number 60000 Rate limit window (ms)
rateLimit.max number 100 Max requests per window
cors.origin string/array "*" Allowed origins
cors.credentials boolean false Allow credentials

Project Settings

Option Type Required Description
repo string βœ… GitHub repo (owner/name)
branch string βœ… Branch to monitor
script string βœ… Deployment command
workingDirectory string ❌ Script execution directory
timeout number ❌ Script timeout (ms)
enabled boolean ❌ Enable/disable project

πŸ”— GitHub Webhook Setup

Step 1: Repository Settings

  1. Go to your GitHub repository
  2. Navigate to Settings β†’ Webhooks
  3. Click Add webhook

Step 2: Webhook Configuration

Payload URL: https://your-server.com/github-webhook
Content type: application/json
Secret: [your WEBHOOK_SECRET from .env]
SSL verification: Enable (recommended)

Step 3: Event Selection

  • Recommended: Select "Just the push event"
  • Advanced: Choose individual events if needed

Step 4: Test Webhook

GitHub provides a test button to verify your setup. You should see:

  • βœ… 200-202 response for successful deployments
  • βœ… Request ID in response for tracking

πŸ“š API Documentation

Health Check

GET /health

curl http://localhost:6521/health

Response:

{
  "status": "ok",
  "uptime": 3600.5,
  "timestamp": 1640995200000,
  "version": "0.2.0",
  "deployments": {
    "total": 42,
    "successful": 40,
    "failed": 2
  }
}

Metrics

GET /metrics

curl http://localhost:6521/metrics

Response:

{
  "deployments": {
    "total": 42,
    "successful": 40,
    "failed": 2
  },
  "activeDeployments": 1,
  "uptime": 3600.5,
  "projectCount": 3,
  "enabledProjects": 2
}

GitHub Webhook

POST /github-webhook

Headers:

Content-Type: application/json
X-GitHub-Event: push
X-Hub-Signature-256: sha256=<signature>

Success Response (202):

{
  "message": "Deployment triggered",
  "requestId": "abc123def456",
  "projects": 1
}

Error Responses:

Status Error Description
400 Invalid signature Webhook signature verification failed
403 Forbidden Rate limit exceeded
404 Not found Endpoint doesn't exist
422 Validation error Invalid payload format

πŸ› οΈ Development

Available Scripts

# Development
pnpm run dev          # Start with hot reload
pnpm run watch        # TypeScript watch mode

# Building
pnpm run build        # Compile TypeScript
pnpm run start        # Run production build

# Testing
pnpm test             # Run test suite
pnpm run test:watch   # Run tests in watch mode
pnpm run test:coverage # Run with coverage report

# Code Quality
pnpm run lint         # Run ESLint
pnpm run lint:fix     # Fix ESLint issues
pnpm run format       # Format with Prettier

Project Structure

webhook-deploy/
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ types/           # TypeScript type definitions
β”‚   β”‚   └── index.ts
β”‚   β”œβ”€β”€ services/        # Business logic services
β”‚   β”‚   β”œβ”€β”€ ConfigService.ts      # Configuration management
β”‚   β”‚   β”œβ”€β”€ LoggerService.ts      # Logging and monitoring
β”‚   β”‚   β”œβ”€β”€ WebhookService.ts     # Webhook verification
β”‚   β”‚   └── DeploymentService.ts  # Script execution
β”‚   β”œβ”€β”€ app.ts          # Express application setup
β”‚   └── index.ts        # Application entry point
β”œβ”€β”€ tests/
β”‚   β”œβ”€β”€ services/       # Service unit tests
β”‚   └── setup.ts       # Test configuration
β”œβ”€β”€ dist/               # Compiled JavaScript (generated)
β”œβ”€β”€ logs/               # Application logs (generated)
β”œβ”€β”€ config.json         # Project configuration
β”œβ”€β”€ .env               # Environment variables
└── package.json       # Dependencies and scripts

Adding New Features

  1. Create a service in src/services/
  2. Add types in src/types/index.ts
  3. Write tests in tests/services/
  4. Update the main app in src/app.ts

Example service:

// src/services/NotificationService.ts
class NotificationService {
  private static instance: NotificationService;
  
  public static getInstance(): NotificationService {
    if (!NotificationService.instance) {
      NotificationService.instance = new NotificationService();
    }
    return NotificationService.instance;
  }
  
  public async sendDeploymentNotification(result: DeploymentResult): Promise<void> {
    // Implementation here
  }
}

export default NotificationService;

πŸ§ͺ Testing

Running Tests

# Run all tests
pnpm test

# Run specific test file
pnpm test ConfigService

# Run with coverage
pnpm run test:coverage

# Watch mode for development
pnpm run test:watch

Writing Tests

Tests use Jest and follow this structure:

// tests/services/YourService.test.ts
import YourService from '../../src/services/YourService';

describe('YourService', () => {
  let service: YourService;

  beforeEach(() => {
    service = YourService.getInstance();
  });

  describe('methodName', () => {
    it('should do something', () => {
      // Test implementation
      expect(service.methodName()).toBe(expectedValue);
    });
  });
});

Integration Testing

Test webhook endpoints with curl:

# Test webhook with signature
PAYLOAD='{"repository":{"full_name":"test/repo"},"ref":"refs/heads/main"}'
SIGNATURE=$(echo -n "$PAYLOAD" | openssl dgst -sha256 -hmac "your-secret" | cut -d' ' -f2)

curl -X POST http://localhost:6521/github-webhook \
  -H "Content-Type: application/json" \
  -H "X-GitHub-Event: push" \
  -H "X-Hub-Signature-256: sha256=$SIGNATURE" \
  -d "$PAYLOAD"

πŸ—οΈ Architecture

Service Layer Pattern

The application follows a clean service layer architecture:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   HTTP Layer    β”‚  ← Express.js routes and middleware
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  Service Layer  β”‚  ← Business logic services
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚   Data Layer    β”‚  ← Configuration and logging
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Service Dependencies

graph TD
    A[WebhookApp] --> B[ConfigService]
    A --> C[LoggerService]
    A --> D[WebhookService]
    A --> E[DeploymentService]
    
    E --> C
    E --> D
    C --> B
Loading

Request Flow

sequenceDiagram
    participant GH as GitHub
    participant WH as Webhook Server
    participant DS as DeploymentService
    participant Script as Deploy Script
    
    GH->>WH: POST /github-webhook
    WH->>WH: Verify signature
    WH->>WH: Validate payload
    WH->>WH: Find matching projects
    WH-->>GH: 202 Accepted
    WH->>DS: Execute deployment
    DS->>Script: Run deployment script
    Script-->>DS: Success/Failure
    DS-->>WH: Log result
Loading

πŸ”’ Security

Security Features

  • πŸ” Signature Verification: HMAC-SHA256 webhook validation
  • πŸ›‘οΈ Rate Limiting: Prevent abuse and DoS attacks
  • 🌐 CORS Protection: Control cross-origin requests
  • πŸ”’ Security Headers: Helmet.js security middleware
  • βœ… Input Validation: Joi schema validation
  • 🚫 Path Sanitization: Prevent directory traversal

Security Best Practices

1. Environment Security

# Use strong, unique webhook secrets
WEBHOOK_SECRET=$(openssl rand -hex 32)

# Restrict environment access
chmod 600 .env

2. Network Security

# Run behind reverse proxy (nginx/apache)
# Enable HTTPS/TLS
# Configure firewall rules

3. System Security

# Run as non-root user
useradd --system --home /opt/webhook-deploy webhook
chown -R webhook:webhook /opt/webhook-deploy

# Restrict file permissions
chmod 750 scripts/
chmod +x scripts/*.sh

4. Script Security

#!/bin/bash
set -euo pipefail  # Exit on error, undefined vars, pipe failures

# Validate inputs
if [[ -z "${WEBHOOK_REPO:-}" ]]; then
  echo "Error: WEBHOOK_REPO not set"
  exit 1
fi

# Use absolute paths
cd /var/www/production

# Log all commands
exec > >(tee -a deployment.log)
exec 2>&1

Security Checklist

  • Strong webhook secret (32+ characters)
  • HTTPS enabled
  • Rate limiting configured
  • CORS properly configured
  • Scripts use absolute paths
  • Non-root user execution
  • File permissions restricted
  • Log monitoring enabled

πŸš€ Deployment

Docker Deployment

# Dockerfile
FROM node:18-alpine

WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production

COPY dist/ ./dist/
COPY config.json ./

USER node
EXPOSE 3000

CMD ["node", "dist/index.js"]
# Build and run
docker build -t webhook-deploy .
docker run -d -p 3000:3000 --env-file .env webhook-deploy

PM2 Deployment

# Install PM2
npm install -g pm2

# Start application
pm2 start dist/index.js --name webhook-deploy

# Save PM2 configuration
pm2 save

# Setup startup script
pm2 startup

Systemd Service

# /etc/systemd/system/webhook-deploy.service
[Unit]
Description=GitHub Webhook Deploy Server
After=network.target

[Service]
Type=simple
User=webhook
WorkingDirectory=/opt/webhook-deploy
ExecStart=/usr/bin/node dist/index.js
Restart=on-failure
RestartSec=5
Environment=NODE_ENV=production

[Install]
WantedBy=multi-user.target
# Enable and start service
sudo systemctl enable webhook-deploy
sudo systemctl start webhook-deploy

# Check status
sudo systemctl status webhook-deploy

Reverse Proxy (Nginx)

# /etc/nginx/sites-available/webhook-deploy
server {
    listen 80;
    server_name webhook.yourdomain.com;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name webhook.yourdomain.com;

    ssl_certificate /path/to/cert.pem;
    ssl_certificate_key /path/to/key.pem;

    location / {
        proxy_pass http://localhost:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_cache_bypass $http_upgrade;
    }
}

πŸ“Š Monitoring

Built-in Monitoring

The server provides several monitoring endpoints:

# Health check
curl https://webhook.yourdomain.com/health

# Detailed metrics
curl https://webhook.yourdomain.com/metrics

Log Analysis

# View recent logs
tail -f logs/combined.log

# Filter error logs
grep "error" logs/combined.log | tail -20

# Search by request ID
grep "abc123def456" logs/combined.log

External Monitoring

Prometheus Integration

// Custom metrics endpoint for Prometheus
app.get('/prometheus', (req, res) => {
  const stats = loggerService.getDeploymentStats();
  const metrics = `
# HELP webhook_deployments_total Total number of deployments
# TYPE webhook_deployments_total counter
webhook_deployments_total ${stats.total}

# HELP webhook_deployments_successful Successful deployments
# TYPE webhook_deployments_successful counter
webhook_deployments_successful ${stats.successful}

# HELP webhook_deployments_failed Failed deployments
# TYPE webhook_deployments_failed counter
webhook_deployments_failed ${stats.failed}
  `;
  res.set('Content-Type', 'text/plain');
  res.send(metrics);
});

Grafana Dashboard

Key metrics to monitor:

  • Request rate and response times
  • Deployment success/failure rates
  • Active deployment count
  • Error rates by type
  • Server uptime and memory usage

πŸ”§ Troubleshooting

Common Issues

1. Webhook Not Triggering

# Check if server is running
curl http://localhost:6521/health

# Verify GitHub can reach your server
# Check webhook delivery in GitHub settings

# Test webhook manually
curl -X POST http://localhost:6521/github-webhook \
  -H "Content-Type: application/json" \
  -H "X-GitHub-Event: push" \
  -d '{"repository":{"full_name":"test/repo"},"ref":"refs/heads/main"}'

2. Signature Verification Failed

# Check webhook secret matches
echo $WEBHOOK_SECRET

# Test signature generation
PAYLOAD='{"test":"data"}'
echo -n "$PAYLOAD" | openssl dgst -sha256 -hmac "your-secret"

3. Script Execution Fails

# Check script permissions
ls -la scripts/deploy.sh

# Test script manually
cd /path/to/working/directory
./scripts/deploy.sh

# Check script logs
tail -f logs/combined.log | grep "deployment"

4. Rate Limiting Issues

# Check current rate limit settings
grep -A5 "rateLimit" config.json

# Monitor rate limit hits
tail -f logs/combined.log | grep "rate limit"

Debug Mode

Enable detailed logging:

# Set debug log level
export LOG_LEVEL=debug

# Or in .env file
echo "LOG_LEVEL=debug" >> .env

Performance Issues

# Check memory usage
ps aux | grep node

# Monitor active deployments
curl http://localhost:6521/metrics | grep active

# Check log file sizes
du -sh logs/*

🀝 Contributing

We welcome contributions! Here's how to get started:

Development Setup

# Fork the repository
git clone https://github.com/your-username/webhook-deploy.git
cd webhook-deploy

# Install dependencies
pnpm install

# Create feature branch
git checkout -b feature/amazing-feature

# Make your changes
# ... code changes ...

# Run tests
pnpm test

# Check code quality
pnpm run lint
pnpm run format

# Commit changes
git commit -m "Add amazing feature"

# Push to GitHub
git push origin feature/amazing-feature

# Create Pull Request

Code Style

We use ESLint and Prettier for consistent code style:

# Check code style
pnpm run lint

# Auto-fix issues
pnpm run lint:fix

# Format code
pnpm run format

Testing Requirements

  • All new features must include tests
  • Maintain or improve test coverage
  • Tests must pass in CI/CD
# Run tests with coverage
pnpm run test:coverage

# Coverage should be > 80%

Pull Request Guidelines

  1. Clear Description: Explain what your PR does and why
  2. Small Changes: Keep PRs focused and small
  3. Tests: Include tests for new functionality
  4. Documentation: Update README if needed
  5. Backwards Compatibility: Don't break existing APIs

Issue Reporting

When reporting issues, please include:

  • Environment: Node.js version, OS, etc.
  • Configuration: Relevant config (remove secrets!)
  • Logs: Error logs and stack traces
  • Steps to Reproduce: Clear reproduction steps

πŸ“„ License

This project is licensed under the MIT License - see the LICENSE file for details.

πŸ™ Acknowledgments


πŸ”— Links


Made with ❀️ by Emad Mirzaie

⭐ Star this repository if it helped you! ⭐

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors