forge

package
v0.41.0 Latest Latest
Warning

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

Go to latest
Published: Oct 27, 2026 License: MIT Imports: 14 Imported by: 0

Documentation

Overview

Package forge is the binding to a forge's CLI, gh or glab, and the reads more than one command shares: the pull requests a host lists, their commits, the checks on a commit. Every call to either CLI goes through Host: the spelling each wants is picked in one place, the JSON each returns is read into one shape per noun, and a non-zero exit is the CLI's own stderr, verbatim, since it names the cause better than any paraphrase. The command package wraps Host with the nouns only it acts on (issues, merges, reviews); standup and lookaround read through it.

Index

Constants

View Source
const (
	EventOpened          = "opened"
	EventReady           = "ready" // out of draft
	EventCommit          = "commit"
	EventMergeFromBase   = "merge-from-base" // a commit with more than one parent
	EventForcePush       = "force-push"
	EventReview          = "review"
	EventReviewRequested = "review-requested"
	EventMerged          = "merged"
	EventClosed          = "closed"
)

What happened to a request in a window, one kind per event. Opened is read off the request's creation time; the rest are timeline events.

View Source
const (
	Open   = "open"
	Merged = "merged"
	Closed = "closed"
)

The three states a request can be in, whichever CLI spelled them.

View Source
const (
	OwnFields     = "number,title,state,isDraft,headRefName,baseRefName,url,author,createdAt,mergedAt,changedFiles,mergeCommit"
	ForeignFields = "number,title,state,url,author,mergedAt,mergedBy"
	BoardFields   = "number,title,state,isDraft,headRefName,baseRefName,headRefOid,url,author,assignees," +
		"reviewRequests,reviews,reviewDecision,mergeable,mergeStateStatus,statusCheckRollup," +
		"createdAt,updatedAt,mergedAt,mergedBy,closedAt,body,mergeCommit"
)

The gh field sets: a person's own requests, other people's, and what a board wants to know about every request.

View Source
const (
	Approved         = "approved"
	ChangesRequested = "changes_requested"
	ReviewRequired   = "review_required"
	Commented        = "commented"

	Clean     = "clean"
	Conflicts = "conflicts"
	Blocked   = "blocked"

	Green   = "green"
	Red     = "red"
	Pending = "pending"
)

The vocabularies the host's words are read into.

View Source
const ActivityQuery = `` /* 1365-byte string literal not displayed */

ActivityQuery is the GraphQL a GitHub feed reads: every request updated since the window's start with its timeline since then, and the open requests nobody touched, counted, the oldest first. gh pages it on the main search's cursor; a timeline keeps its newest 100 events, which a request rarely passes within a window.

View Source
const ForcePushesQuery = "query($owner: String!, $name: String!, $number: Int!) { repository(owner: $owner, name: $name) " +
	"{ pullRequest(number: $number) { timelineItems(last: 100, itemTypes: [HEAD_REF_FORCE_PUSHED_EVENT]) " +
	"{ nodes { ... on HeadRefForcePushedEvent { createdAt actor { login } beforeCommit { oid } afterCommit { oid } } } } } } }"

ForcePushesQuery is the GraphQL that lists a PR's force-pushes.

View Source
const PRLimit = 1000

PRLimit caps each gh search. Reaching it is an error rather than a report silently missing the rest.

Variables

This section is empty.

Functions

func DecodePages

func DecodePages[T any](ctx context.Context, h Host, path string) ([]T, error)

DecodePages runs a `api --paginate` call and reads every page into one list. gh merges the pages of an array into one array; glab may print one document per page, so every document in the output is read in turn.

func SameLogin

func SameLogin(declared, login string) bool

SameLogin matches a declared identity against a login the host returned, case-insensitive. A bare `claude` also matches `claude[bot]`, but a declared `claude[bot]` never matches a bare `claude`: only an app's login can carry the suffix, so dropping it on the host's side would let a user who registered the bare name post as the declared bot.

func Status

func Status(state string) string

Status normalises a request's state: gh spells them OPEN / MERGED / CLOSED, glab opened / merged / closed / locked.

Types

type Activity added in v0.39.0

type Activity struct {
	Requests    []Touched
	QuietCount  int
	QuietOldest time.Time // the oldest quiet request's creation time
}

Activity is what a repository did since a time: every request touched, and how many open ones nobody touched, with the oldest of them.

type CI

type CI struct {
	Red bool
	At  time.Time
}

CI is how the checks on a commit ended: Red when any failed, At the time of the last failure.

type Event added in v0.39.0

type Event struct {
	Kind    string
	At      time.Time
	Actor   string
	Message string // a commit's whole message, trailers included
	State   string // a review's: Approved, ChangesRequested or Commented
}

An Event is one thing that happened to a request.

type ForcePush

type ForcePush struct {
	At     time.Time
	Before string
	After  string
	// SamePatches is whether every commit kept its patch, nil when git
	// cannot tell; the reader that owns the checkout fills it.
	SamePatches *bool
}

A ForcePush is one force-push to a PR: the head it replaced and the one it set.

type Host

type Host struct {
	Name string // "gh" or "glab"
	Dir  string // the checkout the CLI reads the remote from
	// Repo names the repository when there is no checkout to read it from:
	// `owner/repo`, or `host/owner/repo` off the CLI's default host. Set,
	// every command carries it: a noun command as -R, an api call with its
	// placeholders filled in.
	Repo string
}

A Host is one of the two forge CLIs, bound to the checkout it answers for, or to a repository named outright where no checkout exists.

