Documentation
¶
Overview ¶
Package ghapi is the typed GitHub REST boundary of the engine.
Every request is assembled from a validated base URL and individually escaped path segments, so an owner, a repository name, or a workflow file name can never become part of the URL's structure. The credential is supplied by an Authorizer and set as one Authorization header on the outgoing request. It never travels in a URL, a query string, or an error, and a redirect that leaves the configured origin is refused rather than followed, because following one would offer the header to a host the engine did not choose.
This package provides the typed HTTP boundary for authenticated GitHub API calls. The token is supplied by an Authorizer and set as one Authorization header on the outgoing request, so the base URL rules, the redirect refusal, the response size bound, and the typed errors all cover every endpoint.
Index ¶
- Constants
- Variables
- type Account
- type Authorizer
- type AuthorizerFunc
- type Client
- func (c *Client) BaseURL() string
- func (c *Client) CreateIssue(ctx context.Context, owner, repo string, issue IssueRequest) (Issue, error)
- func (c *Client) CreateIssueComment(ctx context.Context, owner, repo string, number int64, body string) (IssueComment, error)
- func (c *Client) DefaultBranch(ctx context.Context, owner, name string) (string, error)
- func (c *Client) ListOpenIssues(ctx context.Context, owner, repo string, labels []string) ([]Issue, error)
- func (c *Client) Repository(ctx context.Context, owner, name string) (Repository, error)
- func (c *Client) UpdateIssue(ctx context.Context, owner, repo string, number int64, update IssueUpdate) (Issue, error)
- func (c *Client) Workflow(ctx context.Context, owner, repo, file string) (Workflow, error)
- type Config
- type Issue
- type IssueComment
- type IssueLink
- type IssueRequest
- type IssueUpdate
- type Label
- type RateLimit
- type Repository
- type StaticBearer
- type StatusError
- type Workflow
Constants ¶
const ( WorkflowActive = "active" WorkflowDisabledManually = "disabled_manually" WorkflowDisabledInactivity = "disabled_inactivity" WorkflowDisabledFork = "disabled_fork" )
Workflow states GitHub reports for an Actions workflow.
const DefaultBaseURL = "https://api.github.com"
DefaultBaseURL is the REST API root of github.com.
const DefaultHTTPTimeout = 30 * time.Second
DefaultHTTPTimeout bounds one REST call when the caller supplies no client.
A client with no timeout waits forever on a destination that accepts the connection and then says nothing, which in a scheduled publishing run is a job that never ends rather than one that fails.
const DefaultMaxResponseBytes = 4 << 20
DefaultMaxResponseBytes bounds a decoded response body.
Every response this package reads is a small JSON document, and the largest of them is one page of repositories. The bound exists so a destination that answers with an unbounded stream cannot exhaust memory before it is refused.
const DefaultUserAgent = "soapbox/" + buildinfo.Version
DefaultUserAgent identifies the engine and its exact version to GitHub, which asks every client to send one and answers an anonymous agent with a refusal.
Variables ¶
var ( ErrUnauthorized = errors.New("github rejected the credential") // ErrForbidden reports that the credential is valid but lacks the required // repository or workflow permission. ErrForbidden = errors.New("github refused the request") // ErrNotFound reports that GitHub has no visible such resource. A resource // hidden from the credential can be answered this way rather than with a // refusal, so a caller cannot tell the two apart from the status alone. ErrNotFound = errors.New("github reports no such resource") // ErrRateLimited reports a primary or secondary rate limit. The error also // carries the retry metadata GitHub returned. ErrRateLimited = errors.New("github rate limited the request") // ErrResponseTooLarge reports a response body past the configured bound. ErrResponseTooLarge = errors.New("github response exceeds the response limit") // ErrRedirectRefused reports a redirect that left the configured origin. ErrRedirectRefused = errors.New("github redirected the request off its origin") // ErrTooManyRedirects reports a redirect chain that never settled. ErrTooManyRedirects = errors.New("github redirected the request too many times") )
Refusals a caller can branch on with errors.Is.
Functions ¶
This section is empty.
Types ¶
type Authorizer ¶
Authorizer supplies the Authorization header value for one request.
It is an interface rather than a token string so the client remains ignorant of how a credential is sourced and tests can fail a request before transport. The production GITHUB_TOKEN implementation is static for one workflow job.
type AuthorizerFunc ¶
AuthorizerFunc adapts a function to Authorizer.
func (AuthorizerFunc) AuthorizationHeader ¶
func (f AuthorizerFunc) AuthorizationHeader(ctx context.Context) (string, error)
AuthorizationHeader calls f.
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client is a typed GitHub REST client bound to one origin and one credential.
func (*Client) CreateIssue ¶
func (c *Client) CreateIssue(ctx context.Context, owner, repo string, issue IssueRequest) (Issue, error)
CreateIssue opens an issue.
func (*Client) CreateIssueComment ¶
func (c *Client) CreateIssueComment(ctx context.Context, owner, repo string, number int64, body string) (IssueComment, error)
CreateIssueComment adds a comment to an issue.
func (*Client) DefaultBranch ¶
DefaultBranch reads one repository's default branch.
func (*Client) ListOpenIssues ¶
func (c *Client) ListOpenIssues(ctx context.Context, owner, repo string, labels []string) ([]Issue, error)
ListOpenIssues lists the open issues carrying every one of labels, with pull requests dropped.
func (*Client) Repository ¶
Repository reads one repository's metadata, including its default branch.
func (*Client) UpdateIssue ¶
func (c *Client) UpdateIssue(ctx context.Context, owner, repo string, number int64, update IssueUpdate) (Issue, error)
UpdateIssue edits an existing issue.
type Config ¶
type Config struct {
// Authorizer supplies the credential presented on every request. It is
// required because an anonymous client could silently read a public subset
// instead of the authenticated repository view the caller intends.
Authorizer Authorizer
// BaseURL is the REST API root. It defaults to DefaultBaseURL and must be an
// https URL with no user information, query, or fragment.
BaseURL string
// HTTPClient is the transport. It defaults to a client with a request
// timeout. Whatever is supplied is copied before its redirect policy and
// cookie jar are replaced, so the caller's client is never mutated.
HTTPClient *http.Client
// UserAgent defaults to DefaultUserAgent.
UserAgent string
// MaxResponseBytes defaults to DefaultMaxResponseBytes.
MaxResponseBytes int64
// Clock reports the current time. It defaults to time.Now and is injected so
// a Retry-After deadline resolves to an exact delay in tests.
Clock func() time.Time
// AllowPlaintextLoopback permits an http base URL that names a loopback
// address. It exists for httptest servers and nothing else: a plaintext
// request to any routable host would put the Authorization header on the
// wire in clear text, so the loopback restriction is enforced rather than
// documented.
AllowPlaintextLoopback bool
}
Config describes one GitHub REST client.
type Issue ¶
type Issue struct {
Number int64 `json:"number"`
Title string `json:"title"`
Body string `json:"body"`
State string `json:"state"`
Labels []Label `json:"labels"`
User Account `json:"user"`
HTMLURL string `json:"html_url"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
// PullRequest is present when this issue is really a pull request. The
// issues endpoint returns both kinds, and a pull request is never a
// tracking issue, so listings drop the entries that carry it.
PullRequest *IssueLink `json:"pull_request"`
}
Issue is the subset of a GitHub issue the engine reads and writes. The engine keeps at most one tracking issue per repository.
type IssueComment ¶
type IssueComment struct {
ID int64 `json:"id"`
Body string `json:"body"`
User Account `json:"user"`
HTMLURL string `json:"html_url"`
CreatedAt time.Time `json:"created_at"`
}
IssueComment is a comment on an issue.
type IssueLink ¶
type IssueLink struct {
HTMLURL string `json:"html_url"`
}
IssueLink is the pull request reference attached to an issue.
type IssueRequest ¶
type IssueRequest struct {
Title string `json:"title"`
Body string `json:"body,omitempty"`
Labels []string `json:"labels,omitempty"`
}
IssueRequest creates an issue.
type IssueUpdate ¶
type IssueUpdate struct {
Title string `json:"title,omitempty"`
Body string `json:"body,omitempty"`
State string `json:"state,omitempty"`
Labels []string `json:"labels,omitempty"`
}
IssueUpdate edits an existing issue. An empty field is left as it is.
type RateLimit ¶
type RateLimit struct {
// Limit, Remaining, and Used are the primary rate limit counters. They are
// zero when GitHub did not report them.
Limit int
Remaining int
Used int
// Resource names the budget the request was charged against, such as core
// or search.
Resource string
// Reset is when the primary budget refills. It is zero when unreported.
Reset time.Time
// RetryAfter is how long GitHub asked the client to wait. Secondary limits
// report this and no counters, so it is the only field a caller can rely on
// for an abuse refusal. It is clamped at zero for a deadline that has
// already passed, which means retry now rather than never.
RetryAfter time.Duration
// RetryAfterSet reports whether GitHub sent a Retry-After header at all.
// It is separate from RetryAfter because a delay of zero is a real answer:
// a header naming a moment in the past asks for an immediate retry, and a
// missing header asks for nothing.
RetryAfterSet bool
}
RateLimit is the retry metadata GitHub attaches to a throttled response.
type Repository ¶
type Repository struct {
ID int64 `json:"id"`
Name string `json:"name"`
FullName string `json:"full_name"`
Owner Account `json:"owner"`
Private bool `json:"private"`
Fork bool `json:"fork"`
Archived bool `json:"archived"`
Disabled bool `json:"disabled"`
DefaultBranch string `json:"default_branch"`
}
Repository is the subset of a GitHub repository the engine reads.
type StaticBearer ¶ added in v0.2.0
type StaticBearer struct {
// contains filtered or unexported fields
}
StaticBearer is an Authorizer that presents a fixed bearer token.
It is the production credential for GITHUB_TOKEN workflows: the token is minted per-job by Actions and does not expire within the job's lifetime, so it needs no renewal and a static holder is the honest representation.
The token is validated at construction: empty, whitespace-bearing, and control-character-bearing values are refused so a misconfigured caller fails immediately rather than after a network round trip with a malformed header.
func NewStaticBearer ¶ added in v0.2.0
func NewStaticBearer(token string) (*StaticBearer, error)
NewStaticBearer builds an authorizer from a raw token value.
The token must not be empty, must not contain control characters (including newlines), and must not contain whitespace. These are the characters that would either break the HTTP header or indicate a configuration error (such as passing the full "Bearer xxx" string instead of just the token).
func (*StaticBearer) AuthorizationHeader ¶ added in v0.2.0
func (s *StaticBearer) AuthorizationHeader(_ context.Context) (string, error)
AuthorizationHeader returns the bearer header value.
func (*StaticBearer) GoString ¶ added in v0.2.0
func (*StaticBearer) GoString() string
GoString renders the authorizer safely for the %#v format.
func (*StaticBearer) String ¶ added in v0.2.0
func (*StaticBearer) String() string
String renders the authorizer without exposing the bearer token.
type StatusError ¶
type StatusError struct {
// Method and Path locate the request.
Method string
Path string
// Status is the HTTP status code.
Status int
// Message and DocumentationURL are GitHub's own explanation, empty when the
// body was not an error envelope.
Message string
DocumentationURL string
// RateLimit is the retry metadata, nil when GitHub reported none.
RateLimit *RateLimit
// contains filtered or unexported fields
}
StatusError reports a GitHub response the client refused to treat as success.
It carries the request's method and escaped path rather than its URL, so a caller that logs it cannot write down a query string, and it carries only GitHub's own message and documentation link out of the body. Response bytes never reach an error.
func (*StatusError) Error ¶
func (e *StatusError) Error() string
Error renders the refusal without the response body.
func (*StatusError) Is ¶
func (e *StatusError) Is(target error) bool
Is maps a status onto the package refusals.
func (*StatusError) RetryAfter ¶
func (e *StatusError) RetryAfter() (time.Duration, bool)
RetryAfter reports how long GitHub asked the client to wait, and whether it asked at all. A zero delay with ok true means retry now.
type Workflow ¶
type Workflow struct {
ID int64 `json:"id"`
Name string `json:"name"`
Path string `json:"path"`
State string `json:"state"`
}
Workflow is an Actions workflow as GitHub reports it.
The scheduled publishing workflow is disabled by GitHub after a period of repository inactivity, and a disabled workflow fails silently by simply never running, so each run checks that it is still active.