https://github.com/stuttgart-things/homerun2-git-pitcher
Active GitHub repository watcher for homerun2 — polls GitHub API and pitches events to Redis Streams
https://github.com/stuttgart-things/homerun2-git-pitcher
git git-events golang homerun2
Last synced: about 14 hours ago
JSON representation
Active GitHub repository watcher for homerun2 — polls GitHub API and pitches events to Redis Streams
- Host: GitHub
- URL: https://github.com/stuttgart-things/homerun2-git-pitcher
- Owner: stuttgart-things
- Created: 2026-03-17T19:29:26.000Z (4 months ago)
- Default Branch: main
- Last Pushed: 2026-07-03T07:05:31.000Z (21 days ago)
- Last Synced: 2026-07-03T09:08:57.875Z (21 days ago)
- Topics: git, git-events, golang, homerun2
- Language: Go
- Homepage:
- Size: 168 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 9
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# homerun2-git-pitcher

Active GitHub repository watcher for homerun2 — polls GitHub API and pitches events to Redis Streams.
[](https://github.com/stuttgart-things/homerun2-git-pitcher/actions/workflows/build-test.yaml)
## What it does
homerun2-git-pitcher watches configured GitHub repositories for events (pushes, pull requests, releases, workflow runs) and forwards them as structured messages to Redis Streams, where downstream homerun2 catchers can consume them.
```
GitHub API ──poll──> git-pitcher ──pitch──> Redis Stream ──consume──> catchers
```
## Usage
### Watch profile
Create a YAML file defining which repos to watch:
```yaml
github:
token: env:GITHUB_TOKEN # reads from environment variable
repos:
- owner: stuttgart-things
name: homerun2-led-catcher
interval: 5m
events: [push, pull_request, release, workflow_run]
- owner: stuttgart-things
name: homerun2-core-catcher
interval: 10m
events: [push, release]
- owner: hzeller
name: rpi-rgb-led-matrix
interval: 30m
events: [release]
```
**Fields:**
| Field | Required | Default | Description |
|-------|----------|---------|-------------|
| `github.token` | yes | — | GitHub PAT or `env:VAR_NAME` to read from env |
| `repos[].owner` | yes | — | GitHub organization or user |
| `repos[].name` | yes | — | Repository name |
| `repos[].interval` | no | `5m` | Poll interval (minimum `30s`) |
| `repos[].events` | no | all 4 types | Event types to watch |
**Supported event types:** `push`, `pull_request`, `release`, `workflow_run`
**GitHub token permissions:** the watcher only calls `Activity.ListRepositoryEvents` against the configured repos. For an unscoped token, GitHub still grants the authenticated 5000/hr rate limit on public-repo reads, so a token with no permissions is enough for public repos.
If you prefer to be explicit:
- **Classic PAT:** `public_repo` scope (public repos) or `repo` (also covers private).
- **Fine-grained PAT** (recommended):
- Resource owner: the org whose repos you watch.
- Repository access: all (or just the watched ones).
- Permissions — repository:
- `Metadata`: Read-only — mandatory for any fine-grained PAT.
- `Contents`: Read-only — needed for the events API.
- `Actions`: Read-only — only required if you want `workflow_run` events on private repos.
No write permissions, no org/user permissions.
### Run with watcher
```bash
export GITHUB_TOKEN=ghp_...
export AUTH_TOKEN=mysecret
export WATCH_CONFIG=./tests/watch-profile.yaml
export DEDUP_STATE_FILE=./dedup-state.json
go run .
```
### Run without watcher (HTTP API only)
```bash
export REDIS_ADDR=localhost REDIS_PORT=6379 REDIS_STREAM=messages
export AUTH_TOKEN=mysecret
go run .
```
### Dev mode (no Redis)
```bash
PITCHER_MODE=file AUTH_TOKEN=test go run .
```
## API Endpoints
| Endpoint | Method | Auth | Description |
|----------|--------|------|-------------|
| `/health` | `GET` | None | Health check with version, rate limit status |
| `/pitch` | `POST` | Bearer token | Submit a message to Redis Streams |
Pitch a message
```bash
curl -X POST http://localhost:8080/pitch \
-H "Authorization: Bearer $AUTH_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"title": "Test Notification",
"message": "Hello from homerun2-git-pitcher",
"severity": "info",
"author": "test"
}'
```
Health check (with rate limit)
```bash
curl -s http://localhost:8080/health | jq .
```
```json
{
"status": "healthy",
"version": "1.2.0",
"commit": "abc1234",
"rateLimit": {
"limit": 5000,
"remaining": 4850,
"reset": "2026-03-18T09:00:00Z",
"backingOff": false
}
}
```
## Event message mapping
| GitHub Event | Title | Severity | URL |
|-------------|-------|----------|-----|
| Push | `Push to {branch} on {repo}` | info | compare URL |
| Pull Request | `PR #{n}: {title} ({action})` | info / success (merged) / warning (closed) | PR URL |
| Release | `Release {tag} on {repo}` | success | release URL |
| Workflow Run | `Workflow {name} {conclusion}` | success / error / warning | run URL |
## Configuration reference
### Core
| Variable | Description | Default |
|----------|-------------|---------|
| `PORT` | HTTP server port | `8080` |
| `AUTH_TOKEN` | Bearer token for `/pitch` endpoint | **required** |
| `PITCHER_MODE` | Backend: `redis` or `file` | `redis` |
| `PITCHER_FILE` | Output file path (file mode only) | `pitched.log` |
### Redis
| Variable | Description | Default |
|----------|-------------|---------|
| `REDIS_ADDR` | Redis server address | `localhost` |
| `REDIS_PORT` | Redis server port | `6379` |
| `REDIS_PASSWORD` | Redis password | (empty) |
| `REDIS_STREAM` | Redis stream name | `messages` |
### Watcher
| Variable | Description | Default |
|----------|-------------|---------|
| `WATCH_CONFIG` | Path to YAML watch profile | (disabled) |
| `DEDUP_STATE_FILE` | Path to persist dedup state | (in-memory only) |
### Logging
| Variable | Description | Default |
|----------|-------------|---------|
| `LOG_FORMAT` | `json` or `text` | `json` |
| `LOG_LEVEL` | `debug`, `info`, `warn`, `error` | `info` |
| `LOG_HEALTH_CHECKS` | Set `true` to log health requests | `false` |
## Development
```bash
# Unit tests (no Redis needed)
go test ./internal/...
# Lint
task lint
# Run locally with file backend
task run-local
```
## KCL Deployment
Kubernetes manifests are generated using [KCL](https://kcl-lang.io/). See [kcl/README.md](kcl/README.md) for full configuration reference.
```bash
# Render manifests
kcl run kcl/ -Y tests/kcl-deploy-profile.yaml
# Apply to cluster
kcl run kcl/ -Y tests/kcl-deploy-profile.yaml \
-D 'config.namespace=homerun2' \
-D 'config.redisPassword=' \
-D 'config.githubToken=' \
| python3 -c "
import yaml, sys
data = yaml.safe_load(sys.stdin)
for m in data['manifests']:
print('---')
print(yaml.dump(m, default_flow_style=False).rstrip())
" | kubectl apply -f -
```
For production, use the [Flux component](https://github.com/stuttgart-things/flux/tree/main/apps/homerun2/components/git-pitcher) which references the KCL OCI artifact and applies cluster-specific patches.
See [docs/](docs/) for full development, deployment, and CI/CD documentation.
## Links
- [Releases](https://github.com/stuttgart-things/homerun2-git-pitcher/releases)
- [homerun-library](https://github.com/stuttgart-things/homerun-library)
- [homerun2 Flux app](https://github.com/stuttgart-things/flux/tree/main/apps/homerun2)