Documentation
¶
Overview ¶
Package board picks the next item of work from a GitHub project board: it reads the board's open items once, ranks them by the configured order and says for every item above the pick why it was skipped. Which items a session already serves is the caller's to decide; the rank is pure.
Index ¶
- Constants
- func ByPriority(cands []Candidate, r Ranks, parent func(string) string)
- func Matches(st config.BoardStep, it Item, now time.Time) bool
- func Ref(s string) (owner, repo string, n int, err error)
- func Resolve(field string, values []string, in string) (string, error)
- func ResolveOrder(order []config.BoardStep, statuses, kinds []string) ([]config.BoardStep, error)
- func Unoffered(order []config.BoardStep, it Item, now time.Time) string
- type Candidate
- type Change
- type Client
- func (c *Client) AnswerThread(ctx context.Context, ref string) (*Thread, error)
- func (c *Client) Block(ctx context.Context, ref, by string) (*Held, error)
- func (c *Client) Closes(ctx context.Context, refs []string) (map[string][]string, error)
- func (c *Client) Comment(ctx context.Context, t *Thread, body string) (string, error)
- func (c *Client) Epics(ctx context.Context) ([]Epic, error)
- func (c *Client) FindPlans(ctx context.Context, refs []string) (map[string]*Plan, error)
- func (c *Client) Hold(ctx context.Context, ref string) (*Moved, error)
- func (c *Client) Move(ctx context.Context, ref, in string) (*Moved, error)
- func (c *Client) Open(ctx context.Context, ref string) (bool, error)
- func (c *Client) Priorities(ctx context.Context) (Priorities, error)
- func (c *Client) Rank(ctx context.Context, ref string, at Place) (*Ranked, error)
- func (c *Client) RankedNow(ctx context.Context, ref string) (*RankedEpic, error)
- func (c *Client) Read(ctx context.Context, now time.Time) (*Snapshot, error)
- func (c *Client) RecordGo(ctx context.Context, g *Go, body string) (string, error)
- func (c *Client) Release(ctx context.Context, ref, held, back string) (*Moved, error)
- func (c *Client) SetPriority(ctx context.Context, ref string, rank int) (*Prioritized, error)
- func (c *Client) TeamLabel() string
- func (c *Client) Unblock(ctx context.Context, ref, by string) (*Held, error)
- type Epic
- type Gate
- type Go
- type GraphQLError
- type Held
- type Item
- type Moved
- type Partial
- type Place
- type Plan
- type Priorities
- type Prioritized
- type Ranked
- type RankedEpic
- type Ranks
- type Refusal
- type Seen
- type Snapshot
- type Thread
Constants ¶
const ( StatusField = "Status" KindField = "Kind" TeamField = "Team" )
The board's fields the order and the moves read.
const HeldPrefix = "held by its Status"
HeldPrefix starts the refusal of an item its Status holds.
const HeldStatus = "Blocked / Waiting"
HeldStatus is the Status a hold on a person sets (see Resolve): the item waits on the person's answer to a decision.
const HeldWord = "held: "
HeldWord starts the skip reason of an item a hold keeps back: its own open blocker, or an open blocker of an issue above it.
const KindLabel = "kind/"
KindLabel is the prefix of an issue label naming its kind: kind/bug.
const LeaseLabel = "lease/"
LeaseLabel is the prefix of an issue label naming a lease the work needs: lease/agentlab-1.
const OutOfScope = "not in a priority epic"
OutOfScope is the skip reason of an item a workScope step offers outside the work scope.
const PriorityField = "Priority"
PriorityField is the board's number field that holds the priority list: an epic's rank, 1 the top. Only the person sets it (beekeeper priority set --as <person>); board next applies it.
Variables ¶
This section is empty.
Functions ¶
func ByPriority ¶ added in v0.131.0
ByPriority orders cands by the rank each has (Ranks.Of, from its own parent and upward through parent) first, unranked after ranked, and keeps their order within a rank: the priority list over the configured order. It records the rank applied on each candidate.
func Resolve ¶
Resolve returns the one value of values that in stands for: the value itself, or the only one whose words (case, emoji and punctuation ignored) begin with or contain in's. Anything else is an error that lists the values.
func ResolveOrder ¶
ResolveOrder returns order with its Status and Kind values replaced by the board's canonical ones.
Types ¶
type Candidate ¶
type Candidate struct {
Item
// Step is the name of the step that offers it; Epic the epic whose
// sub-issue it is.
Step string `json:"step"`
Epic string `json:"epic,omitempty"`
// Match says what of the step placed it there: the values of its
// item that the step's matchers took ("status Backlog, kind Bug by
// label bug"), the epic's for a sub-issue offered through one.
Match string `json:"match,omitempty"`
// Skip says why it is not free, empty when it is.
Skip string `json:"skip,omitempty"`
// Priority is the rank ByPriority applied, 0 for none; PriorityOf the
// epic it is the rank of, the candidate itself or an ancestor.
Priority int `json:"priority,omitempty"`
PriorityOf string `json:"priorityOf,omitempty"`
}
Candidate is an item the order offers, in its place.
func Rank ¶
Rank returns the candidates of every step in order, each item once, in the first step that matches it. A SubIssues step offers an item with open sub-issues through them (its remainder) and is the item's only step; an item without is offered itself. A sub-issue that is a board item, of any Team or Status, is held to the order like any other: one no step offers on its own (an old Backlog item, a blocked one, one in Inbox) is marked skipped with the reason. An item with an open recorded blocker is held: marked skipped naming its blockers ("held: blocked by …"), an epic offered as itself, never through its sub-issues, and an issue under a held one through the sub-issue relation is marked skipped naming it, a blocked one whose Status was set after its recorded blockers closed too (HeldByStatus). Items with an open recorded blocker, without activity for b.StaleAfter or assigned to anybody outside b.People are marked skipped too. The epic gate (snap.Gate) holds an epic in planning and every issue under it whatever the step: the epic is marked skipped with its plan, never offered through its sub-issues, and an issue whose nearest epic among its parents on the board is in planning is marked skipped naming it. What GitHub answered with an error is never read as absent: a sub-issue whose board fields are unknown takes them from its item among snap.Items, else is marked skipped naming them, and an item whose sub-issues are unknown is offered skipped itself.
func (Candidate) PriorityText ¶ added in v0.131.0
PriorityText says the rank applied: "priority 2", "priority 2 of epic owner/repo#n" when inherited; "" for none.
type Change ¶ added in v0.148.0
type Change struct {
Ref string `json:"ref"`
FromRank int `json:"fromRank,omitempty"`
ToRank int `json:"toRank,omitempty"`
RankBy string `json:"rankBy,omitempty"`
FromTeam string `json:"fromTeam,omitempty"`
ToTeam string `json:"toTeam,omitempty"`
TeamBy string `json:"teamBy,omitempty"`
At time.Time `json:"at,omitzero"`
}
Change is a change of a ranked epic's Team or Priority between two reads of the priority list that beekeeper did not write: the values before and after (0 or "" for none) and who wrote the new ones where GitHub names them, the field value's creator.
func Changes ¶ added in v0.148.0
func Changes(was []Seen, now Priorities, gone func(ref string) *RankedEpic) []Change
Changes compares the record of the last read with the list read now. An epic on both whose rank or Team differs is a change, with the writers the list read; an epic that left the list is looked up with gone (its own item on the board, nil when GitHub has none or it is closed: no change). Epics that joined the list are no change of a ranked epic.
type Client ¶
Client reads and moves the items of one board through the GitHub GraphQL API; Plans are the plans repositories (owner/repo) an epic's plan is found in.
func (*Client) AnswerThread ¶ added in v0.142.0
AnswerThread is the thread an answer about ref is recorded on: one read of ref and its parents acting on ref's repository, and one search for the plans naming it or its parents when its repository is public. A ref that does not exist, or a public one with no private thread, is refused.
func (*Client) Block ¶ added in v0.128.0
Block records the issue by as a blocker of the issue ref, a GitHub blocked-by dependency (addBlockedBy): board next skips ref while one of its blockers is open and offers it again once they closed, with no session acting. A dependency recorded already is left as it is.
func (*Client) Closes ¶ added in v0.102.0
Closes reads the issues each pull request among refs closes (its closing references, the Closes and Fixes keywords resolved across repositories), keyed by the lower-cased ref as given, closesBatch refs of one owner per request, acting on their repository (github.ActingOn). An issue among refs, or a ref that is no issue, closes nothing; nil when nothing was asked.
func (*Client) Comment ¶ added in v0.142.0
Comment posts body on the thread, acting on its repository, and returns the comment's URL.
func (*Client) Epics ¶ added in v0.127.0
Epics reads the board's epics in planning or past their go, the team's, with their plans: one request for the fields, one per 100 items, one per plansBatch epics, and one per plansBatch epics past their go of one owner for their threads. Nil when the gate is off.
func (*Client) FindPlans ¶ added in v0.127.0
FindPlans finds the plan of each epic of refs, keyed by the lower-cased ref: one search of the plans repositories (Client.Plans) per epic for pull requests naming it, plansBatch searches per request, acting on the first plans repository. Of those whose description's Epic line names the epic (its URL or owner/repo#n), the most recently updated open one is the plan, else the most recently updated merged one; a closed one never is. Nil when no plans repository is configured.
func (*Client) Hold ¶ added in v0.150.0
Hold sets the Status of the board item of the issue ref to HeldStatus and returns the move, Moved.From the Status it had. An issue without an item on the board, or whose item has no Status to return to, is refused before anything is written: a hold never adds an item to the board.
func (*Client) Move ¶
Move sets the Status of the board item of the issue ref to the status in stands for (see Resolve): the item the issue's projectItems name, else the one addProjectV2ItemById answers, which is the issue's existing item or a new one, with board.team as its Team when it has none; no read of the board's items. An issue of a repository outside the board's owner whose projectItems name no item of the board is never added to it: its item on a project of its repository's own owner (a personal account's user project) moves there by that project's Status values, Moved.Board naming it, without a Team or the epic gate; with no such item, or more than one, it is refused, naming the recipe. Every call acts on the issue's repository (github.ActingOn), so a gh link choosing its login by the owner reaches the issue. An unknown or ambiguous status, and a board.team that is none of the Team field's values, are refused with the board's values before anything is written; an issue GitHub does not resolve is refused as no issue, with GitHub's reason. A move of an epic to the epic gate's go status is the go: Moved.Go names its plan and the private thread to record it on (RecordGo), and an epic with a plan but no private thread is refused before anything is written.
func (*Client) Open ¶ added in v0.73.2
Open reports whether the issue or pull request ref is open, acting on its repository (github.ActingOn).
func (*Client) Priorities ¶ added in v0.131.0
func (c *Client) Priorities(ctx context.Context) (Priorities, error)
Priorities reads the priority list: the open epics (the epic gate's kinds) of board.team, by their Team field or their team label, whose Priority field holds a rank, by rank and, within one, in the board's order. A board without a Priority field has an empty list. It is the one read every consumer of the priorities calls (board next; lease, lanes and capacity next), with Ranks and Ranks.Of on top.
func (*Client) Rank ¶ added in v0.126.0
Rank moves the board item of the issue ref to the place at with updateProjectV2ItemPosition, the board's position being the order board next reads its items in. An issue without an item is added first, as Move adds it, with board.team as its Team. The item of a Before or After issue is found by that issue's projectItems and must be on the board already; Before is two moves, the item after the other one and the other one after the item, so nothing reads the board's items to find the other one's predecessor. Each call acts on the repository of the issue it asks about. No issue, a reference without an item on the board and an item placed against itself are refused before anything is written.
func (*Client) RankedNow ¶ added in v0.148.0
RankedNow is the epic ref as its own item on the board says now, read from the issue's projectItems, never the board's: its rank and Team with their writers. nil for an issue that is closed or has no item on the board.
func (*Client) Read ¶
Read reads the board items the order can offer and the epic gate holds, the open sub-issues of the items a SubIssues step matches, the search steps' issues, the priority list (Priorities, kept as snap.Priorities) with the ranked epics a Team field moved off the team but its label keeps (rankedOffTeam), and the plans of the epics the gate watches: one request for the fields, one per 100 items, one per extrasBudget sub-issues for the sub-issues and searches, one per plansBatch epics.
func (*Client) RecordGo ¶ added in v0.127.0
RecordGo posts the go's comment on its private thread, acting on the thread's repository, and returns the comment's URL.
func (*Client) Release ¶ added in v0.150.0
Release returns the board item of the issue ref from the Status held to back, the Status it had before the hold. An item whose Status is no longer held was moved since the hold was set: it is left as it is, Moved.To its Status and Moved.Kept true.
func (*Client) SetPriority ¶ added in v0.131.0
SetPriority puts the epic ref at rank in the priority list, 1 the top: the epics at and below the rank move down one and every epic above keeps its rank (a rank past its end is its end; reranked). rank 0 takes the epic off the list, the epics below it moving up one. Only the items whose rank changes are written, each by its id; the list is read with Priorities, again after the writes until no read names an epic the change has not seen, the epic's item from its projectItems (added to the board as Move adds one). The caller is the one to check that the person asked for it. Refused before anything is written: a board without a number field Priority, an issue that does not exist, is closed or is no epic of the epic gate's kinds.
type Epic ¶ added in v0.127.0
type Epic struct {
Ref string `json:"ref"`
URL string `json:"url"`
Title string `json:"title"`
Status string `json:"status"`
Plan *Plan `json:"plan"`
NoGo bool `json:"noGo,omitempty"`
}
Epic is an epic the gate watches, in planning or past its go, with its plan, nil when no plan pull request names it. NoGo is an epic past its go with no go recorded.
type Gate ¶ added in v0.127.0
Gate is the epic gate (config.EpicGate) in the board's canonical values; the zero Gate is off.
func ResolveGate ¶ added in v0.127.0
ResolveGate resolves e against the board's statuses and kinds. A value board.epics names must be the board's; a default the board has not turns the gate off (no epic kind, no planning status) or the go unrecorded.
func (Gate) InPlanning ¶ added in v0.127.0
InPlanning reports whether it is an epic in planning.
type Go ¶ added in v0.127.0
type Go struct {
Epic string `json:"epic"`
Plan *Plan `json:"plan,omitempty"`
Thread string `json:"thread,omitempty"`
// Comment is the URL of the comment that records the go.
Comment string `json:"comment,omitempty"`
// contains filtered or unexported fields
}
Go is the go a move is: an epic moved to the gate's go status. Plan is its plan, nil for none, and Thread the private thread the go is recorded on (owner/repo#n): the epic itself in a private repository, else its plan pull request in a private one.
type GraphQLError ¶ added in v0.129.0
GraphQLError is one error GitHub answered beside the data: Path is the field it could not resolve (aliases, field names and list indexes), Type GitHub's kind of error (NOT_FOUND, RESOURCE_LIMITS_EXCEEDED, …).
type Held ¶ added in v0.128.0
type Held struct {
Ref string `json:"ref"`
By string `json:"by"`
// ByOpen says the blocker is open: the hold keeps the item from board
// next until it closes.
ByOpen bool `json:"byOpen"`
// Changed says the dependency was added or removed; false when it was
// recorded already (block) or not recorded (unblock).
Changed bool `json:"changed"`
// Blockers and OpenBlockers are the issue's recorded blockers after the
// change, all and the open ones.
Blockers int `json:"blockers"`
OpenBlockers int `json:"openBlockers"`
}
Held is a hold recorded on an issue, or removed from it: the issue's GitHub blocked-by dependency on another issue, which board next reads.
type Item ¶
type Item struct {
// Ref is owner/repo#n.
Ref string `json:"ref"`
URL string `json:"url"`
Title string `json:"title"`
// OnBoard says it is an item of the board project, Status and Kind
// its board fields.
OnBoard bool `json:"onBoard,omitempty"`
Status string `json:"status,omitempty"`
Kind string `json:"kind,omitempty"`
Labels []string `json:"labels,omitempty"`
Assignees []string `json:"assignees,omitempty"`
Created time.Time `json:"created"`
Updated time.Time `json:"updated"`
// Blockers counts the recorded blockers, OpenBlockers the open ones;
// OpenBlockedBy names those read (owner/repo#n), from a read of the
// board's items or the priority list only.
Blockers int `json:"blockers,omitempty"`
OpenBlockers int `json:"openBlockers,omitempty"`
OpenBlockedBy []string `json:"openBlockedBy,omitempty"`
// StatusSet is when its Status was last set, BlockersClosed when the
// last of its recorded blockers closed; zero when unknown.
StatusSet time.Time `json:"statusSet,omitzero"`
BlockersClosed time.Time `json:"blockersClosed,omitzero"`
// OpenSubIssues counts the open sub-issues; SubIssues are they, read
// for a SubIssues step only.
OpenSubIssues int `json:"openSubIssues,omitempty"`
SubIssues []Item `json:"subIssues,omitempty"`
// Parent is the issue it is a sub-issue of, owner/repo#n.
Parent string `json:"parent,omitempty"`
// Missing names the fields of it GitHub answered with an error instead
// of their value ("<field>: <message>"): they are unknown, not absent.
Missing []string `json:"missing,omitempty"`
// contains filtered or unexported fields
}
Item is an open issue: a board item or a sub-issue or search result.
func (Item) HeldByStatus ¶ added in v0.146.2
HeldByStatus reports whether its Status was set after the last of its recorded blockers closed: set for a reason no blocker records (a person, a plan), the Status holds it until it changes.
type Moved ¶
type Moved struct {
Ref string `json:"ref"`
From string `json:"from,omitempty"`
To string `json:"to"`
// Added says the issue had no item on the board: one was added before
// the move. Team is the Team the item was given, board.team, "" when
// it had one or the board names none.
Added bool `json:"added,omitempty"`
Team string `json:"team,omitempty"`
// Go is the go the move is, an epic moved to the epic gate's go
// status; nil for any other move.
Go *Go `json:"go,omitempty"`
// Board is the project the item moved on when it is not the configured
// board: the project of the issue's repository's own owner
// (owner/number); "" for the configured board.
Board string `json:"board,omitempty"`
// Kept says a release left the item as it is: its Status was changed
// since the hold set it (Release).
Kept bool `json:"kept,omitempty"`
}
Moved is a move of an item to a status.
type Partial ¶ added in v0.129.0
type Partial struct{ Errors []GraphQLError }
Partial is an answer with errors beside its data: the data is decoded, and every field an error's path names is missing from it, never absent.
type Place ¶ added in v0.126.0
Place is where Rank puts an item: at the top of the board, or right before or right after the item of another issue, one of the three.
type Plan ¶ added in v0.127.0
type Plan struct {
Ref string `json:"ref"`
URL string `json:"url"`
Title string `json:"title"`
// State is draft, open or merged, closed only for the plan an epic past
// its go was implemented at when no other names it; Head the head commit.
State string `json:"state"`
Head string `json:"head"`
Private bool `json:"private"`
// The go, read for an epic past it from the latest go comment on its
// private thread: the plan it named (ImplementedPlan, its URL) at the
// head Implemented, given at GoAt in the comment GoComment. Since says how
// the plan moved on from it, Moved the commits pushed to it since; ""
// and 0 when it did not.
ImplementedPlan string `json:"implementedPlan,omitempty"`
Implemented string `json:"implemented,omitempty"`
GoAt *time.Time `json:"goAt,omitempty"`
GoComment string `json:"goComment,omitempty"`
Since string `json:"since,omitempty"`
Moved int `json:"moved,omitempty"`
// contains filtered or unexported fields
}
Plan is an epic's plan: the open or merged pull request of a plans repository whose description names the epic.
type Priorities ¶ added in v0.131.0
type Priorities []RankedEpic
Priorities is the priority list, by rank.
func (Priorities) Ranks ¶ added in v0.131.0
func (p Priorities) Ranks() Ranks
Ranks is the priority list as each epic's rank by its lower-cased ref.
type Prioritized ¶ added in v0.131.0
type Prioritized struct {
Ref string `json:"ref"`
From int `json:"from,omitempty"`
To int `json:"to,omitempty"`
List Priorities `json:"list"`
// Added says the epic had no item on the board, Team the Team it was
// given; as in Moved.
Added bool `json:"added,omitempty"`
Team string `json:"team,omitempty"`
}
Prioritized is a change of the priority list: the epic, its rank before and after (0 for none), and the list as it is now.
type Ranked ¶ added in v0.126.0
type Ranked struct {
Ref string `json:"ref"`
// Above and Below are the item's new neighbours that the move itself
// names: the item it went after or before, and for the top the item
// now below it; "" for one no request read. A neighbour that is no
// issue is "a draft".
Above string `json:"above,omitempty"`
Below string `json:"below,omitempty"`
Top bool `json:"top,omitempty"`
// Added and Team are as in Moved: the issue had no item on the board.
Added bool `json:"added,omitempty"`
Team string `json:"team,omitempty"`
}
Ranked is a move of an item to a place on the board.
type RankedEpic ¶ added in v0.131.0
type RankedEpic struct {
Rank int `json:"rank"`
Ref string `json:"ref"`
URL string `json:"url"`
Title string `json:"title"`
Status string `json:"status,omitempty"`
// Team is its Team; RankBy and TeamBy the logins that wrote its rank
// and Team (the field values' creator), RankAt and TeamAt when.
Team string `json:"team,omitempty"`
RankBy string `json:"rankBy,omitempty"`
RankAt time.Time `json:"rankAt,omitzero"`
TeamBy string `json:"teamBy,omitempty"`
TeamAt time.Time `json:"teamAt,omitzero"`
// Held is its hold (Item.HeldText): its open blockers, "" for none. A
// held epic keeps its rank; board next skips it and every issue under it.
Held string `json:"held,omitempty"`
// contains filtered or unexported fields
}
RankedEpic is an epic of the priority list.
type Ranks ¶ added in v0.131.0
Ranks are the epics' ranks by their lower-cased ref.
type Refusal ¶
type Refusal struct{ Reason string }
Refusal is a move the board's values or items do not allow.
type Seen ¶ added in v0.148.0
type Seen struct {
Ref string `json:"ref"`
Rank int `json:"rank"`
Team string `json:"team,omitempty"`
}
Seen is a ranked epic as the last read of the priority list found it, which the next read is compared with.
type Snapshot ¶
type Snapshot struct {
// Order is the configured order, its Status and Kind values the
// board's canonical names.
Order []config.BoardStep
// Items are the open board items in the board's order.
Items []Item
// Search holds each search step's issues by its index in Order.
Search map[int][]Item
// Statuses are the board's Status values, in the board's order.
Statuses []string
// Gate is the epic gate; Plans the plans of the epics it watches by
// their lower-cased ref.
Gate Gate
Plans map[string]*Plan
// Missing names what GitHub answered with an error and no item's
// Missing holds ("<path>: <message>"), a search step's issues for one.
Missing []string
// Priorities is the priority list Read read with the items.
Priorities Priorities
}
Snapshot is what one read of the board returned.
func (*Snapshot) Epics ¶ added in v0.127.0
Epics are the snapshot's epics in planning or past their go, in the board's order, each with its plan.
func (*Snapshot) Parent ¶ added in v0.131.0
Parent is the parent of the issue ref among the items snap read, the board items and the sub-issues read with them; "" when snap read none.
func (*Snapshot) Scope ¶ added in v0.159.0
Scope marks skipped, as OutOfScope, each candidate of a workScope step that is outside the work scope; ByPriority has ranked cands. The work scope is an issue with a rank, its own or its ranked ancestor's (a priority epic's sub-issue), an order (a candidate of a step without workScope: what the person's own moves put there), and an open recorded blocker of either, however deep. An item under an unranked epic, or without one, is skipped, not ordered last. A skip already given stays.
type Thread ¶ added in v0.142.0
type Thread struct {
About string `json:"about"`
AboutURL string `json:"aboutURL,omitempty"`
Ref string `json:"thread"`
Via string `json:"via,omitempty"`
// contains filtered or unexported fields
}
Thread is where an answer about an issue or pull request is recorded: Ref itself in a private repository; else the first private plan pull request naming it or an epic above it, then the nearest epic above it in a private repository. Via says why Ref is not About, "" when it is. AboutURL is About's URL as GitHub gives it.