func (Host) Activity added in v0.39.0

func (h Host) Activity(ctx context.Context, since time.Time) (Activity, error)

Activity reads what the repository did since since: one paged query on GitHub; on GitLab one list of the merge requests updated since, then the commits, notes and versions of those alone, and one list of the open ones nobody touched.

func (Host) At

func (h Host) At(dir string) Host

At is the same host answering from another checkout of the repo.

func (Host) Branches added in v0.39.0

func (h Host) Branches(ctx context.Context) (map[string]bool, error)

Branches is every branch the host has, one paged listing: what the board asks of every merged request at once.

func (Host) CommitCI

func (h Host) CommitCI(ctx context.Context, sha string) (*CI, error)

CommitCI is how the checks on a commit ended, nil when it has none. On GitHub that is its check runs and its commit statuses; on GitLab its last pipeline.

func (Host) Decode

func (h Host) Decode(ctx context.Context, v any, args ...string) error

Decode runs one host CLI command and reads its JSON stdout into v.

func (Host) DefaultBranchName

func (h Host) DefaultBranchName(ctx context.Context) (string, error)

DefaultBranchName is the repo's default branch as the host has it.

func (Host) EarlierRequests added in v0.39.0

func (h Host) EarlierRequests(ctx context.Context, ids []string) (map[string][]PR, error)

EarlierRequests is every request whose title or branch may name each id, read to tell a task's first request from a follow-up: one GraphQL call on GitHub for every id at once, a search on the text and one on the head branch per id; on GitLab, whose search reads titles alone, one list per id, a few at a time. The caller matches each request's branch and title against the id; the searches only narrow.

func (Host) GLab

func (h Host) GLab() bool

GLab reports whether the host is GitLab's CLI.

func (Host) Login

func (h Host) Login(ctx context.Context) (string, error)

Login is who the CLI is signed in as: gh names it login, glab username.

func (Host) MRForcePushes

func (h Host) MRForcePushes(ctx context.Context, iid int, since time.Time) ([]ForcePush, error)

MRForcePushes reads GitLab's MR versions, one per push, since the time: a version whose previous head is not its ancestor replaced commits. GitLab records no pusher, so on the user's own MR each counts as theirs.

func (Host) MRReviews

func (h Host) MRReviews(ctx context.Context, iid int) ([]Review, error)

MRReviews is the notes that count as reviewing: any plain comment, and the system note an approval leaves.

func (Host) PRForcePushes

func (h Host) PRForcePushes(ctx context.Context, number int, login string) ([]ForcePush, error)

PRForcePushes is the force-pushes the user made to a PR, the last 100: GitHub records each with the head before and after.

func (Host) Pick

func (h Host) Pick(gh, glab []string) []string

Pick is this host's spelling of a command: gh's or glab's.

func (Host) PullCommits

func (h Host) PullCommits(ctx context.Context, number int) ([]PRCommit, error)

PullCommits is the commits a request lists, one call per request: asked for inside the list query, gh's GraphQL refuses a node count past its limit.

func (Host) Pulls

func (h Host) Pulls(ctx context.Context, login string, since time.Time) ([]PR, error)

Pulls is every request a person's standup needs: their own, with their commits and force-pushes, and others' they reviewed or merged since since.

func (Host) PullsSince

func (h Host) PullsSince(ctx context.Context, since time.Time) ([]PR, error)

PullsSince is what a board reads: every open request, and every one merged since since, each with the board's fields and no commits.

func (Host) Run

func (h Host) Run(ctx context.Context, args ...string) (string, error)

Run runs one host CLI command and returns its stdout.

func (Host) SearchPRs

func (h Host) SearchPRs(ctx context.Context, query, fields string) ([]PR, error)

SearchPRs is one `gh pr list --search` with the fields named.

type PR

type PR struct {
	Number    int
	Title     string
	URL       string
	Status    string // Open, Merged or Closed
	Draft     bool
	Empty     bool // no changes yet: no changed files on GitHub, no commits on GitLab
	Head      string
	HeadSHA   string
	Base      string
	Author    string
	Assignees []string
	Reviewers []string // reviewers requested and not yet heard from
	CreatedAt time.Time
	UpdatedAt time.Time
	MergedAt  time.Time // zero unless merged
	ClosedAt  time.Time // zero unless closed
	MergedBy  string
	Body      string
	Decision  string     // Approved, ChangesRequested, ReviewRequired or ""
	Mergeable string     // Clean, Conflicts, Blocked or ""
	Checks    string     // Green, Red, Pending or "" with none
	Commits   []PRCommit // the commits the host lists for it, where asked for
	MergeSHA  []string   // the merge and squash commits it landed as
	Reviews   []Review   // where asked for

	ForcePushes []ForcePush // the requester's own, where asked for
}

A PR is a pull or merge request as the host reported it.

type PRCommit

type PRCommit struct {
	SHA     string
	Subject string
	Message string // the whole message, trailers included
	Email   string // the author's email, as the commit records it
	At      time.Time
	Parents int // more than one is a merge
}

A PRCommit is one commit a PR lists. A rebase merge lands it on the default branch under a new sha, keeping its author date and subject.

type Review

type Review struct {
	Author string
	State  string // Approved, ChangesRequested, Commented or ""
	At     time.Time
}

A Review is one review event on a PR: a submitted review, an approval or a comment, by anyone.

type Touched added in v0.39.0

type Touched struct {
	PR

	Events []Event
}

A Touched is a request updated in the window, with what happened to it there.

Jump to

Keyboard shortcuts

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