pr

package
v0.17.2 Latest Latest
Warning

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

Go to latest
Published: Sep 15, 2026 License: Apache-2.0 Imports: 15 Imported by: 0

Documentation

Index

Constants

View Source
const (
	StaleGroupHeader     = "Stale (behind base, failing checks green there -- refresh first)"
	RefreshedGroupHeader = "Refreshed (re-checking)"
	CancelledGroupHeader = "Cancelled (CircleCI auto-cancelled the build -- retry first)"
	RetriedGroupHeader   = "Retried (re-checking)"
	// NoVerdictGroupHeader names the bucket for checks that established
	// nothing about the code. Each entry's detail names its own remedy.
	NoVerdictGroupHeader = "CI unavailable (no verdict) -- the checks decided nothing, see the detail"
	// ObsoleteGroupHeader names the bucket for bot PRs that are not worth
	// fixing. Each entry's detail says whether a higher-version sibling
	// replaced it or its diff changes nothing that executes.
	ObsoleteGroupHeader = "Obsolete (nothing to fix -- close these)"
)

Section headers shared by the plain-text and TUI summaries for the stale and cancelled buckets, so both outputs name them identically.

View Source
const BudgetEnforced = false

BudgetEnforced reports whether this build enforces the rescue budget. It is false until the platform accepts a budget on a run and reports the cost of a finished one. It stays false once RescuesDispatched is true and the budget still needs the platform.

View Source
const LabelPrefix = "bot-prs-sweep/"

LabelPrefix is the namespace of the one classification label the sweep keeps on every bot PR it touched. Labels are display only: no guard reads them back.

View Source
const MarkerKindEvidence = "evidence"

MarkerKindEvidence marks a comment the sweep wrote as evidence of a guard decision or an action it performed. Evidence carries the head SHA and the fingerprint like a rescue marker so a later sweep can tell whether the same evidence already stands for the current change.

View Source
const RescuesDispatched = false

RescuesDispatched reports whether this build dispatches a rescue. It is false until the rescue agent runs on an installation. While it is false every bound in RescuePolicy is declared and none of them applies, so a sweep must say so rather than let a team read its file as a guarantee.

Variables

This section is empty.

Functions

func AdjustColumnWidths

func AdjustColumnWidths(cols []TableColumn, prs []PRInfo)

AdjustColumnWidths widens each column so every value fits without truncation.

func AgeColorCode

func AgeColorCode(created, now time.Time) string

