No description
  • Python 97.8%
  • Shell 1.3%
  • Dockerfile 0.9%
Find a file
Eric Schewe 2f7252bb07
All checks were successful
Build and Publish Docker Image / test (push) Successful in 25s
Build and Publish Docker Image / build-and-push (push) Successful in 15s
v1.2.4: silence PytestCollectionWarning for Quart's TestApp
pytest's auto-collection scans imported classes matching Test*. Quart's
quart.testing.app.TestApp matches but has an __init__, producing a
non-actionable warning twice per test run. Filter by exact message in
[tool.pytest.ini_options].

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-17 18:04:45 -07:00
.forgejo/workflows v1.2.3: strip CR from version strings in build step 2026-05-17 18:02:17 -07:00
app v1.2.4: silence PytestCollectionWarning for Quart's TestApp 2026-05-17 18:04:45 -07:00
tests v1.1.3: Review 004 fixes — config validation, dead code, test quality 2026-03-14 16:19:18 -07:00
.dockerignore Initial implementation of Gitea-Apprise webhook bridge 2026-02-08 16:33:45 -08:00
.env.example Migrate to async: Quart + httpx + uvicorn (v1.1.0) 2026-03-13 23:06:19 -07:00
.gitignore Updated gitignore 2026-03-13 19:41:49 -07:00
Claude.md v1.2.4: silence PytestCollectionWarning for Quart's TestApp 2026-05-17 18:04:45 -07:00
docker-compose.yml Reliability, observability, and CI improvements (v1.1.2) 2026-03-14 00:06:25 -07:00
docker-entrypoint.sh Reliability, observability, and CI improvements (v1.1.2) 2026-03-14 00:06:25 -07:00
Dockerfile v1.2.1: bump Python 3.12 → 3.14 2026-05-17 16:17:57 -07:00
LICENSE Added GPL3 2026-02-08 23:06:47 -08:00
pyproject.toml v1.2.4: silence PytestCollectionWarning for Quart's TestApp 2026-05-17 18:04:45 -07:00
README.md v1.1.3: Review 004 fixes — config validation, dead code, test quality 2026-03-14 16:19:18 -07:00
requirements-dev.txt Migrate to async: Quart + httpx + uvicorn (v1.1.0) 2026-03-13 23:06:19 -07:00
requirements.txt Migrate to async: Quart + httpx + uvicorn (v1.1.0) 2026-03-13 23:06:19 -07:00
SECURITY.md Reliability, observability, and CI improvements (v1.1.2) 2026-03-14 00:06:25 -07:00

Gitea-Apprise Webhook Bridge

A lightweight bridge service that forwards Gitea webhook events to Apprise notifications. Get notified about pushes, issues, pull requests, releases, and more through any of Apprise's 100+ supported notification services (Telegram, Discord, Slack, email, etc.).

AI Disclosure

This container was completely written by Claude AI.

How It Works

Gitea  --(webhook)-->  Bridge  --(API call)-->  Apprise API  -->  Your notification services
  1. You configure a webhook in Gitea pointing at the bridge
  2. When events happen in your repo, Gitea sends the event data to the bridge
  3. The bridge formats it into a human-readable message and forwards it to your Apprise API instance
  4. Apprise delivers the notification to your configured services

Features

  • 22 Gitea event types supported: push, issues, pull requests, comments, reviews, releases, wiki, branch/tag create/delete, forks, and more
  • Markdown and plain-text formatting with clickable links, bold text, and smart truncation
  • HMAC-SHA256 signature verification to ensure webhooks are actually from your Gitea
  • Two Apprise modes: stateful (pre-configured key) or stateless (direct notification URLs)
  • Event filtering to only forward the events you care about
  • Apprise tags for routing notifications to specific services
  • Stateless and lightweight -- no database, no storage, just a single container
  • Health check endpoint at /health

Quick Start

1. Run the bridge

# docker-compose.yml
services:
  gitea-apprise-bridge:
    image: thefizi/apprise-gitea-notifications
    ports:
      - "5000:5000"
    environment:
      - GITEA_WEBHOOK_SECRET=your-shared-secret
      - APPRISE_BASE_URL=http://your-apprise-instance:8000
      - APPRISE_STATEFUL_KEY=gitea
    restart: unless-stopped
docker-compose up -d

2. Configure Gitea

  1. Go to your repository (or organization) Settings > Webhooks > Add Webhook
  2. Select Gitea as the webhook type
  3. Set Target URL to http://your-bridge-host:5000/webhook
  4. Set Secret to the same value as GITEA_WEBHOOK_SECRET
  5. Choose which events to trigger on (or select "All Events")
  6. Save

Note: Depending on your Gitea configuration you may need to update your ALLOWED_HOST_LIST

3. Test it

Push a commit or open an issue -- you should receive a notification through Apprise.

Configuration

All configuration is done through environment variables.

Variable Required Default Description
APPRISE_BASE_URL Yes URL of your Apprise API instance (e.g. http://apprise:8000)
APPRISE_STATEFUL_KEY One of these Apprise configuration key for stateful mode (uses /notify/{key})
APPRISE_STATELESS_URLS One of these Apprise notification URLs for stateless mode (e.g. tgram://bot/chat,slack://token)
GITEA_WEBHOOK_SECRET Recommended (empty) Shared secret for HMAC-SHA256 signature verification
APPRISE_TAGS No (empty) Comma-separated Apprise tags included on every notification
NOTIFICATION_FORMAT No markdown Message format: markdown or text
ALLOWED_EVENTS No (empty = all) Comma-separated list of event types to forward
LOG_LEVEL No INFO Logging level: DEBUG, INFO, WARNING, ERROR
APPRISE_TIMEOUT No 30 Read timeout in seconds for Apprise API calls
APPRISE_CONNECT_TIMEOUT No 5 Connection timeout in seconds when reaching the Apprise API. A shorter value than APPRISE_TIMEOUT means a down Apprise instance fails fast rather than holding the Gitea connection open.
LISTEN_HOST No 0.0.0.0 Host address to bind the server to. When BEHIND_PROXY=true and the proxy is on the same host, consider setting this to 127.0.0.1 to prevent direct external access to the bridge port. (entrypoint-only — not read by the app)
LISTEN_PORT No 5000 Port to bind the server to. (entrypoint-only — not read by the app)
BEHIND_PROXY No false Set to exactly true to pass --proxy-headers to uvicorn when behind a reverse proxy (Nginx, Traefik, etc.). (entrypoint-only — not read by the app)
PROXY_TRUSTED_IPS No 127.0.0.1 IP(s) trusted to set X-Forwarded-For headers. Only used when BEHIND_PROXY=true. Accepts a single IP, a CIDR range, or a comma-separated list. Do not set to * unless your proxy is on a fully private network. (entrypoint-only — not read by the app)
UVICORN_NO_ACCESS_LOG No false Set to true to suppress uvicorn's per-request access logs. Useful when a reverse proxy already handles access logging, or to reduce log noise from health check polling. (entrypoint-only — not read by the app)

Note: Do not wrap env var values in quotes in your .env file — the bridge strips them automatically. For example, use GITEA_WEBHOOK_SECRET=mysecret, not GITEA_WEBHOOK_SECRET="mysecret".

Apprise Modes

You must configure one of the two Apprise modes:

Stateful mode (APPRISE_STATEFUL_KEY): Your Apprise API instance has pre-configured notification URLs saved under a key. The bridge sends to /notify/{key}. This is the simplest setup if you already manage your Apprise configuration.

Stateless mode (APPRISE_STATELESS_URLS): You provide the Apprise notification URLs directly. The bridge sends them with each request to /notify/. Useful if you don't want to configure state in Apprise.

If both are set, stateful mode takes precedence.

Event Filtering

By default, the bridge forwards all events it receives. To limit which events are forwarded, set ALLOWED_EVENTS to a comma-separated list:

ALLOWED_EVENTS=push,issues,pull_request,release

Events not in the list are silently skipped (HTTP 200 returned to Gitea so it doesn't retry).

Supported Events

Event Example Notification Severity
Push [user/repo] 3 new commits to main info
Issue opened [user/repo] Issue #42 opened info
Issue closed [user/repo] Issue #42 closed success
Issue reopened [user/repo] Issue #42 reopened warning
Issue assigned/labeled/milestoned [user/repo] Issue #42 assigned info
PR opened [user/repo] PR #15 opened info
PR merged [user/repo] PR #15 merged success
PR closed (not merged) [user/repo] PR #15 closed warning
PR assigned/labeled/milestoned/synced [user/repo] PR #15 synchronized info
PR review approved [user/repo] PR #15 approved success
PR review changes requested [user/repo] PR #15 changes requested failure
Comment (issue or PR) [user/repo] Comment on issue #42 info
Review comment [user/repo] Review comment on PR #15 info
Release published [user/repo] Release v1.0.0 published success
Branch/tag created [user/repo] Branch created: feature-x info
Branch/tag deleted [user/repo] Branch deleted: feature-x warning
Fork [user/repo] Repository forked info
Wiki [user/repo] Wiki page edited: Home info

The severity maps to Apprise notification types (info, success, warning, failure), which some notification services render differently (e.g. different colors or icons).

Security

  • Webhook signature verification: Set GITEA_WEBHOOK_SECRET to a strong shared secret. The bridge verifies every incoming request using HMAC-SHA256 before processing it. Without this, anyone who knows your bridge URL could send fake notifications.
  • Non-root container: The Docker image runs as a non-root user.
  • No secrets in logs: Webhook secrets and Apprise URLs are never logged.

Development

Running locally

python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements-dev.txt
APPRISE_BASE_URL=http://localhost:8000 APPRISE_STATEFUL_KEY=test uvicorn app:create_app --factory --reload

Running tests

source .venv/bin/activate
pytest -v                          # run all tests
pytest --cov=app --cov-report=term-missing  # with coverage report

Project structure

app/
  __init__.py          # Quart app factory
  config.py            # Environment variable configuration
  webhook.py           # POST /webhook endpoint
  signature.py         # HMAC-SHA256 verification
  apprise_client.py    # Apprise API HTTP client
  formatters/          # One module per event type
    base.py            # Shared formatting helpers
    push.py, issues.py, pull_request.py, comments.py,
    review.py, repository.py, release.py, wiki.py

License

GNU General Public License v3.0