mappings

package
v0.20.1 Latest Latest
Warning

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

Go to latest
Published: Jun 30, 2026 License: MIT Imports: 17 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, in the configured digest timezone (default UTC; see DigestConfig.Timezone).

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
	Timezone string
}

DigestConfig is the `digest:` section: a scheduled reminder that lists open PRs nobody has touched since the previous day. The global section is optional and the feature is on by default, so an absent section behaves like `{enabled: true}` with the default schedule. Per-repo tiers reuse this type to override Enabled/Schedule.

Timezone is global-only — the server runs a single cron clock, so the zone belongs on the global section and is rejected on a per-repo tier. Empty means the default (UTC); config.Load resolves it to a *time.Location and fails fast on an invalid zone.

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
	// PathChannels are the distinct channels a repo's `paths:` rules add on top
	// of Channel (sorted, deduped). They feed both validation (bot membership)
	// and the entry hash, so adding or repointing a path channel re-triggers
	// validation. Always empty for a wildcard entry (paths are named-tier only).
	PathChannels []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 PathRule added in v0.20.0

type PathRule struct {
	Dir             string
	Channel         string
	Mentions        []string
	MentionsPresent bool
}

PathRule is one entry in a repo tier's `paths:` block: a normalized directory and the routing applied to files under it. Channel/Mentions carry the same tri-state inheritance as a repo tier (empty Channel inherits; MentionsPresent distinguishes absent from an explicit empty list).

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) HasPathRules added in v0.20.0

func (p *Provider) HasPathRules() bool

HasPathRules reports whether any repo tier in the mappings configures a `paths:` block. Used to gate the "path routing needs GITHUB_TOKEN" warnings: without paths there is nothing to warn about.

func (*Provider) PathChannels added in v0.20.0

func (p *Provider) PathChannels(repository string) []string

PathChannels returns the distinct channels explicitly set on the repository's path rules (those that override the base channel), in sorted order. The base channel is validated separately; these are the extra Slack channels path routing can post to, so validation must confirm the bot is in each.

func (*Provider) RepoHasPathRules added in v0.20.0

func (p *Provider) RepoHasPathRules(repository string) bool

RepoHasPathRules reports whether the specific repository's tier configures a `paths:` block. The runtime uses it to decide, per webhook, whether fetching the PR's changed files is worthwhile — repos without path rules skip the GitHub call entirely.

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.

func (*Provider) TargetsForFiles added in v0.20.0

func (p *Provider) TargetsForFiles(repository string, files []string) []store.Target

TargetsForFiles returns the fan-out destinations for a PR touching files: one Target per distinct matched channel, mentions unioned within each channel. With no path rules, no files, or no match it returns a single base target.

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

	// Paths is the optional per-directory routing for a monorepo, in
	// declaration order (order is significant for tie-breaking). Only valid on
	// a named repo tier — ValidateMappings rejects it on the "*" tier.
	Paths []PathRule
}

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