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

ghapi

package
v0.4.0 Latest Latest
Warning

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

Go to latest
Published: Oct 23, 2026 License: MIT Imports: 30 Imported by: 0

Documentation

Overview

Package ghapi provides a unified GitHub API client that owns both REST and GraphQL connections, retry transport, and profiling instrumentation. All GitHub API access in the CLI should go through a single Client instance constructed at startup.

Index

Constants

View Source
const AnonymousFallbackHint = "authenticate public github.com lookups, which are otherwise anonymous and limited to 60 requests/hour per IP: " +
	"locally, run `gh auth login --hostname github.com`; in CI, set " + DotcomTokenEnv + " to a github.com token with no permissions"

AnonymousFallbackHint tells the user how to authenticate the github.com public fallback.

View Source
const DotcomTokenEnv = "GH_ACTIONS_LOCK_GITHUB_COM_TOKEN"

DotcomTokenEnv holds a github.com token for the public fallback of a *.ghe.com run, for CI where there is no gh login. A GHE.com job's own token is issued by the tenant, so github.com doesn't accept it.

Variables

This section is empty.

Functions

func AcceptedRefName added in v0.3.0

func AcceptedRefName(name string) bool

AcceptedRefName reports whether name is a ref name the Actions backend accepts: a valid ref name that isn't shaped like `git describe` output.

func AnonymousFallbackLimited added in v0.4.0

func AnonymousFallbackLimited(err error) bool

AnonymousFallbackLimited reports whether err's cause is a rate limit on the anonymous github.com public fallback of a *.ghe.com run. That limit is per IP, so no tenant token can raise it.

func Brief added in v0.3.0

func Brief(err error) string

Brief omits HTTP request URLs and keeps authorization guidance, shortening other HTTP failures to their status. The run log and --json keep the full error.

func ConfigError added in v0.3.0

func ConfigError(err error) error

ConfigError returns a one-line error when err shows the run itself is misconfigured: GitHub's TLS certificate isn't trusted (a proxy or CA bundle), or GitHub rejected the token. Every request would fail the same way, so it is reported once rather than per pin. It returns nil otherwise.

func DefaultSleep

func DefaultSleep(ctx context.Context, d time.Duration)

DefaultSleep waits d but returns early when ctx is canceled. Useful for rate-limit retry loops that need to honor cancellation.

func DependabotProxy added in v0.2.0

func DependabotProxy() bool

DependabotProxy reports whether the CLI runs inside Dependabot, behind its REST-only proxy. Dependabot reads back only actions.lock and does not retry a failed run.

func DescribeName added in v0.3.0

func DescribeName(name string) bool

DescribeName reports whether name is a valid ref name shaped like `git describe` output, which the Actions backend refuses.

func IsNotFound

func IsNotFound(err error) bool

IsNotFound reports whether err is a 404 from the GitHub REST API.

func IsPermissionDenied

func IsPermissionDenied(err error) bool

IsPermissionDenied reports whether err is a 401, or a 403 that is not a rate-limit response. Rate-limit 403s are reported by IsRateLimited instead so callers can back off rather than treat the repo as inaccessible.

func IsProxima added in v0.2.0

func IsProxima(hostname string) bool

IsProxima reports whether hostname is a canonical tenant hostname.

func IsRateLimited

func IsRateLimited(err error) bool

IsRateLimited reports whether err is a rate-limit response: HTTP 429, or a 403 with the budget exhausted or a Retry-After (secondary rate limit), the same rule the retry transport uses.

func IsSAMLEnforcement added in v0.1.5

func IsSAMLEnforcement(err error) bool

IsSAMLEnforcement reports whether err represents a SAML/SSO enforcement block. It matches both REST 403s (api.HTTPError) and plain errors whose message indicates SAML enforcement (e.g. from the GraphQL resolution path).

func IsTransient added in v0.2.0

func IsTransient(err error) bool

IsTransient reports whether err is a failure a retry may fix: a rate limit, a 5xx, or a transport error other than an untrusted certificate. 401s, 404s, 422s and permission errors are answers, not outages. A joined error is transient when any branch is, so one 404 can't mask an outage elsewhere in the batch. It follows the rules the retry transport uses.

