This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
This is a modular Node.js webhook server for Gogs (Git service) that receives webhooks and executes configurable shell commands based on events. It uses zero external dependencies - only built-in Node.js modules.
The codebase follows a clean separation of concerns with five main modules:
index.js (Entry Point)
├── server.js (HTTP Server)
│ └── webhookHandler.js (Event Management)
├── jobQueue.js (Job Queue & Persistence)
│ └── commandExecutor.js (Command Execution)
├── config.js (Configuration)
└── logger.js (Logging)
WebhookServer (src/server.js)
http modulePOST /webhook, GET /healthgetHandler() to access WebhookHandler for callback registrationWebhookHandler (src/webhookHandler.js)
handler.on(eventType, callback) - Event-specific callbackshandler.onAny(callback) - Global callbacks for all events(payload, headers) or (eventType, payload, headers) for globalJobQueue (src/jobQueue.js)
queue.json for crash recoveryCommandExecutor (src/commandExecutor.js)
{{variable}} syntax/bin/bash with timeout protection{{repo}}, {{branch}}, {{pusher}}, {{commit}}, {{pr_number}}, {{issue_number}}, {{issue_title}}, etc.COMMAND_TIMEOUTConfig (src/config.js)
.env file synchronously on startupprocess.env (env vars take precedence)get(), getBoolean(), getNumber()Logger (src/logger.js)
info(), error(), webhook()index.js loads config and creates WebhookServerqueue.json (if exists)WEBHOOK_PUSH_ENABLED=true, etc.)npm start # Start production server
npm run dev # Start with auto-reload (--watch flag)
node examples/with-callbacks.js # Run callback example
Testing endpoints:
curl http://localhost:3000/health
curl -X POST http://localhost:3000/webhook \
-H "Content-Type: application/json" \
-H "X-Gogs-Event: push" \
-H "X-Gogs-Delivery: 12345" \
-d '{"ref":"refs/heads/main","repository":{"name":"test-repo","full_name":"user/test-repo"},"pusher":{"username":"testuser"},"commits":[{"id":"abc123","message":"Test commit"}]}'
Configuration is split between two files:
.env)Copy from .env.example and configure:
Server Configuration:
HOST=0.0.0.0 # Network interface (0.0.0.0=all, 127.0.0.1=localhost only)
PORT=3000 # Port number
WEBHOOK_PATH=/webhook # Webhook endpoint path
WEBHOOK_SECRET= # Optional webhook verification secret
Event Enable/Disable:
WEBHOOK_{EVENT_TYPE}_ENABLED=true|false
Supported Event Types: push, pull_request, create, delete, release, issues, issue_comment, global
commands.json)Copy from commands.json.example and configure commands for each event type.
Command Structure:
{
"commands": {
"event_type": [
{
"name": "command-identifier",
"description": "Human-readable description",
"type": "shell" | "node",
"command": "path/to/command",
"args": ["arg1", "{{variable}}", "arg3"],
"cwd": "/working/directory" | null,
"timeout": 300000,
"filterBranch": "branch-name" | null
}
]
}
}
Command Types:
shell - Execute bash shell command/scriptnode - Execute Node.js script (automatically prepends node command)Per-Command Settings:
name - Unique identifier for the commanddescription - Human-readable descriptiontype - Execution type (shell or node)command - Command or script pathargs - Array of arguments (supports variable substitution)cwd - Working directory (null = project root)timeout - Timeout in millisecondsfilterBranch - Only execute for specific branch (null = all branches)import/export syntax ("type": "module" in package.json)server.stop() before exit.env for enable/disable flags, commands.json for command definitionsfilterBranch fieldThe server supports custom callback registration beyond .env command execution:
import { WebhookServer } from './src/server.js';
const server = new WebhookServer({ port: 3000 });
const handler = server.getHandler();
// Event-specific callback
handler.on('push', async (payload, headers) => {
console.log('Push to:', payload.repository.name);
// Custom logic here
});
// Global callback (all events)
handler.onAny(async (eventType, payload, headers) => {
console.log('Event:', eventType);
// Send to analytics, logging, etc.
});
server.start();
See examples/with-callbacks.js for complete example.
Commands in .env support variable substitution using {{variable}} syntax.
Available variables:
{{event}} - Event type (push, create, delete, etc.){{repo}} - Repository name only{{full_repo}} - User/repository format{{branch}} - Branch name (extracted from refs){{pusher}} - Username of person who triggered event{{commit}} - Short commit hash (push events){{commit_msg}} - Last commit message (push events){{pr_number}} - Pull request number (PR events){{pr_action}} - PR action (opened, closed, etc.){{tag}} - Tag name (create/delete tag events){{ref_type}} - "branch" or "tag"{{issue_number}} - Issue number (issue events){{issue_title}} - Issue title (issue events){{issue_action}} - Issue action (opened, closed, reopened, etc.){{issue_body}} - Issue description/body (issue events){{comment_body}} - Comment text (issue_comment events)Variables are extracted in commandExecutor.js via extractVariables() and substituted via substituteVariables().
The webhook server uses a persistent job queue to ensure reliable command execution:
queue.json after every change{
"queue": [
{
"id": 1,
"eventType": "push",
"status": "pending",
"createdAt": "2025-10-28T12:00:00.000Z",
"config": { "command": "...", "enabled": true }
}
],
"jobIdCounter": 5,
"lastSaved": "2025-10-28T12:05:00.000Z"
}
.env.example when adding new configuration optionsexport default new ClassName()No formal test suite currently. Test manually using:
npm run dev