registry

package
v0.21.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 10, 2026 License: MIT Imports: 12 Imported by: 0

Documentation

Overview

Package registry holds the set of repositories served by the stackit server, keyed by a stable repoID. Each entry owns the per-repository engine, GitHub client, event broadcaster, and ref watcher — so multiple repositories can be served from one process without leaking events between them.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Broadcaster

type Broadcaster struct {
	// contains filtered or unexported fields
}

Broadcaster fans out SSE messages to subscribed clients. Each RepoEntry owns one so events stay scoped to a single repo — a client subscribed to /repos/A/events never receives B's refresh notifications.

func NewBroadcaster

func NewBroadcaster() *Broadcaster

NewBroadcaster creates a new event broadcaster.

func (*Broadcaster) Broadcast

func (b *Broadcaster) Broadcast(event, data string)

Broadcast sends an SSE event to every subscribed client. Slow clients drop the message rather than blocking the broadcast.

func (*Broadcaster) Close

func (b *Broadcaster) Close()

Close notifies all subscribers to exit and prevents future broadcasts. It closes every active client channel so SSE handlers blocked on a receive unblock immediately. Safe to call multiple times.

func (*Broadcaster) Done

func (b *Broadcaster) Done() <-chan struct{}

Done returns a channel that closes when the broadcaster is shutting down. SSE handlers select on this to exit promptly during server shutdown.

func (*Broadcaster) Subscribe

func (b *Broadcaster) Subscribe() (chan string, bool)

Subscribe returns a buffered channel that receives every broadcast message until Unsubscribe is called or the broadcaster is closed. The bool is false when the broadcaster is already shut down.

func (*Broadcaster) Unsubscribe

func (b *Broadcaster) Unsubscribe(ch chan string)

Unsubscribe removes ch from the broadcast set and closes it. Safe to call from a defer even if ch was already removed (e.g. by Close).

type EntryConfig

type EntryConfig struct {
	ID          string
	DisplayName string
	RepoRoot    string
	Remote      string
	// AddedBy is the GitHub login of the user who onboarded this repo, or
	// empty for operator-seeded repos. Handlers use it to scope per-user
	// visibility so a user only sees repos they added.
	AddedBy string
	// Managed marks a server-owned mirror checkout (DB-backed / onboarded under
	// the repos root) that the sync loop may mirror-fetch. The -cwd dev repo is
	// the operator's own working tree and is left unmanaged.
	Managed bool
	// Owner and Name are the GitHub coordinates, set for managed repos. The
	// sync loop uses them to resolve a GitHub App installation token for the
	// fetch.
	Owner  string
	Name   string
	Engine engine.Engine
	GitHub github.Client
}

EntryConfig is the input to NewEntry. It captures the per-repo state the constructor needs to wire up the broadcaster + watcher.

type Registry

type Registry struct {
	// contains filtered or unexported fields
}

Registry is a goroutine-safe collection of RepoEntries keyed by ID, with a secondary index keyed by owner/repo so handlers can resolve the GitHub-style /repos/{owner}/{repo} routes.

func New

func New() *Registry

New returns an empty registry.

func (*Registry) Add

func (r *Registry) Add(e *RepoEntry) error

Add inserts an entry. The entry's ID must be non-empty, match the allowed pattern, and be unique within the registry. When the entry carries GitHub coordinates (Owner and Name), they must also be unique so the owner/repo index stays unambiguous.

func (*Registry) Close

func (r *Registry) Close() error

Close runs every entry's closers and returns the joined error. The registry is unusable afterwards.

func (*Registry) FindManaged added in v0.20.0

func (r *Registry) FindManaged(owner, name string) (*RepoEntry, bool)

FindManaged returns the managed entry whose GitHub owner/name match the arguments case-insensitively, or false when none does. The interval sync loop and the webhook receiver use it to map a remote-change signal (a push for owner/name) onto the local checkout to refresh.

Only managed mirrors are considered: the unmanaged -cwd working repo must never be selected here, because the sync path mirror-fetches into a detached HEAD and would corrupt a real working tree (see safety-invariants.md).

func (*Registry) Get

func (r *Registry) Get(id string) (*RepoEntry, bool)

Get returns the entry for id. The bool is false when no such entry exists, matching the comma-ok idiom of map access.

func (*Registry) GetByOwnerRepo added in v0.20.0

func (r *Registry) GetByOwnerRepo(owner, repo string) (*RepoEntry, bool)

GetByOwnerRepo returns the entry for owner/repo, matched case-insensitively. The bool is false when no such entry exists or the coordinates are empty.

func (*Registry) Len

func (r *Registry) Len() int

Len reports the number of registered entries.

func (*Registry) List

func (r *Registry) List() []*RepoEntry

List returns every entry sorted by ID so callers (and tests) get a stable ordering for serialization.

type RepoEntry

type RepoEntry struct {
	ID          string
	DisplayName string
	RepoRoot    string
	Remote      string
	// AddedBy is the GitHub login of the user who onboarded this repo, or
	// empty for operator-seeded repos visible to everyone.
	AddedBy string
	// Managed marks a server-owned mirror checkout the sync loop may
	// mirror-fetch (see EntryConfig.Managed).
	Managed bool
	// Owner and Name are the GitHub coordinates, set for managed repos; the
	// sync loop resolves an installation token from them.
	Owner  string
	Name   string
	Engine engine.Engine
	GitHub github.Client

	Broadcaster *Broadcaster
	Watcher     *watcher.RefWatcher
	// contains filtered or unexported fields
}

RepoEntry is the per-repository state required to serve API requests for one repo. Constructed via NewEntry so the watcher + broadcaster lifecycle stays consistent across call sites; raw struct literals are still usable in tests where neither is needed.

func NewEntry

func NewEntry(cfg EntryConfig) *RepoEntry

NewEntry constructs a ready-to-use entry with a broadcaster, a ref watcher (when RepoRoot is set), and closers that cleanly tear down both when registry.Close is called. The watcher goroutine starts immediately.

func (*RepoEntry) AddCloser

func (e *RepoEntry) AddCloser(fn func() error)

AddCloser registers a teardown function that Registry.Close will invoke in reverse-registration order. Use it to attach lifecycle hooks (logger closers, etc.) when constructing an entry.

func (*RepoEntry) Refresh added in v0.20.0

func (e *RepoEntry) Refresh()

Refresh rebuilds the engine from current refs and broadcasts a "refresh" event (plus "branch_switched" when the served HEAD changed) so connected SSE clients refetch. It is the shared post-change path invoked by both the ref watcher (local changes) and the sync loop (remote changes); refreshMu serializes the two so the engine isn't rebuilt concurrently.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL