A monitoring script that polls Forgejo repositories for changes when webhooks aren't available. It detects events like bot collaborator invitations and triggers actions such as starting systemd services. Designed as a lightweight alternative to webhooks for automation workflows.
  • Python 99.2%
  • Shell 0.8%
Find a file
Sebastian Schulz 577a7b6a51
All checks were successful
Tests / Check Python Version (push) Successful in -3m2s
Tests / Lint (push) Successful in -2m46s
Tests / Test (push) Successful in -2m18s
chore(release): bump version to v0.2.2
2026-03-31 21:43:47 +00:00
.agents/skills Add skill to pick next task 2026-03-27 10:24:41 +00:00
.beans fix(forgejo_client): unassign using assignees list, preserve other assignees 2026-03-31 21:42:33 +00:00
.devcontainer chore: add beans setup feature 2026-03-31 13:43:30 +00:00
.forgejo/workflows ci: update release workflow to handle git operations 2026-03-31 20:37:58 +00:00
scripts build(release): sync uv.lock when bumping version 2026-03-31 21:17:57 +00:00
src/forgejo_watcher chore(release): bump version to v0.2.2 2026-03-31 21:43:47 +00:00
tests fix(forgejo_client): unassign using assignees list, preserve other assignees 2026-03-31 21:42:33 +00:00
.beans.yml Create first ticket with beans 2026-03-25 22:45:24 +01:00
.gitignore Initial commit 2026-03-25 18:12:48 +00:00
.pre-commit-config.yaml ci(pre-commit): fix escaped newlines in beans markdown files 2026-03-31 13:43:30 +00:00
.python-version fix(renovate): prevent renovate from updating python version 2026-03-31 15:38:29 +00:00
AGENTS.md Implement SystemdPlugin with D-Bus service management 2026-03-26 23:08:46 +00:00
LICENSE Initial commit 2026-03-25 18:12:48 +00:00
pyproject.toml chore(release): bump version to v0.2.2 2026-03-31 21:43:47 +00:00
README.md docs: update installation instructions to use HTTPS 2026-03-31 16:15:22 +00:00
renovate.json chore(config): migrate Renovate config (#5) 2026-03-31 15:51:02 +00:00
uv.lock chore(release): bump version to v0.2.2 2026-03-31 21:43:47 +00:00

forgejo-watcher

Poll Forgejo for repository changes and trigger actions—no webhooks required.

A lightweight CLI tool that monitors your Forgejo instance, automatically syncs repositories, and triggers systemd services. Ideal when webhooks aren't available or you need a simpler, firewall-friendly alternative.

Quick Start (5 minutes)

Get running in 4 steps:

Step 1: Set Environment Variables

export FW_API_URL="https://your-forgejo.example.com"
export FW_ACTOR="dummy"  # Use "dummy" for testing first

If your Forgejo instance requires authentication:

export FW_TOKEN="your-api-token-here"

Step 2: Check Available Actors

$ forgejo-watcher list-actors

Available actors:
  dummy     - Dummy actor for testing without systemd dependencies
  systemd   - Start systemd user services (requires: python3-dbus)

Active actor: dummy

Use dummy for testing; switch to systemd for production.

Step 3: Initialize Your Repository Tracking

Fetch all repositories from your Forgejo instance:

$ forgejo-watcher initialize

Loaded 5 repositories from Forgejo:
  - myorg/myrepo
  - myorg/another-project
  - teamA/service-x
  - teamA/service-y
  - automation/scripts

Step 4: Verify Your Setup

$ forgejo-watcher list-repos

Tracked repositories:
  myorg/myrepo
  myorg/another-project
  teamA/service-x
  teamA/service-y
  automation/scripts

You're done! forgejo-watcher is now tracking these repositories. See Common Workflows for how to automate synchronization and trigger actions.

Features

  • Poll-based monitoring — Check Forgejo for changes on a schedule (no webhooks required)
  • Bidirectional synchronization — Automatically add new repositories and remove archived ones
  • Pluggable actor system — Extend with custom actions or use built-in integrations
  • Systemd service launcher — Start user services when repositories change (D-Bus integration)
  • Simple JSON state — Track repositories in a local, human-readable file
  • Environment-based configuration — No complex config files; set variables and go
  • Lightweight — Python 3.13+, minimal dependencies
  • Comprehensive testing — Unit and integration tests included

Requirements

  • Python 3.13 or later — Uses modern syntax (match/case expressions, type hints)
  • pipx — For installing the tool as a standalone command
  • Forgejo instance — With API access (v1 API)

For systemd integration (optional):

  • python3-dbus system package — Provides D-Bus bindings for systemd control

For authentication (optional):

  • API token (if your instance requires it)

Installation

Standard Installation (Testing/Dummy Actor)

pipx install git+https://forge.sebatec.eu/sebatec-eu/forgejo-watcher.git@main

Systemd integration requires access to D-Bus. Install with system site packages enabled:

pipx install --system-site-packages git+https://forge.sebatec.eu/sebatec-eu/forgejo-watcher.git@main

First install the D-Bus package:

sudo apt install python3-dbus

Verify Installation

forgejo-watcher --help

Configuration

forgejo-watcher uses environment variables for all config. Set them before running commands, or add to .bashrc / .zshrc for persistence.

Required Variables

FW_API_URL (required) Base URL of your Forgejo instance (no trailing slash).

export FW_API_URL="https://forgejo.example.com"
export FW_API_URL="https://git.company.internal"

Optional Variables

FW_TOKEN (optional) API token for authentication.

export FW_TOKEN="abc123def456..."  # Create from Forgejo Settings → Applications

FW_TOKEN_FILE (optional) Path to file containing API token. Takes precedence over FW_TOKEN.

export FW_TOKEN_FILE="$HOME/.config/forgejo-watcher/token.txt"

FW_ACTOR (optional) Actor to use. Defaults to dummy.

export FW_ACTOR="dummy"      # Safe testing (logs only)
export FW_ACTOR="systemd"    # Production (starts services via D-Bus)

FW_STATE_FILE (optional) Where to store repositories. Defaults to ~/.local/state/forgejo-watcher/repositories.json.

export FW_STATE_FILE="$HOME/.local/state/forgejo-watcher/repositories.json"

STATE_DIRECTORY (optional) State directory. Defaults to ~/.local/state/forgejo-watcher.

export STATE_DIRECTORY="$HOME/.local/state/forgejo-watcher"

FW_SERVICE_TEMPLATE (optional) Template for service names (placeholder {} is replaced with repo). Defaults to forgejo-{}.service.

export FW_SERVICE_TEMPLATE="forgejo-{}.service"
# For org/myrepo, this becomes: forgejo-org-myrepo.service

Example: Production Configuration

export FW_API_URL="https://git.company.internal"
export FW_TOKEN_FILE="$HOME/.config/forgejo-watcher/token.txt"
export FW_ACTOR="systemd"
export FW_SERVICE_TEMPLATE="git-automation-{}.service"
export STATE_DIRECTORY="/var/lib/forgejo-watcher"

Example: Using Token File (Recommended for Security)

export FW_API_URL="https://git.company.internal"
export FW_TOKEN_FILE="$HOME/.config/forgejo-watcher/token.txt"
export FW_ACTOR="systemd"

# Create token file with restricted permissions
echo "glpat_abc123def456xyz..." > "$HOME/.config/forgejo-watcher/token.txt"
chmod 600 "$HOME/.config/forgejo-watcher/token.txt"

Commands Reference

forgejo-watcher provides seven primary commands for managing and monitoring repositories.

initialize [--trigger]

Fetch all repositories from Forgejo and initialize local tracking.

Options:

  • --trigger — Trigger actor execution for newly discovered repositories

Example:

$ forgejo-watcher initialize

Loaded 3 repositories from Forgejo:
  - team/project-a
  - team/project-b
  - automation/setup-scripts

Repositories saved to: /home/user/.local/state/forgejo-watcher/repositories.json

With --trigger, each new repository will trigger the configured actor (e.g., starting a systemd service).

add <repo_id> [--trigger]

Manually add a repository to tracking without initializing all of them.

Arguments:

  • repo_id — Repository in the format organization/repository

Options:

  • --trigger — Trigger actor execution for the added repository

Example:

$ forgejo-watcher add team/new-project

Added: team/new-project

Tracked repositories (4 total):
  - team/project-a
  - team/project-b
  - automation/setup-scripts
  - team/new-project

remove <repo_id>

Remove a repository from tracking (doesn't delete anything on Forgejo).

Arguments:

  • repo_id — Repository in the format organization/repository

Example:

$ forgejo-watcher remove team/archived-project

Removed: team/archived-project

Tracked repositories (3 total):
  - team/project-a
  - team/project-b
  - automation/setup-scripts

list-repos

Show all currently tracked repositories.

Example:

$ forgejo-watcher list-repos

Tracked repositories (3 total):
  - team/project-a
  - team/project-b
  - automation/setup-scripts

list-actors

Show available actors and check if their prerequisites are met.

Example:

$ forgejo-watcher list-actors

Available actors:

  dummy
    Dummy actor for testing without systemd dependencies
    Status: ✓ Available

  systemd
    Start systemd user services (requires: python3-dbus)
    Status: ✓ Available (python3-dbus is installed)

Active actor: dummy

If D-Bus isn't installed, the systemd actor will show:

  systemd
    Start systemd user services (requires: python3-dbus)
    Status: ✗ Unavailable (python3-dbus not found)

sync [--trigger]

Synchronize your local repository list with Forgejo (bidirectional).

  • Adds any new repositories found on Forgejo
  • Removes repositories that no longer exist on Forgejo
  • Useful for running on a schedule (e.g., cron job)

Options:

  • --trigger — Execute the actor for newly added repositories

Example:

$ forgejo-watcher sync

Syncing with Forgejo...

New repositories:
  + team/fresh-project

Removed repositories:
  - team/archived-project

Repositories saved to: /home/user/.local/state/forgejo-watcher/repositories.json

trigger <repo_id>

Manually execute the configured actor for a specific repository.

Arguments:

  • repo_id — Repository in the format organization/repository

Example (with dummy actor):

$ forgejo-watcher trigger team/project-a

Dummy actor triggered for team/project-a

Example (with systemd actor):

$ forgejo-watcher trigger team/project-a

Started systemd service: forgejo-team-project-a.service

Actors: Triggering Actions

Pluggable handlers that execute actions when repositories change.

What Are Actors?

When you run forgejo-watcher trigger or use --trigger with other commands, the configured actor executes.

Available Actors

dummy Actor (Safe for Testing)

Logs execution without side effects. Safe for testing.

systemd Actor (Production Automation)

Starts user systemd services when repositories change. Integrates with D-Bus to control systemd.

Requires:

  • python3-dbus system package
  • Installation with --system-site-packages
  • Pre-configured systemd user services
export FW_ACTOR="systemd"
export FW_SERVICE_TEMPLATE="deploy-{}.service"
forgejo-watcher trigger team/project-a  # Starts: deploy-team-project-a.service

Setting Up Systemd Integration

Convert repo names to service names and start them via D-Bus.

Step 1: Create Service File

mkdir -p ~/.config/systemd/user/

Create ~/.config/systemd/user/deploy-team-project-a.service:

[Unit]
Description=Deploy team/project-a
After=network-online.target

[Service]
Type=oneshot
ExecStart=/path/to/your/deploy/script.sh

[Install]
WantedBy=default.target

Step 2: Enable & Reload

systemctl --user daemon-reload
systemctl --user enable deploy-team-project-a.service

Step 3: Configure & Test

export FW_ACTOR="systemd"
export FW_SERVICE_TEMPLATE="deploy-{}.service"
forgejo-watcher trigger team/project-a

Creating Custom Actors

Create a Python module with execute() and check_prerequisites() functions. See Contributing for details.

Common Workflows

Set up tracking for all repositories in your instance:

export FW_API_URL="https://git.company.int"
export FW_TOKEN="your-api-token"
export FW_ACTOR="dummy"

forgejo-watcher list-actors      # Verify setup
forgejo-watcher initialize       # Fetch all repositories
forgejo-watcher list-repos       # Verify state

# Later: switch to production
export FW_ACTOR="systemd"

Keep repositories in sync with a cron job:

0 * * * * export FW_API_URL="https://git.company.int" && \
           export FW_TOKEN="token..." && \
           /home/user/.local/bin/forgejo-watcher sync >> /var/log/forgejo-watcher.log 2>&1

Runs hourly: adds new repos, removes archived ones.

Automatically start deployment services:

export FW_API_URL="https://git.company.int"
export FW_ACTOR="systemd"
export FW_SERVICE_TEMPLATE="deploy-{}.service"

# Create systemd services for each repo (see Systemd Integration section)
forgejo-watcher sync --trigger

Test your setup:

forgejo-watcher trigger team/project-a
# dummy: logs execution
# systemd: starts the service

Troubleshooting

Can't reach Forgejo?

Verify URL, check connectivity, confirm no firewall blocking.

API token invalid?

Verify token at Forgejo Settings → Applications. Ensure API permissions. Try without token if allowed.

Python3-dbus not found?

sudo apt install python3-dbus
pipx install --system-site-packages git+ssh://git@forge.sebatec.eu/sebatec-eu/forgejo-watcher.git@main

Can't read/write state file?

mkdir -p ~/.local/state/forgejo-watcher
chmod 700 ~/.local/state/forgejo-watcher

Systemd service won't start?

systemctl --user list-unit-files | grep deploy          # Service exists?
systemctl --user status deploy-team-project-a.service   # Status?
journalctl --user -u deploy-team-project-a.service      # Logs?
systemctl --user daemon-reload                          # Reload systemd

Testing

Run all tests:

uv run pytest tests/ -v

Run integration tests (requires systemd user mode):

uv run pytest tests/test_systemd_integration.py --run-integration -v

Run with coverage:

uv run pytest tests/ --cov=src/forgejo_watcher --cov-report=term-missing

Contributing

Found a bug? Have a feature idea? Open an issue.

When reporting:

  • Environment (OS, Python version)
  • Exact command + output
  • Config (redacted)
  • Relevant logs

License

This project is licensed under the GNU General Public License v3.0 (GPLv3). See the LICENSE file for details.