func Outage added in v0.3.0

func Outage(err error) (reason string, limited bool)

Outage describes a transient failure for the terminal, e.g. "GitHub API unreachable (connection refused)", and reports whether it was a rate limit. For a joined error it describes the branch cause picks.

func RecordableRefName added in v0.3.0

func RecordableRefName(name string) bool

RecordableRefName reports whether name can be recorded as a lock entry's ref: a short tag or branch name the Actions backend accepts, not shaped like a full commit SHA and not a refs/ name. lockfile.ValidEntryRef and every reverse-lookup candidate use it.

func RetryAt added in v0.3.0

func RetryAt(err error) time.Time

RetryAt returns the earliest time GitHub said a retry of the request behind err could succeed, or the zero time if it didn't say. For a joined error it reads the branch cause picks.

func SetRetryNotifier added in v0.3.0

func SetRetryNotifier(fn func(string))

SetRetryNotifier routes retry progress ("retrying in 20s, attempt 3/8") to fn, so a long wait doesn't look like a hang. nil silences it.

func StatusCode

func StatusCode(err error) (code int, ok bool)

StatusCode reports the HTTP status code carried by err when it originates from a REST call. ok is false when err is nil, a transport failure, or a GraphQL error that carries no HTTP status. It lets callers classify failures without importing go-gh, keeping the API transport internal to this package.

func ValidRefName added in v0.3.0

func ValidRefName(name string) bool

ValidRefName reports whether name is a short tag or branch name that `git check-ref-format --allow-onelevel` accepts, minus a leading "-". A name that fails it may be a revision expression (main~1, v1^, v1@{1}) that a commit-ish lookup would evaluate instead of resolving literally. The rules match actions-lockfile's RefName so the CLI and library agree.

Types

type ActionFileRequest

type ActionFileRequest struct {
	Owner string
	Repo  string
	Path  string // sub-action path within the repo, may be empty
	Ref   string // tag, branch, or SHA
	// Dockerfile also looks for the Dockerfile the runner falls back to
	// when there is no action.yml or action.yaml.
	Dockerfile bool
}

ActionFileRequest identifies a GitHub Action ref to resolve via GraphQL.

func (ActionFileRequest) NWO

func (r ActionFileRequest) NWO() string

NWO returns "Owner/Repo".

type ActionFileResult

type ActionFileResult struct {
	Hostname    string
	Owner       string
	Repo        string
	OriginalNWO string
	Path        string
	Ref         string
	CommitOID   string
	ActionYML   string
	// Dockerfile reports a Dockerfile at Path, when the request asked.
	Dockerfile bool
	Err        error
}

ActionFileResult holds the resolved commit OID and action.yml content for one ActionFileRequest. Err is non-nil when this specific ref could not be resolved (e.g. not found, SSO required).

Owner and Repo are the repository's canonical name. OriginalNWO is set when the request followed a rename or transfer redirect.

type ActionRef

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

ActionRef is the path-aware key used by the resolver's per-ref cache and the BFS dedup set in ResolveAllRecursive. Sub-action paths must be distinct identities for graph traversal — actions/cache/save@v4 visits a different action.yml than actions/cache@v4 — even though they collapse to the same repo+ref tarball at runner-download granularity. Use NWORef (not ActionRef) when path is irrelevant.

func ForActionRef

func ForActionRef(owner, repo, path, ref string) ActionRef

ForActionRef builds an ActionRef key.

func (ActionRef) String

func (k ActionRef) String() string

String is diagnostics-only; do not parse.

type BranchHead

type BranchHead struct {
	Name      string
	SHA       string
	Protected bool
}

BranchHead holds a branch name, the SHA of its HEAD commit, and whether the branch has branch-protection rules enabled in the upstream repo.

func OrderedBranches

func OrderedBranches(branches []BranchHead, hintBranch, hintRef, defaultBranch string) []BranchHead

OrderedBranches returns branches in tiered order so the most trust-bearing candidates are compared first:

  1. hintBranch (previously recorded in lockfile for this commit)
  2. hintRef (ref the user wrote in the workflow)
  3. defaultBranch (e.g. main / master)
  4. protected branches, lex-sorted within tier
  5. unprotected branches, lex-sorted within tier

