Documentation
¶
Index ¶
- Constants
- func DeriveOverallCIStatus(runs []*gh.CheckRun, combined *gh.CombinedStatus) string
- func DeriveReviewDecision(reviews []*gh.PullRequestReview) string
- func FilterWorkflowRunsAwaitingApproval(runs []*gh.WorkflowRun, number int, headSHA string) []*gh.WorkflowRun
- func IsNotModified(err error) bool
- func NormalizeCIChecks(runs []*gh.CheckRun, combined *gh.CombinedStatus) string
- func NormalizeCheckRuns(runs []*gh.CheckRun) string
- func NormalizeCommentEvent(mrID int64, c *gh.IssueComment) db.MREvent
- func NormalizeCommitEvent(mrID int64, c *gh.RepositoryCommit) db.MREvent
- func NormalizeForcePushEvent(mrID int64, fp ForcePushEvent) db.MREvent
- func NormalizeIssue(repoID int64, ghIssue *gh.Issue) *db.Issue
- func NormalizeIssueCommentEvent(issueID int64, c *gh.IssueComment) db.IssueEvent
- func NormalizePR(repoID int64, ghPR *gh.PullRequest) *db.MergeRequest
- func NormalizeReviewEvent(mrID int64, r *gh.PullRequestReview) db.MREvent
- func WithSyncBudget(ctx context.Context) context.Context
- type BulkIssue
- type BulkPR
- type Client
- type DiffSyncError
- type DiffSyncErrorCode
- type ForcePushEvent
- type GraphQLFetcher
- type QueueItem
- type QueueItemType
- type RateTracker
- func (rt *RateTracker) HourStart() time.Time
- func (rt *RateTracker) IsPaused() bool
- func (rt *RateTracker) Known() bool
- func (rt *RateTracker) RateLimit() int
- func (rt *RateTracker) RecordRequest()
- func (rt *RateTracker) Remaining() int
- func (rt *RateTracker) RequestsThisHour() int
- func (rt *RateTracker) ResetAt() *time.Time
- func (rt *RateTracker) SetOnWindowReset(fn func())
- func (rt *RateTracker) ShouldBackoff() (bool, time.Duration)
- func (rt *RateTracker) ThrottleFactor() int
- func (rt *RateTracker) UpdateFromRate(rate gh.Rate)
- type RepoBulkResult
- type RepoRef
- type RepoSyncResult
- type SyncBudget
- type SyncStatus
- type Syncer
- func (s *Syncer) Budgets() map[string]*SyncBudget
- func (s *Syncer) ClientForHost(host string) (Client, error)
- func (s *Syncer) ClientForRepo(owner, name string) (Client, error)
- func (s *Syncer) HasDiffSync() bool
- func (s *Syncer) HostForRepo(owner, name string) string
- func (s *Syncer) IsTrackedRepo(owner, name string) bool
- func (s *Syncer) RateTrackers() map[string]*RateTracker
- func (s *Syncer) RunOnce(ctx context.Context)
- func (s *Syncer) SetFetchers(fetchers map[string]*GraphQLFetcher)
- func (s *Syncer) SetOnMRSynced(fn func(owner, name string, mr *db.MergeRequest))
- func (s *Syncer) SetOnStatusChange(fn func(status *SyncStatus))
- func (s *Syncer) SetOnSyncCompleted(fn func(results []RepoSyncResult))
- func (s *Syncer) SetParallelism(n int)
- func (s *Syncer) SetRepos(repos []RepoRef)
- func (s *Syncer) SetWatchInterval(d time.Duration)
- func (s *Syncer) SetWatchedMRs(mrs []WatchedMR)
- func (s *Syncer) Start(ctx context.Context)
- func (s *Syncer) Status() *SyncStatus
- func (s *Syncer) Stop()
- func (s *Syncer) SyncIssue(ctx context.Context, owner, name string, number int) error
- func (s *Syncer) SyncItemByNumber(ctx context.Context, owner, name string, number int) (string, error)
- func (s *Syncer) SyncMR(ctx context.Context, owner, name string, number int) error
- func (s *Syncer) TriggerRun(ctx context.Context)
- type WatchedMR
- type WorkflowApprovalState
Constants ¶
const IssueDetailWorstCase = 2
IssueDetailWorstCase is the maximum API calls an issue detail fetch can make (detail + comments).
const PRDetailWorstCase = 8
PRDetailWorstCase is the maximum API calls a PR detail fetch can make (detail + GetUser + comments + reviews + commits + force-push events + combined status + check runs).
const RateReserveBuffer = 200
Variables ¶
This section is empty.
Functions ¶
func DeriveOverallCIStatus ¶
func DeriveOverallCIStatus( runs []*gh.CheckRun, combined *gh.CombinedStatus, ) string
DeriveOverallCIStatus computes an aggregate CI status from check runs and the legacy combined status API. The combined status API only reports on commit statuses (the older mechanism); repos using only GitHub Actions check runs will have an empty or "pending" combined state even when all checks pass. This function merges both sources to produce the correct overall status.
func DeriveReviewDecision ¶
func DeriveReviewDecision(reviews []*gh.PullRequestReview) string
DeriveReviewDecision computes the aggregate review decision from a list of reviews. It keeps the latest APPROVED or CHANGES_REQUESTED review per user. Returns "changes_requested" if any user has that state, "approved" if at least one approval exists, or "" if no actionable reviews are present.
func FilterWorkflowRunsAwaitingApproval ¶
func FilterWorkflowRunsAwaitingApproval( runs []*gh.WorkflowRun, number int, headSHA string, ) []*gh.WorkflowRun
FilterWorkflowRunsAwaitingApproval narrows action-required workflow runs down to those that target the given PR number and head SHA.
func IsNotModified ¶
IsNotModified returns true if the error represents a 304 Not Modified response from the GitHub API.
func NormalizeCIChecks ¶
func NormalizeCIChecks( runs []*gh.CheckRun, combined *gh.CombinedStatus, ) string
NormalizeCIChecks merges check runs and commit statuses into a single JSON string of CICheck objects. Commit statuses (used by GitHub Apps like roborev) use the older status API and need to be mapped into the same shape as check runs.
func NormalizeCheckRuns ¶
NormalizeCheckRuns converts GitHub check runs to a JSON string of CICheck objects.
func NormalizeCommentEvent ¶
func NormalizeCommentEvent(mrID int64, c *gh.IssueComment) db.MREvent
NormalizeCommentEvent converts a GitHub IssueComment to a db.MREvent.
func NormalizeCommitEvent ¶
func NormalizeCommitEvent(mrID int64, c *gh.RepositoryCommit) db.MREvent
NormalizeCommitEvent converts a GitHub RepositoryCommit to a db.MREvent. Author is taken from the GitHub user login if available, falling back to the git commit author name.
func NormalizeForcePushEvent ¶
func NormalizeForcePushEvent(mrID int64, fp ForcePushEvent) db.MREvent
func NormalizeIssue ¶
NormalizeIssue converts a GitHub Issue to a db.Issue.
func NormalizeIssueCommentEvent ¶
func NormalizeIssueCommentEvent(issueID int64, c *gh.IssueComment) db.IssueEvent
NormalizeIssueCommentEvent converts a GitHub IssueComment to a db.IssueEvent.
func NormalizePR ¶
func NormalizePR(repoID int64, ghPR *gh.PullRequest) *db.MergeRequest
NormalizePR converts a GitHub PullRequest to a db.MergeRequest. If the PR is merged, State is set to "merged". LastActivityAt is initialized to UpdatedAt.
func NormalizeReviewEvent ¶
func NormalizeReviewEvent(mrID int64, r *gh.PullRequestReview) db.MREvent
NormalizeReviewEvent converts a GitHub PullRequestReview to a db.MREvent.
Types ¶
type BulkIssue ¶
type BulkIssue struct {
Issue *gh.Issue
Comments []*gh.IssueComment
CommentsComplete bool
}
BulkIssue holds an issue and its nested comments from a single GraphQL query. CommentsComplete indicates whether the comments connection was fully paginated.
type BulkPR ¶
type BulkPR struct {
PR *gh.PullRequest
Comments []*gh.IssueComment
Reviews []*gh.PullRequestReview
Commits []*gh.RepositoryCommit
CheckRuns []*gh.CheckRun
Statuses []*gh.RepoStatus
CommentsComplete bool
ReviewsComplete bool
CommitsComplete bool
CIComplete bool
}
BulkPR holds a PR and its nested data from a single GraphQL query. The *Complete flags indicate whether each nested connection was fully paginated. When false, the data is partial and the detail drain should fill in via REST.
type Client ¶
type Client interface {
ListOpenPullRequests(ctx context.Context, owner, repo string) ([]*gh.PullRequest, error)
GetPullRequest(ctx context.Context, owner, repo string, number int) (*gh.PullRequest, error)
GetUser(ctx context.Context, login string) (*gh.User, error)
ListOpenIssues(ctx context.Context, owner, repo string) ([]*gh.Issue, error)
GetIssue(ctx context.Context, owner, repo string, number int) (*gh.Issue, error)
ListIssueComments(ctx context.Context, owner, repo string, number int) ([]*gh.IssueComment, error)
ListReviews(ctx context.Context, owner, repo string, number int) ([]*gh.PullRequestReview, error)
ListCommits(ctx context.Context, owner, repo string, number int) ([]*gh.RepositoryCommit, error)
ListForcePushEvents(ctx context.Context, owner, repo string, number int) ([]ForcePushEvent, error)
GetCombinedStatus(ctx context.Context, owner, repo, ref string) (*gh.CombinedStatus, error)
ListCheckRunsForRef(ctx context.Context, owner, repo, ref string) ([]*gh.CheckRun, error)
ListWorkflowRunsForHeadSHA(ctx context.Context, owner, repo, headSHA string) ([]*gh.WorkflowRun, error)
ApproveWorkflowRun(ctx context.Context, owner, repo string, runID int64) error
CreateIssueComment(ctx context.Context, owner, repo string, number int, body string) (*gh.IssueComment, error)
GetRepository(ctx context.Context, owner, repo string) (*gh.Repository, error)
CreateReview(ctx context.Context, owner, repo string, number int, event string, body string) (*gh.PullRequestReview, error)
MarkPullRequestReadyForReview(ctx context.Context, owner, repo string, number int) (*gh.PullRequest, error)
MergePullRequest(ctx context.Context, owner, repo string, number int, commitTitle, commitMessage, method string) (*gh.PullRequestMergeResult, error)
EditPullRequest(ctx context.Context, owner, repo string, number int, state string) (*gh.PullRequest, error)
EditIssue(ctx context.Context, owner, repo string, number int, state string) (*gh.Issue, error)
ListPullRequestsPage(ctx context.Context, owner, repo, state string, page int) ([]*gh.PullRequest, bool, error)
ListIssuesPage(ctx context.Context, owner, repo, state string, page int) ([]*gh.Issue, bool, error)
// InvalidateListETagsForRepo drops cached conditional-GET
// validators for the given repo's list endpoints so the next
// list call issues an unconditional fetch. The endpoints
// parameter selects which caches to clear ("pulls", "issues").
// If empty, both are cleared. Used to recover from a
// partial-failure sync.
InvalidateListETagsForRepo(owner, repo string, endpoints ...string)
}
Client is the interface for interacting with the GitHub API.
func NewClient ¶
func NewClient( token string, platformHost string, rateTracker *RateTracker, budget *SyncBudget, ) (Client, error)
NewClient creates a GitHub Client authenticated with the given token. platformHost selects the API endpoint: "" or "github.com" uses the public API; any other value creates an Enterprise client. rateTracker and budget may be nil.
type DiffSyncError ¶
type DiffSyncError struct {
Code DiffSyncErrorCode
Err error
}
DiffSyncError reports a non-fatal failure to compute or update the diff SHAs for a PR. SyncMR returns this when only the diff portion of the sync failed: the PR row, timeline, and CI status were updated successfully, so callers should still treat the PR data as fresh, but the diff view will be stale or missing until the underlying problem is fixed.
Code categorizes the failure for client-facing messaging via UserMessage. Err preserves the underlying detail for server-side logging only — never expose Err.Error() to API clients, since it can contain clone paths, refs, SHAs, and git stderr.
func (*DiffSyncError) Error ¶
func (e *DiffSyncError) Error() string
func (*DiffSyncError) Unwrap ¶
func (e *DiffSyncError) Unwrap() error
func (*DiffSyncError) UserMessage ¶
func (e *DiffSyncError) UserMessage() string
UserMessage returns a sanitized message safe to surface to API clients. It never includes clone paths, refs, SHAs, or other internal details from the underlying error.
type DiffSyncErrorCode ¶
type DiffSyncErrorCode string
DiffSyncErrorCode categorizes the reason a diff sync failed. The frontend uses this category to render a user-facing message that does not leak local clone paths, refs, SHAs, or git stderr.
const ( // created or updated (network failure, disk full, permission denied). DiffSyncCodeCloneUnavailable DiffSyncErrorCode = "clone_unavailable" // DiffSyncCodeCommitUnreachable means a commit needed to compute the diff // (PR head, merge commit, or its first parent) is not present in the local // clone and could not be fetched. DiffSyncCodeCommitUnreachable DiffSyncErrorCode = "commit_unreachable" // DiffSyncCodeMergeBaseFailed means git merge-base could not compute the // fork point between the PR head and the base. DiffSyncCodeMergeBaseFailed DiffSyncErrorCode = "merge_base_failed" // DiffSyncCodeInternal covers database failures and other unexpected // internal errors during diff computation. DiffSyncCodeInternal DiffSyncErrorCode = "internal" )
type ForcePushEvent ¶
type GraphQLFetcher ¶
type GraphQLFetcher struct {
// contains filtered or unexported fields
}
GraphQLFetcher fetches PR data via GitHub's GraphQL API (v4).
func NewGraphQLFetcher ¶
func NewGraphQLFetcher( token string, platformHost string, rateTracker *RateTracker, budget *SyncBudget, ) *GraphQLFetcher
NewGraphQLFetcher creates a fetcher for the given host. budget may be nil.
func NewGraphQLFetcherWithClient ¶
func NewGraphQLFetcherWithClient( client *githubv4.Client, rateTracker *RateTracker, ) *GraphQLFetcher
NewGraphQLFetcherWithClient wraps a pre-built githubv4.Client as a GraphQLFetcher. Used by tests that need to point the fetcher at a mock HTTP backend.
func (*GraphQLFetcher) FetchRepoIssues ¶
func (g *GraphQLFetcher) FetchRepoIssues( ctx context.Context, owner, name string, ) (*RepoBulkResult, error)
func (*GraphQLFetcher) FetchRepoPRs ¶
func (g *GraphQLFetcher) FetchRepoPRs( ctx context.Context, owner, name string, ) (*RepoBulkResult, error)
func (*GraphQLFetcher) ShouldBackoff ¶
func (g *GraphQLFetcher) ShouldBackoff() (bool, time.Duration)
type QueueItem ¶
type QueueItem struct {
Type QueueItemType
RepoOwner string
RepoName string
Number int
PlatformHost string
Score float64
// Scoring inputs
UpdatedAt time.Time
DetailFetchedAt *time.Time
CIHadPending bool
Starred bool
Watched bool
IsOpen bool
}
QueueItem holds scoring inputs and result for a single item that may need a detail fetch.
func BuildQueue ¶
BuildQueue filters items by staleness, scores eligible ones, and returns them sorted by score descending.
func (*QueueItem) WorstCaseCost ¶
WorstCaseCost returns the maximum API calls this item's detail fetch could require.
type QueueItemType ¶
type QueueItemType int
QueueItemType distinguishes PRs from issues for cost estimation.
const ( QueueItemPR QueueItemType = iota QueueItemIssue )
type RateTracker ¶
type RateTracker struct {
// contains filtered or unexported fields
}
RateTracker records per-host API request counts and rate limit state, persisting to SQLite for cross-restart visibility.
func NewRateTracker ¶
func NewRateTracker( database *db.DB, platformHost string, apiType string, ) *RateTracker
NewRateTracker creates a tracker for the given platform host and API type. It hydrates from DB if a row exists for the current hour.
func (*RateTracker) HourStart ¶
func (rt *RateTracker) HourStart() time.Time
HourStart returns the start of the current tracking hour.
func (*RateTracker) IsPaused ¶
func (rt *RateTracker) IsPaused() bool
IsPaused returns true when remaining quota is at or below the reserve buffer and quota info is fresh.
func (*RateTracker) Known ¶
func (rt *RateTracker) Known() bool
Known returns true if we have received at least one rate limit response with a positive limit value.
func (*RateTracker) RateLimit ¶
func (rt *RateTracker) RateLimit() int
RateLimit returns the last known rate limit.
func (*RateTracker) RecordRequest ¶
func (rt *RateTracker) RecordRequest()
RecordRequest increments the hourly request counter and persists to DB.
func (*RateTracker) Remaining ¶
func (rt *RateTracker) Remaining() int
Remaining returns the last known remaining request count.
func (*RateTracker) RequestsThisHour ¶
func (rt *RateTracker) RequestsThisHour() int
RequestsThisHour returns the number of requests recorded in the current hour window.
func (*RateTracker) ResetAt ¶
func (rt *RateTracker) ResetAt() *time.Time
ResetAt returns a copy of the reset time, or nil if unknown.
func (*RateTracker) SetOnWindowReset ¶
func (rt *RateTracker) SetOnWindowReset(fn func())
SetOnWindowReset registers a callback invoked when a GitHub rate limit window reset is detected. The callback runs with the tracker's mutex released.
func (*RateTracker) ShouldBackoff ¶
func (rt *RateTracker) ShouldBackoff() (bool, time.Duration)
ShouldBackoff returns true and the wait duration if the rate limit is exhausted (remaining==0). If resetAt is nil, defaults to 60s. Returns false if remaining is >0 or unknown (-1).
func (*RateTracker) ThrottleFactor ¶
func (rt *RateTracker) ThrottleFactor() int
ThrottleFactor returns a multiplier (1, 2, 4, or 8) based on how much remaining quota is left relative to the limit.
func (*RateTracker) UpdateFromRate ¶
func (rt *RateTracker) UpdateFromRate(rate gh.Rate)
UpdateFromRate updates remaining/limit/reset from a go-github Rate. If the reset time moved forward, GitHub started a new rate window — the request counter resets to stay aligned with GitHub's window.
type RepoBulkResult ¶
RepoBulkResult holds all open PRs and issues fetched via GraphQL for a repo.
type RepoRef ¶
type RepoRef struct {
Owner string
Name string
PlatformHost string // "github.com" or GHE hostname
}
RepoRef identifies a GitHub repository.
type RepoSyncResult ¶
type RepoSyncResult struct {
Owner string
Name string
PlatformHost string
Error string // empty on success
}
RepoSyncResult holds the outcome of syncing a single repo.
type SyncBudget ¶
type SyncBudget struct {
// contains filtered or unexported fields
}
SyncBudget tracks hourly API call spend for background detail fetches on a single host.
func NewSyncBudget ¶
func NewSyncBudget(limit int) *SyncBudget
func (*SyncBudget) CanSpend ¶
func (b *SyncBudget) CanSpend(n int) bool
func (*SyncBudget) Limit ¶
func (b *SyncBudget) Limit() int
func (*SyncBudget) Refund ¶
func (b *SyncBudget) Refund(n int)
Refund returns n calls back to the budget.
func (*SyncBudget) Remaining ¶
func (b *SyncBudget) Remaining() int
func (*SyncBudget) Reset ¶
func (b *SyncBudget) Reset()
func (*SyncBudget) Spend ¶
func (b *SyncBudget) Spend(n int)
func (*SyncBudget) Spent ¶
func (b *SyncBudget) Spent() int
func (*SyncBudget) TrySpend ¶
func (b *SyncBudget) TrySpend(n int) bool
TrySpend atomically checks and increments the budget. Returns true if the spend was successful.
type SyncStatus ¶
type SyncStatus struct {
Running bool `json:"running"`
CurrentRepo string `json:"current_repo,omitempty"`
Progress string `json:"progress,omitempty"`
LastRunAt time.Time `json:"last_run_at,omitzero"`
LastError string `json:"last_error,omitempty"`
}
SyncStatus holds the current state of the sync engine.
type Syncer ¶
type Syncer struct {
// contains filtered or unexported fields
}
Syncer periodically pulls PR data from GitHub into SQLite.
func NewSyncer ¶
func NewSyncer( clients map[string]Client, database *db.DB, clones *gitclone.Manager, repos []RepoRef, interval time.Duration, rateTrackers map[string]*RateTracker, budgets map[string]*SyncBudget, ) *Syncer
NewSyncer creates a Syncer that polls the given repos on the given interval. clients maps host -> Client; rateTrackers maps host -> RateTracker. Both may contain nil values. clones may be nil. budgets maps host -> SyncBudget; nil or empty disables detail drain and backfill. Budgets are created by the caller (typically main.go) and wired into each Client's HTTP transport at construction time so every sync-context RoundTrip is automatically counted.
func (*Syncer) Budgets ¶
func (s *Syncer) Budgets() map[string]*SyncBudget
Budgets returns the per-host sync budgets map.
func (*Syncer) ClientForHost ¶
ClientForHost returns the Client for a specific host, or an error if no client is configured for that host.
func (*Syncer) ClientForRepo ¶
ClientForRepo returns the Client for a tracked repo by owner/name, or an error if the repo is not tracked.
func (*Syncer) HasDiffSync ¶
HasDiffSync reports whether the syncer has a clone manager configured and is therefore expected to populate diff SHAs for tracked PRs. The HTTP layer uses this to decide whether a missing diff is a sync issue worth warning about, or simply a deployment that opted out of diffs.
func (*Syncer) HostForRepo ¶
HostForRepo returns the platform host for a tracked repo. Thread-safe.
func (*Syncer) IsTrackedRepo ¶
IsTrackedRepo checks whether the given repo is in the configured list.
func (*Syncer) RateTrackers ¶
func (s *Syncer) RateTrackers() map[string]*RateTracker
RateTrackers returns the per-host rate trackers map.
func (*Syncer) RunOnce ¶
RunOnce performs a single sync pass across all configured repos. If a sync is already in progress it returns immediately (single-flight).
Repos are synced in parallel using a bounded worker pool sized by SetParallelism (default defaultParallelism). The bound keeps the per-host GitHub rate limit and abuse-detection thresholds happy while still capturing most of the wall-clock win on network I/O.
func (*Syncer) SetFetchers ¶
func (s *Syncer) SetFetchers(fetchers map[string]*GraphQLFetcher)
SetFetchers registers GraphQL fetchers keyed by platform host.
func (*Syncer) SetOnMRSynced ¶
func (s *Syncer) SetOnMRSynced( fn func(owner, name string, mr *db.MergeRequest), )
SetOnMRSynced registers a callback invoked after each MR is upserted during a sync pass.
Concurrency: RunOnce processes repos in parallel (see SetParallelism), so the callback may be invoked from up to `parallelism` goroutines concurrently. Implementations must be safe for concurrent use. The callback also runs on the goroutine that is mid-sync for a repo, so it must not block indefinitely or it will stall sync progress.
Call SetOnMRSynced before Start/RunOnce. Mutating the hook while a sync is in flight is not safe.
func (*Syncer) SetOnStatusChange ¶
func (s *Syncer) SetOnStatusChange(fn func(status *SyncStatus))
SetOnStatusChange registers a callback invoked whenever the sync status transitions (start, per-repo progress, rate-limit wait, completion). Used by the server to broadcast live sync state over SSE.
func (*Syncer) SetOnSyncCompleted ¶
func (s *Syncer) SetOnSyncCompleted( fn func(results []RepoSyncResult), )
SetOnSyncCompleted registers a callback invoked at the end of each RunOnce pass with per-repo sync results.
Concurrency: this hook fires once per RunOnce pass on the goroutine that drives RunOnce, so it is not invoked concurrently with itself. Call SetOnSyncCompleted before Start/RunOnce; mutating the hook while a sync is in flight is not safe.
func (*Syncer) SetParallelism ¶
SetParallelism sets the maximum number of repos synced concurrently in RunOnce. Values <= 0 are clamped to 1 (sequential).
func (*Syncer) SetWatchInterval ¶
SetWatchInterval sets the fast-sync interval for watched MRs. Must be called before Start.
func (*Syncer) SetWatchedMRs ¶
SetWatchedMRs replaces the fast-sync watch list. Each watched MR is synced on the watch interval via SyncMR, independent of the bulk sync cycle.
func (*Syncer) Start ¶
Start runs an immediate sync then launches a background ticker. It returns as soon as the goroutine is started; call Stop to shut it down. A second goroutine runs watched-MR fast-syncs on a shorter interval.
The caller's ctx and the syncer's internal lifetime ctx (canceled by Stop) are both honored: either one unblocks any in-flight work.
func (*Syncer) Status ¶
func (s *Syncer) Status() *SyncStatus
Status returns a snapshot of the current sync state.
func (*Syncer) Stop ¶
func (s *Syncer) Stop()
Stop signals the background goroutine to exit. Safe to call multiple times. Cancels the syncer's lifetime context first so blocked RunOnce and TriggerRun goroutines can observe the cancellation and unwind their GitHub calls, then waits for the wait group up to stopGracePeriod. The bounded wait prevents Stop from hanging the process in pathological cases where a client ignores ctx.
func (*Syncer) SyncIssue ¶
SyncIssue fetches fresh data for a single issue from GitHub and updates the DB. Returns an error if the repo is not in the configured repo list.
func (*Syncer) SyncItemByNumber ¶
func (s *Syncer) SyncItemByNumber( ctx context.Context, owner, name string, number int, ) (string, error)
SyncItemByNumber fetches an item by number from GitHub, determines whether it is a PR or issue, syncs it into the DB, and returns the item type ("pr" or "issue"). Returns an error if the repo is not in the configured repo list.
func (*Syncer) SyncMR ¶
SyncMR fetches fresh data for a single MR from GitHub and updates the DB. Unlike the periodic sync, this always does a full fetch (details, timeline, CI). Returns an error if the repo is not in the configured repo list.
func (*Syncer) TriggerRun ¶
TriggerRun kicks off a non-blocking RunOnce on the Syncer's wait group so callers can request an ad-hoc sync without blocking the caller. The run participates in the Syncer's lifecycle: Stop cancels the merged context so any in-flight GitHub call unblocks, then waits for the goroutine to exit. The caller's ctx is honored too, so per-request deadlines still apply.
type WatchedMR ¶
type WatchedMR struct {
Owner string
Name string
Number int
PlatformHost string // "github.com" or GHE hostname
}
WatchedMR identifies a merge request to sync on a fast interval.
type WorkflowApprovalState ¶
WorkflowApprovalState describes whether workflow approval is needed for a PR.
func WorkflowApprovalStateFromRuns ¶
func WorkflowApprovalStateFromRuns(runs []*gh.WorkflowRun) WorkflowApprovalState
WorkflowApprovalStateFromRuns converts matched workflow runs into state.