{"id":50573696,"url":"https://github.com/langevc/ops-engine","last_synced_at":"2026-06-04T21:00:15.440Z","repository":{"id":359226819,"uuid":"1245079982","full_name":"LangeVC/ops-engine","owner":"LangeVC","description":"Generic, rate-limited async webhook queue and orchestration engine for GitHub and Forgejo.","archived":false,"fork":false,"pushed_at":"2026-05-31T11:15:14.000Z","size":53,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-05-31T13:09:26.638Z","etag":null,"topics":["automation","devops","forgejo","github-apps","python","rate-limiting","webhook-queue"],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/LangeVC.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","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":"2026-05-20T22:23:32.000Z","updated_at":"2026-05-25T13:58:20.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/LangeVC/ops-engine","commit_stats":null,"previous_names":["langevc/ops-engine"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/LangeVC/ops-engine","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LangeVC%2Fops-engine","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LangeVC%2Fops-engine/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LangeVC%2Fops-engine/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LangeVC%2Fops-engine/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/LangeVC","download_url":"https://codeload.github.com/LangeVC/ops-engine/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LangeVC%2Fops-engine/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33917202,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-04T02:00:06.755Z","response_time":64,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":["automation","devops","forgejo","github-apps","python","rate-limiting","webhook-queue"],"created_at":"2026-06-04T21:00:13.644Z","updated_at":"2026-06-04T21:00:15.376Z","avatar_url":"https://github.com/LangeVC.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003eOps Engine\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cem\u003eEvent-driven multi-org release orchestration for GitHub and Forgejo.\u003c/em\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/version-2.0.0-blue.svg\" alt=\"Version 2.0.0\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/python-3.10+-blue.svg\" alt=\"Python 3.10+\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/License-Apache%202.0-green.svg\" alt=\"License: Apache 2.0\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/architecture-config--driven-orange.svg\" alt=\"Architecture: Config-Driven\"\u003e\n\u003c/p\u003e\n\n---\n\n## The Problem\n\nWhen managing multiple organizations and repositories, relying purely on GitHub Actions for organizational hygiene quickly drains runner capacity and exhausts API rate limits. Beyond that, release automation, auto-merge, and cross-forge mirror verification require custom glue code scattered across repos.\n\n## The Solution\n\n`ops-engine` is a pure-infrastructure Python framework that captures incoming webhooks from git forges, places them in a bounded async queue, and processes them through configurable handler modules. Zero business logic — all behavior is driven by YAML config.\n\n### Modules\n\n| Module | Description | Trigger |\n|--------|-------------|---------|\n| **Rate Limit Queue** | Bounded async queue with backpressure, retry, dead letter, and metrics | Always |\n| **Triage \u0026 Auto-Labeling** | Labels PRs and issues based on title keywords | Webhook |\n| **Stale Cleanup** | Marks and closes old issues across all orgs | Cron |\n| **Dependency Triggers** | Fires `repository_dispatch` on downstream repos when upstream releases | Webhook |\n| **Cron Dispatcher** | Centralized sequential cron (CodeQL, etc.) | Cron |\n| **Release Automation** | Creates GitHub/Forgejo releases on tag push with CHANGELOG parsing | Webhook |\n| **Auto-Merge** | Merges PRs when CI passes and trigger label is present | Webhook |\n| **Mirror Verification** | Verifies cross-forge mirror sync (Forgejo ↔ GitHub) | Webhook |\n| **Notifications** | Multi-channel alerts (webhook, Slack, Discord) with event filtering | Event |\n| **Event Deduplication** | In-memory webhook dedup for GitHub, Forgejo, and Gitea delivery IDs | Always |\n| **Health Monitor** | Scheduled HTTP probes with pluggable sinks (stdout / file / webhook / GitHub Issue). Replaces the anti-pattern of committing health logs back to the source repo. | Cron |\n| **Migration Runner** | Forward-only SQL migration tracker with `schema_migrations` table, drift checks, lock-timeout backoff, and `CREATE INDEX CONCURRENTLY` handling. Optional `[postgres]` extra. | Cron + Manual |\n\n---\n\n## Architecture: The 3-Layer Model\n\n```\nLayer 1: ops-engine (this package — pip install)\n  QueueManager, ForgeAdapter, Handler Modules, Config Models\n\nLayer 2: xyz-ops (your org layover — private repo per org)\n  FastAPI app, config.yml, webhook endpoints, cron loop\n\nLayer 3: .ops.yaml (per-repo overrides — optional)\n  Repo-specific config that overrides org defaults\n```\n\n`ops-engine` has **no dependency** on any specific CI system, AI tool, or orchestration framework. The interface is the webhook. A `git push --tags` produces the same result regardless of what triggered it.\n\n---\n\n## Quickstart\n\n```bash\npip install git+https://github.com/LangeVC/ops-engine.git\n```\n\n### Layover Example\n\n```python\nimport asyncio\nfrom fastapi import FastAPI, Request\nfrom ops_engine import (\n    QueueManager, QueueMetrics, EventDeduplicator,\n    TriageHandler, ReleaseHandler, MergeHandler,\n    OpsEngineConfig,\n)\nfrom ops_engine.adapters.github_adapter import GithubAdapter\n\napp = FastAPI()\nqueue = QueueManager(rate_limit_delay_seconds=1.0, max_queue_size=1000)\ndedup = EventDeduplicator()\nadapter = GithubAdapter(token=\"...\", webhook_secret=\"...\")\n\nconfig = OpsEngineConfig(...)  # loaded from config.yml\n\n@app.post(\"/webhooks/github\")\nasync def github_webhook(request: Request):\n    headers = dict(request.headers)\n    payload = await request.body()\n\n    # Dedup\n    delivery_id = dedup.extract_delivery_id(headers)\n    if dedup.is_duplicate(delivery_id):\n        return {\"status\": \"duplicate\"}\n\n    # Verify signature\n    if not adapter.verify_signature(payload, headers):\n        return {\"status\": \"invalid\"}, 401\n\n    event = await adapter.parse_webhook(headers, payload)\n    repo_config = config.get_repo_config(org, repo)\n\n    # Enqueue handlers\n    if repo_config.release and repo_config.release.enabled:\n        await queue.enqueue(adapter, event,\n            lambda a, e: ReleaseHandler.process_event(a, e, repo_config.release))\n\n    if repo_config.auto_merge and repo_config.auto_merge.enabled:\n        await queue.enqueue(adapter, event,\n            lambda a, e: MergeHandler.process_event(a, e, repo_config.auto_merge))\n\n    return {\"status\": \"queued\"}\n```\n\n---\n\n## Config Reference\n\nAll modules are configured via YAML in your org layover's `config.yml`:\n\n### Release\n\n```yaml\nrelease:\n  enabled: true\n  trigger: \"tag_push\"         # tag_push | merge_label | both\n  tag_pattern: \"v*\"           # fnmatch pattern\n  changelog_path: \"CHANGELOG.md\"\n  draft: false\n```\n\n### Auto-Merge\n\n```yaml\nauto_merge:\n  enabled: true\n  trigger_label: \"auto-merge\"\n  merge_method: \"squash\"      # squash | merge | rebase\n  required_checks: [\"test\", \"lint\"]\n  delete_branch: true\n```\n\n### Mirror Verification\n\n```yaml\nmirror:\n  enabled: true\n  primary_forge: \"forgejo\"    # forgejo | github\n  mirror_url: \"github.com/org/repo\"\n  verify_on_push: true\n  max_drift_seconds: 300\n```\n\n### Notifications\n\n```yaml\nnotifications:\n  enabled: true\n  channels:\n    - type: \"slack\"           # webhook | slack | discord\n      url: \"${SLACK_WEBHOOK_URL}\"\n      events: [\"release\", \"mirror_drift\"]\n```\n\n### Migration Runner\n\nForward-only SQL migration tracker. Install the optional Postgres driver\n(`pip install 'ops-engine[postgres]'`) on layovers that use this module. One\ntarget per service; the layover wires `check_pending()` to its cron loop and\n`apply_pending()` to an admin endpoint.\n\n```yaml\nmigrations:\n  \u003cservice-name\u003e:\n    db_url_env: SERVICE_DB_URL\n    source:\n      type: git              # git | local\n      url: https://github.com/Org/service.git\n      ref: main\n      subpath: migrations\n      token_env_var: GITHUB_APP_TOKEN\n    table_name: schema_migrations\n    lock_timeout: 5s\n    max_retries: 5\n    check_interval_seconds: 900\n    mode: manual_apply       # manual_apply (safe) | auto_apply (ephemeral only)\n```\n\nThe runner stores `(version, applied_at, checksum, applied_by)` in the\nconfigured table, detects file drift via sha256 checksums, retries on\n`lock_timeout` with backoff `[2, 5, 10, 20, 30]s`, and automatically pulls\n`CREATE INDEX CONCURRENTLY` out of the per-file transaction (Postgres\nrequirement). The siblings `lvc-ops` and `fusionaize-ops` layovers can opt in\nby adding the same `migrations:` block — only `capacium-ops` wires it today.\n\n### Config Inheritance\n\nOrg-level defaults are inherited by all repos. Repo-level config overrides org defaults. Mirror config is repo-specific only (no org inheritance).\n\n```yaml\nMyOrg:\n  # Org defaults — inherited by all repos\n  auto_triage:\n    add_needs_triage_label: true\n  stale_management:\n    days_until_stale: 60\n\n  repositories:\n    my-repo:\n      release:\n        enabled: true\n        trigger: \"tag_push\"\n      # Inherits org-level auto_triage and stale_management\n```\n\n---\n\n## Development\n\n```bash\ngit clone https://github.com/LangeVC/ops-engine.git\ncd ops-engine\npython3 -m venv .venv \u0026\u0026 source .venv/bin/activate\npip install -e \".[dev]\"\npytest tests/ -v\n```\n\n---\n\n## Community \u0026 Support\n\n- **Contributing:** See [CONTRIBUTING.md](CONTRIBUTING.md)\n- **Security:** See [SECURITY.md](SECURITY.md)\n- **Code of Conduct:** See [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md)\n\n## License\n\nLicensed under the Apache License 2.0. See [LICENSE](LICENSE) for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flangevc%2Fops-engine","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flangevc%2Fops-engine","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flangevc%2Fops-engine/lists"}