type BriefError added in v0.3.0

type BriefError struct {
	Msg string
	Err error
}

BriefError is an error whose message is short enough for the terminal. Err carries the full message for --json and the run log.

func (*BriefError) Error added in v0.3.0

func (e *BriefError) Error() string

func (*BriefError) Unwrap added in v0.3.0

func (e *BriefError) Unwrap() error

func (*BriefError) Verbose added in v0.3.0

func (e *BriefError) Verbose() string

Verbose returns the full message.

type Client

type Client struct {
	Hostname string
	// contains filtered or unexported fields
}

Client holds authenticated REST and GraphQL clients for a single GitHub hostname. Construct via New with ClientOption values.

func New

func New(hostname string, opts ...ClientOption) (*Client, error)

New creates an authenticated Client for the given hostname using the ambient gh credential store. Use WithClientTransport for test stubs and WithClientProfile for profiling.

func (*Client) BatchBranchContains

func (c *Client) BatchBranchContains(ctx context.Context, owner, repo, sha string, branches []BranchHead) (matchedBranch string, anyChecked bool, err error)

BatchBranchContains checks whether sha is reachable from any of the given branches using the GraphQL Ref.compare API. It batches all branches into one or a few GraphQL queries (batchReachabilitySize per query) and returns the first matching branch name in the order provided.

Returns:

  • matchedBranch: name of the first branch containing sha, or "" if none
  • anyChecked: true if at least one branch was successfully checked
  • err: non-nil only on transport/auth failures (not per-branch misses)

func (*Client) CheckPins added in v0.2.0

func (c *Client) CheckPins(ctx context.Context, pins []PinCheck) []PinState

CheckPins reports, per pin, the repository's canonical name and IDs, whether the pinned commit exists, and where Head points. It does not walk action.yml: a pinned commit fixes the dependencies it declares.

func (*Client) CommitSHA

func (c *Client) CommitSHA(ctx context.Context, owner, repo, ref string) (string, error)

CommitSHA resolves a ref (branch, tag, or SHA) to its commit SHA via the repos/commits endpoint.

func (*Client) CompareCommits

func (c *Client) CompareCommits(ctx context.Context, owner, repo, sha, branchHeadSHA string) (bool, error)

CompareCommits reports whether sha is on the lineage of branchHeadSHA using the Compare API. A 404 or 422 response (unrelated histories or missing commit) is treated as a non-error false return. Results are memoized for the lifetime of the Client and concurrent identical comparisons are coalesced via singleflight. The request runs under a cancel-free context so that one fanned-out caller's cancellation (the reachability scan cancels siblings on first match) cannot abort the shared comparison the others are waiting on.

func (*Client) CompareRefs

func (c *Client) CompareRefs(ctx context.Context, owner, repo, base, head string) (status, mergeBaseSHA string, err error)

CompareRefs returns the Compare API status and merge-base SHA for base...head. Unlike CompareCommits it surfaces the raw verdict and the underlying error (including *api.HTTPError) so callers can distinguish ancestry, forgery, and inconclusive results. Not cached: ancestry checks key on distinct base/head pairs that rarely repeat within a run.

func (*Client) ForRepo added in v0.2.0

func (c *Client) ForRepo(ctx context.Context, owner, repo string) (*Client, error)

ForRepo selects a host once per repository. Only a repository-level 404 permits public fallback; missing refs, denied access and transport errors do not.

func (*Client) GetBranchHead

func (c *Client) GetBranchHead(ctx context.Context, owner, repo, name string) (BranchHead, bool)

GetBranchHead resolves a single branch's HEAD commit directly via the git/ref endpoint. Unlike ListBranches this is not subject to the paginated 300-branch cap. Results (including 404s) are cached and concurrent lookups are coalesced via singleflight. Returns ok=false on any error.

func (*Client) GetDefaultBranch

func (c *Client) GetDefaultBranch(ctx context.Context, owner, repo string) string

GetDefaultBranch returns the repo's default branch name (e.g. "main"), or "" if the lookup fails. Backed by the shared repoMetadata fetch.

