gh

package
v0.5.1 Latest Latest
Warning

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

Go to latest
Published: Oct 3, 2026 License: GPL-3.0 Imports: 21 Imported by: 0

Documentation

Overview

Package gh wraps the GitHub REST and GraphQL APIs for syncing issues, PRs, and project items.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ParseGitHubURL

func ParseGitHubURL(gitHubURL string) (owner, name, typ string, number int, err error)

ParseGitHubURL parses a GitHub PR/Issue URL and returns the owner, repo name, type, and number.

func QueryWithRetry added in v0.2.0

func QueryWithRetry(ctx context.Context, client *githubv4.Client, q any, variables map[string]any) error

QueryWithRetry runs a githubv4 query, retrying transient network errors. The retryablehttp transport only guards the round-trip itself; errors while reading the response body (e.g. http2 stream resets mid-body: "stream error: stream ID N; CANCEL") surface here instead, so they need their own retry loop.

Types

type ClosingIssue

type ClosingIssue struct {
	NodeID string
	Number int
}

type ItemValueType

type ItemValueType int

ItemValueType is the type of a project item field value (text, number, single select, or date).

const (
	ItemValueTypeText ItemValueType = iota
	ItemValueTypeNumber
	ItemValueTypeSingleSelect
	ItemValueTypeDate
)

func (ItemValueType) String

func (t ItemValueType) String() string

type PRApproval

type PRApproval struct {
	Data struct {
		Repository struct {
			PullRequest struct {
				Title          string
				ReviewDecision string
			}
		}
	}
}

type Project

type Project struct {
	Owner  string
	Number int
	Token

	*ProjectDetails
}

func NewProject

func NewProject(owner string, number int, token string) Project

func (*Project) AddItem

func (p *Project) AddItem(nodeID string) (*string, error)

func (*Project) GetItemFieldValuesByNodeID

func (p *Project) GetItemFieldValuesByNodeID(contentNodeID string, fieldNames []string) (map[string]ProjectItemFieldValue, error)

GetItemFieldValuesByNodeID looks up the project item for a given content node ID (e.g. an issue) and returns the field values for the requested field names. The returned map is keyed by field name. If the item is not found in the project, returns nil map with no error.

func (*Project) GetItems

func (p *Project) GetItems() ([]ProjectItem, error)

GetItems returns all items in the project. todo: allow configure the fields we want to get

func (*Project) HasItem

func (p *Project) HasItem(nodeID string) (*string, error)

HasItem checks whether a given content node (issue or PR) is already in this project. Returns the project item ID if found, nil if not found.

func (*Project) LoadDetails

func (p *Project) LoadDetails() error

func (*Project) SetItemStatus

func (p *Project) SetItemStatus(itemID, status string) error

func (*Project) UpdateItem

func (p *Project) UpdateItem(itemID string, fields []ProjectItemField) error

UpdateItem updates the fields of a project item by building a dynamic GraphQL mutation.

type ProjectDetails

type ProjectDetails struct {
	ID     string
	Fields []struct {
		ID      string
		Name    string
		Options []struct {
			ID   string
			Name string
		}
	}
	FieldIDs                map[string]string
	StatusIDs               map[string]string
	FieldTypes              map[string]ItemValueType     // field name -> type
	SingleSelectOptionIDs   map[string]map[string]string // field name -> option name -> option ID
	SingleSelectOptionNames map[string]map[string]string // field name -> option ID -> option name
}

type ProjectDetailsResult

type ProjectDetailsResult struct {
	Data struct {
		Organization struct {
			ProjectV2 struct {
				ID     string `json:"id"`
				Fields struct {
					Nodes []struct {
						ID      string `json:"id"`
						Name    string `json:"name"`
						Options []struct {
							ID   string `json:"id"`
							Name string `json:"name"`
						} `json:"options"`
					} `json:"nodes"`
				} `json:"fields"`
			} `json:"projectV2"`
		} `json:"organization"`
	} `json:"data"`
}

type ProjectItem

type ProjectItem struct {
	ID          string
	Type        string
	Title       string
	URL         string
	RequestType string
	DueDate     string
	Status      string
	NodeID      string                           // actual pr/issue node id
	FieldValues map[string]ProjectItemFieldValue // current board values keyed by field name
}

