github

package
v0.1.3 Latest Latest
Warning

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

Go to latest
Published: Jul 15, 2026 License: Apache-2.0 Imports: 26 Imported by: 0

Documentation

Overview

Package github is the agent's pull-request review surface: fetch a pull request and its diff, post inline and summary comments, and submit a formal review verdict. It is the read-and-review half of the GitHub tool, distinct from cloning a contributor's branch and running its code, which stays behind the sandbox.

The tools here hold no host access. They call the GitHub REST API through an injected HTTP client, which defaults to a netguard-backed client so the egress policy applies to every request. Nothing here executes code, reads the working tree, or spawns a process: a review is a read of a diff and a write to an API.

Authority is deliberately narrow. A Set authenticates as a GitHub App installation, which holds pull_requests:write and contents:read and no contents:write, so the reviewer can never push to a branch. Submitting an APPROVE verdict is refused unless the caller opts in through Config, and is always refused on a pull request the reviewer itself authored.

Index

Constants

View Source
const DefaultAPIBase = "https://api.github.com"

DefaultAPIBase is GitHub's REST API root, used when Config leaves APIBase empty.

Variables

View Source
var ErrAmbiguousCredential = errors.New("github: Config carries both App and Token; set exactly one")

ErrAmbiguousCredential is returned when a Config carries both a GitHub App and a token. Choosing one silently would decide, on the caller's behalf, which identity a review is published under, so it is refused instead.

View Source
var ErrApproveNotEnabled = errors.New("github: APPROVE verdict is not enabled for this reviewer")

ErrApproveNotEnabled is returned when a submit_review call asks for an APPROVE verdict but the Set was built without Config.AllowApprove. Approval is off by default: a reviewer that approves unless told otherwise silently becomes a merge gate on every repository it is installed on.

View Source
var ErrDiffTooLarge = errors.New("github: pull request exceeds the review budget")

ErrDiffTooLarge is returned when a pull request exceeds the configured review budget and the caller has not opted in to reviewing it anyway. A runaway diff costs real money to review and produces a worse review at the end of it, so the default is to refuse and let a human ask for it explicitly.

View Source
var ErrIncompleteDiff = errors.New("github: refusing to approve a pull request whose diff was truncated")

ErrIncompleteDiff is returned when an APPROVE verdict is attempted on a pull request whose diff did not reach the review in full. An approval asserts the change was reviewed, so approving a diff that was truncated on the way in states something untrue. Blocking verdicts stay available on partial evidence: one real defect is enough to say no, but most of a diff is never enough to say yes.

View Source
var ErrNoCredential = errors.New("github: Config needs either App or Token")

ErrNoCredential is returned when a Config carries neither a GitHub App nor a token, so nothing could authenticate a request.

View Source
var ErrSelfApproval = errors.New("github: refusing to approve the reviewer's own pull request")

ErrSelfApproval is returned when the reviewer would approve a pull request it authored itself. A self-approval is not a second opinion, and no configuration enables it.

Functions

func ParsePrivateKey

func ParsePrivateKey(pemBytes []byte) (*rsa.PrivateKey, error)

ParsePrivateKey reads the RSA private key a GitHub App downloads at registration.

GitHub issues the key as PKCS#1 ("BEGIN RSA PRIVATE KEY"), but tooling that round-trips it through a secret store commonly re-encodes it as PKCS#8 ("BEGIN PRIVATE KEY"). Both arrive in the wild, and a caller holding PEM bytes from an environment variable cannot tell which it has, so both are accepted.

The key is the App's sole credential and grants everything its installations do. It belongs in the vault, and the bytes handed here should come from there.

Types

type App

type App struct {
	// Issuer is the App's client ID (preferred) or its numeric App ID as a string.
	// It becomes the "iss" claim of the signed assertion.
	Issuer string

	// InstallationID identifies the installation whose token the App mints. An App
	// with no installation has no repository access.
	InstallationID int64

	// PrivateKey signs the assertion. It is the App's sole credential and grants
	// everything its installations do; store it in the vault, not on disk.
	PrivateKey *rsa.PrivateKey
}

App is a GitHub App's credentials and the installation it acts as. It is a value type holding no mutable state, so a Config carrying one is safe to copy.

The zero value is unusable. Build one with the App's client ID (or numeric App ID), the installation ID, and the RSA private key GitHub issued at registration.

type ChangedFile