func (*Client) ListBranches

func (c *Client) ListBranches(ctx context.Context, owner, repo string) ([]BranchHead, error)

ListBranches returns all branches with their HEAD SHAs for a repo. Paginates up to 3 pages (300 branches). Results are cached per owner/repo and coalesced via singleflight.

func (*Client) ListProtectedBranches

func (c *Client) ListProtectedBranches(ctx context.Context, owner, repo string) []BranchHead

ListProtectedBranches returns the repo's protected branches. Best-effort: any error yields whatever was collected so far (possibly empty). Results are cached per owner/repo and coalesced via singleflight.

func (*Client) ListTags

func (c *Client) ListTags(ctx context.Context, owner, repo string) ([]TagEntry, error)

ListTags returns all tags with their commit SHAs for a repo (first page, up to 100). Results are cached per owner/repo and coalesced via singleflight.

func (*Client) LookupRefName added in v0.3.0

func (c *Client) LookupRefName(ctx context.Context, owner, repo, name string) (RefTarget, error)

LookupRefName looks name up the way the Actions backend resolver looks up a lock entry ref: refs/tags/<name>, then refs/heads/<name>, by exact name. A name the backend doesn't accept is never looked up.

func (*Client) MatchingHeadRefs

func (c *Client) MatchingHeadRefs(ctx context.Context, owner, repo, prefix string) []BranchHead

MatchingHeadRefs returns branches whose names start with prefix via the git/matching-refs endpoint. Best-effort: any error yields nil.

func (*Client) PeelTagObject

func (c *Client) PeelTagObject(ctx context.Context, owner, repo, sha string) (PeelTagObjectResult, error)

PeelTagObject queries whether sha is an annotated tag object in owner/repo and, if so, returns the commit it dereferences to. Returns a zero result (not an error) when the OID or repo is not accessible — callers decide how to interpret the negative.

func (*Client) PinHost added in v0.2.0

func (c *Client) PinHost(owner, repo, hostname string) error

PinHost binds a repository to its recorded host before any resolution starts. A repository cannot span hosts within a run: caches and ref selection are repository-scoped, and guessing would mix identities.

func (*Client) Releases

func (c *Client) Releases(ctx context.Context, owner, repo string) ([]RepoRelease, error)

Releases lists a repository's most recent releases (up to 30).

func (*Client) RepoIDs

func (c *Client) RepoIDs(ctx context.Context, owner, repo string) (int64, int64, error)

RepoIDs returns the numeric owner ID and repo ID for a NWO. Backed by the shared repoMetadata fetch, so it shares a single repos/{owner}/{repo} round-trip with GetDefaultBranch.

func (*Client) RepoMetadata

func (c *Client) RepoMetadata(ctx context.Context, owner, repo string) (RepoMetadata, error)

RepoMetadata fetches a repository's default branch, visibility, and last push time. Delegates to the shared repoMetadata fetch so it shares the cached, singleflight-coalesced repos/{owner}/{repo} round-trip with GetDefaultBranch and RepoIDs.

func (*Client) RepoTags

func (c *Client) RepoTags(ctx context.Context, owner, repo string) ([]RepoTag, error)

RepoTags lists a repository's tags (up to 100) with the commit SHA each resolves to. Delegates to ListTags so it shares the cached, singleflight- coalesced tag fetch rather than issuing a second identical request.

func (*Client) ResolveActionFiles

func (c *Client) ResolveActionFiles(ctx context.Context, refs []ActionFileRequest) []ActionFileResult

ResolveActionFiles resolves action refs to commit OIDs and fetches their action.yml/yaml content in a single batched GraphQL round-trip. Results are returned in the same order as inputs; per-ref failures are recorded in each result's Err field rather than aborting the batch.

If GraphQL rejects the whole query — query cost/complexity, or an unattributed SAML block — the batch is split in half and each half retried, recursively down to a single ref, so a few oversized or bad refs can't fail their batch-mates. An outage or rate limit is never split: it rejects every half the same way, and the transport already retried it. Splitting is skipped once ctx is canceled so a canceled scan doesn't fan out into a flurry of doomed retries.