type ProjectItemField

type ProjectItemField struct {
	Name    string // A short name for this field (used in GraphQL alias, e.g. "set_key")
	FieldID string // The GraphQL ID of the field
	Type    ItemValueType
	Value   any
}

ProjectItemField represents a single field update for the project item. Type should be either "text" or "number".

type ProjectItemFieldValue

type ProjectItemFieldValue struct {
	Type  ItemValueType
	Value any // string for text/date/singleSelect option ID, float64 for number
}

ProjectItemFieldValue holds a field value read from a project item.

type ProjectItemsResult

type ProjectItemsResult struct {
	Data struct {
		Organization struct {
			ProjectV2 struct {
				ID    string `json:"id"`
				Items struct {
					PageInfo struct {
						HasNextPage bool   `json:"hasNextPage"`
						EndCursor   string `json:"endCursor"`
					} `json:"pageInfo"`
					Nodes []struct {
						ID     string `json:"id"`
						Type   string `json:"type"`
						Status *struct {
							SingleSelectOptionID string `json:"singleSelectOptionId"`
						}
						RequestType *struct {
							Text string `json:"text"`
						} `json:"requestType"`
						DueDate *struct {
							Date string `json:"date"`
						} `json:"dueDate"`
						FieldValues struct {
							Nodes []struct {
								Typename             string  `json:"__typename"`
								Text                 string  `json:"text"`
								Number               float64 `json:"number"`
								Date                 string  `json:"date"`
								SingleSelectOptionID string  `json:"singleSelectOptionId"`
								Field                struct {
									Name string `json:"name"`
								} `json:"field"`
							} `json:"nodes"`
						} `json:"fieldValues"`
						Content struct {
							ID    string `json:"id"`
							Title string `json:"title"`
							URL   string `json:"url"`
						} `json:"content"`
					} `json:"nodes"`
				} `json:"items"`
			} `json:"projectV2"`
		} `json:"organization"`
	} `json:"data"`
}

ProjectItemsResult is the result of the project items query; for now we hard code the project fields we want (dueDate and type). TODO in the future we can make this configurable / get all of them

type PullRequest

type PullRequest struct {
	NodeID                     string
	Author                     string
	Number                     int
	Title                      string
	State                      string
	ReviewDecision             string
	CreatedAt                  time.Time
	UpdatedAt                  time.Time
	ClosedAt                   time.Time
	MergedAt                   time.Time
	MergedBy                   string
	ReviewedAt                 time.Time // when the most recent submitted review (any state except pending) was left, zero when unreviewed
	LastReviewer               string    // who left that review
	Draft                      bool
	Milestone                  string
	Mergeable                  string // MERGEABLE, CONFLICTING, or UNKNOWN (github may still be computing)
	CheckState                 string // combined CI state of the head commit: SUCCESS, FAILURE, ERROR, PENDING, EXPECTED, or "" when the PR has no checks
	TotalCommentCount          int
	TotalReviewCount           int
	ReviewCommentCount         int
	FilteredReviewCount        int
	FilteredReviewCommentCount int

	ClosingIssues            []ClosingIssue
	Assignees                []string
	ReviewedBy               []string               // left a changes requested, commented, or dismissed review
	ApprovedBy               []string               // left an approving review
	ChangesRequestedBy       []ReviewerCommentCount // requested changes, ordered by first request, with comment totals across all their change requests
	AssociatedLabels         map[string]bool
	AssociatedProjectNumbers map[int]bool
}

type Rate

type Rate struct {
	Limit     int `json:"limit"`
	Used      int `json:"used"`
	Remaining int `json:"remaining"`
	Reset     int `json:"reset"` // epoch seconds
}

type RateLimits

type RateLimits struct {
	Core                      Rate
	GraphQL                   Rate
	Search                    Rate
	SourceImport              Rate
	IntegrationManifest       Rate
	CodeScanning              Rate // code_scanning_upload
	ActionsRunnerRegistration Rate
	Scim                      Rate

	// "rate" (alias for core)
	Rate Rate

	// Any new/unknown buckets GitHub adds in the future
	Other map[string]Rate
}

RateLimits is the flat set of rate limit buckets, without the "resources" nesting of the API response.

