{"id":35205607,"url":"https://github.com/yindia/githook","last_synced_at":"2026-02-19T16:00:38.269Z","repository":{"id":330856424,"uuid":"1124203347","full_name":"yindia/githook","owner":"yindia","description":"Githooks routes GitHub/GitLab/Bitbucket webhooks through rules to Watermill brokers and provides a worker SDK for provider-aware event handling.","archived":false,"fork":false,"pushed_at":"2026-02-16T16:43:55.000Z","size":12044,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-02-17T00:20:37.839Z","etag":null,"topics":["bitbucket","github","githubapp","gitlab","riverqueue","server","watermill-io","webhook","wroker"],"latest_commit_sha":null,"homepage":"https://githook-app.vercel.app","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/yindia.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-12-28T14:57:57.000Z","updated_at":"2026-02-16T16:43:59.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/yindia/githook","commit_stats":null,"previous_names":["yindia/githook"],"tags_count":14,"template":false,"template_full_name":null,"purl":"pkg:github/yindia/githook","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yindia%2Fgithook","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yindia%2Fgithook/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yindia%2Fgithook/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yindia%2Fgithook/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/yindia","download_url":"https://codeload.github.com/yindia/githook/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yindia%2Fgithook/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29621876,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-19T13:04:20.082Z","status":"ssl_error","status_checked_at":"2026-02-19T13:03:33.775Z","response_time":117,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bitbucket","github","githubapp","gitlab","riverqueue","server","watermill-io","webhook","wroker"],"created_at":"2025-12-29T13:45:25.556Z","updated_at":"2026-02-19T16:00:36.222Z","avatar_url":"https://github.com/yindia.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# githook ⚡\n\n\u003e **⚠️ Warning:** This project is for research and development only and is **not production-ready**. Do not deploy it in production environments.\n\n## Table of Contents\n\n1. [About](#about)\n2. [How It Works](#how-it-works)\n3. [Features](#features)\n4. [Why githook](#why-githook)\n5. [Installing the CLI](#installing-the-cli)\n6. [Quick Start Guide (GitHub Apps)](#quick-start-guide-github-apps)\n7. [OAuth Onboarding Flow](#oauth-onboarding-flow)\n8. [SCM-Specific Documentation](#scm-specific-documentation)\n9. [Terminology](#terminology)\n10. [Storage](#storage)\n11. [Webhook URLs](#webhook-urls)\n12. [Drivers](#drivers)\n13. [Rules](#rules)\n14. [SDK](#sdk)\n15. [Examples](#examples)\n16. [Documentation](#documentation)\n\n---\n\n## About\n\ngithook is an event automation layer for GitHub, GitLab, and Bitbucket. It receives webhooks from these providers, evaluates configurable rules against the payload, and publishes matching events to your message broker using [Watermill](https://watermill.io/).\n\n**What problem does it solve?**\n\nManaging webhooks across multiple Git providers (GitHub, GitLab, Bitbucket) typically requires:\n- Writing provider-specific webhook handlers for each platform\n- Manually normalizing different payload formats\n- Hardcoding event routing logic in your application\n- Managing authentication for each provider's API separately\n\ngithook solves this by providing:\n- A unified webhook receiver for all three providers\n- A rule-based event routing system using JSONPath expressions\n- Automatic payload normalization\n- Provider-aware SCM clients injected into your workers\n- Multi-broker support (AMQP, NATS, Kafka, SQL, HTTP, etc.)\n\n**Architecture Overview:**\n\ngithook consists of two main components:\n1. **Server**: Receives webhooks, validates signatures, evaluates rules, and publishes events to message brokers\n2. **Worker SDK**: Consumes events from brokers with provider-aware API clients pre-configured and ready to use\n\n---\n\n## How It Works\n\n```\n┌─────────────┐      ┌──────────────┐      ┌─────────────┐      ┌─────────────┐\n│   GitHub    │      │              │      │   Message   │      │   Workers   │\n│   GitLab    │─────▶│  githook    │─────▶│   Broker    │─────▶│  (Your App) │\n│  Bitbucket  │      │   Server     │      │   (AMQP)    │      │             │\n└─────────────┘      └──────────────┘      └─────────────┘      └─────────────┘\n   Webhooks           Rules Engine          Watermill           Business Logic\n                      + Publishing                              + SCM Clients\n```\n\n### Workflow\n\n1. **Webhook Received**: A webhook arrives from GitHub, GitLab, or Bitbucket at the configured endpoint (e.g., `/webhooks/github`)\n\n2. **Signature Validation**: The server validates the webhook signature using the provider's secret to ensure authenticity\n\n3. **Payload Normalization**: The raw JSON payload is parsed and normalized into a common structure\n\n4. **Rule Evaluation**: Each configured rule is evaluated against the normalized payload using JSONPath expressions and boolean logic\n\n5. **Event Publishing**: If a rule matches, the event is published to the configured message broker(s) with the specified topic name\n\n6. **Worker Consumption**: Workers subscribe to topics, receive events, and execute business logic with provider-aware API clients (GitHub SDK, GitLab SDK, Bitbucket SDK) automatically injected\n\n7. **API Interactions**: Workers can interact with the provider's API using the injected client, which is pre-authenticated using GitHub App installation tokens or OAuth tokens\n\n### Component Interaction\n\n- **CLI**: Manage provider instances, list installations, configure the system\n- **Server**: HTTP server that receives webhooks and publishes to brokers\n- **Storage**: PostgreSQL database storing OAuth tokens and installation metadata\n- **Message Brokers**: AMQP, NATS, Kafka, SQL, HTTP, or any Watermill-supported broker\n- **Worker SDK**: Go library for consuming events with provider clients injected\n\n---\n\n## Features\n\n- **Multi-Provider Support**: GitHub, GitLab, Bitbucket webhooks unified\n- **Rule Engine**: JSONPath + boolean expressions for event routing\n- **Multi-Broker Publishing**: AMQP, NATS, Kafka, HTTP, SQL, GoChannel, RiverQueue\n- **API-First Architecture**: Connect RPC (gRPC) API for all operations\n- **Multi-Tenant Ready**: Provider instance management with OAuth onboarding\n- **Worker SDK**: Go SDK with auto-injected provider API clients\n- **SCM Client Injection**: Pre-authenticated GitHub, GitLab, Bitbucket clients\n- **Event Normalization**: Common payload structure across providers\n- **Request Tracing**: End-to-end tracing with `X-Request-ID`\n\n---\n\n## Why githook\n\n**Stop reinventing the wheel.** Every company builds the same webhook infrastructure over and over. We built it once, so you can reuse it.\n\n- **Unified Webhooks**: One platform for GitHub, GitLab, and Bitbucket\n- **Declarative Routing**: JSONPath rules instead of hardcoded logic\n- **API-First Design**: Connect RPC (gRPC) API for programmatic control\n- **Multi-Tenant**: Support multiple organizations with isolated configurations\n- **Broker Agnostic**: AMQP, NATS, Kafka, SQL, HTTP - use any broker\n- **Auto-Authenticated**: Workers get pre-configured API clients\n- **Event-Driven**: Decouple webhook processing from business logic\n\n---\n\n## Installing the CLI\n\n### Homebrew (macOS/Linux)\n\n```bash\nbrew install yindia/homebrew-yindia/githook\n```\n\n### Install Script (Linux/macOS)\n\n```bash\ncurl -fsSL https://raw.githubusercontent.com/yindia/githook/refs/heads/main/install.sh | sh\n```\n\n### From Source\n\n```bash\ngit clone https://github.com/yindia/githook.git\ncd githook\ngo build -o githook ./main.go\n```\n\n### Verify Installation\n\n```bash\ngithook --version\n```\n\n---\n\n## Quick Start Guide (GitHub Apps)\n\nGet githook running locally with GitHub Apps in 4 steps:\n\n### Prerequisites\n\n- **Go 1.24+**\n- **Docker + Docker Compose**\n- **PostgreSQL** (started via Docker Compose)\n- **RabbitMQ** (started via Docker Compose)\n- **ngrok** (for local development - [download here](https://ngrok.com/download))\n- **GitHub App** (create one at https://github.com/settings/apps)\n\n### Step 1: Start Dependencies\n\n```bash\ndocker compose up -d\n```\n\nThis starts PostgreSQL and RabbitMQ.\n\n### Step 2: Expose Local Server with ngrok\n\nFor local development, use ngrok to expose your local server to the internet:\n\n```bash\nngrok http 8080\n```\n\nCopy the HTTPS forwarding URL (e.g., `https://abc123.ngrok-free.app`) - you'll need this for the next steps.\n\n**Note:** Keep this ngrok terminal running throughout your testing session.\n\n### Step 3: Configure GitHub App\n\nCreate a GitHub App with these settings:\n- **Webhook URL**: `https://\u003cyour-ngrok-url\u003e/webhooks/github` (use the URL from ngrok)\n- **Webhook Secret**: `devsecret` (for local testing)\n- **Permissions**: Repository metadata (read), Pull requests (read \u0026 write)\n- **Subscribe to events**: Pull request, Push, Check suite\n\nDownload the private key and note your App ID.\n\n### Step 4: Configure githook\n\nEdit `config.yaml` and replace `\u003cyour-ngrok-url\u003e` with your actual ngrok URL:\n\n```yaml\nserver:\n  port: 8080\nendpoint: https://\u003cyour-ngrok-url\u003e\n\nstorage:\n  driver: postgres\n  dsn: postgres://githook:githook@localhost:5432/githook?sslmode=disable\n  dialect: postgres\n  auto_migrate: true\n\nauth:\n  oauth2:\n    enabled: false\n\nredirect_base_url: https://app.example.com/success\n\n```\n\n\u003e The per-provider configuration (app IDs, webhook secrets, driver settings) is no longer stored in `config.yaml`. Use the `githook` CLI to bootstrap providers/drivers/rules before the worker starts.\n\n#### Bootstrap with the CLI\n\n1. **Create a driver** (e.g., AMQP) so rules can publish to your broker:\n\n```bash\ngithook --endpoint http://localhost:8080 drivers set \\\n  --tenant-id default \\\n  --name amqp \\\n  --config-file drivers/amqp.json\n```\n\n`drivers/amqp.json`:\n\n```json\n{\n  \"name\": \"amqp\",\n  \"config_json\": {\n    \"URL\": \"amqp://guest:guest@localhost:5672/\",\n    \"Mode\": \"durable_queue\"\n  }\n}\n```\n\n2. **Create a provider instance** with OAuth/webhook metadata:\n\n```bash\ngithook --endpoint http://localhost:8080 providers set \\\n  --tenant-id default \\\n  --provider github \\\n  --config-file providers/github.json\n```\n\n3. **Create a rule** that emits a single topic and points at the driver:\n\n```bash\ngithook --endpoint http://localhost:8080 rules create \\\n  --tenant-id default \\\n  --driver-id default:amqp \\\n  --when 'action == \"opened\"' \\\n  --emit github.main\n```\n\n4. **List resources** to confirm:\n\n```bash\ngithook --endpoint http://localhost:8080 providers list\ngithook --endpoint http://localhost:8080 drivers list\ngithook --endpoint http://localhost:8080 rules list\n```\n\nEach rule must emit exactly one topic, which the worker will subscribe to using the rule’s `emit` value and resolved driver ID.\n\n\n### Step 5: Start the Server\n\n```bash\ngo run ./main.go serve --config config.yaml\n```\n\nThe server should start on `http://localhost:8080` and be accessible via your ngrok URL.\n\n### Step 6: Start a Worker\n\nIn another terminal (replace `RULE_ID` with a rule you created through the API):\n\n```bash\ngo run ./example/github/worker/main.go --rule-id RULE_ID \\\n  --endpoint https://\u003cyour-ngrok-url\u003e\n```\n\n### Step 7: Install the GitHub App\n\nGet the provider instance hash:\n```bash\ngithook --endpoint http://localhost:8080 providers list --provider github\n# Copy the instance hash (e.g., a1b2c3d4)\n```\n\nVisit the OAuth installation URL:\n```\nhttp://localhost:8080/?provider=github\u0026instance=\u003cinstance-hash\u003e\n```\n\nFollow the GitHub authorization flow to complete installation. See [OAuth Onboarding Flow](#oauth-onboarding-flow) for detailed steps.\n\n### Step 8: Trigger Events\n\nNow test the integration by performing actions on an installed repository:\n\n**Create a Pull Request:**\n1. Open a repository where the GitHub App is installed\n2. Create a new branch and make some changes\n3. Open a pull request\n4. The worker should log: `PR opened: github/pull_request`\n\n**Push a Commit:**\n1. Make a commit and push to the repository\n2. The worker should log: `github.commit.created: repo=owner/repo commit=abc123...`\n\n**Troubleshooting:**\n- If webhooks aren't being received, check that ngrok is still running\n- Verify your `endpoint` in `config.yaml` matches your ngrok URL\n- Check GitHub App webhook delivery logs in GitHub App settings → Advanced → Recent Deliveries\n- Ensure OAuth credentials (`client_id` and `client_secret`) are configured in `config.yaml`\n\n---\n\n## OAuth Onboarding Flow\n\nOAuth onboarding allows users to connect their GitLab or Bitbucket accounts (or GitHub with user authorization) to githook.\n\n### When to Use\n\n- **GitLab**: Required for all GitLab integrations\n- **Bitbucket**: Required for all Bitbucket integrations\n- **GitHub**: Optional (only if \"Request user authorization\" is enabled in GitHub App)\n\n### Configuration\n\nConfigure OAuth credentials and redirect URL:\n\n```yaml\nendpoint: https://your-domain.com  # Your public URL\n\nredirect_base_url: https://app.example.com/success  # Where to send users after OAuth\n```\n\n**Callback URLs** (configure in provider settings):\n- **GitHub**: `https://your-domain.com/auth/github/callback`\n- **GitLab**: `https://your-domain.com/auth/gitlab/callback`\n- **Bitbucket**: `https://your-domain.com/auth/bitbucket/callback`\n\n### How It Works\n\n**Step 1: Get Instance Hash**\n\n```bash\ngithook --endpoint https://your-domain.com providers list --provider github\n# Output: Instance: a1b2c3d4\n```\n\n**Step 2: Redirect User to OAuth URL**\n\n```\nhttps://your-domain.com/?provider=github\u0026instance=a1b2c3d4\n```\n\n**Step 3: Complete Authorization**\n\n1. User is redirected to provider (GitHub/GitLab/Bitbucket)\n2. User authorizes the application\n3. Provider redirects back to githook with authorization code\n4. githook exchanges code for access token\n5. Token stored in PostgreSQL\n6. User redirected to `redirect_base_url`\n\n**Step 4: Done!**\n\n- ✅ Installation created in database\n- ✅ Webhooks will be processed\n- ✅ Workers get authenticated API clients\n- ✅ User redirected to `redirect_base_url` (if configured)\n\n### Flow Diagram\n\n```\nUser → githook → Provider OAuth → Callback → Store Token → Redirect to App\n```\n\nSee [docs/oauth-callbacks.md](docs/oauth-callbacks.md) for detailed OAuth documentation.\n\n---\n\n## SCM-Specific Documentation\n\nDetailed setup guides for each supported Git provider:\n\n- **[GitHub Setup Guide](docs/getting-started-github.md)** - GitHub Apps, OAuth, webhook configuration\n- **[GitLab Setup Guide](docs/getting-started-gitlab.md)** - OAuth application, webhook setup, namespaces\n- **[Bitbucket Setup Guide](docs/getting-started-bitbucket.md)** - OAuth consumer, webhook configuration\n\nEach guide includes:\n- Provider-specific prerequisites\n- Step-by-step configuration\n- Webhook payload examples\n- Testing instructions\n\n---\n\n## Terminology\n\n### Providers\nGit platforms: `github`, `gitlab`, or `bitbucket`.\n\n### Provider Instances\nSpecific configurations of a provider (e.g., GitHub.com vs GitHub Enterprise). Each instance has a unique hash (e.g., `a1b2c3d4`) and separate credentials.\n\n### Installation\nThe relationship between a provider instance, an account (org/user), and authentication credentials.\n\n### Account ID\nUnique identifier for the organization or user:\n- **GitHub**: Login/username (e.g., `octocat`)\n- **GitLab**: Group/user ID\n- **Bitbucket**: Workspace slug\n\n### Namespaces\nOrganizational units within a provider:\n- **GitHub**: Organizations and user accounts\n- **GitLab**: Groups and subgroups (hierarchical)\n- **Bitbucket**: Workspaces\n\nWebhooks can be configured at namespace level (affects all repos) or individual repository level.\n\n### Drivers\nMessage brokers: `amqp`, `nats`, `kafka`, `sql`, `http`, `gochannel`, `riverqueue`.\n\n### Rules\nJSONPath conditions that route events to topics. Has `when` (condition), `emit` (topic), and optional `drivers`.\n\n### Topics\nMessage broker queues/subjects that events are published to. Workers subscribe to topics.\n\n---\n\n## Storage\n\ngithook uses PostgreSQL to persist OAuth tokens, GitHub App installation metadata, and provider instance configurations.\n\n```yaml\nstorage:\n  driver: postgres\n  dsn: postgres://githook:githook@localhost:5432/githook?sslmode=disable\n  dialect: postgres\n  auto_migrate: true\n```\n\nSee [docs/storage.md](docs/storage.md) for advanced storage configuration.\n\n---\n\n## Webhook URLs\n\nWebhook URL schema: `\u003cbase-url\u003e/webhooks/\u003cprovider\u003e`\n\n**Default webhook paths:**\n- **GitHub:** `/webhooks/github`\n- **GitLab:** `/webhooks/gitlab`\n- **Bitbucket:** `/webhooks/bitbucket`\n\n---\n\n## Drivers\n\nDrivers are message broker implementations that githook uses to publish events. Powered by [Watermill](https://watermill.io/), githook supports multiple brokers simultaneously.\n\n### Available Drivers\n\n**AMQP (RabbitMQ)**\n```yaml\nwatermill:\n  driver: amqp\n  amqp:\n    url: amqp://guest:guest@localhost:5672/\n    mode: durable_queue\n```\n\nModes:\n- `durable_queue`: Persistent queues (survives broker restart)\n- `nondurable_queue`: Ephemeral queues\n- `durable_pubsub`: Persistent topic exchanges\n- `nondurable_pubsub`: Ephemeral topic exchanges\n\n**NATS Streaming**\n```yaml\nwatermill:\n  driver: nats\n  nats:\n    url: nats://localhost:4222\n    cluster_id: test-cluster\n    client_id: githook-publisher\n```\n\n**Kafka**\n```yaml\nwatermill:\n  driver: kafka\n  kafka:\n    brokers: [\"localhost:9092\"]\n    consumer_group: githook\n```\n\n**SQL (PostgreSQL/MySQL)**\n```yaml\nwatermill:\n  driver: sql\n  sql:\n    driver: postgres\n    dsn: postgres://user:pass@localhost/db?sslmode=disable\n    table: events\n```\n\n**HTTP**\n```yaml\nwatermill:\n  driver: http\n  http:\n    mode: base_url\n    base_url: http://localhost:9000/hooks\n```\n\n**GoChannel (In-Memory)**\n```yaml\nwatermill:\n  driver: gochannel\n```\n\nUse for testing or single-binary deployments.\n\n**RiverQueue (Postgres Job Queue)**\n```yaml\nwatermill:\n  driver: riverqueue\n  riverqueue:\n    driver: postgres\n    dsn: postgres://user:pass@localhost:5432/db?sslmode=disable\n    table: river_job\n    queue: default\n    kind: githook.event\n```\n\n### Multi-Driver Fan-Out\n\nPublish the same event to multiple brokers:\n\n```yaml\nwatermill:\n  drivers: [amqp, http, sql]\n  amqp:\n    url: amqp://guest:guest@localhost:5672/\n  http:\n    base_url: http://localhost:9000/hooks\n  sql:\n    driver: postgres\n    dsn: postgres://user:pass@localhost/db\n```\n\n### Per-Rule Driver Targeting\n\nOverride drivers for specific rules:\n\n```yaml\nrules:\n  - when: action == \"opened\"\n    emit: pr.opened\n    drivers: [amqp]  # Only publish to AMQP\n\n  - when: action == \"closed\"\n    emit: pr.closed\n    drivers: [amqp, http]  # Publish to both AMQP and HTTP\n```\n\nIf `drivers` is omitted, the default `driver` or `drivers` from the Watermill config is used.\n\nSee [docs/drivers.md](docs/drivers.md) for advanced driver configuration.\n\n---\n\n## Rules\n\nRules are the heart of githook' event routing system. They define which webhook events to publish and where to send them.\n\n### Rule Structure\n\n```yaml\nrules:\n  - when: \u003cboolean-expression\u003e\n    emit: \u003ctopic-name\u003e\n    drivers: [\u003cdriver-list\u003e]  # optional\n```\n\n### Rule Evaluation\n\n1. A webhook is received and validated\n2. The payload is normalized\n3. Each rule's `when` condition is evaluated against the normalized payload\n4. If the condition is `true`, the event is published to the `emit` topic\n5. Multiple rules can match the same event (multi-match)\n\n### JSONPath Expressions\n\nThe `when` field uses JSONPath with boolean operators:\n\n**Simple field access:**\n```yaml\nwhen: action == \"opened\"\n```\n\n**Nested fields:**\n```yaml\nwhen: pull_request.draft == false\n```\n\n**Full JSONPath syntax:**\n```yaml\nwhen: $.pull_request.head.ref == \"main\"\n```\n\n**Boolean operators:**\n```yaml\nwhen: action == \"opened\" \u0026\u0026 pull_request.draft == false\nwhen: action == \"closed\" || action == \"merged\"\nwhen: pull_request.draft != true\n```\n\n**Helper functions:**\n```yaml\n# Check if string contains substring\nwhen: contains(pull_request.title, \"[WIP]\")\n\n# Pattern matching with wildcards\nwhen: like(pull_request.head.ref, \"feature/%\")\n```\n\n### Multi-Topic Publishing\n\nPublish the same event to multiple topics:\n\n```yaml\nrules:\n  - when: action == \"closed\" \u0026\u0026 pull_request.merged == true\n    emit: [pr.merged, audit.pr.merged, notifications.pr.merged]\n```\n\n### Strict Mode\n\nBy default, rules with missing fields won't match. Enable strict mode to fail loudly:\n\n```yaml\nrules_strict: true\nrules:\n  - when: nonexistent.field == \"value\"\n    emit: will.never.match\n```\n\n### Example Rules\n\n**Pull request events:**\n```yaml\nrules:\n  # Non-draft PR opened\n  - when: action == \"opened\" \u0026\u0026 pull_request.draft == false\n    emit: pr.opened.ready\n\n  # PR merged\n  - when: action == \"closed\" \u0026\u0026 pull_request.merged == true\n    emit: pr.merged\n\n  # PR draft converted to ready\n  - when: action == \"ready_for_review\"\n    emit: pr.ready_for_review\n```\n\n**Commit/push events:**\n```yaml\nrules:\n  # Single commit pushed\n  - when: head_commit.id != \"\" \u0026\u0026 commits[0].id != \"\" \u0026\u0026 commits[1] == null\n    emit: github.commit.created\n\n  # Check suite requested (also contains commit info)\n  - when: action == \"requested\" \u0026\u0026 check_suite.head_commit.id != \"\"\n    emit: github.commit.created\n```\n\n**Tag events:**\n```yaml\nrules:\n  - when: ref_type == \"tag\"\n    emit: github.tag.created\n```\n\n**Branch-specific rules:**\n```yaml\nrules:\n  - when: pull_request.base.ref == \"main\"\n    emit: pr.targeting.main\n\n  - when: like(pull_request.head.ref, \"release/%\")\n    emit: pr.from.release.branch\n```\n\nSee [docs/rules.md](docs/rules.md) for advanced rule patterns.\n\n---\n\n## SDK\n\nThe githook Worker SDK provides a Go library for consuming events from message brokers with provider-aware API clients injected.\n\n### Installation\n\n```bash\ngo get githook/sdk/go/worker\n```\n\n### Basic Usage\n\n```go\npackage main\n\nimport (\n    \"context\"\n    \"log\"\n    \"os\"\n\n    \"githook/sdk/go/worker\"\n)\n\nfunc main() {\n    wk := worker.New(\n        worker.WithEndpoint(os.Getenv(\"GITHOOK_ENDPOINT\")),\n        worker.WithAPIKey(os.Getenv(\"GITHOOK_API_KEY\")),\n        worker.WithDefaultDriver(\"driver-id\"),\n        worker.WithTopics(\"pr.opened.ready\", \"pr.merged\"),\n    )\n\n    wk.HandleTopic(\"pr.opened.ready\", \"driver-id\", func(ctx context.Context, evt *worker.Event) error {\n        log.Printf(\"PR opened: %s/%s\", evt.Provider, evt.Type)\n        return nil\n    })\n\n    if err := wk.Run(context.Background()); err != nil {\n        log.Fatal(err)\n    }\n}\n```\n\n### Event Structure\n\n```go\ntype Event struct {\n    Topic      string                 // Topic name (e.g., \"pr.opened.ready\")\n    Provider   string                 // Provider name (\"github\", \"gitlab\", \"bitbucket\")\n    Type       string                 // Event type (e.g., \"pull_request\")\n    Payload    []byte                 // Raw JSON payload from webhook\n    Normalized map[string]interface{} // Normalized payload\n    Metadata   map[string]string      // Additional metadata (driver, request ID)\n    Client     interface{}            // Provider API client (if configured)\n}\n```\n\n### Using Provider Clients\n\nThe SDK automatically injects authenticated API clients:\n\n**GitHub:**\n```go\nwk.HandleTopic(\"pr.merged\", \"driver-id\", func(ctx context.Context, evt *worker.Event) error {\n    if evt.Provider != \"github\" {\n        return nil\n    }\n\n    if evt.Client != nil {\n        ghClient := evt.Client.(*github.Client)\n\n        // Use the GitHub SDK\n        repo, _, err := ghClient.Repositories.Get(ctx, \"owner\", \"repo\")\n        if err != nil {\n            return err\n        }\n\n        log.Printf(\"Repository: %s, Stars: %d\", repo.GetName(), repo.GetStargazersCount())\n    }\n\n    return nil\n})\n```\n\n**GitLab:**\n```go\nwk.HandleTopic(\"gitlab.mr.opened\", \"driver-id\", func(ctx context.Context, evt *worker.Event) error {\n    if evt.Client != nil {\n        glClient := evt.Client.(*gitlab.Client)\n        // Use GitLab SDK\n    }\n    return nil\n})\n```\n\n**Bitbucket:**\n```go\nwk.HandleTopic(\"bitbucket.pr.opened\", \"driver-id\", func(ctx context.Context, evt *worker.Event) error {\n    if evt.Client != nil {\n        bbClient := evt.Client.(*bitbucket.Client)\n        // Use Bitbucket SDK\n    }\n    return nil\n})\n```\n\n### Client Provider Configuration\n\nEnable client injection by passing provider config:\n\n```go\nappCfg, _ := core.LoadConfig(\"config.yaml\")\n\nwk := worker.New(\n    worker.WithSubscriber(sub),\n    worker.WithTopics(\"pr.opened.ready\"),\n    worker.WithClientProvider(worker.NewSCMClientProvider(appCfg.Providers)),\n)\n```\n\n### Concurrency\n\nControl how many events are processed simultaneously:\n\n```go\nwk := worker.New(\n    worker.WithConcurrency(20), // Process 20 events in parallel\n)\n```\n\n### Middleware\n\nUse Watermill middleware for retry, logging, throttling:\n\n```go\nimport wmmw \"github.com/ThreeDotsLabs/watermill/message/router/middleware\"\n\nretryMiddleware := worker.MiddlewareFromWatermill(\n    wmmw.Retry{MaxRetries: 3}.Middleware,\n)\n\nwk := worker.New(\n    worker.WithMiddleware(retryMiddleware),\n)\n```\n\n### Error Handling\n\nImplement custom retry logic:\n\n```go\ntype retryOnce struct{}\n\nfunc (retryOnce) OnError(ctx context.Context, evt *worker.Event, err error) worker.RetryDecision {\n    // Retry once, then nack\n    return worker.RetryDecision{Retry: true, Nack: true}\n}\n\nwk := worker.New(\n    worker.WithRetry(retryOnce{}),\n)\n```\n\n### Lifecycle Hooks\n\n```go\nwk := worker.New(\n    worker.WithListener(worker.Listener{\n        OnStart: func(ctx context.Context) {\n            log.Println(\"Worker started\")\n        },\n        OnExit: func(ctx context.Context) {\n            log.Println(\"Worker stopped\")\n        },\n        OnError: func(ctx context.Context, evt *worker.Event, err error) {\n            log.Printf(\"Error processing event: %v\", err)\n        },\n        OnMessageFinish: func(ctx context.Context, evt *worker.Event, err error) {\n            log.Printf(\"Finished: provider=%s type=%s err=%v\", evt.Provider, evt.Type, err)\n        },\n    }),\n)\n```\n\n### Worker invocation\n\nWorkers already resolve the driver and topic from a rule, so you only need the rule ID:\n\n```bash\ngo run ./worker/main.go --rule-id RULE_ID --endpoint https://your-domain.com\n```\n\nUse `subCfg.RuleID` or custom handler wiring when building reusable worker logic, and consult [docs/sdk_clients.md](docs/sdk_clients.md) for advanced SDK helpers (HTTP clients, middleware, API clients, etc.).\n## Documentation\n\n## Architecture: Control Plane vs Data Plane\n\nGithooks is split into a **control plane** and a **data plane**:\n\n- **Control Plane (Server)**: Hosts the Connect RPC API, stores configuration and installation data, manages rules, and publishes events to the message bus. This is the source of truth for providers, drivers, and rules.\n- **Data Plane (Worker)**: Subscribes to topics and processes events. It resolves provider clients via the server API and focuses on business logic. Workers should not access platform storage directly.\n\nThis separation lets you scale event processing independently while keeping configuration centralized.\n\n### Configuration Guides\n- [API Reference](https://buf.build/githook/cloud) - Connect RPC API documentation\n- [Driver Configuration](docs/drivers.md) - Message broker setup\n- [Rules Engine](docs/rules.md) - Event routing patterns\n- [Storage](docs/storage.md) - Database configuration\n- [OAuth Callbacks](docs/oauth-callbacks.md) - OAuth flow details\n- [Webhook Setup](docs/webhooks.md) - Provider webhook configuration\n\n### Provider Guides\n- [GitHub Setup](docs/getting-started-github.md) - GitHub Apps, OAuth, webhooks\n- [GitLab Setup](docs/getting-started-gitlab.md) - GitLab OAuth, webhooks, namespaces\n- [Bitbucket Setup](docs/getting-started-bitbucket.md) - Bitbucket OAuth, webhooks\n\n### SDK \u0026 Integration\n- [SDK Client Injection](docs/sdk_clients.md) - Using provider API clients in workers\n- [SDK DSL](docs/sdk-dsl.md) - Portable worker specification\n- [CLI Usage](docs/cli.md) - Command-line interface reference\n\n### Advanced Topics\n- [API Authentication](docs/auth.md) - OAuth2/OIDC for Connect RPC\n- [SCM Authentication](docs/scm-auth.md) - GitHub App, GitLab/Bitbucket tokens\n- [Event Compatibility](docs/events.md) - Event payload formats\n- [Observability](docs/observability.md) - Logging, metrics, tracing\n\n---\n\n**Made with ❤️ for developers who automate Git workflows**\n\nQuestions? Issues? Check the [documentation](docs/) or [open an issue](https://github.com/yindia/githook/issues).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyindia%2Fgithook","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fyindia%2Fgithook","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyindia%2Fgithook/lists"}