func (*Client) RetriedFor added in v0.3.0

func (c *Client) RetriedFor() time.Duration

RetriedFor is the longest the run spent retrying through one outage.

func (*Client) SSOFallbackEligible added in v0.1.5

func (c *Client) SSOFallbackEligible(ctx context.Context, owner string) bool

SSOFallbackEligible reports whether the given owner's repos can be accessed anonymously when SSO blocks authenticated access. On first call for an owner, it probes the GitHub API with an unauthenticated request to determine accessibility, then caches a definitive result.

func (*Client) TagExists added in v0.3.0

func (c *Client) TagExists(ctx context.Context, owner, repo, name string) (bool, error)

TagExists reports whether owner/repo has a tag named exactly name.

func (*Client) TagsAtCommit added in v0.3.0

func (c *Client) TagsAtCommit(ctx context.Context, owner, repo, sha string) ([]string, error)

TagsAtCommit returns the names of every tag in owner/repo whose commit is sha, reading every page of the tag listing (one request per 100 tags). Use it only where a later page matters: ListTags reads the first page.

type ClientOption

type ClientOption func(*clientConfig)

ClientOption configures a Client at construction time. Pass to New.

func WithClientProfile

func WithClientProfile(p *profile.Session) ClientOption

WithClientProfile attaches profiling instrumentation to API calls.

func WithClientTransport

func WithClientTransport(t http.RoundTripper) ClientOption

WithClientTransport overrides the HTTP transport. Use in tests with httpmock.

type Compare

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

Compare identifies a Compare API result between two SHAs in a repo, e.g. "is base an ancestor of head?". Both SHAs are lowercased.

func ForCompare

func ForCompare(owner, repo, base, head string) Compare

ForCompare builds a Compare key, lowercasing both SHAs.

func (Compare) String

func (k Compare) String() string

String is diagnostics-only; do not parse.

type NWOName

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

NWOName pairs a repo with an arbitrary name (e.g. a branch name). The name preserves case; callers that want case-insensitive lookup must normalize before passing in.

func ForNWOName

func ForNWOName(owner, repo, name string) NWOName

ForNWOName builds an NWOName key.

func (NWOName) String

func (k NWOName) String() string

String is diagnostics-only; do not parse.

type NWORef

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

NWORef pairs a repo with a git ref (tag, branch, or SHA-as-written). Refs preserve their original case because git refs are case-sensitive.

func ForNWORef

func ForNWORef(owner, repo, ref string) NWORef

ForNWORef builds an NWORef key.

func (NWORef) String

func (k NWORef) String() string

String is diagnostics-only; do not parse.

type NWOSha

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

NWOSha pairs a repo with a lowercased commit SHA.

func ForNWOSha

func ForNWOSha(owner, repo, sha string) NWOSha

ForNWOSha builds an NWOSha key, lowercasing the SHA.

func (NWOSha) String

func (k NWOSha) String() string

String is diagnostics-only; do not parse.

type PeelTagObjectResult

type PeelTagObjectResult struct {
	// Typename is the __typename of the object at the given OID
	// ("Tag", "Commit", "Blob", "Tree"), or empty if the OID/repo
	// was not found.
	Typename string

	// CommitOID is the commit SHA the tag peels to (via ^{commit}).
	// Empty when the peel fails or the object is not a tag.
	CommitOID string
}

PeelTagObjectResult holds the outcome of a tag-object peel query.

type PinCheck added in v0.2.0

type PinCheck struct {
	Owner string
	Repo  string
	SHA   string
	// Head, when set, is a ref whose current commit is reported as HeadOID
	// so the caller can check SHA is still in its history. It is looked up
	// only as an existing refs/tags/<name>, then refs/heads/<name>, of
	// exactly that name, as the Actions backend resolver does: rev-parse
	// rules would also accept a pull request head (pull/1/head).
	Head string
}

PinCheck is a locked dependency to re-check against the host, mirroring what the runner verifies at job start.

type PinState added in v0.2.0

