Documentation
¶
Overview ¶
Package discovery maintains a private GitHub inbox cache. It performs no git fetch, generation or write to GitHub.
Index ¶
- Variables
- func AfterPush(ctx context.Context, ...)
- func DefaultDirectory() (string, error)
- func WithIdleTimeout(parent context.Context, idle time.Duration) (context.Context, context.CancelFunc)
- type Observation
- type PullRequest
- type RateLimit
- type Revision
- type Service
- func (s *Service) Cached(ctx context.Context, repository github.Repository) (Snapshot, error)
- func (s *Service) CachedStable(ctx context.Context, repository github.Repository) (Snapshot, error)
- func (s *Service) Epoch() uint64
- func (s *Service) GraphQLBudget(ctx context.Context, repository github.Repository) (RateLimit, error)
- func (s *Service) Merged(ctx context.Context, repository github.Repository, number int, head string) error
- func (s *Service) Observe(ctx context.Context, repository github.Repository, value Observation) error
- func (s *Service) Poll(ctx context.Context, repository github.Repository) (Snapshot, error)
- func (s *Service) PollNow(ctx context.Context, repository github.Repository) (Snapshot, error)
- func (s *Service) Probe(ctx context.Context, repositories []github.Repository) (map[string]bool, error)
- func (s *Service) ResetIdentity()
- func (s *Service) Revision(ctx context.Context, repository github.Repository) (Revision, error)
- func (s *Service) Unresolved(ctx context.Context, repository github.Repository, number, count int) (bool, error)
- type Snapshot
Constants ¶
This section is empty.
Variables ¶
var ErrTimeout = errors.New("GitHub discovery timed out")
ErrTimeout is a poll or probe that ran out of time. Whatever it read before is kept.
var PushedDelays = []time.Duration{0, 5 * time.Second, 10 * time.Second, 15 * time.Second, 30 * time.Second}
PushedDelays are the waits between the polls after baum itself pushed a pull request's head (a fix). A poll right after the push finds the head before GitHub has queued its checks: no checks and a clean merge state, which reads as ready to merge. The checks usually appear within seconds, and neither the probe (check results do not move a pull request's update time) nor the settle poll (the background scheduler does not see a poll it did not make) would notice them before the usual backoff.
Functions ¶
func AfterPush ¶
func AfterPush(ctx context.Context, poll func(context.Context, github.Repository) (Snapshot, error), repository github.Repository, number int, head string, changed func(), delays []time.Duration)
AfterPush polls repository at PushedDelays (delays, in tests) until the pull request shows head with its checks reported, or is no longer open, and calls changed after each poll that succeeded. A repository without checks reports none for every poll, so the polls stop with the delays. poll is Service.PollNow, which still honors the shared rate budgets.
func DefaultDirectory ¶
DefaultDirectory is the shared application database directory.
func WithIdleTimeout ¶ added in v0.2.0
func WithIdleTimeout(parent context.Context, idle time.Duration) (context.Context, context.CancelFunc)
WithIdleTimeout returns a context for a poll that is cancelled once idle passes without GitHub answering one of the poll's requests. A poll reads every open pull request, so how long it takes grows with the repository; a fixed deadline long enough for the busiest repository would also leave a stalled connection hanging that long.
The timeout is paused while the poll waits for its locks: a poll queued behind another one, which may itself take minutes, is not waiting on GitHub.
Types ¶
type Observation ¶
type Observation struct {
Identity string `json:"-"`
Number int `json:"number"`
HeadSHA string `json:"head_sha"`
Title string `json:"title"`
State string `json:"state"`
Draft bool `json:"draft"`
FetchedAt time.Time `json:"fetched_at"`
Checks string `json:"checks"`
}
Observation contains only safe current GitHub metadata, never description bodies, signed image URLs or session contents.
type PullRequest ¶
type PullRequest struct {
Repository string `json:"repository"`
Number int `json:"number"`
HeadSHA string `json:"head_sha"`
// HeadRef is the head branch's name, which links a local session on that
// branch to the pull request. It is empty in a snapshot cached before it
// was kept, until the next poll.
HeadRef string `json:"head_ref"`
// HeadRepository is the canonical host/repository owning the head branch.
HeadRepository string `json:"head_repository,omitempty"`
// BaseRef is the base branch's name; a pull request whose base is
// another open pull request's head branch is stacked on it. Empty in a
// snapshot cached before it was kept, until the next poll.
BaseRef string `json:"base_ref"`
// Additions, Deletions and ChangedFiles size the change as GitHub counts
// it. They are zero in a snapshot cached before they were kept, until the
// next poll.
Additions int `json:"additions"`
Deletions int `json:"deletions"`
ChangedFiles int `json:"changed_files"`
Title string `json:"title"`
URL string `json:"url"`
Author string `json:"author"`
// AuthorBot is true for a GitHub App or other bot author. Snapshots cached
// before it existed decode as false.
AuthorBot bool `json:"author_bot"`
State string `json:"state"`
Draft bool `json:"draft"`
Fork bool `json:"fork"`
AuthoredByMe bool `json:"authored_by_me"`
ReviewRequestedFromMe bool `json:"review_requested_from_me"`
// AssignedToMe is true when the viewer is one of the first 20 assignees.
// Snapshots cached before it existed decode as false.
AssignedToMe bool `json:"assigned_to_me"`
// Assigned is true when the pull request has at least one of the first 20
// assignees fetched, whoever they are. Snapshots cached before it existed
// decode as false.
Assigned bool `json:"assigned"`
// ReviewRequested is true when a review is requested from at least one
// user or team, whoever they are. Snapshots cached before it existed
// decode as false.
ReviewRequested bool `json:"review_requested"`
// Nil means the token could not establish repository write permission.
AuthorHasWrite *bool `json:"author_has_write"`
// ViewerCanPush is whether the signed-in account may push to the
// repository (GitHub's viewerPermission is write, maintain or admin),
// which a fix needs; nil when GitHub did not say.
ViewerCanPush *bool `json:"viewer_can_push"`
// False means team membership was unavailable; false review-request flags
// must not be interpreted as proof that no team requested this viewer.
TeamMembershipKnown bool `json:"team_membership_known"`
Review string `json:"review"`
// ReviewRequired is true only when GitHub's reviewDecision is REVIEW_REQUIRED;
// nil means an older snapshot did not retain the requirement.
ReviewRequired *bool `json:"review_required,omitempty"`
Checks string `json:"checks"`
Merge string `json:"merge"`
// Behind is GitHub's report (mergeStateStatus BEHIND) that the head
// branch is out of date with its base and not conflicting, which Update
// branch fixes. A draft never reports it.
Behind bool `json:"behind"`
Unresolved *int `json:"unresolved"`
UnresolvedCapped bool `json:"unresolved_capped"`
// UpdatedAt is GitHub's last update of the pull request: a push, an edit,
// a review or a comment. The background refresh re-reads a saved review's
// evidence when it is newer than the last read.
UpdatedAt time.Time `json:"updated_at,omitzero"`
}
type Revision ¶ added in v0.2.0
Revision stamps what Cached reads of a repository without opening the cache: Cached reads the same snapshot at two equal revisions, except that its Stale turns true once NextPoll passes. It changes when the identity resets or the credential changes, when this service changes the repository's cache (store, including times it keeps unsaved), and when a poll validates it or fails. Only this service writes the cache, and only the daemon, which runs once, makes one on app.sqlite.
type Service ¶
type Service struct {
// Updated, when set before use, is called after anything Cached reads
// changes: a repository's cache write, an identity reset, or live
// freshness. A write of the credential's budget record alone, which
// Cached does not read, is not reported. It may run with the service's
// locks held, so it must not call the service.
Updated func()
// contains filtered or unexported fields
}
func (*Service) Cached ¶
Cached never makes a network request. Resolving the configured local token partitions results by credential, so changing accounts cannot reuse an inbox. Disk snapshots are always stale until a successful conditional poll.
func (*Service) CachedStable ¶ added in v0.2.0
CachedStable reports data freshness without aging a successful poll at its next deadline. Errors and caches not validated in this process remain stale. The review index uses it so the passage of time does not change its rows.
func (*Service) GraphQLBudget ¶ added in v0.2.0
func (s *Service) GraphQLBudget(ctx context.Context, repository github.Repository) (RateLimit, error)
GraphQLBudget is the credential's GraphQL points budget as discovery last read it, for another reader of the same token, such as the background refresh, to leave discovery its share. It makes no request. Remaining is nil while no poll or probe has read it, and a Reset that has passed means a new window GitHub has not reported yet.
func (*Service) Merged ¶
func (s *Service) Merged(ctx context.Context, repository github.Repository, number int, head string) error
Merged records that the app just merged the pull request at head: GitHub accepted the merge, so it leaves the open pull requests and is observed merged without another read. Its title and draft state are the cached ones.
func (*Service) Observe ¶
func (s *Service) Observe(ctx context.Context, repository github.Repository, value Observation) error
Observe retains fresh free metadata for open and saved historical PRs. A separate service instance still treats the disk cache as stale on startup. An observation older than the one kept is dropped: a read under way when the app merged the pull request (Merged) does not undo it.
func (*Service) Poll ¶
Poll refreshes the open pull requests and conditional REST metadata without waiting through a rate limit. Callers schedule NextPoll; unchanged results back off from 30s to 15m. Failure returns the cached snapshot, with what this poll did read merged into it, explicitly stale, and an error.
func (*Service) PollNow ¶
PollNow is Poll without waiting for NextPoll: something local (a push, an agent session ending) says the pull requests changed. It still honors the shared REST and GraphQL rate budgets.
func (*Service) Probe ¶
func (s *Service) Probe(ctx context.Context, repositories []github.Repository) (map[string]bool, error)
Probe asks GitHub whether each repository's open pull requests changed since its last complete poll, and returns the ones that did, keyed by Repository.String(). It sends one GraphQL query per probeBatch repositories, about one point each, on the polls' shared budget. A repository never polled, or one GitHub does not return (renamed, or access lost), is left to its scheduled poll, as is one whose credential differs from the first repository's. It takes no repository's lock, so polls under way do not hold it up.
func (*Service) ResetIdentity ¶
func (s *Service) ResetIdentity()
ResetIdentity cancels the old transactions and invalidates live discovery. Disk partitions remain private and are keyed by their original credential.
func (*Service) Revision ¶ added in v0.2.0
Revision never makes a request and reads nothing from disk.
func (*Service) Unresolved ¶
func (s *Service) Unresolved(ctx context.Context, repository github.Repository, number, count int) (bool, error)
Unresolved records the count of an open pull request's unresolved review threads that the app just read from GitHub, ahead of the next poll: GitHub reports no update to a pull request when a thread is resolved, so a probe cannot see it. It reports whether the count changed.
type Snapshot ¶
type Snapshot struct {
Epoch uint64 `json:"-"`
Repository string `json:"repository"`
Observations []Observation `json:"observations,omitempty"`
Viewer string `json:"viewer"`
PullRequests []PullRequest `json:"pull_requests"`
Stale bool `json:"stale"`
FetchedAt time.Time `json:"fetched_at"`
NextPoll time.Time `json:"next_poll"`
Rate RateLimit `json:"rate"`
Error string `json:"error,omitempty"`
// Settling: this poll found a new head or a mergeability GitHub forgot,
// or GitHub still works one out since, so NextPoll comes after
// settleInterval.
Settling bool `json:"settling,omitempty"`
}