type ChangedFile struct {
	Filename  string `json:"filename"`
	Status    string `json:"status"`
	Additions int    `json:"additions"`
	Deletions int    `json:"deletions"`
	Patch     string `json:"patch"`

	// PatchTruncated reports that Patch was shortened to fit the configured cap.
	PatchTruncated bool `json:"patch_truncated,omitempty"`
}

ChangedFile is one file in a pull request's diff, with its patch when GitHub supplies one. A binary or very large file has no patch.

type Config

type Config struct {
	// App identifies the GitHub App and the installation the Set acts as. Set exactly
	// one of App and Token.
	//
	// The App path gives the reviewer an identity of its own, distinct from whoever
	// authored the pull request, which is what a formal approval requires.
	App App

	// Token authenticates as whoever issued it: a workflow's ambient GITHUB_TOKEN, or
	// a personal access token. Set exactly one of App and Token.
	//
	// A review posted with a workflow's GITHUB_TOKEN is authored by
	// github-actions[bot], and a repository refuses an approving review from that
	// identity unless an owner has enabled "Allow GitHub Actions to create and approve
	// pull requests", which is off by default. So this path can comment and request
	// changes anywhere, and can approve only where that setting was deliberately
	// turned on.
	Token secret.Text

	// Owner and Repo bound the Set to a single repository. Every tool call operates
	// on this repository; the model cannot redirect a review at another one because
	// the coordinates are not part of any tool's input schema.
	Owner string
	Repo  string

	// Number is the pull request the reviewer was invoked for. Like Owner and Repo it
	// binds the whole review: no tool takes a pull-request number from the model, so
	// the diff a review reads cannot steer a comment or a verdict onto a different pull
	// request by naming its number. A review whose own diff mentions "#123" still writes
	// only to the pull request it was launched against. Must be positive.
	Number int

	// SelfLogin is the reviewer's own login, such as "my-reviewer[bot]". When a pull
	// request's author matches it, an APPROVE verdict is refused. Empty disables the
	// check, which is only correct in tests.
	SelfLogin string

	// AllowApprove permits the APPROVE verdict. It is off by default on purpose, and
	// a host should set it only from committed repository configuration, never from
	// a value a pull-request comment can reach.
	AllowApprove bool

	// MaxFiles caps how many changed files a fetch returns, and MaxPatchBytes caps
	// each file's patch, so a large pull request cannot flood the model's context.
	// Zero selects the defaults.
	MaxFiles      int
	MaxPatchBytes int

	// MaxChangedLines is the review budget: a pull request whose additions plus
	// deletions exceed it is refused rather than reviewed, because a runaway diff
	// costs real money and yields a worse review. Zero selects the default. Negative
	// disables the budget, which is what an explicit "review it anyway" invocation
	// passes.
	//
	// The count is GitHub's own total for the whole pull request, so a diff cannot
	// slip under the budget by being truncated on the way in.
	MaxChangedLines int

	// ReviewOversize permits reviewing a pull request that exceeds MaxChangedLines.
	// It exists for the explicit, human-initiated "yes, review the big one" path. It
	// does not permit approving one: see ErrIncompleteDiff.
	ReviewOversize bool

	// HTTPClient issues the API requests. Nil selects a netguard-backed client with a
	// public-only egress policy, which refuses connections to private, loopback, and
	// cloud-metadata addresses after DNS resolution.
	HTTPClient *http.Client

	// APIBase overrides the REST API root. Empty selects DefaultAPIBase. Tests point
	// it at an httptest server; a GitHub Enterprise host points it at its own API.
	APIBase string

	// Clock supplies the current time for App token minting and expiry. Nil selects
	// clock.System.
	Clock clock.Clock
}

Config describes how a Set reaches GitHub and what it is permitted to do.

type Finding

type Finding struct {
	// Path and Line locate the finding on the right-hand side of the diff.
	Path string `json:"path"`
	Line int    `json:"line"`

	// Rule names the check that produced the finding. It participates in the
	// comment's identity, so re-running a review updates a finding in place rather
	// than posting it twice.
	Rule string `json:"rule"`

	// Summary states the defect in one sentence.
	Summary string `json:"summary"`

	// Failure is the concrete scenario the defect causes: inputs or state, and the
	// wrong output or crash that results. A finding without one is not postable,
	// because a comment that cannot name what breaks is a nitpick.
	Failure string `json:"failure"`
}

Finding is one reviewable defect anchored to a line of the diff.

type PostedFinding

