Help us shape the future of Go! Take the 2026 Go Developer Survey

github

package
v1.55.0 Latest Latest
Warning

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

Go to latest
Published: Sep 21, 2026 License: AGPL-3.0 Imports: 32 Imported by: 0

Documentation

Index

Constants

View Source
const (
	PRStateOpen   = "open"
	PRStateClosed = "closed"
	PRStateMerged = "merged"
)

PR state strings. These are the single source of truth for the three PR lifecycle states surfaced by PRInfo.State — GetPRByNumber and any other consumer of PRInfo.State should compare against these constants rather than hardcoding "open"/"closed"/"merged" string literals.

Variables

View Source
var DefaultRateLimiter = &RateLimiter{}

DefaultRateLimiter is the shared GitHub API rate limiter used by all native HTTP calls. It is updated automatically by rateLimitTransport on every response; pollers check IsLimited() before dispatching work.

View Source
var ErrAuthorizationPending = errors.New("authorization_pending")

ErrAuthorizationPending is returned by PollDeviceAuth while waiting for the user to complete the browser authorization step.

View Source
var ErrDeviceFlowExpired = errors.New("expired_token")

ErrDeviceFlowExpired is returned when the device code has expired before the user authorized. The caller should restart the flow.

View Source
var ErrGitHubAccessDenied = errors.New("github: access denied")

ErrGitHubAccessDenied is 401, or 403 with no rate-limit signal — retrying with the same credentials will not change the outcome.

View Source
var ErrGitHubRefNotFound = errors.New("github: reference not found")

ErrGitHubRefNotFound is a definitive 404: the referenced PR/issue/commit doesn't exist, or exists but is invisible to the configured token (GitHub disguises "exists, no access" as "not found" for security).

View Source
var ErrNoPR = errors.New("no pull request found for branch")

ErrNoPR is returned by GetPRForBranch when no pull request exists for the branch.

View Source
var ErrNotAuthenticated = errors.New("github token not configured")

ErrNotAuthenticated is returned by SearchUserRepos and ListRepoIssues when no GitHub token is configured (GITHUB_TOKEN, GH_TOKEN, or OS keychain).

Functions

func CheckGHAuth

func CheckGHAuth(ctx context.Context) error

CheckGHAuth verifies github.com authentication. Equivalent to CheckGHAuthForHost(ctx, "") — kept for callers with no host to give it yet.

ctx is threaded through to the underlying request so GitHubCallOriginFrom sees the caller's origin tag (e.g. an interactive PR action) instead of always falling back to the background-tier default — a singleflight-joined call still only uses the first caller's ctx for the shared in-flight request, the same limitation singleflight coalescing always has.

func CheckGHAuthForHost added in v1.54.0

func CheckGHAuthForHost(ctx context.Context, host string) error

CheckGHAuthForHost verifies GitHub authentication for host ("" means github.com) via GET /user using the native HTTP client. No subprocess is invoked — avoids forkExec lock contention. Results are cached for 5 minutes, keyed per host so a github.com auth failure/success is never conflated with a GHE host's. Concurrent callers for the same host share a single inflight call via singleflight.

func ClosePR

func ClosePR(ctx context.Context, ref RepoRef, prNumber int) error

ClosePR closes a pull request without merging.

func DeleteKeychainToken added in v1.35.0

func DeleteKeychainToken() error

DeleteKeychainToken removes the legacy single-account token.

func DeleteKeychainTokenForAccount added in v1.35.0

func DeleteKeychainTokenForAccount(host, username string) error

DeleteKeychainTokenForAccount removes the token for username on host and removes it from the accounts list.

func GeneratePRPrompt

func GeneratePRPrompt(pr *PRInfo, includeDescription bool) string

GeneratePRPrompt generates a context prompt from PR information This can be used to initialize a Claude Code session with PR context

func GetCLIToken added in v1.41.0

func GetCLIToken(ctx context.Context, host string) (string, error)

GetCLIToken shells out to `gh auth token --hostname <host>` to retrieve the token gh has stored (keyring or hosts.yml) for host. Requires the gh CLI to be installed and already authenticated to that host.

func GetCurrentUserLogin added in v1.35.0

func GetCurrentUserLogin(ctx context.Context, host string) (string, error)

GetCurrentUserLogin returns the GitHub login of the authenticated user on host ("" means github.com) via GET /user. Returns an empty string (not an error) when unauthenticated so callers can degrade gracefully. Returns a non-nil error when the request is rate limited instead — that isn't the same as being unauthenticated.

func GetCurrentUserLoginWithToken added in v1.35.0

func GetCurrentUserLoginWithToken(ctx context.Context, host, token string) (string, error)

GetCurrentUserLoginWithToken fetches the GitHub login for an explicit token on host ("" means github.com). Returns ("", nil) when the token is invalid or unauthenticated, and a non-nil error when the request is rate limited instead.

func GetKeychainToken added in v1.35.0

func GetKeychainToken() string

GetKeychainToken returns a token for the default single-token auth flow (getGHToken/newGHRequest), which always targets api.github.com. It must therefore prefer a github.com account's token over any other configured host: returning an enterprise account's token here sends valid credentials to the wrong API and GitHub correctly rejects them as 401 Bad credentials, regardless of account order in ListKeychainAccounts. Falls back to the first account of any host, then the legacy single-account slot, only when no github.com account is configured.

func GetKeychainTokenForAccount added in v1.35.0

func GetKeychainTokenForAccount(host, username string) string

GetKeychainTokenForAccount returns the stored token for username on host, or "".

func GetKeychainTokenForHost added in v1.41.0

func GetKeychainTokenForHost(host string) string

GetKeychainTokenForHost returns any stored token for host, regardless of which account it belongs to. Backlog sync plugins (session/backlog_plugin_github.go, session/backlog_plugin_github_prs.go) only know a host, not a username, so they can't call GetKeychainTokenForAccount directly. Falls back to the legacy single-account slot for github.com when no named account matches.

func GetPRDiff

func GetPRDiff(ctx context.Context, ref RepoRef, prNumber int) (string, error)

