mappings

package
v0.18.0 Latest Latest
Warning

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

Go to latest
Published: Jun 26, 2026 License: MIT Imports: 15 Imported by: 0

Documentation

Overview

Package mappings owns the declarative repository → Slack-channel configuration: parsing mappings.yaml, the in-memory Provider used at runtime, and the mappings.lock cache that records which entries have been validated. The package replaces the database-backed RepoMappings store.

Index

Constants

View Source
const ChannelMention = "<!channel>"

ChannelMention is the Slack wire token used when an entry omits the `mentions:` key — operators see `@channel` in Slack.

View Source
const DefaultDigestSchedule = "0 9 * * *"

DefaultDigestSchedule is the cron spec used when the digest section is absent or omits `schedule`: 9am every morning, server-local time.

View Source
const LockFileComment = "DO NOT EDIT — regenerated by notifycat on validation"

LockFileComment is the human-facing warning baked into every written lock.

View Source
const LockVersion = 1

LockVersion is the current lock-file schema version.

Variables

This section is empty.

Functions

func LockPath

func LockPath(yamlPath string) string

LockPath derives the lock-file path that lives next to the given mappings.yaml. .yaml / .yml extensions are swapped for .lock; any other suffix gets .lock appended.

func ValidateMappings added in v0.18.0

func ValidateMappings(m map[string]Org) error

ValidateMappings runs the same per-org/per-tier structural checks as Parse over a mappings map. An empty map (no orgs) is valid. Returns an error for invalid org names, empty orgs, bad repo keys, malformed channel IDs, or any repo tier that cannot resolve a channel.

func WriteLock

func WriteLock(path string, l Lock) error

WriteLock writes the lock atomically (write to tmp + rename).

Types

type Defaults added in v0.18.0

type Defaults struct {
	Reactions        store.Reactions
	IgnoreAIReviews  bool
	DependabotFormat bool
}

Defaults is the global tier: the config.yaml top-level behavioral settings that per-repo tiers override.

type Diff

type Diff struct {
	Needs []Entry
	Stale []string
}

Diff is the result of DiffEntries: entries needing validation + lock keys that should be dropped on the next write.

func DiffEntries

func DiffEntries(current []Entry, lock Lock) Diff

DiffEntries compares the current entries against the lock and returns which entries need validation (new or hash-changed) and which lock keys are stale (present in the lock but not in the file).

type DigestConfig added in v0.16.0

type DigestConfig struct {
	Enabled  bool
	Schedule string
}

DigestConfig is the optional global `digest:` section: a scheduled reminder that lists open PRs nobody has touched since the previous day. It is a global parameter — one schedule for every org/repo, not per-entry. The section is optional and the feature is on by default, so an absent section behaves like `{enabled: true}` with the default schedule.

func (*DigestConfig) UnmarshalYAML added in v0.16.0

func (d *DigestConfig) UnmarshalYAML(node *yaml.Node) error

UnmarshalYAML walks the mapping node by hand (like Org) so we can default Enabled to true — distinguishing a missing `enabled:` key from an explicit `enabled: false` — while keeping KnownFields-style rejection of typos.

type Entry

type Entry struct {
	Org      string
	Repo     string // empty when Wildcard is true
	Wildcard bool
	Channel  string
	Mentions []string
}

Entry is one validation unit: an explicit (org, repo) pair or an (org, "*") wildcard. Each entry has its own hash in mappings.lock.

func (Entry) Hash

func (e Entry) Hash() string

Hash is the cache key for an entry: sha256 over canonical JSON of the validation-relevant fields. Mentions are deliberately excluded — they only affect message formatting at Slack-send time, not anything the validator checks (channel membership, bot scopes, webhook events). A mention edit shouldn't invalidate the entry's cache.

func (Entry) Key

func (e Entry) Key() string

Key returns the lock-file key for the entry: "org/repo" or "org/*".

type File

type File struct {
	Digest   *DigestConfig  `yaml:"digest"`
	Mappings map[string]Org `yaml:"mappings"`
}

File is the parsed mappings.yaml document.

func Parse

func Parse(r io.Reader) (File, error)

Parse reads + validates the YAML document. Unknown keys and shape errors are returned as errors (the server fails fast at startup).

`mentions:` is optional: an absent key means "ping @channel"; `mentions: []` means "ping nobody"; `mentions: null` is rejected (ambiguous).

type FileNotFoundError added in v0.7.0

type FileNotFoundError struct {
	Path string
	Err  error
}

FileNotFoundError is returned by Load when the mappings file cannot be opened.

func (*FileNotFoundError) Error added in v0.7.0

func (e *FileNotFoundError) Error() string

func (*FileNotFoundError) Unwrap added in v0.7.0

func (e *FileNotFoundError) Unwrap() error

type Lock

type Lock struct {
	Comment string               `json:"_comment,omitempty"`
	Version int                  `json:"version"`
	Entries map[string]LockEntry `json:"entries"`
}

Lock is the on-disk validation cache.