AgeColorCode returns the ANSI color prefix for a PR age: "" while the PR is fresh, yellow once it passes ageWarnAfter, red once it passes ageStaleAfter. Callers must reset with \033[0m when non-empty.

func AgeColorFunc

func AgeColorFunc(p PRInfo) string

AgeColorFunc is the table-column color accessor for the Age column.

func AgeDays

func AgeDays(created, now time.Time) int

AgeDays returns the whole number of days since created, or 0 for a zero creation time.

func AgeInfoFunc

func AgeInfoFunc(p PRInfo) string

AgeInfoFunc is the table-column accessor for the Age column.

func AuthorInfoFunc

func AuthorInfoFunc(p PRInfo) string

func ChangeID

func ChangeID(title string) string

ChangeID fingerprints a dependency-update PR by what it updates: the dependency name and the target version parsed from the PR title, e.g. "typescript@v7" for "chore(deps): update dependency typescript to v7". Titles naming no single dependency or no version ("Update all non-major dependencies", "Lock file maintenance") yield "".

func ColorizeRescue

func ColorizeRescue(m *RescueMarker, now time.Time) string

ColorizeRescue wraps the FormatRescue annotation in ANSI colors: a stale marker renders yellow (retriable -- the change differs from the one attempted), a fresh one bold red (a rescue already failed on exactly this change, whether or not the branch was rebased since; a human is needed).

func ColorizeStatus

func ColorizeStatus(state StatusState, detail string) string

func DependencyInfoFunc

func DependencyInfoFunc(p PRInfo) string

func DisableLineWrap

func DisableLineWrap(w *os.File)

DisableLineWrap turns off the terminal's auto-wrap (DECAWM) so rows wider than the terminal are clipped at the right edge instead of wrapping onto a second physical line. UpdateTable's cursor-up math counts logical rows, so a wrapped row would desync the redraw and produce the garbled output users see when CI detail strings push a line past the terminal width.

func EnableLineWrap

func EnableLineWrap(w *os.File)

EnableLineWrap restores the terminal's auto-wrap mode. Always pair it with DisableLineWrap (typically via defer) so an early return does not leave the user's terminal in a clipped state.

func ExtractDependencyName

func ExtractDependencyName(title string) string

func ExtractOwnerRepo

func ExtractOwnerRepo(htmlURL string) (string, string, error)

ExtractOwnerRepo parses owner and repo from a GitHub HTML URL (e.g. "https://github.com/OWNER/REPO/pull/123").

func ExtractTargetVersion

func ExtractTargetVersion(title string) string

ExtractTargetVersion returns the version a dependency update moves to, regardless of whether the title also names the version it moves from.

func ExtractVersion

func ExtractVersion(title string) string

ExtractVersion renders the version change named in a title for display: "4.17.20 -> 4.17.21" for a from/to title, "v1.10.2" for a to-only title, "" when the title names no version.

func FormatAge

func FormatAge(created, now time.Time) string

FormatAge renders the time since created as a compact human string ("5h", "3d", "2w"). It returns "" for a zero creation time so callers can omit the column value when the discovery path did not provide it.

func FormatRescue

func FormatRescue(m *RescueMarker, now time.Time) string

FormatRescue renders the marker as a short human-readable annotation for status output, e.g. "rescue failed 1d ago (klaus): ESM-only", "rescue failed 3d ago (klaus), stale: new commits since" or "rescue blocked 5d ago (klaus), rebased since: same change: ESM-only".

func IsDependencyUpdateTitle

func IsDependencyUpdateTitle(title string) bool

IsDependencyUpdateTitle returns true if the PR title looks like an automated dependency update (Renovate or Dependabot), regardless of who authored it.

func LabelClass added in v0.12.0

func LabelClass(s StatusState) string

LabelClass returns the classification label suffix for a final state, or "" for a transient state that never ends a sweep.

func MakeHyperlink(text, url string) string

func NoOpDiff added in v0.11.0

func NoOpDiff(files []*github.CommitFile) bool

NoOpDiff reports whether a PR's changed files carry no semantic change: every changed line is a pinned-SHA `uses:` line in a GitHub workflow whose pinned commit is unchanged and whose trailing version comment is all that moved.

The verdict is conservative, because a wrong "no-op" hides a real change: an empty file list, a file outside .github/workflows or .github/actions, a patch GitHub truncated or omitted, an unequal number of added and removed lines, or one changed line that is not a pinned `uses:` all make it false.

Within a file the removed and added lines are paired by order. A pair of unrelated lines can only make the verdict stricter, because samePinnedAction accepts a pair only when the two lines are equal once the comment is gone.

func PRKey added in v0.11.0

func PRKey(p PRInfo) string

PRKey identifies one PR across repositories.

func ParsePRURL

func ParsePRURL(htmlURL string) (owner, repo string, number int, err error)

ParsePRURL parses owner, repo, and PR number from a GitHub pull request URL (e.g. "https://github.com/OWNER/REPO/pull/123").

func PatchID

func PatchID(files []*github.CommitFile, changedFiles int) string

PatchID computes the content fingerprint of a PR from the files of its compare response (GET /repos/{owner}/{repo}/compare/{base}...{head}). changedFiles is the PR's changed_files count. The compare API returns at most 300 files and omits the patch of a file whose diff is too large, so PatchID returns "" whenever the input cannot describe the whole change rather than fingerprinting part of it.

Per file the hash covers status, previous and current path, and every added or removed line of the patch in order. Hunk headers and context lines are skipped: both shift when the base branch changes around the PR's lines, which is exactly what a rebase produces. A file without a patch and without changed lines (binary content, a pure rename, a mode change) contributes its blob SHA instead.

func PrintPlainResults

func PrintPlainResults(w *os.File, status *PRStatus)

func PrintRow

func PrintRow(w *os.File, e StatusEntry, cols []TableColumn)

func PrintTableHeader

func PrintTableHeader(w *os.File, cols []TableColumn)

func RepoInfoFunc

func RepoInfoFunc(p PRInfo) string

func TrustedLogins added in v0.12.0

func TrustedLogins() []string

TrustedLogins returns the logins of the four trusted bots, sorted.

func UpdateTable

func UpdateTable(w *os.File, entries []StatusEntry, cols []TableColumn)

func VersionInfoFunc

func VersionInfoFunc(p PRInfo) string

Types

type Budget added in v0.13.0

type Budget struct {
	PerRescueUSD float64
	WeeklyUSD    float64
}

Budget is what a team is willing to spend on rescues, in US dollars. Both figures are part of the team contract and neither is enforced yet: a per-rescue budget needs execution_budget_usd on the run, and a weekly budget needs the cost of finished runs to be readable. BudgetEnforced says so in every outcome the sweep records.

type Concurrency added in v0.13.0

type Concurrency struct {
	PerTeam int
	PerRepo int
}

Concurrency bounds how much of a sweep runs at once: PerTeam is the number of repositories swept in parallel, PerRepo the number of PRs of one repository. Two PRs of one repository race for the same base branch, so PerRepo defaults to 1.

type Confirm added in v0.13.0

type Confirm string

Confirm says who confirms a rescue before it is dispatched: the person confirms every PR, or once for the whole sweep.

const (
	ConfirmPerPR    Confirm = "per-pr"
	ConfirmPerSweep Confirm = "per-sweep"
)

type Counts

type Counts struct {
	Merged    int
	Failed    int
	Blocked   int
	NoVerdict int
	Stale     int
	Refreshed int
	Cancelled int
	Retried   int
	Obsolete  int
	Remedied  int
	Waiting   int
	Skipped   int
}

Counts holds the aggregate tallies of a sweep by outcome category.

Failed covers every action-required outcome (plain and security failures, conflicts, untrusted authors). Blocked (CI could not run because of an Actions budget block), NoVerdict (the failing checks established nothing about the code), Stale (failing checks are green on the base branch head and the PR is behind it), Refreshed (a stale branch was just updated from its base), Cancelled (CircleCI auto-cancelled the failing builds), Retried (those builds were just retried) and Obsolete (a sibling PR carries a higher version of the same dependency, or the diff changes nothing that executes) are counted separately from Failed: none of them is a genuine CI failure and none of them belongs in the rescue path.

type Fingerprint

type Fingerprint struct {
	PatchID  string `json:"patch_id,omitempty"`
	ChangeID string `json:"change_id,omitempty"`
}

Fingerprint identifies what a PR changes independently of its head SHA. Renovate force-pushes a rebased branch whenever its base moves, so the head SHA changes while the change itself stays byte-identical; a fingerprint lets a rescue marker survive that.

PatchID is the precise fingerprint: a hash over the PR's compare diff normalised the way `git patch-id --stable` does, minus the context lines -- hunk headers and line numbers are ignored, only file paths and the added/removed lines count. It is empty when the diff could not be fetched or GitHub truncated it (see PatchID).

ChangeID is the cheap approximation for dependency-update PRs: the dependency name and target version parsed from the PR title, e.g. "typescript@v7". A rebase never changes it and a new version always does, but a version update that keeps the title (7.0.1 -> 7.0.2 under "to v7") is invisible to it, so it only decides when no patch ID is available. It is empty when the title is not a recognisable single-dependency update.

func (Fingerprint) Comparable

func (f Fingerprint) Comparable() bool

Comparable reports whether the fingerprint carries anything to compare.

func (Fingerprint) Matches

func (f Fingerprint) Matches(other Fingerprint) bool

Matches reports whether other describes the same change as f. The patch ID decides whenever both sides have one; the title-derived change ID is the fallback for when the diff could not be fingerprinted on either side. Nothing comparable means "not the same" -- a marker is only kept alive on positive evidence.

type Kind added in v0.12.0

type Kind string

Kind is the bot that authored a PR. The sweep touches PRs of these four kinds and nothing else; a human-authored PR has no kind.

const (
	KindRenovate   Kind = "renovate"
	KindAlignFiles Kind = "align-files"
	KindHerald     Kind = "herald"
	KindDependabot Kind = "dependabot"
)

func KindOf added in v0.12.0

func KindOf(login string) Kind

KindOf returns the kind of the bot with the given login, or "" when the login is not one of the four trusted bots.

func (Kind) CarriesVersion added in v0.13.0

func (k Kind) CarriesVersion() bool

CarriesVersion reports whether a PR of this kind names a dependency version. An Align files or Herald PR does not, so its update type is always UpdateNone.

type ObsoleteReason added in v0.11.0

type ObsoleteReason string

ObsoleteReason names why a PR is not worth fixing.

const (
	// ReasonSuperseded: a sibling PR in the same repository carries a
	// higher version of the same dependency.
	ReasonSuperseded ObsoleteReason = "superseded"
	// ReasonNoOp: the diff changes nothing that executes.
	ReasonNoOp ObsoleteReason = "no_op"
)

type PRGroup

type PRGroup struct {
	Key   string
	PRs   []PRInfo
	Count int
}

func GroupByDependency

func GroupByDependency(prs []PRInfo) []PRGroup

func GroupByRepo

func GroupByRepo(prs []PRInfo) []PRGroup

type PRInfo

type PRInfo struct {
	Owner     string
	Repo      string
	Number    int
	Title     string
	URL       string
	Author    string
	CreatedAt time.Time
	// BaseRef is the branch the PR targets. Empty when the PR came from a
	// GitHub issue search, which does not report it.
	BaseRef string
}

type PRStatus

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

func NewPRStatus

func NewPRStatus() *PRStatus

func (*PRStatus) ActionRequired

func (s *PRStatus) ActionRequired() []StatusEntry

ActionRequired returns the failure entries, oldest PR first: the longer a dependency PR has been open, the more sweeps it has already survived, so the old ones are the most likely to need manual work. Entries without a known creation time sort last, in insertion order.

func (*PRStatus) Add

func (s *PRStatus) Add(pr PRInfo) int

func (*PRStatus) BlockedEntries

func (s *PRStatus) BlockedEntries() []StatusEntry

BlockedEntries returns entries whose CI could not run because a GitHub Actions budget / spending-limit block prevented every job from starting. These are deliberately kept out of ActionRequired and the failed counts: the remedy is "raise or await the Actions budget", not "rescue the code".

func (*PRStatus) CancelledEntries

func (s *PRStatus) CancelledEntries() []StatusEntry

CancelledEntries returns entries whose every failing check is a CircleCI build that CircleCI itself cancelled. Like StaleEntries these are kept out of ActionRequired and the failed counts -- the remedy is "retry the build and read the real verdict", not "rescue the code". Oldest PR first.

func (*PRStatus) FormatSummary

func (s *PRStatus) FormatSummary() string

func (*PRStatus) Len

func (s *PRStatus) Len() int

func (*PRStatus) MarkObsolete added in v0.11.0

func (s *PRStatus) MarkObsolete(idx int, reason ObsoleteReason, detail string)

MarkObsolete records an entry as obsolete together with the reason, so a consumer can dispatch on the reason instead of parsing the detail.

func (*PRStatus) MergedEntries

func (s *PRStatus) MergedEntries() []StatusEntry

func (*PRStatus) NoVerdictEntries added in v0.10.2

func (s *PRStatus) NoVerdictEntries() []StatusEntry

NoVerdictEntries returns entries whose every failing check established nothing about the code. Like BlockedEntries they are kept out of ActionRequired and the failed counts: there is nothing to rescue, and each entry's detail names its own remedy. Oldest PR first.

func (*PRStatus) ObsoleteEntries added in v0.11.0

func (s *PRStatus) ObsoleteEntries() []StatusEntry

ObsoleteEntries returns entries that are not worth fixing: a sibling PR carries a higher version, or the diff changes nothing that executes. Like StaleEntries they are kept out of ActionRequired and the failed counts: the remedy is to close them, not to rescue them. Oldest PR first.

func (*PRStatus) RefreshedEntries

func (s *PRStatus) RefreshedEntries() []StatusEntry

RefreshedEntries returns the stale entries whose branch was updated from its base during this run. Their CI is running again; the next sweep decides what they are.

func (*PRStatus) RescueAt

func (s *PRStatus) RescueAt(idx int) *RescueMarker

RescueAt returns the rescue marker attached to the entry at idx, or nil when none is attached or idx is out of range.

func (*PRStatus) RetriedEntries

func (s *PRStatus) RetriedEntries() []StatusEntry

RetriedEntries returns the cancelled entries whose builds were retried during this run. Their CI is running again; the next sweep decides what they are.

func (*PRStatus) SecurityFailedEntries

func (s *PRStatus) SecurityFailedEntries() []StatusEntry

SecurityFailedEntries returns entries that failed specifically because a security-related check reported a problem.

func (*PRStatus) SetClassification added in v0.12.0

func (s *PRStatus) SetClassification(idx int, kind Kind, updateType UpdateType)

SetClassification records what kind of bot PR the entry is and the size of the update it carries.

func (*PRStatus) SetLabel added in v0.12.0

func (s *PRStatus) SetLabel(idx int, label string)

SetLabel records the classification label that is on the PR.

func (*PRStatus) SetPolicy added in v0.13.0

func (s *PRStatus) SetPolicy(idx int, policy Policy)

SetPolicy records the sweep policy resolved for the entry's repository, so every decision the sweep took can be explained afterwards.

func (*PRStatus) SetRescue

func (s *PRStatus) SetRescue(idx int, marker *RescueMarker)

SetRescue attaches a prior rescue-attempt marker to an entry.

func (*PRStatus) SetUnhandled added in v0.17.0

func (s *PRStatus) SetUnhandled(idx int, unhandled *Unhandled)

SetUnhandled records that no rule recognised this PR's failure.

func (*PRStatus) SkippedEntries

func (s *PRStatus) SkippedEntries() []StatusEntry

func (*PRStatus) Snapshot

func (s *PRStatus) Snapshot() []StatusEntry

func (*PRStatus) StaleEntries

func (s *PRStatus) StaleEntries() []StatusEntry

StaleEntries returns entries whose failure is stale: the PR head is behind its base branch and every failing check is green on the base branch head. Like BlockedEntries these are kept out of ActionRequired and the failed counts -- the remedy is "update the branch and let CI re-run", not "rescue the code". Oldest PR first, like ActionRequired.

func (*PRStatus) StateAt

func (s *PRStatus) StateAt(idx int) StatusState

StateAt returns the current state of the entry at idx, or StatusPending when idx is out of range.

func (*PRStatus) Summary

func (s *PRStatus) Summary() Counts

Summary returns aggregate counts across all entries.

func (*PRStatus) UnhandledEntries added in v0.17.0

func (s *PRStatus) UnhandledEntries() []StatusEntry

UnhandledEntries returns the entries whose failure no rule recognised.

func (*PRStatus) Update

func (s *PRStatus) Update(idx int, state StatusState, detail string)

func (*PRStatus) WaitingEntries added in v0.12.0

func (s *PRStatus) WaitingEntries() []StatusEntry

WaitingEntries returns entries whose required checks have not all reported. They are kept out of ActionRequired: the remedy is time.

type Policy added in v0.13.0

type Policy struct {
	// Sweep is false when the repository's exception switches the sweep
	// off. Every PR of that repository is then skipped.
	Sweep bool
	// UpdateTypes lists the update types that merge when green, per bot PR
	// kind. A kind that is absent merges nothing.
	UpdateTypes map[Kind][]UpdateType
	// Schedule reports whether the scheduled sweep runs for the team.
	Schedule     bool
	Rescue       RescuePolicy
	Concurrency  Concurrency
	ModelConfig  string
	SlackChannel string
	// Sources names every file that produced this policy, in the order the
	// files were applied, so one outcome explains its own decision.
	Sources []string
}

Policy is the resolved bot PR sweep policy of one repository: the company defaults, the owning team's deviations and the repository's own exception, merged in that order.

func CompanyDefaults added in v0.13.0

func CompanyDefaults() Policy

CompanyDefaults is the policy that applies where no file says otherwise: patch and minor updates merge when green for Renovate and Dependabot, Align files and Herald PRs carry no version change and always merge, a major and an update whose size could not be read wait for a person. The schedule and the rescues are off.

func (Policy) AllowsUpdate added in v0.13.0

func (p Policy) AllowsUpdate(updateType UpdateType) bool

AllowsUpdate reports whether any bot PR kind of the policy merges this update type.

func (Policy) Clone added in v0.13.0

func (p Policy) Clone() Policy

Clone returns a copy that shares nothing with the original, so one repository's exception cannot reach another repository's policy.

func (Policy) DeclaredUnenforced added in v0.13.0

func (p Policy) DeclaredUnenforced() []string

DeclaredUnenforced names the caps the policy declares that this build does not enforce, so a team is told rather than left to assume. It is empty while the team has the rescues off: a cap on a rescue the team does not want bounds nothing either way.

func (Policy) Eligible added in v0.13.0

func (p Policy) Eligible(kind Kind, updateType UpdateType) bool

Eligible reports whether a green PR of this kind and update type merges under the policy.

type RescueMarker

type RescueMarker struct {
	Tool    string `json:"tool,omitempty"`
	Outcome string `json:"outcome"`
	// Kind tells a rescue record ("" or "rescue") from a sweep evidence
	// record ("evidence"). Evidence never counts as a prior rescue attempt.
	Kind    string    `json:"kind,omitempty"`
	Reason  string    `json:"reason,omitempty"`
	HeadSHA string    `json:"head_sha,omitempty"`
	At      time.Time `json:"at,omitempty"`
	Fingerprint

	// Stale and Rebased are computed by MarkStale, never serialized into
	// the marker. Rebased is set when the head moved but the change did not.
	Stale   bool `json:"-"`
	Rebased bool `json:"-"`
}

RescueMarker records a prior automated rescue attempt on a PR. It is embedded as a machine-readable HTML comment inside an ordinary PR comment, so any tool that can comment on a PR (a klaus agent, a Cursor agent, a GitHub Action) can participate without coupling to marge:

<!-- ai-rescue: {"tool":"klaus","outcome":"failed","reason":"...","head_sha":"...","at":"2026-06-09T18:40:00Z"} -->

HeadSHA ties the attempt to the code it was attempted against. Renovate force-pushes on rebase or version change, so a marker whose head_sha no longer matches the PR head describes code that may no longer exist. The embedded Fingerprint (patch_id, change_id; optional, absent from markers written before it existed) tells the two apart: when the head moved but the fingerprint still matches, the branch was only rebased and the attempt still stands; otherwise it is stale and the PR is fair game for another rescue.

func ParseRescueMarker

func ParseRescueMarker(body string) *RescueMarker

ParseRescueMarker extracts the last ai-rescue marker from a comment body. It returns nil when the body contains no parseable marker; a malformed JSON payload is ignored rather than treated as an error so a mangled comment can never break a sweep.

func (*RescueMarker) CommentBody

func (m *RescueMarker) CommentBody() string

CommentBody renders the full PR comment for this marker: a human-readable summary followed by the machine-readable marker.

func (*RescueMarker) IsEvidence added in v0.12.0

func (m *RescueMarker) IsEvidence() bool

IsEvidence reports whether the marker is sweep evidence rather than the record of a rescue attempt.

func (*RescueMarker) MarkStale

func (m *RescueMarker) MarkStale(currentHeadSHA string, current func() Fingerprint)

MarkStale sets Stale and Rebased by comparing the marker with the PR's current head. Same head (short-vs-full SHA prefixes match): fresh. Head moved: the marker stays fresh, flagged Rebased, when the change it was written for is still the change on the branch -- decided by the marker's fingerprint against the current one, which `current` computes on demand. It is only called when the head moved and the marker carries a fingerprint; nil means none can be computed. Otherwise the marker is stale. A marker without a recorded SHA cannot be aged out, so it stays fresh until a newer marker replaces it.

type RescuePolicy added in v0.13.0

type RescuePolicy struct {
	Enabled bool
	Timeout time.Duration
	Weekly  int
	Budget  Budget
	Confirm Confirm
}

RescuePolicy bounds the rescues of one team. Timeout is the wall-clock budget of a single rescue and is meant to become the run's execution timeout; Weekly is how many rescues the team spends per week. See RescuesDispatched for what this build applies.

type StatusEntry

type StatusEntry struct {
	PR     PRInfo
	State  StatusState
	Detail string
	// Kind and UpdateType are set once the PR has been read; both are
	// empty for a PR the sweep could not fetch.
	Kind       Kind
	UpdateType UpdateType
	// Label is the bot-prs-sweep/<class> label that is on the PR after the
	// sweep; empty when none was written (dry run, or the write failed).
	Label string
	// Rescue is the most recent prior automated rescue attempt found on
	// the PR, if any. Only populated for failure-state entries.
	Rescue *RescueMarker
	// ObsoleteReason says why an obsolete PR is obsolete, for consumers
	// that dispatch on it rather than on Detail. Empty unless State is
	// StatusObsolete.
	ObsoleteReason ObsoleteReason
	// Policy is the sweep policy resolved for this PR's repository. Nil
	// for a PR the sweep could not fetch.
	Policy *Policy
	// Unhandled describes a failure no rule of the catalogue recognised.
	// Nil when a rule matched, or when the state is not one a rule acts on.
	Unhandled *Unhandled
}

func SplitActionRequired

func SplitActionRequired(entries []StatusEntry) (security, other []StatusEntry)

SplitActionRequired partitions the action-required list into security failures and everything else, preserving the input order in each group.

type StatusState

type StatusState int
const (
	StatusPending StatusState = iota
	StatusChecking
	StatusApproving
	StatusMerging
	StatusRetrying
	StatusMerged
	StatusAlreadyMerged
	StatusAutoMerge
	StatusFailed
	StatusFailedSecurity
	StatusBlockedCI
	StatusSkipped
	StatusConflict
	StatusUntrustedAuthor
	// StatusStale marks a failing PR whose head is behind its base branch
	// and whose every failing check is green on the base branch head: the
	// failure was most likely fixed on the base branch after the PR's last
	// build, so the first move is to refresh the branch, not to rescue it.
	StatusStale
	// StatusRefreshed marks a stale PR whose branch was just updated from
	// its base (the "Update branch" button); CI is running again and the
	// next sweep decides.
	StatusRefreshed
	// StatusCancelled marks a failing PR whose every failing check is a
	// CircleCI build that CircleCI itself cancelled (a newer pipeline on the
	// branch, a redundant workflow) rather than one that failed a step.
	// There is no verdict on the code yet: the remedy is a retry, not a
	// rescue.
	StatusCancelled
	// StatusRetried marks a cancelled PR whose builds were just retried on
	// the same commit; CI is running again and the next sweep decides.
	StatusRetried
	// StatusNoVerdict marks a failing PR whose every failing check
	// established nothing about the code: the job was cancelled, or a
	// CircleCI project setting refused the pipeline. There is nothing to
	// rescue, and a security check in this shape is not a finding. The
	// detail names the remedy for each check.
	StatusNoVerdict
	// StatusObsolete marks a bot PR that is not worth fixing: either a
	// sibling PR carries a higher version of the same dependency, or the
	// diff changes nothing that executes. Both want closing, not rescuing.
	// The entry's ObsoleteReason says which, and Detail says why.
	StatusObsolete
	// StatusWaitingChecks marks a PR whose required status checks have not
	// all reported: a required context is missing or pending. The sweep
	// waits; it never merges past a required check.
	StatusWaitingChecks
	// StatusAwaitingApproval marks a green PR that GitHub refused to merge
	// for a review reason the sweep's own approval did not satisfy.
	StatusAwaitingApproval
	// StatusHeld marks a green PR the sweep policy leaves to a person, for
	// instance a major update or one whose update type could not be read.
	StatusHeld
	// StatusEligible marks a green eligible PR the sweep did not merge
	// because the merge action was not selected.
	StatusEligible
	// StatusRemedied marks a PR a rule of the catalogue acted on. The
	// action's evidence names the rule and what it did; the next sweep
	// classifies the result.
	StatusRemedied
)

func (StatusState) String

func (s StatusState) String() string

type SupersededBy added in v0.11.0

type SupersededBy map[string]string

SupersededBy maps a PR to the detail line naming the sibling that supersedes it, keyed by PRKey. It is computed once per sweep from the PR list and read without locking while the PRs are processed.

func FindSuperseded added in v0.11.0

func FindSuperseded(prs []PRInfo) SupersededBy

FindSuperseded returns, for every PR that a sibling supersedes, the detail naming the PR that supersedes it. Two PRs are siblings when they are open on the same repository and the same base branch and their titles name the same dependency; the one whose target version is highest wins and every other one is superseded.

A PR is only ever superseded on positive evidence: both titles must name a dependency and a target version, and both versions must parse as semver. Equal versions supersede nothing, because neither is higher.

PRInfo.BaseRef is empty on the PRs that come from a GitHub issue search, which does not report a base branch. Those group together as before, so a repository with a maintenance branch can group two PRs that target different branches. A supersession never suppresses a merge, so the cost of such a group is a mislabelled report line.

type TableColumn

type TableColumn struct {
	Label string
	Width int
	Fn    func(PRInfo) string
	// Color optionally returns an ANSI color prefix for a value. The
	// value is padded to Width first, then wrapped, so colored cells
	// stay aligned with the rest of the table.
	Color func(PRInfo) string
}

func DependencySelectedColumns

func DependencySelectedColumns() []TableColumn

func FullColumns

func FullColumns() []TableColumn

func RepoSelectedColumns

func RepoSelectedColumns() []TableColumn

type Unhandled added in v0.17.0

type Unhandled struct {
	// Signature identifies the shape of the failure: the failing checks and
	// the log excerpt, normalised.
	Signature string
	// Checks are the failing checks that produced a verdict.
	Checks []string
	// Excerpt is the log excerpt a rule would match against, when one was
	// read. Empty when no rule asked for a log.
	Excerpt string
}

Unhandled is a failure the catalogue does not recognise yet. Its signature groups the same failure across PRs, so a pattern worth a rule is visible as a count rather than as one PR at a time.

type UpdateType added in v0.12.0

type UpdateType string

UpdateType is the semantic size of a dependency update. Align files and Herald PRs carry no version change and are UpdateNone; a Renovate or Dependabot PR whose versions cannot be read is UpdateUnknown.

const (
	UpdateMajor    UpdateType = "major"
	UpdateMinor    UpdateType = "minor"
	UpdatePatch    UpdateType = "patch"
	UpdateDigest   UpdateType = "digest"
	UpdatePin      UpdateType = "pin"
	UpdateLockfile UpdateType = "lockfile"
	UpdateNone     UpdateType = "none"
	UpdateUnknown  UpdateType = "unknown"
)

func ClassifyUpdate added in v0.12.0

func ClassifyUpdate(kind Kind, title, body string) UpdateType

ClassifyUpdate derives the update type of a bot PR from what the bot wrote. Dependabot titles name both versions ("from X to Y"); a Dependabot group names them per dependency in the body. Renovate titles name only the target, so the source comes from the body's Change column. Grouped PRs take the largest change of their rows. Anything that cannot be read is UpdateUnknown, never a guess.

Jump to

Keyboard shortcuts

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