type PinState struct {
	NameWithOwner string
	OwnerID       int64
	RepoID        int64
	// RepoMissing means the locked owner/repo did not resolve. A 404 can't
	// tell a deleted repository from one this token can't see.
	RepoMissing bool
	// ViaFallback marks a verdict from the REST-only or SSO fallback path,
	// where a missing token or SSO grant is the likelier cause.
	ViaFallback bool
	CommitFound bool
	// HeadOID is the commit Head peels to; empty when the ref is gone.
	HeadOID string
	// HeadIsTag marks a Head that resolved as a tag rather than a branch.
	HeadIsTag bool
	Err       error
}

PinState is the host's view of a PinCheck. Err marks an inconclusive check.

type RefKind added in v0.3.0

type RefKind int

RefKind is which namespace a ref name resolved in.

const (
	// NoRef means the name is neither a tag nor a branch.
	NoRef RefKind = iota
	TagRef
	BranchRef
)

type RefNotFoundError added in v0.2.0

type RefNotFoundError struct {
	Owner, Repo, Ref string
	Err              error
}

RefNotFoundError reports a ref that does not exist in a repository that does. Unlike a transport or rate-limit failure, retrying will not help.

func (*RefNotFoundError) Error added in v0.2.0

func (e *RefNotFoundError) Error() string

func (*RefNotFoundError) Unwrap added in v0.2.0

func (e *RefNotFoundError) Unwrap() error

type RefTarget added in v0.3.0

type RefTarget struct {
	Kind RefKind
	// Commit is the commit the ref peels to; empty when it doesn't peel
	// to one.
	Commit string
}

RefTarget is what a ref name resolves to. Existence and the peeled commit are separate facts: a tag can exist yet not peel to a commit (it targets a tree or blob), and tag precedence keys off existence alone.

type Repo

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

Repo identifies a GitHub repository by owner and name. Both components are lowercased on construction so case variants share a single cache slot.

func ForRepo

func ForRepo(owner, repo string) Repo

ForRepo builds a Repo key, lowercasing owner and repo.

func (Repo) String

func (r Repo) String() string

String returns the normalized "owner/repo" key, used for cache-key diagnostics and for singleflight coalescing.

type RepoMetadata

type RepoMetadata struct {
	DefaultBranch string
	Visibility    string // "public", "private", or "internal"
	PushedAt      string // ISO 8601 timestamp of last push
}

RepoMetadata holds repository-level metadata relevant for pinning decisions.

type RepoRelease

type RepoRelease struct {
	TagName     string
	PublishedAt string // ISO 8601 date
	Immutable   bool
}

RepoRelease holds release metadata for a tag.

type RepoTag

type RepoTag struct {
	Name string
	SHA  string
}

RepoTag is a tag name paired with the commit SHA it resolves to, as returned by the repos/tags endpoint (annotated tags dereferenced).

type SSORateLimitedError added in v0.2.0

type SSORateLimitedError struct {
	Host, Owner string
	Err         error
}

SSORateLimitedError reports a SAML-blocked token whose anonymous fallback was rate-limited. The message reuses the SSO guidance so callers render the same authorization hint.

func (*SSORateLimitedError) Error added in v0.2.0

func (e *SSORateLimitedError) Error() string

func (*SSORateLimitedError) Unwrap added in v0.2.0

func (e *SSORateLimitedError) Unwrap() error

type TagEntry

type TagEntry struct {
	Name string
	SHA  string
}

TagEntry holds a tag name and the commit SHA it points at.

type UnknownOutcomeError added in v0.3.0

type UnknownOutcomeError struct {
	Cause   error
	RetryAt time.Time
}

UnknownOutcomeError reports a request that ended without an answer from GitHub: the connection failed or timed out on every attempt. Retrying later may succeed. RetryAt, when set, is the earliest time GitHub said a retry could succeed.

func (*UnknownOutcomeError) Error added in v0.3.0

func (e *UnknownOutcomeError) Error() string

func (*UnknownOutcomeError) Unwrap added in v0.3.0

func (e *UnknownOutcomeError) Unwrap() error

Directories

Path Synopsis
Package httpmock provides HTTP test fakes for the ghapi client.
Package httpmock provides HTTP test fakes for the ghapi client.

Jump to

Keyboard shortcuts

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