Utility relay service to bridge Hugging Face webhook events into GitHub Actions using repository_dispatch.
This project is useful when Hugging Face does not natively support the workflow trigger you need.
Hugging Face repositories can emit webhook events, but many delivery patterns (for example directly triggering your GitHub CI/CD workflow with custom filtering) require an intermediate service.
This relay does that job:
- Receive webhook call from Hugging Face.
- Validate request with a shared secret.
- Filter by tracked HF repositories and files.
- Trigger a target GitHub Action through
repository_dispatch.
See full architecture details and diagrams in docs/ARCHITECTURE.md.
flowchart TD
A[Hugging Face Webhook] --> B[/POST /v1/github_hook/]
B --> C{Secret Valid?}
C -- No --> X[401 Invalid Secret]
C -- Yes --> D{Repo and Type in TRACKING_PATHS?}
D -- No --> E[filtered: true]
D -- Yes --> F[Fetch latest HF commit]
F --> G[Build changed file list from repo tree]
G --> H{Tracked file changed?}
H -- No --> I[ignored: true]
H -- Yes --> J[POST GitHub repository_dispatch]
J --> K[success: true]
.
|-- app/
| |-- app.py # FastAPI webhook relay endpoint
| |-- watch_list.py # Static tracked repo/file configuration
| |-- Dockerfile # Runtime image for HF Space
| `-- requirements.txt # Runtime dependencies
|-- .github/workflows/
| `-- pipeline.yml # Deploy app folder to HF Space
|-- deploy-app.py # HF Space bootstrap + secret injection + upload
`-- util.py # Shared helper functions for deploy scripts
GET /: liveness probe.POST /v1/github_hook: relay endpoint.
hf_repo: Hugging Face repo id (user/repo).gh_repo: GitHub repo id (owner/repo).hf_repo_type: one ofdataset,model,space.
x_webhook_secret: must matchHF_WEBHOOK_SECRET.
- Python 3.12+
- A Hugging Face token with required permissions
- A GitHub PAT that can trigger repository dispatch on target repo
python -m venv .venv
# Linux/macOS
source .venv/bin/activate
# Windows (PowerShell)
# .\.venv\Scripts\Activate.ps1
pip install --upgrade pip
pip install -r app/requirements.txt# Required by app runtime
export HF_TOKEN="<hf_token>"
export HF_WEBHOOK_SECRET="<shared_secret>"
export GH_PAT="<github_pat>"
# Required by deploy-app.py
export HF_REPO="<hf_user>/<space_name>"PowerShell example:
$env:HF_TOKEN = "<hf_token>"
$env:HF_WEBHOOK_SECRET = "<shared_secret>"
$env:GH_PAT = "<github_pat>"
$env:HF_REPO = "<hf_user>/<space_name>"uvicorn app.app:app --host 0.0.0.0 --port 7860 --reloadWorkflow .github/workflows/pipeline.yml deploys when files under app/* are pushed to master.
Required GitHub configuration:
- Secret
HF_WEBHOOK_SECRET - Secret
HUGGING_FACE_API_KEY - Secret
GH_PAT - Variable
HF_REPO
python deploy-app.pyTracked paths are configured in app/watch_list.py:
TRACKING_PATHS = {
"user/repo": {
"dataset": ["file.csv", "folder/file.json"]
}
}Only configured repositories and file paths can trigger downstream GitHub dispatch.
Current relay logic is Hugging Face specific. To support more providers:
- Separate request verification and change extraction into source adapters.
- Keep a normalized internal event model.
- Reuse a common GitHub dispatch sender.
Suggested interface:
class WebhookSourceAdapter:
def verify(self, headers: dict, body: bytes) -> bool: ...
def extract_event(self, request) -> dict: ...
def changed_files(self, event: dict) -> list[str]: ...Current implementation is functional but minimal. For production hardening:
- Add payload signature validation (HMAC) in addition to shared secret.
- Add replay protection (timestamp and nonce cache).
- Add rate limiting and request timeouts.
- Use structured logging and avoid sensitive values in logs.
401 Invalid Secret: verify webhook secret andx_webhook_secretheader.{"filtered": true}:hf_repoorhf_repo_typeis not whitelisted inTRACKING_PATHS.{"ignored": true}: event did not modify tracked files.- GitHub workflow not starting: verify
gh_repo, PAT permissions, and workflowrepository_dispatchhandler.
Add a license file if this repository is intended for public reuse.