Skip to content

Instantly share code, notes, and snippets.

@JoelLarson
Last active April 18, 2026 18:53
Show Gist options
  • Select an option

  • Save JoelLarson/70cd8f40bef6fc0ed95eb0c3a24716d3 to your computer and use it in GitHub Desktop.

Select an option

Save JoelLarson/70cd8f40bef6fc0ed95eb0c3a24716d3 to your computer and use it in GitHub Desktop.

Objective

Provide a decoupled, consumer-driven system that triggers repository workflows when upstream repositories publish tags or releases, without requiring any changes to producer repositories (e.g., quark-compiler).


Problem

Forgejo Actions supports:

  • Repo-local triggers (push.tags, release)
  • Manual/API triggers (workflow_dispatch)

It does not support:

  • Cross-repo event subscriptions
  • Platform-wide workflow triggers

Result: No native way for repo B to react to repo A’s release without coupling.


Solution Overview

fordgejo-eventd introduces a local event routing service (forgejo-eventd) that:

  1. Receives all tag/release events via Forgejo org/system webhook.
  2. Normalizes events into a consistent internal format.
  3. Discovers consumer subscriptions defined inside repo workflows.
  4. Dispatches matching workflows via workflow_dispatch.

Architecture

Forgejo (org/system webhook)
→ forgejo-eventd (local service)
→ scan workflow metadata
→ match subscriptions
→ POST workflow_dispatch → consumer repo


Key Design Principles

  • Producer-agnostic: upstream repos (e.g., quark-compiler) require zero changes
  • Consumer-owned subscriptions: each repo declares what it listens to
  • Colocated definition: subscription is tied directly to the workflow it triggers
  • No inference as source of truth: avoid relying on flake.lock or comments

Subscription Model

Location

Inside workflow files in .forgejo/workflows/

Format (embedded metadata)

x-quark-subscription:  
  version: 1  
  match:  
    repo: quark/quark-compiler  
    events:  
      - tag_pushed  
      - release_published  
    tag_pattern: '^v[0-9]+\.[0-9]+\.[0-9]+$'  
  dispatch:  
    workflow: refresh-compiler-input.yml  
    ref: main  
    inputs:  
      dependency_name: quark-compiler

Execution trigger (standard Forgejo)

on:  
  workflow_dispatch:  
    inputs:  
      source_repo:  
        type: string  
        required: true  
      source_event:  
        type: string  
        required: true  
      source_tag:  
        type: string

Event Flow

  1. quark-compiler publishes tag/release
  2. Forgejo sends webhook → forgejo-eventd
  3. forgejo-eventd normalizes event:
    {  
      "repo": "quark/quark-compiler",  
      "event": "release_published",  
      "tag": "v1.2.3"  
    }
  1. forgejo-eventd scans indexed subscriptions

  2. Matching workflows are dispatched via:

    POST /repos/{owner}/{repo}/actions/workflows/{workflow}/dispatches


Optional Enhancements

  • flake.lock inspection
    • Used for suggestions only, not triggering
  • Sidecar metadata files
    • Fallback if Forgejo rejects unknown YAML keys:

      workflow.yml
      workflow.quark.yaml


Required Components

forgejo-eventd service

  • Webhook receiver (with signature validation)
  • Subscription indexer (scans repos)
  • Matcher (repo + event + tag pattern)
  • Dispatcher (calls Forgejo API)
  • Deduplication store (event_id + subscription_id)

Constraints

  • Relies on Forgejo webhooks (org or system scope)
  • Uses workflow_dispatch as the only cross-repo execution entrypoint
  • Custom metadata (x-forgejo-eventd-subscription) is not native Forgejo syntax

Risks / Open Questions

  1. Workflow YAML tolerance
    • Unknown keys (x-forgejo-eventd-*) may not be officially supported → must test
  2. Event duplication
    • Requires explicit idempotency handling
  3. Token scope
    • Service must hold credentials to dispatch workflows across repos

Verification Plan

  1. Configure org-level webhook → forgejo-eventd
  2. Add one subscription to quark-nix
  3. Publish test tag in quark-compiler
  4. Confirm:
    • event received
    • subscription matched
    • workflow dispatched exactly once
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment