github

package
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Sep 6, 2026 License: Apache-2.0 Imports: 16 Imported by: 0

Documentation

Overview

Package github implements pkg/forge.Forge against the real GitHub REST API, using github.com/cli/go-gh/v2 (AGENTS.md §4.7's one sanctioned new dependency for M3 — not google/go-github, not a hand-rolled REST client, not the full gh CLI as a library). New resolves credentials exactly the way the gh CLI itself would (its own keyring/config, via go-gh's api.DefaultRESTClient): this package never reads a token from a flag or an environment variable of its own invention, and never sees the token value directly — go-gh sets the Authorization header inside its own HTTP transport (AGENTS.md §4.3, R-002).

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Client

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

Client implements forge.Forge against api.github.com (or a GitHub Enterprise host, via the gh environment's own resolution — this package does not special-case it).

func New

func New(ownerRepo string) (*Client, error)

New builds a Client for ownerRepo ("owner/name", RepoConfig.GitHub), authenticating through the user's own `gh` login exactly as the gh CLI would.

func (*Client) BlameLines

func (c *Client) BlameLines(ctx context.Context, ref, path string, lines []int) (map[int]forge.LineOrigin, error)

BlameLines implements forge.Forge via GraphQL. A nil object means ref does not resolve in this repo; that is reported as forge.ErrUnknownRef so pkg/migrate can fall back to the base branch and mark the age approximate, rather than as a scope failure. Requires a GraphQL client: a Client built through newWithClient alone (the M3 test seam) reports that it has none rather than dereferencing nil.

func (*Client) Checks

func (c *Client) Checks(ctx context.Context, sha string) (forge.CheckSummary, error)

Checks implements forge.Forge: the check-run rollup CIGreenStep gates on (M4), paged through every page of check-runs GitHub reports for sha (Known bug classes: "only page 1 of a check-run set is fetched" — a commit with more than 100 check runs would otherwise silently hide a pending or failed run past the first page, and CI could appear green when it isn't). FailedNames names every check-run that concluded in something other than success/neutral/skipped; SkippedNames separately names every run that concluded `skipped`, which forge.CheckSummary's own doc comment explains is never folded into Success. A run's own name is upstream text nothing here wrote (a CI system can name a job anything), so both name lists go through redact.Strings at this adaptor boundary the same way translateErr already does for GitHub's own free-form error messages (AGENTS.md invariant 6).

func (*Client) Comments

func (c *Client) Comments(ctx context.Context, prNumber int, since time.Time) ([]forge.Comment, error)

Comments implements forge.Forge: PR conversation comments (GitHub models these as issue comments), newer than since, with AuthorType carrying the API's own account "type" through (M4: internal/engine.ApprovedStep is what actually enforces R-001's author check against it, against the API's login field, never the comment body — see that package's doc comment for why enforcement lives there and not here). Bots (Type == "Bot") are additionally excluded here too, as a politeness layer only (AGENTS.md §8 "layered checks": deleting this filter would not change what a correctly-written ApprovedStep accepts, only which layer reports it). This deliberately excludes only "Bot", not "anything other than User": GitHub's account "type" field also has legitimate non-"User" values such as "Organization" (an org-owned account, not a bot), and the set of values isn't closed, so filtering on Type != "User" would silently drop a real, non-bot commenter along with actual bots.

Paged through every page GitHub reports (Known bug classes: "only page 1 of a PR's comments is fetched" — a PR with more than 100 comments could otherwise hide an approval or a later reject past the first page, so ApprovedStep's newest-match scan would never see it).

func (*Client) CommitFiles

func (c *Client) CommitFiles(ctx context.Context, sha string) ([]string, bool, error)

CommitFiles implements forge.Forge: GET .../commits/{sha}, whose files[] pages at 100 like everything else here and is capped by GitHub at githubFilesCap in total.

func (*Client) CommitsTouching

func (c *Client) CommitsTouching(ctx context.Context, ref, path string, since time.Time) ([]string, error)

CommitsTouching implements forge.Forge: GET .../commits?sha={ref}&path={path}&since=…, newest first as GitHub orders it, paged to the same bound.

func (*Client) Compare

func (c *Client) Compare(ctx context.Context, base, head string) (forge.Comparison, error)

Compare implements forge.Forge: GET .../compare/{base}...{head}, paged. GitHub's total_commits is authoritative for the whole range regardless of paging, so Truncated is len(commits) < total rather than a sentinel request. The file list is only ever on page 1 and GitHub caps it at githubFilesCap; a full list is reported FilesTruncated. A 404 is forge.ErrUnknownRef, wrapped with the same scope hint translateErr gives — an unknown ref and a private repo the token cannot see are the same status code.

func (*Client) CreatePR

func (c *Client) CreatePR(ctx context.Context, spec forge.PRSpec) (forge.PR, error)

CreatePR implements forge.Forge.

func (*Client) FindPR

func (c *Client) FindPR(ctx context.Context, headBranch, bodyMarker string) (forge.PR, bool, error)

FindPR implements forge.Forge: first the exact head-branch match GitHub's own "head" filter gives for free, then — when a branch was renamed or recreated, so nothing shares its name anymore — a bounded scan of open, then closed, PRs for the body marker (Known bug classes: "Finding an existing PR only by branch name and missing one whose branch was renamed").

func (*Client) GetPR

func (c *Client) GetPR(ctx context.Context, number int) (forge.PR, bool, error)

GetPR implements forge.Forge: getPR with a 404 reported as ok=false rather than an error, since "no such PR" is an answer the caller falls back from, not a failure.

func (*Client) IsAllowedAuthor

func (c *Client) IsAllowedAuthor(ctx context.Context, login string) (bool, error)

IsAllowedAuthor implements forge.Forge: true for "admin", "maintain" or "write" — R-001's stated bar is "collaborator with write (or higher) permission" (forge.Forge's own doc comment), and GitHub ranks maintain strictly above write (admin > maintain > write > triage > read), so a maintain-level collaborator qualifies exactly like write and admin do; only "triage" and "read" fall below the bar. A 404 (login is not a collaborator — the ordinary case for a drive-by commenter on a public PR) is folded into (false, nil) rather than propagated as an error: treating it as fatal would make any non-collaborator's comment error and block the whole promotion whenever collaborators=true, rather than simply not counting as an approval.

func (*Client) MergePR

func (c *Client) MergePR(ctx context.Context, prNumber int, expectedHeadSHA string) (forge.PR, error)

MergePR implements forge.Forge: a squash merge, gated atomically on the server's own "sha" parameter (Known bug classes: "don't roll your own check-then-merge race") — a head that has moved since expectedHeadSHA was observed is refused with a 409, translated to forge.ErrStaleHead. Squash is always used, so a multi-commit branch's title would normally concatenate every commit subject; this promotion is guaranteed exactly one commit (M3), so that default composes to that one commit's own message, never a leak of unrelated history (Known bug classes, confirmed here rather than assumed).

func (*Client) ReadFile

func (c *Client) ReadFile(ctx context.Context, ref, path string) ([]byte, bool, error)

ReadFile implements forge.Forge: GET .../contents/{path}?ref=…, base64-decoded. A 404 is ok=false (the file is simply not there); a directory at that path is an error, since the caller asked for a file.

func (*Client) ResolveRef

func (c *Client) ResolveRef(ctx context.Context, ref string) (string, bool, error)

ResolveRef implements forge.Forge: GET .../commits/{ref}, the same endpoint Tags already uses to date a tag, which accepts a tag, a branch, or a sha prefix. 404 (no such ref) and 422 (an ambiguous prefix) are ok=false; anything else — a 403 in particular, the scope-gap signature — is an error, per the interface's own contract.

func (*Client) Tags

func (c *Client) Tags(ctx context.Context) ([]forge.GitTag, error)

Tags implements forge.Forge: every tag on the repo, with the date of the commit it points to. This is N+1 requests (one to list tags, one per tag to read its commit date — GitHub's tag-list endpoint reports a commit SHA but no date, and the M6 brief's own case for this method is exactly "the registry has no dates, the git history does") — acceptable for a single-operator tool's own app repos, bounded by maxTagPages, and only ever called when the tag picker actually needs ordering for a mapped image repo, not on every plan.

Jump to

Keyboard shortcuts

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