func GetRateLimit

func GetRateLimit(ctx context.Context, token string) (*RateLimits, error)

type Repo

type Repo struct {
	Owner string
	Name  string
	Token
}

func NewRepo

func NewRepo(repo, token string) (*Repo, error)

func NewRepoOwnerName

func NewRepoOwnerName(owner, name, token string) Repo

func (Repo) GetAllIssueEvents

func (r Repo) GetAllIssueEvents(number int) (*[]github.Timeline, error)

func (Repo) GetAllIssues

func (r Repo) GetAllIssues(state string) (*[]github.Issue, error)

func (Repo) GetAllPullRequests

func (r Repo) GetAllPullRequests(state string) (*[]github.PullRequest, error)

func (Repo) GetAllPullRequestsGQL

func (r Repo) GetAllPullRequestsGQL(states, reviewers []string, limit int, mergedSince *time.Time, progress func(int)) (*[]PullRequest, error)

GetAllPullRequestsGQL retrieves all pull requests matching the given states. If mergedSince is set only PRs merged at or after that time are returned, and pagination walks PRs by most recently updated so it can stop once it reaches PRs untouched since then (a PR's updatedAt is always >= its mergedAt).

func (Repo) GetIssue added in v0.2.0

func (r Repo) GetIssue(number int) (*github.Issue, error)

func (Repo) GetLabelsFor

func (r Repo) GetLabelsFor(number int) (*[]string, error)

func (Repo) GetPullRequest

func (r Repo) GetPullRequest(pr int) (*github.PullRequest, error)

func (Repo) GetPullRequestMergeStatus added in v0.2.0

func (r Repo) GetPullRequestMergeStatus(number int) (mergeable, checkState string, err error)

GetPullRequestMergeStatus returns a single PR's mergeable state (MERGEABLE/CONFLICTING/UNKNOWN) and the combined CI state of its head commit (SUCCESS/FAILURE/ERROR/PENDING/EXPECTED, or "" when the PR has no checks). While github reports UNKNOWN it retries for a bit, as the query itself triggers the async mergeability computation; UNKNOWN is returned only if it never settles.

func (Repo) GetPullRequestReviewComments added in v0.2.0

func (r Repo) GetPullRequestReviewComments(pr int) ([]*github.PullRequestComment, error)

GetPullRequestReviewComments returns all review (inline) comments for a PR; each carries the ID of the review it was submitted with in PullRequestReviewID.

func (Repo) GetPullRequestReviews added in v0.2.0

func (r Repo) GetPullRequestReviews(pr int) ([]*github.PullRequestReview, error)

GetPullRequestReviews returns all reviews for a PR in submission order (oldest first).

func (Repo) ListAllIssueEvents

func (r Repo) ListAllIssueEvents(number int, cb func([]*github.Timeline, *github.Response) error) error

func (Repo) ListAllIssues

func (r Repo) ListAllIssues(state string, cb func([]*github.Issue, *github.Response) error) error

func (Repo) ListAllPullRequests

func (r Repo) ListAllPullRequests(state string, cb func([]*github.PullRequest, *github.Response) error) error

func (Repo) PRReviewDecision

func (r Repo) PRReviewDecision(pr int) (*string, error)

func (Repo) PrURL

func (r Repo) PrURL(pr int) string

type ReviewerCommentCount added in v0.2.0

type ReviewerCommentCount struct {
	Login    string
	Requests int
	Comments int
}

ReviewerCommentCount pairs a reviewer login with how many reviews of a given state they left (e.g. changes requested) and the total number of review comments across those reviews.

type Token

type Token struct {
	Token *string
}

func (Token) GraphQLQuery

func (t Token) GraphQLQuery(query string, params [][]string) (*string, error)

func (Token) GraphQLQueryUnmarshal

func (t Token) GraphQLQueryUnmarshal(query string, params [][]string, data any) error

func (Token) NewClient

func (t Token) NewClient() (*github.Client, context.Context)

func (Token) NewGraphQLClient

func (t Token) NewGraphQLClient() (*githubv4.Client, context.Context, error)

NewGraphQLClient returns a githubv4 client with rate limit aware retries. todo we may want to update the above retry logic to match this one

Jump to

Keyboard shortcuts

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