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
- func AcceptedRefName(name string) bool
- func AnonymousFallbackLimited(err error) bool
- func Brief(err error) string
- func ConfigError(err error) error
- func DefaultSleep(ctx context.Context, d time.Duration)
- func DependabotProxy() bool
- func DescribeName(name string) bool
- func IsNotFound(err error) bool
- func IsPermissionDenied(err error) bool
- func IsProxima(hostname string) bool
- func IsRateLimited(err error) bool
- func IsSAMLEnforcement(err error) bool
- func IsTransient(err error) bool
- func Outage(err error) (reason string, limited bool)
- func RecordableRefName(name string) bool
- func RetryAt(err error) time.Time
- func SetRetryNotifier(fn func(string))
- func StatusCode(err error) (code int, ok bool)
- func ValidRefName(name string) bool
- type ActionFileRequest
- type ActionFileResult
- type ActionRef
- type BranchHead
- type BriefError
- type Client
- func (c *Client) BatchBranchContains(ctx context.Context, owner, repo, sha string, branches []BranchHead) (matchedBranch string, anyChecked bool, err error)
- func (c *Client) CheckPins(ctx context.Context, pins []PinCheck) []PinState
- func (c *Client) CommitSHA(ctx context.Context, owner, repo, ref string) (string, error)
- func (c *Client) CompareCommits(ctx context.Context, owner, repo, sha, branchHeadSHA string) (bool, error)
- func (c *Client) CompareRefs(ctx context.Context, owner, repo, base, head string) (status, mergeBaseSHA string, err error)
- func (c *Client) ForRepo(ctx context.Context, owner, repo string) (*Client, error)
- func (c *Client) GetBranchHead(ctx context.Context, owner, repo, name string) (BranchHead, bool)
- func (c *Client) GetDefaultBranch(ctx context.Context, owner, repo string) string
- func (c *Client) ListBranches(ctx context.Context, owner, repo string) ([]BranchHead, error)
- func (c *Client) ListProtectedBranches(ctx context.Context, owner, repo string) []BranchHead
- func (c *Client) ListTags(ctx context.Context, owner, repo string) ([]TagEntry, error)
- func (c *Client) LookupRefName(ctx context.Context, owner, repo, name string) (RefTarget, error)
- func (c *Client) MatchingHeadRefs(ctx context.Context, owner, repo, prefix string) []BranchHead
- func (c *Client) PeelTagObject(ctx context.Context, owner, repo, sha string) (PeelTagObjectResult, error)
- func (c *Client) PinHost(owner, repo, hostname string) error
- func (c *Client) Releases(ctx context.Context, owner, repo string) ([]RepoRelease, error)
- func (c *Client) RepoIDs(ctx context.Context, owner, repo string) (int64, int64, error)
- func (c *Client) RepoMetadata(ctx context.Context, owner, repo string) (RepoMetadata, error)
- func (c *Client) RepoTags(ctx context.Context, owner, repo string) ([]RepoTag, error)
- func (c *Client) ResolveActionFiles(ctx context.Context, refs []ActionFileRequest) []ActionFileResult
- func (c *Client) RetriedFor() time.Duration
- func (c *Client) SSOFallbackEligible(ctx context.Context, owner string) bool
- func (c *Client) TagExists(ctx context.Context, owner, repo, name string) (bool, error)
- func (c *Client) TagsAtCommit(ctx context.Context, owner, repo, sha string) ([]string, error)
- type ClientOption
- type Compare
- type NWOName
- type NWORef
- type NWOSha
- type PeelTagObjectResult
- type PinCheck
- type PinState
- type RefKind
- type RefNotFoundError
- type RefTarget
- type Repo
- type RepoMetadata
- type RepoRelease
- type RepoTag
- type SSORateLimitedError
- type TagEntry
- type UnknownOutcomeError
Constants ¶
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.
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
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
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
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
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 ¶
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
DescribeName reports whether name is a valid ref name shaped like `git describe` output, which the Actions backend refuses.
func IsNotFound ¶
IsNotFound reports whether err is a 404 from the GitHub REST API.
func IsPermissionDenied ¶
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 IsRateLimited ¶
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
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
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
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
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
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 ¶
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
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.
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 ¶
ForActionRef builds an ActionRef key.
type BranchHead ¶
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:
- hintBranch (previously recorded in lockfile for this commit)
- hintRef (ref the user wrote in the workflow)
- defaultBranch (e.g. main / master)
- protected branches, lex-sorted within tier
- unprotected branches, lex-sorted within tier
type BriefError ¶ added in v0.3.0
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
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 ¶
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
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 ¶
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 ¶
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 ¶
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 ¶
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
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
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) RepoIDs ¶
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 ¶
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 ¶
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
RetriedFor is the longest the run spent retrying through one outage.
func (*Client) SSOFallbackEligible ¶ added in v0.1.5
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
TagExists reports whether owner/repo has a tag named exactly name.
func (*Client) TagsAtCommit ¶ added in v0.3.0
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 ¶
ForCompare builds a Compare key, lowercasing both SHAs.
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 ¶
ForNWOName builds an NWOName key.
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.
type NWOSha ¶
type NWOSha struct {
// contains filtered or unexported fields
}
NWOSha pairs a repo with a lowercased commit SHA.
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 RefNotFoundError ¶ added in v0.2.0
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.
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 ¶
RepoRelease holds release metadata for a tag.
type RepoTag ¶
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
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 UnknownOutcomeError ¶ added in v0.3.0
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