GetPRDiff fetches the diff for a pull request.

func GetRemoteURL

func GetRemoteURL(repoPath string) (string, error)

GetRemoteURL returns the remote URL of a repository (used to determine owner/repo). Reads the URL out of the repo's own git config via go-git (session/git.OpenRepo, which sets EnableDotGitCommonDir so linked worktrees resolve correctly) instead of shelling out to `git remote get-url` — no subprocess, so no ForkLock contention. Cached and singleflight-coalesced per canonical repo (shared across all its worktrees), not per repoPath — a repo's remote is identical for every worktree, so without the shared key, every session worktree caused its own cache miss.

func GhBaseURL added in v1.35.0

func GhBaseURL() string

GhBaseURL returns the current GitHub REST/GraphQL API base URL for github.com.

func GitHubCallSiteFrom added in v1.54.0

func GitHubCallSiteFrom(ctx context.Context) string

GitHubCallSiteFrom returns the call site tagged on ctx, or "" ("unattributed") when none was set — an untagged native HTTP call is still counted, just not attributable to a specific wrapper.

func HTTPClient added in v1.42.0

func HTTPClient() *http.Client

HTTPClient returns the shared GitHub HTTP client, so other packages (e.g. session's backlog GitHub plugins) route their calls through the same rate-limit-observing Transport instead of constructing their own client.

func IsGitHubCom added in v1.41.0

func IsGitHubCom(host string) bool

IsGitHubCom reports whether host (after normalization) is github.com.

func IsGitHubRef

func IsGitHubRef(input string) bool

IsGitHubRef checks if the input string looks like a GitHub URL or reference This is a quick check that doesn't validate the full format

func IsGitHubRefWithHosts added in v1.41.0

func IsGitHubRefWithHosts(input string, enterpriseHosts []string) bool

IsGitHubRefWithHosts is the host-aware variant of IsGitHubRef, additionally recognizing URLs against any of the supplied enterpriseHosts.

func IsTerminal added in v1.12.0

func IsTerminal(priority PRPriority) bool

IsTerminal returns true if the priority indicates a terminal PR state. Terminal sessions should not be polled at normal frequency.

func MergePR

func MergePR(ctx context.Context, ref RepoRef, prNumber int, method string) error

MergePR merges a pull request. method can be: "merge", "squash", or "rebase"

func NewConditionalRequest added in v1.54.0

func NewConditionalRequest(ctx context.Context, path string, cache *ETagCache) (*http.Request, error)

NewConditionalRequest builds an authenticated GET request to the github.com REST API for path, setting If-None-Match from cache's last-known ETag for path (if any) so GitHub can answer with a zero-rate-limit-cost 304 when nothing has changed. This only builds the request — the caller reads the response's ETag header and stores it back via cache.set(...) itself, mirroring GetPRInfoConditional's existing split between building the request and handling the response. This is one of the two approved constructors for new native GitHub call sites (the other being NewConditionalRequestNoCache); see .claude/rules/norawghrequest.md.

func NewConditionalRequestNoCache added in v1.54.0

func NewConditionalRequestNoCache(ctx context.Context, path string) (*http.Request, error)

NewConditionalRequestNoCache builds an authenticated GET request to the github.com REST API for path with no conditional (If-None-Match) semantics — the deliberate, reviewable opt-out for a call site that genuinely has no need for ETag caching (e.g. a one-off fetch with no meaningful cache key), so skipping conditional semantics is a visible decision rather than a silent omission. Prefer NewConditionalRequest whenever an *ETagCache is available.

func NormalizeHost added in v1.41.0

func NormalizeHost(host string) string

NormalizeHost returns the canonical form of a GitHub host: lowercased, no scheme, no trailing slash, and "" mapped to github.com. Hostnames are case-insensitive (DNS), and GHE hosts are free-text admin/user input, so lowercasing here keeps registration and URL-match comparisons consistent regardless of how a host was typed.

func PollDeviceAuth added in v1.35.0

func PollDeviceAuth(ctx context.Context, host, clientIDOverride, deviceCode string) (string, error)

PollDeviceAuth polls GitHub's token endpoint once.

  • Returns (token, nil) on success — the caller should store the token.
  • Returns ("", ErrAuthorizationPending) if the user hasn't approved yet.
  • Returns ("", ErrDeviceFlowExpired) if the device code has expired.
  • Returns ("", err) for any other error.

func PostPRComment

func PostPRComment(ctx context.Context, ref RepoRef, prNumber int, body string) error

PostPRComment posts a comment on a pull request.

func ResetPriorityAdmissionFlagCacheForTest added in v1.54.0

func ResetPriorityAdmissionFlagCacheForTest()

ResetPriorityAdmissionFlagCacheForTest clears priorityAdmissionEnabled's TTL cache so a consumer-package test that flips githubPriorityAdmissionFlagName sees the change immediately. Exported because _test.go files (where this package keeps its own copy) aren't importable across packages.

func ResetRateLimiterForTest added in v1.45.1

func ResetRateLimiterForTest(t testing.TB)

ResetRateLimiterForTest swaps in a fresh DefaultRateLimiter for the duration of t, restoring the original via t.Cleanup. Any test — in this package or a consumer package (e.g. session's GitHub backlog plugins) — that drives a request through HTTPClient() and can trigger a rate-limit -setting response (403 with Retry-After, 403 with X-RateLimit-Remaining: 0, or 429) must call this. DefaultRateLimiter is a package-level global (rate_limit.go) shared by every test in the same test binary/process, and since rateLimitTransport.RoundTrip fails fast when it's already limited, one test's rate-limit fixture otherwise poisons every test that runs after it in that process.

func ResourceForRequest added in v1.54.0

func ResourceForRequest(req *http.Request) string

ResourceForRequest classifies an outbound GitHub HTTP request into the GitHub API resource bucket it consumes quota from (core, search, graphql), mirroring GitHub's own per-endpoint resource assignment. Derived from the request rather than the eventual response, since AdmitOrigin must decide before dispatch — before any X-RateLimit-Resource response header exists.

func RestBaseURLForHost added in v1.41.0

func RestBaseURLForHost(host string) string

RestBaseURLForHost returns the REST API base URL for host, including a trailing slash. For github.com this returns GhBaseURL unchanged, preserving the test seam that overrides it directly.

func SetCommitStatus added in v1.44.0

func SetCommitStatus(repo RepoRef, req CommitStatusRequest) error

SetCommitStatus posts a commit status to the GitHub Statuses API (POST /repos/{owner}/{repo}/statuses/{sha}) via the gh CLI. repo.Host() "" means github.com; a GHE host is targeted via --hostname/GH_HOST, mirroring IsForkRepo/GetPRComments in github/client.go.

This targets the Statuses API rather than the Checks API because Checks API writes are restricted to GitHub Apps, and this repo's entire GitHub write path is PAT/OAuth-token-based via the gh CLI (see ADR-001 in project_plans/pr-comment-check-runs).

func SetEnterpriseBaseURLOverride added in v1.52.0

func SetEnterpriseBaseURLOverride(host, url string)

SetEnterpriseBaseURLOverride registers a per-host API override for tests (pass url == "" to clear it), mirroring GhBaseURL's test seam for github.com. host is normalized the same way RestBaseURLForHost/ graphQLURLForHost normalize their lookup key, so callers may pass a raw host (with scheme, casing, or trailing slash) and still hit the override.

func SetGHHTTPBaseTransportForTest added in v1.54.0

func SetGHHTTPBaseTransportForTest(rt http.RoundTripper) (restore func())

SetGHHTTPBaseTransportForTest swaps the innermost RoundTripper in ghHTTPClient's transport chain (normally http.DefaultTransport) for rt, returning a restore func — mirroring SetGhBaseURLForTest's swap-a-var-return-a-restore-func pattern. The githubTelemetryTransport, otelhttp.NewTransport, and rateLimitTransport layers above rt keep running unchanged, so a test using this seam can observe GitHubCallOriginFrom(req.Context()) on whatever request actually reaches the "wire" while those real layers still execute.

func SetGhBaseURLForTest added in v1.52.0

func SetGhBaseURLForTest(url string) (restore func())

SetGhBaseURLForTest overrides GhBaseURL for the duration of a test (e.g. to point at an httptest.Server so requests never reach the real API) and returns a restore func. A missing trailing slash is added — graphQLURLForHost builds the GraphQL endpoint via straight concatenation (GhBaseURL()+"graphql"), so an un-slashed override would otherwise produce a malformed URL.

func SetKeychainToken added in v1.35.0

func SetKeychainToken(token string) error

SetKeychainToken stores a token under the legacy single-account slot. Prefer SetKeychainTokenForAccount when the username is known.

func SetKeychainTokenForAccount added in v1.35.0

func SetKeychainTokenForAccount(host, username, token string) error

SetKeychainTokenForAccount stores a token under a per-account key and adds the account to the accounts list if not already present.

func StoreTokenForDiscoveredUser added in v1.35.0

func StoreTokenForDiscoveredUser(ctx context.Context, host, token string) error

StoreTokenForDiscoveredUser fetches the GitHub login for token on host and stores it under the per-account keychain slot. Falls back to the legacy slot (github.com only) if the login cannot be determined.

func WithGitHubCallOrigin added in v1.54.0

func WithGitHubCallOrigin(ctx context.Context, origin CallOrigin) context.Context

WithGitHubCallOrigin returns a copy of ctx tagged with the given CallOrigin.

func WithGitHubCallSite added in v1.54.0

func WithGitHubCallSite(ctx context.Context, callSite string) context.Context

WithGitHubCallSite returns a copy of ctx tagged with callSite, the fixed, bounded name of the wrapper function issuing a GitHub call (e.g. "pr.view.graphql"). Mirrors WithGitHubCallOrigin's pattern (call_origin.go) but for the orthogonal "which wrapper" dimension: runGHCLICommand (gh_exec.go) sets this per gh-CLI call site directly on its own span, and githubTelemetryTransport.RoundTrip (telemetry_transport.go) reads it via GitHubCallSiteFrom for every native HTTP call site, so both paths populate plan.md's Observability Plan `call_site` label the same way.

Types

type AccountRef added in v1.41.0

type AccountRef struct {
	Username string `json:"username"`
	Host     string `json:"host"`
}

AccountRef identifies one connected GitHub account by username and host.

func ListKeychainAccounts added in v1.35.0

func ListKeychainAccounts() []AccountRef

ListKeychainAccounts returns the ordered list of connected GitHub accounts. The stored shape is normally []AccountRef; a legacy []string of usernames (from before per-host support) is transparently read as github.com accounts.

type AccountToken added in v1.35.0

type AccountToken struct {
	Username string // empty for the legacy single-account slot
	Host     string
	Token    string
}

AccountToken pairs a GitHub username and host with its token.

func GetAllKeychainTokens added in v1.35.0

func GetAllKeychainTokens() []AccountToken

GetAllKeychainTokens returns all stored tokens across all named accounts plus the legacy single-account slot. Each entry is a (username, host, token) tuple.

type CLIHost added in v1.41.0

type CLIHost struct {
	Host     string
	Username string
}

CLIHost describes a GitHub host the `gh` CLI is already authenticated to.

func ListCLIHosts added in v1.41.0

func ListCLIHosts() ([]CLIHost, error)

ListCLIHosts reads gh CLI's hosts.yml to discover which GitHub hosts the user has already run `gh auth login` against. It only reads the host and associated username — safe to display in the UI — never a token: gh 2.x stores tokens in the OS keyring, not in this file, so retrieving the actual token still requires GetCLIToken. Returns an empty slice (not an error) when gh has never been configured on this machine.

type CachedAccount added in v1.41.0

type CachedAccount struct {
	Login string
	Host  string
}

CachedAccount is a resolved (login, host) pair for the accounts list RPC.

type CallOrigin added in v1.54.0

type CallOrigin string

CallOrigin identifies which code path is making a GitHub call, so telemetry (Phase 1) and admission control (Phase 3) can attribute and prioritize traffic by source.

const (
	// OriginInteractive is a call made synchronously on behalf of a user-facing
	// RPC (e.g. GetPRInfo, MergePR triggered from the UI).
	OriginInteractive CallOrigin = "interactive"
	// OriginPRStatusPoller is a call made by the background PR status poller.
	OriginPRStatusPoller CallOrigin = "pr_status_poller"
	// OriginWorktreePRPoller is a call made by the background worktree PR poller.
	OriginWorktreePRPoller CallOrigin = "worktree_pr_poller"
	// OriginBacklogSync is a call made by the backlog GitHub-issue sync loop.
	OriginBacklogSync CallOrigin = "backlog_sync"
	// OriginWebhookReconcile is a call made in response to a GitHub webhook
	// event reconciling local state.
	OriginWebhookReconcile CallOrigin = "webhook_reconcile"
)

func GitHubCallOriginFrom added in v1.54.0

func GitHubCallOriginFrom(ctx context.Context) CallOrigin

GitHubCallOriginFrom returns the CallOrigin tagged on ctx. Per ADR-002, an untagged context defaults to OriginPRStatusPoller (the background tier) rather than OriginInteractive, so an un-instrumented call path is treated as background traffic instead of silently getting interactive priority.

type CheckItem added in v1.49.0

type CheckItem struct {
	Name       string
	Context    string
	State      string
	Status     string
	Conclusion string
}

CheckItem is one itemized CI check from a PR's statusCheckRollup — the data getCheckConclusion collapses into a single string; exported so callers that want the itemized view (e.g. the VCS tab's "why blocked" rollup) don't need to.

type CommitResult added in v1.41.0

type CommitResult struct {
	SHA     string
	HTMLURL string
	Message string
	Author  string
}

CommitResult is the domain return type for GetCommit.

func GetCommit added in v1.41.0

func GetCommit(ctx context.Context, account AccountRef, repo RepoRef, sha string) (*CommitResult, error)

GetCommit fetches a single commit by SHA via the GitHub REST API (native net/http, same auth/error-classification mechanism as GetIssue/GetPR). account.Host "" means github.com; account.Username, when non-empty, selects among multiple connected accounts for that host (see getGHTokenForAccount). Returns ErrNotAuthenticated when no token is configured.

type CommitStatusRequest added in v1.44.0

type CommitStatusRequest struct {
	SHA           string
	State         CommitStatusState
	StatusContext string
	Description   string
	TargetURL     string
}

CommitStatusRequest bundles the fields needed to post a commit status, avoiding a same-typed-string parameter pile on SetCommitStatus (see primitive-obsession-checklist.md).

func NewCommitStatusRequest added in v1.44.0

func NewCommitStatusRequest(sha string, state CommitStatusState, statusContext, description string) (CommitStatusRequest, error)

NewCommitStatusRequest validates and constructs a CommitStatusRequest. TargetURL is optional and may be left empty.

type CommitStatusState added in v1.44.0

type CommitStatusState string

CommitStatusState is the state of a commit status posted via the GitHub Statuses API.

const (
	CommitStatusStatePending CommitStatusState = "pending"
	CommitStatusStateSuccess CommitStatusState = "success"
	CommitStatusStateError   CommitStatusState = "error"
	CommitStatusStateFailure CommitStatusState = "failure"
)

type DeviceAuthStart added in v1.35.0

type DeviceAuthStart struct {
	DeviceCode      string
	UserCode        string
	VerificationURI string
	// ExpiresIn is how long the code is valid (seconds).
	ExpiresIn int
	// Interval is the minimum poll interval suggested by GitHub (seconds).
	Interval int
}

DeviceAuthStart holds the values returned by the first step of the Device Flow: the user-visible code and the URL where they enter it.

func StartDeviceAuth added in v1.35.0

func StartDeviceAuth(ctx context.Context, host, clientIDOverride string) (*DeviceAuthStart, error)

StartDeviceAuth initiates the GitHub Device Flow against host and returns the codes the user must enter at verification_uri. clientID is the OAuth App client ID to use; pass "" for github.com to use the default/env-configured one. The returned DeviceAuthStart.DeviceCode must be passed to PollDeviceAuth.

type ETagCache added in v1.12.0

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

ETagCache stores ETags and cached PRInfo responses per (owner, repo, prNumber). Using conditional requests (If-None-Match) allows GitHub to return 304 Not Modified responses that cost zero rate-limit quota when the PR has not changed. sync.Map gives lock-free reads in the steady state — entries are written once on first PR discovery and then read on every subsequent poll tick.

func NewETagCache added in v1.12.0

func NewETagCache() *ETagCache

NewETagCache creates a new empty ETagCache.

func (*ETagCache) Invalidate added in v1.54.0

func (c *ETagCache) Invalidate(ref RepoRef, prNumber int)

Invalidate drops the cached entry for (ref, prNumber), safe to call concurrently with get/set. The next GetPRInfoConditional call for that key omits If-None-Match, forcing a fresh, unconditional 200 fetch instead of a stale 304.

type IssueResult added in v1.35.0

type IssueResult struct {
	Number    int
	Title     string
	Body      string
	Author    string
	State     string
	URL       string
	Labels    []string
	CreatedAt time.Time
	UpdatedAt time.Time
	IsPR      bool
	// Host is the GitHub host the result came from ("" means github.com),
	// echoed from the account.Host passed to ListRepoIssues.
	Host string
}

IssueResult is the domain return type for ListRepoIssues and GetIssue.

func GetIssue added in v1.41.0

func GetIssue(ctx context.Context, account AccountRef, repo RepoRef, number int) (*IssueResult, error)

GetIssue fetches a single issue (including its body) by number. account.Host "" means github.com; account.Username, when non-empty, selects among multiple connected accounts for that host (see getGHTokenForAccount). Returns ErrNotAuthenticated when no token is configured.

func ListRepoIssues added in v1.35.0

func ListRepoIssues(ctx context.Context, account AccountRef, repo RepoRef, state, search string, limit int) ([]IssueResult, error)

ListRepoIssues fetches issues for a specific repo. When search is empty it uses GET /repos/{owner}/{repo}/issues. When search is non-empty it uses GET /search/issues. account.Host "" means github.com; account.Username, when non-empty, selects among multiple connected accounts for that host (see getGHTokenForAccount). Returns ErrNotAuthenticated when no token is configured.

type PRAnnotationSession added in v1.35.0

type PRAnnotationSession struct {
	ID       string
	Branch   string
	Repo     RepoRef
	PRNumber int // fallback: match by PR number when branch name doesn't match headRef
}

PRAnnotationSession carries session data needed to annotate UserPR entries. Defined here (not in the session package) to avoid an import cycle: session imports github, so github cannot import session.

Repo is a typed value object: holding a valid RepoRef proves owner and repo are non-empty. Sessions without a resolvable GitHub repo are skipped.

type PRAnnotationWorktree added in v1.35.0

type PRAnnotationWorktree struct {
	Branch       string
	Repo         RepoRef
	WorktreePath string
}

PRAnnotationWorktree carries worktree data for annotation.

type PRComment

type PRComment struct {
	ID        int       `json:"id"`
	Author    string    `json:"author"`
	Body      string    `json:"body"`
	CreatedAt time.Time `json:"createdAt"`
	Path      string    `json:"path,omitempty"`     // For review comments
	Line      int       `json:"line,omitempty"`     // For review comments
	IsReview  bool      `json:"isReview,omitempty"` // True if this is a review comment
}

PRComment represents a comment on a PR (either issue comment or review comment)

func GetPRComments

func GetPRComments(ctx context.Context, ref RepoRef, prNumber int) ([]PRComment, error)

GetPRComments fetches all comments on a pull request.

type PRInfo

type PRInfo struct {
	Number       int       `json:"number"`
	Title        string    `json:"title"`
	Body         string    `json:"body"`
	HeadRef      string    `json:"headRefName"`
	HeadSHA      string    `json:"headRefOid"`
	BaseRef      string    `json:"baseRefName"`
	State        string    `json:"state"`
	Author       string    `json:"author"`
	Labels       []string  `json:"labels"`
	HTMLURL      string    `json:"url"`
	CreatedAt    time.Time `json:"createdAt"`
	UpdatedAt    time.Time `json:"updatedAt"`
	IsDraft      bool      `json:"isDraft"`
	Mergeable    string    `json:"mergeable"`
	Additions    int       `json:"additions"`
	Deletions    int       `json:"deletions"`
	ChangedFiles int       `json:"changedFiles"`

	// Review and CI status fields (populated by GetPRInfo with extended fields)
	ReviewDecision        string // "approved" / "changes_requested" / "review_required" / ""
	ApprovedCount         int    // Count of current non-dismissed APPROVED reviews
	ChangesRequestedCount int    // Count of current non-dismissed CHANGES_REQUESTED reviews
	CheckConclusion       string // "success" / "failure" / "pending" / "action_required" / "neutral" / ""
	CheckStatus           string // "completed" / "in_progress" / ""
	Checks                []CheckItem
	Reviews               []ReviewItem
}

PRInfo contains metadata about a GitHub pull request

func GetPRByNumber added in v1.41.0

func GetPRByNumber(ctx context.Context, ref RepoRef, prNumber int) (*PRInfo, error)

GetPRByNumber fetches a single pull request by its number using the GitHub REST API directly (no gh subprocess). Unlike GetPRForBranch, which looks a PR up by head branch name and can therefore match the wrong PR when a branch is reused or renamed, GetPRByNumber looks a PR up by its immutable number — the root-cause fix for branch-name-keyed lookups matching stale or unrelated PRs.

Returns ErrNoPR when no pull request exists for the given number (HTTP 404). Before returning success, the response's base.repo.full_name is compared against the requested owner/repo; a mismatch returns a non-nil, non-ErrNoPR error rather than trusting the response body blindly.

func GetPRForBranch added in v1.12.0

func GetPRForBranch(ctx context.Context, ref RepoRef, branch string) (*PRInfo, error)

GetPRForBranch finds the GitHub PR associated with a branch. Uses the GitHub REST API directly (no gh subprocess) to avoid forkExec lock contention. Returns ErrNoPR when no pull request exists for the branch.

func GetPRForBranchConditional added in v1.37.0

func GetPRForBranchConditional(ctx context.Context, ref RepoRef, branch, etag string) (info *PRInfo, newEtag string, changed bool, err error)

GetPRForBranchConditional is GetPRForBranch with ETag conditional request support. Pass the previously returned newEtag (empty string for first call). Returns (nil, etag, false, nil) on 304 Not Modified — caller should treat as unchanged. Every error path also returns changed=false, including ErrNotAuthenticated when no token is configured — callers must check err before treating changed=false as "unchanged, no error."

func GetPRInfo

func GetPRInfo(owner, repo string, prNumber int) (*PRInfo, error)

GetPRInfo fetches metadata for a pull request including review and CI status.

func GetPRInfoConditional added in v1.12.0

func GetPRInfoConditional(ctx context.Context, ref RepoRef, prNumber int, cache *ETagCache) (*PRInfo, bool, error)

GetPRInfoConditional fetches PR info using ETag conditional requests. Uses native net/http instead of a gh subprocess to avoid forkExec lock contention. Returns (info, changed, error).

  • changed=false means 304 Not Modified; info contains the cached value.
  • changed=true means 200 OK; info contains freshly fetched data.
  • Both info and changed may be zero values when an error is returned.

func GetPRInfoCtx added in v1.12.0

func GetPRInfoCtx(ctx context.Context, ref RepoRef, prNumber int) (*PRInfo, error)

GetPRInfoCtx fetches metadata for a pull request with context support. Includes review decisions and CI/check status. ref.Host() "" means github.com.

func GetPRInfoGraphQL added in v1.54.0

func GetPRInfoGraphQL(ctx context.Context, owner, repo string, prNumber int) (*PRInfo, error)

GetPRInfoGraphQL fetches metadata for a pull request via GitHub's GraphQL API instead of GetPRInfoCtx's `gh pr view` subprocess shell-out, producing a field-identical *PRInfo (verified by the parity test in client_graphql_test.go). It issues one POST through ghHTTPClient (see http_client.go), so it automatically gets the same rate-limit admission control and OTel telemetry (githubTelemetryTransport) every other native GitHub call gets — no separate instrumentation call needed here.

Unlike GetPRInfoCtx, this does not call CheckGHAuth() first: none of this package's other native-HTTP call sites (GetPRForBranch, GetPRByNumber) do either — they dispatch the request and let classifyGHResponse turn a 401/403 into an error, which is what this function does too. CheckGHAuth's pre-flight check exists specifically to give the `gh` subprocess path a clearer error than a raw exec failure would; that rationale doesn't apply to a direct HTTP call.

This function is wired: GetPRInfoCtx (client.go) dispatches here when the github:graphql-pr-info feature flag (githubGraphQLMigrationFlagName) is enabled — see that constant's doc comment for the flag's default-off rationale.

type PRPriority added in v1.12.0

type PRPriority string

PRPriority is a derived single-enum priority computed from compound PR state.

const (
	PRPriorityBlocking  PRPriority = "blocking"   // changes requested or CI failing
	PRPriorityReady     PRPriority = "ready"      // approved + CI passing
	PRPriorityPending   PRPriority = "pending"    // awaiting review or checks running
	PRPriorityDraft     PRPriority = "draft"      // PR is a draft
	PRPriorityComplete  PRPriority = "complete"   // PR is merged or closed
	PRPriorityNoPR      PRPriority = "no_pr"      // no PR found for branch
	PRPriorityAuthError PRPriority = "auth_error" // gh CLI not authenticated
	PRPriorityError     PRPriority = "error"      // transient error fetching status
)

func DerivePRPriority added in v1.12.0

func DerivePRPriority(info *PRInfo) PRPriority

DerivePRPriority computes a single priority enum from compound PR state. Returns PRPriorityNoPR if info is nil.

type PRResult added in v1.41.0

type PRResult struct {
	Number  int
	Title   string
	State   string
	HTMLURL string
}

PRResult is the domain return type for GetPR — a lean existence check, not the richer PRInfo returned by GetPRInfoCtx (which shells out to `gh`).

func GetPR added in v1.41.0

func GetPR(ctx context.Context, account AccountRef, repo RepoRef, number int) (*PRResult, error)

GetPR fetches a single pull request by number via the GitHub REST API (native net/http, not the `gh` CLI subprocess GetPRInfoCtx uses) — a lean existence check sharing GetIssue's auth mechanism and error classification. account.Host "" means github.com; account.Username, when non-empty, selects among multiple connected accounts for that host (see getGHTokenForAccount). Returns ErrNotAuthenticated when no token is configured.

type ParsedGitHubRef

type ParsedGitHubRef struct {
	Type        RefType
	Host        string // GitHub host, e.g. "github.com" or a GHES hostname; "" means github.com
	Owner       string
	Repo        string
	PRNumber    int    // Only populated for RefTypePR
	IssueNumber int    // Only populated for RefTypeIssue
	Branch      string // Populated for RefTypeBranch, or PR head branch after fetching
	CommitSHA   string // Only populated for RefTypeCommit
	FilePath    string // Only populated for RefTypeFile (path within repo)
	LineStart   int    // Only populated for RefTypeFile (optional line number)
	LineEnd     int    // Only populated for RefTypeFile (optional end line for range)
	BaseBranch  string // Only populated for RefTypeCompare (the base of comparison)
	HeadBranch  string // Only populated for RefTypeCompare (the head being compared)
	Tag         string // Only populated for RefTypeRelease
	OriginalURL string // The original input string
}

ParsedGitHubRef represents a parsed GitHub URL or reference

func ParseGitHubRefWithHosts added in v1.41.0

func ParseGitHubRefWithHosts(input string, enterpriseHosts []string) (*ParsedGitHubRef, error)

ParseGitHubRefWithHosts parses a GitHub URL or shorthand reference into a ParsedGitHubRef, matching either github.com or any of the supplied enterpriseHosts. The Host field of the returned ref is set to the matched host (or left empty for shorthand references, which default to github.com).

func (*ParsedGitHubRef) CloneURL

func (p *ParsedGitHubRef) CloneURL() string

CloneURL returns the HTTPS clone URL for the repository

func (*ParsedGitHubRef) DisplayName

func (p *ParsedGitHubRef) DisplayName() string

DisplayName returns a human-readable name for the reference

func (*ParsedGitHubRef) HTMLURL

func (p *ParsedGitHubRef) HTMLURL() string

HTMLURL returns the human-readable GitHub URL

func (*ParsedGitHubRef) RepoFullName

func (p *ParsedGitHubRef) RepoFullName() string

RepoFullName returns "owner/repo" format

func (*ParsedGitHubRef) SuggestedSessionName

func (p *ParsedGitHubRef) SuggestedSessionName() string

SuggestedSessionName returns a suggested session name based on the reference

type RateLimiter added in v1.37.0

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

RateLimiter tracks GitHub primary and secondary rate limit state.

Primary rate limit — hourly quota per authenticated token (5000 req/hr for PAT).

Signalled by X-RateLimit-Remaining → 0 and X-RateLimit-Reset (Unix epoch, seconds).
Response: 403 or 429 with X-RateLimit-Remaining: 0.

Secondary rate limit — concurrent connection / per-minute burst limits.

Signalled by 429 or 403 with Retry-After header present.
X-RateLimit-Remaining may still be nonzero.

Detection order: Retry-After present → secondary; remaining == 0 → primary; neither → auth/permission error (do not pause polling).

func (*RateLimiter) AdmitOrigin added in v1.54.0

func (r *RateLimiter) AdmitOrigin(origin CallOrigin, resource string) (bool, string)

AdmitOrigin decides whether a call for origin targeting resource (as classified by ResourceForRequest) should proceed. OriginInteractive is always admitted while the token isn't globally rate limited — callers must still check IsLimited() themselves first (rateLimitTransport.RoundTrip does), since AdmitOrigin only makes the priority-tier decision, not the already-limited one. Any other origin is rejected once resource's own Remaining drops below backgroundHeadroomPercent of its Limit, so a background poller backs off before it can starve an interactive call. A resource never observed yet (ok == false) is treated as "no data — admit," not "exhausted — reject," since a zero-value ResourceQuota would otherwise read as Remaining: 0. The decision is scoped strictly to resource's own bucket — another resource's quota (e.g. a low-limit search response) never affects it (pre-mortem P1 #2).

func (*RateLimiter) IsLimited added in v1.37.0

func (r *RateLimiter) IsLimited() (bool, time.Time)

IsLimited returns true and the resume time if the client is currently rate limited.

func (*RateLimiter) Reset added in v1.47.0

func (r *RateLimiter) Reset()

Reset clears any recorded rate-limit state. DefaultRateLimiter is a package-level singleton shared by every caller of HTTPClient() (see http_client.go) — a test that deliberately simulates a rate-limit response (e.g. to verify the fail-fast behavior rateLimitTransport.RoundTrip added) leaves it limited for every later test in the same binary otherwise, since RoundTrip's IsLimited() pre-check has no per-test scoping.

func (*RateLimiter) Snapshot added in v1.54.0

func (r *RateLimiter) Snapshot() RateLimiterSnapshot

Snapshot returns a lock-free, point-in-time read of the limiter's state. If Update has never published one, it returns a zero-value RateLimiterSnapshot (Resources is nil) — a caller reading Snapshot().Resources["core"] off a nil map gets ResourceQuota{}'s zero value via Go's safe nil-map-read semantics, not a panic.

func (*RateLimiter) Update added in v1.37.0

func (r *RateLimiter) Update(resp *http.Response)

Update reads GitHub rate-limit headers from resp and updates the limiter. Called automatically by rateLimitTransport on every response — callers do not need to invoke this manually.

type RateLimiterSnapshot added in v1.54.0

type RateLimiterSnapshot struct {
	Resources        map[string]ResourceQuota
	RateLimitedUntil time.Time
}

RateLimiterSnapshot is a lock-free, point-in-time read of RateLimiter's state, partitioned by resource so a low-limit resource (e.g. search, 30/hr) can never clobber or mask another resource's (e.g. core, 5000/hr) quota — core/search/graphql share one token but have independent budgets (see pre-mortem P1 #2).

type RefType

type RefType int

RefType represents the type of GitHub reference parsed from a URL or shorthand

const (
	RefTypePR RefType = iota
	RefTypeBranch
	RefTypeRepo
	RefTypeFile    // File/blob URL
	RefTypeCommit  // Commit URL
	RefTypeIssue   // Issue URL
	RefTypeCompare // Compare URL (branch comparison)
	RefTypeRelease // Release/tag URL
)

func (RefType) String

func (t RefType) String() string

type RepoRef added in v1.35.0

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

RepoRef is a value object that bundles a GitHub owner and repository name, plus the host that owns them (empty means github.com). Both owner and repo are non-empty by construction — holding a RepoRef proves the invariant holds without any further nil/empty checks at use sites.

func GetOwnerRepoFromRemote added in v1.35.0

func GetOwnerRepoFromRemote(repoPath string, enterpriseHosts []string) (RepoRef, error)

GetOwnerRepoFromRemote returns a RepoRef for a local git repository by reading the origin remote URL and parsing it. Returns an invalid zero-value RepoRef (not an error) when the remote is not a GitHub URL.

func NewRepoRef added in v1.35.0

func NewRepoRef(owner, repo string) (RepoRef, error)

NewRepoRef constructs a github.com RepoRef, returning an error if either field is empty. Equivalent to NewRepoRefWithHost(owner, repo, "").

func NewRepoRefWithHost added in v1.54.0

func NewRepoRefWithHost(owner, repo, host string) (RepoRef, error)

NewRepoRefWithHost constructs a RepoRef scoped to host (empty means github.com), returning an error if owner or repo is empty.

func (RepoRef) BranchKey added in v1.35.0

func (r RepoRef) BranchKey(branch string) string

BranchKey returns the map key used to match sessions by branch: "owner/branch".

func (RepoRef) Host added in v1.54.0

func (r RepoRef) Host() string

Host returns the GitHub Enterprise host that owns this repo, or "" for github.com.

func (RepoRef) IsValid added in v1.35.0

func (r RepoRef) IsValid() bool

IsValid reports whether the ref was constructed with non-empty owner and repo. The zero value (RepoRef{}) is not valid.

func (RepoRef) Owner added in v1.35.0

func (r RepoRef) Owner() string

func (RepoRef) PRKey added in v1.35.0

func (r RepoRef) PRKey(number int) string

PRKey returns the map key used to match sessions by PR number: "owner/#number".

func (RepoRef) Repo added in v1.35.0

func (r RepoRef) Repo() string

func (RepoRef) String added in v1.35.0

func (r RepoRef) String() string

String returns "owner/repo".

type RepoResult added in v1.35.0

type RepoResult struct {
	Owner       string
	Repo        string
	Description string
	Private     bool
	// Host is the GitHub host the result came from ("" means github.com),
	// echoed from the account.Host passed to SearchUserRepos so callers can
	// tell results from different accounts/hosts apart after merging.
	Host string
}

RepoResult is the domain return type for SearchUserRepos.

func SearchUserRepos added in v1.35.0

func SearchUserRepos(ctx context.Context, account AccountRef, query string, limit int) ([]RepoResult, error)

SearchUserRepos fetches repos accessible to the authenticated user. When query is empty it uses GET /user/repos (all accessible repos sorted by push time). When non-empty it uses GET /search/repositories. account.Host "" means github.com; account.Username, when non-empty, selects among multiple connected accounts for that host (see getGHTokenForAccount). Returns ErrNotAuthenticated when no token is configured.

type ResourceQuota added in v1.54.0

type ResourceQuota struct {
	Remaining int
	Limit     int
	ResetAt   time.Time
}

ResourceQuota is one GitHub API resource's (core, search, graphql, ...) last-observed quota, as reported by that resource's own X-RateLimit-* response headers.

type ReviewItem added in v1.49.0

type ReviewItem struct {
	Author string
	State  string
	Body   string
}

ReviewItem is one PR review — the data parseReviewCounts collapses into approved/changes-requested counts, exported so callers can also read the reviewer's Body text (e.g. a CHANGES_REQUESTED review's stated reason).

type UserPR added in v1.35.0

type UserPR struct {
	Owner             string
	Repo              string
	Number            int
	Title             string
	URL               string
	HeadRef           string
	BaseRef           string
	State             string
	IsDraft           bool
	UpdatedAt         time.Time
	ClosedAt          time.Time
	MergedAt          time.Time
	ApprovedCount     int
	ChangesReqCount   int
	CheckConclusion   string // "success" / "failure" / "pending" / ""
	SessionIDs        []string
	LocalWorktreePath string
}

UserPR is an open GitHub pull request authored by the authenticated user, optionally annotated with local session IDs and worktree paths.

type UserPRCache added in v1.35.0

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

func NewUserPRCache added in v1.35.0

func NewUserPRCache() *UserPRCache

NewUserPRCache creates a cache with default configuration.

func NewUserPRCacheWithConfig added in v1.35.0

func NewUserPRCacheWithConfig(cfg UserPRCacheConfig) *UserPRCache

NewUserPRCacheWithConfig creates a cache with custom configuration.

func (*UserPRCache) Annotate added in v1.35.0

func (c *UserPRCache) Annotate(sessions []PRAnnotationSession, worktrees []PRAnnotationWorktree)

Annotate enriches the current snapshot with session IDs and worktree paths. It performs a COW update: load → copy → mutate → store. No-op if the snapshot hasn't been populated yet.

func (*UserPRCache) GetAll added in v1.35.0

func (c *UserPRCache) GetAll() []UserPR

GetAll returns a copy of the current PR snapshot. Returns nil before the first successful fetch.

func (*UserPRCache) GetCachedAccounts added in v1.41.0

func (c *UserPRCache) GetCachedAccounts() []CachedAccount

GetCachedAccounts returns all connected GitHub accounts with their host.

func (*UserPRCache) GetCachedLogin added in v1.35.0

func (c *UserPRCache) GetCachedLogin() string

GetCachedLogin returns the first connected GitHub login, or "" if none yet.

func (*UserPRCache) GetCachedLogins added in v1.35.0

func (c *UserPRCache) GetCachedLogins() []string

GetCachedLogins returns all connected GitHub logins.

func (*UserPRCache) InvalidateLoginCache added in v1.35.0

func (c *UserPRCache) InvalidateLoginCache()

InvalidateLoginCache clears the cached login state so the next Refresh call re-fetches the authenticated user from the GitHub API. Call this after storing a new token (e.g. after a successful Device Flow auth) so the cache picks up the new credentials immediately.

Bumping loginGen (not just clearing multiLogin) matters because a stale resolveAllLogins call from before the invalidation may still be in flight (e.g. loop()'s unconditional first fetch, started with zero tokens before a caller adds one) — the static singleflight key "login" would otherwise coalesce a Refresh arriving right after this call into that stale call's empty result. Including the generation in the key forces a genuinely new call instead. Confirmed live via go test -race: TestListGitHubAccounts_ AccountOnUnconfiguredEnterpriseHost_IncludesHostInEnterpriseHosts saw EnterpriseHosts come back empty because its Refresh() joined the just-started cache's own stale zero-token fetch.

func (*UserPRCache) Refresh added in v1.35.0

func (c *UserPRCache) Refresh(ctx context.Context) error

Refresh triggers an immediate fetch from GitHub, coalescing concurrent calls.

func (*UserPRCache) SetOnUpdated added in v1.35.0

func (c *UserPRCache) SetOnUpdated(fn func(prs []UserPR))

SetOnUpdated atomically registers a callback invoked after every successful refresh. Pass nil to clear. The callback receives the current PR slice. Safe to call at any time, including after Start.

func (*UserPRCache) Start added in v1.35.0

func (c *UserPRCache) Start(ctx context.Context)

Start launches the background polling goroutine. Safe to call multiple times; only the first call starts the goroutine.

func (*UserPRCache) Stop added in v1.35.0

func (c *UserPRCache) Stop()

Stop halts background polling and blocks until loop() has actually exited. Without waiting here, a caller (notably a test's t.Cleanup) can return while loop()'s unconditional first fetch (see loop's doc comment) is still running, letting it race the next caller's use of shared package-level state (e.g. go-keyring's mock, which a subsequent test re-initializes via MockInit) — confirmed live via `go test -race`: TestListGitHubAccounts_ AccountOnUnconfiguredEnterpriseHost_IncludesHostInEnterpriseHosts raced against a prior test's still-running fetch() on go-keyring's global state. Safe to call before Start (done is nil, no-op) or more than once (cancel and a receive on an already-closed channel are both idempotent).

func (*UserPRCache) Subscribe added in v1.35.0

func (c *UserPRCache) Subscribe(id string, ch chan []UserPR)

Subscribe registers a channel to receive PR snapshot updates. The channel must be buffered. The caller is responsible for calling Unsubscribe.

func (*UserPRCache) Unsubscribe added in v1.35.0

func (c *UserPRCache) Unsubscribe(id string)

Unsubscribe removes a previously registered subscriber channel.

type UserPRCacheConfig added in v1.35.0

type UserPRCacheConfig struct {
	// PollInterval controls how often the cache refreshes from GitHub.
	PollInterval time.Duration
	// LoginCacheTTL controls how long the authenticated login is cached.
	LoginCacheTTL time.Duration
}

UserPRCacheConfig controls polling behaviour.

func DefaultUserPRCacheConfig added in v1.35.0

func DefaultUserPRCacheConfig() UserPRCacheConfig

DefaultUserPRCacheConfig returns sensible defaults.

Jump to

Keyboard shortcuts

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