type PostedFinding struct {
	Path    string `json:"path"`
	Line    int    `json:"line"`
	Rule    string `json:"rule"`
	Summary string `json:"summary,omitempty"`
	Failure string `json:"failure,omitempty"`
}

PostedFinding is a finding already on the pull request, handed back so the reviewer can decide whether its defect is still there. Path, Line, and Rule regenerate the marker, so reposting updates the existing comment rather than opening a second conversation about the same defect. Summary and Failure are the claim the reviewer made last time: without them it would have only a hash of the location and would restate the finding on faith instead of re-reading what it said and checking the current diff against it.

type PullRequest

type PullRequest struct {
	Number      int    `json:"number"`
	Title       string `json:"title"`
	Body        string `json:"body"`
	State       string `json:"state"`
	Draft       bool   `json:"draft"`
	HeadSHA     string `json:"-"`
	AuthorLogin string `json:"-"`

	// ChangedFiles, Additions, and Deletions are the authoritative totals for the
	// pull request, reported by GitHub on the pull request itself.
	ChangedFiles int `json:"changed_files"`
	Additions    int `json:"additions"`
	Deletions    int `json:"deletions"`

	Head struct {
		SHA string `json:"sha"`
	} `json:"head"`
	User struct {
		Login string `json:"login"`
	} `json:"user"`
}

PullRequest is the subset of a pull request a review needs. The size counts are GitHub's own totals for the whole change, independent of how much of the diff a fetch actually returned, which is what makes them safe to gate a verdict on.

func (PullRequest) ChangedLines

func (p PullRequest) ChangedLines() int

ChangedLines is the pull request's total churn: lines added plus lines removed.

type ReviewComment

type ReviewComment struct {
	ID   int64  `json:"id"`
	Path string `json:"path"`
	Line int    `json:"line"`
	Body string `json:"body"`

	// HTMLURL addresses the comment on the pull request, so a verdict can point at a
	// finding instead of repeating it.
	HTMLURL string `json:"html_url"`

	// User is who wrote the comment. A marker in a body proves nothing about that: it
	// is plain text anyone can copy.
	User struct {
		Login string `json:"login"`
	} `json:"user"`
}

ReviewComment is an existing inline comment on a pull request's diff.

type ReviewThread

type ReviewThread struct {
	// ID is the GraphQL node id, which is what resolveReviewThread takes. It is not
	// the REST comment id.
	ID string

	// Resolved reports whether the thread is already closed.
	Resolved bool

	// Outdated reports GitHub's own judgement that the diff hunk this thread is
	// anchored to no longer matches the head of the pull request.
	Outdated bool

	// Marker keys the thread to the finding that opened it, taken from the body of
	// its first comment. A thread a human opened carries none.
	Marker string

	// Author is the login that opened the thread.
	Author string

	// Participants counts the distinct logins that have commented. More than one means
	// somebody replied.
	Participants int

	// CanResolve reports whether the authenticated identity may close this thread.
	// Resolving a conversation needs write access to the repository, which a reviewer
	// holding only pull_requests:write does not have. A reviewer that can never push is
	// therefore a reviewer that can never resolve, and must retract its finding another
	// way. GitHub answers this per thread, so the capability is read rather than guessed.
	CanResolve bool

	// CommentNodeIDs are the GraphQL ids of the thread's comments, in order. They are
	// what minimizeComment takes, and minimizing is the retraction available to a
	// reviewer that may not resolve.
	CommentNodeIDs []string

	// Truncated reports that the thread carries more comments than were read. Whether
	// anybody replied is then unknown, and a reviewer that guessed would eventually
	// retract a finding somebody was still arguing with.
	Truncated bool
}

ReviewThread is a conversation on a line of a pull request's diff.

type Set

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

Set is the GitHub review toolset for one repository. Construct it with New and hand its tools to a mission executor.

func New

func New(cfg Config) (*Set, error)

New builds a review toolset for the repository named in cfg, filling defaults for the HTTP client, API base, clock, and context caps. It returns an error when cfg omits the repository coordinates, or names neither credential, or names both.

func (*Set) Tools

func (s *Set) Tools() []mission.Tool

Tools returns the review toolset as mission.Tools, ready to register with an executor. The names match the capability names an Agent archetype grants: github_pr_fetch, github_comment, and github_submit_review. A tool the archetype does not list is refused at the dispatch waist, so a reviewer granted only github_pr_fetch can read a diff and post nothing.

Jump to

Keyboard shortcuts

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