func MergeLock

func MergeLock(old Lock, validated map[string]LockEntry, drop []string) Lock

MergeLock returns a new Lock built from `old` by adding the `validated` entries and dropping the keys in `drop`.

func ReadLock

func ReadLock(path string) (Lock, error)

ReadLock parses path. A missing file returns an empty Lock with no error. A malformed file returns an empty Lock and an error so the caller can warn and continue.

type LockEntry

type LockEntry struct {
	SHA256      string    `json:"sha256"`
	ValidatedAt time.Time `json:"validated_at"`
}

LockEntry records the hash and validation timestamp for one entry.

type Org

type Org map[string]RepoConfig

Org maps each repo name (or the literal "*") to its tier config. The "*" key is the org-level tier: it both supplies defaults that explicit repo tiers inherit and matches any repo not named explicitly.

type ParseError added in v0.7.0

type ParseError struct {
	Path string
	Err  error
}

ParseError is returned by Load when the mappings file cannot be parsed.

func (*ParseError) Error added in v0.7.0

func (e *ParseError) Error() string

func (*ParseError) Unwrap added in v0.7.0

func (e *ParseError) Unwrap() error

type Provider

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

Provider serves repository → mapping lookups from a parsed mappings.yaml. Construct with Load; safe for concurrent reads (no mutation after Load).

func Load

func Load(path string) (*Provider, error)

Load reads and validates the file at path.

func NewProvider added in v0.17.0

func NewProvider(defaults Defaults, m map[string]Org, digest *DigestConfig) *Provider

NewProvider builds a Provider from already-decoded sections (config.yaml's `mappings:` map and `digest:` block), the in-memory counterpart to Load. A nil digest leaves the feature on by default (see Digest).

func (*Provider) Digest added in v0.16.0

func (p *Provider) Digest() DigestConfig

Digest returns the effective stuck-PR digest configuration. The feature is enabled by default, so an absent `digest:` section yields {Enabled: true, Schedule: DefaultDigestSchedule}. An explicit section may disable it or override the schedule.

func (*Provider) DigestFor added in v0.18.0

func (p *Provider) DigestFor(repository string) DigestConfig

DigestFor returns the effective digest config for a repository: the global Digest() merged with the org/* and org/repo tiers (most-specific tier that sets enabled/schedule wins). An unmapped repo yields the global digest.

func (*Provider) Entries

func (p *Provider) Entries() []Entry

Entries returns validation units in deterministic order: orgs A→Z, explicit repos within each org A→Z, the wildcard entry last. Each entry's Channel is the resolved channel (the tier's own, or inherited from org/*), so the validator and lock operate on what a webhook would actually route to.

func (*Provider) Get

func (p *Provider) Get(_ context.Context, repository string) (store.RepoMapping, error)

Get returns the resolved mapping for "org/repo": the org/repo tier merged over the org/* tier. Returns store.ErrNotFound when the org is unmapped or neither an explicit tier nor a wildcard tier matches.

func (*Provider) Schedules added in v0.18.0

func (p *Provider) Schedules() []string

Schedules returns the sorted distinct set of effective digest schedules across every mapping entry whose effective digest is enabled. The scheduler registers one cron per returned spec.

type ReactionsOverride added in v0.18.0

type ReactionsOverride struct {
	Enabled       *bool
	NewPR         *string
	MergedPR      *string
	ClosedPR      *string
	Approved      *string
	Commented     *string
	RequestChange *string
	BotReview     *string
}

ReactionsOverride is a tier's optional reaction overrides; each nil field inherits from a less-specific tier (org/* then the global config.yaml set).

func (*ReactionsOverride) UnmarshalYAML added in v0.18.0

func (r *ReactionsOverride) UnmarshalYAML(node *yaml.Node) error

UnmarshalYAML walks the reactions mapping by hand so unknown keys are rejected and every leaf is optional (nil = inherit).

type RepoConfig added in v0.18.0

type RepoConfig struct {
	Channel         string
	Mentions        []string
	MentionsPresent bool

	Reactions        *ReactionsOverride
	IgnoreAIReviews  *bool
	DependabotFormat *bool
	Digest           *DigestConfig
}

RepoConfig is one tier (org/repo or org/*). Phase A carries routing only. An empty Channel means this tier does not set a channel (it inherits). MentionsPresent distinguishes an absent mentions key (inherit) from an explicit empty list (ping nobody).

func (*RepoConfig) UnmarshalYAML added in v0.18.0

func (rc *RepoConfig) UnmarshalYAML(node *yaml.Node) error

UnmarshalYAML walks the mapping node by hand so we can keep the mentions tri-state (absent vs [] vs null) and reject unknown keys, mirroring the 0.17 Org decoder but at the per-repo tier level.

type Resolved added in v0.18.0

type Resolved struct {
	Channel  string
	Mentions []string
}

Resolved is the effective routing config for one repository after merging the org/repo tier over the org/* tier.

Jump to

Keyboard shortcuts

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