Add godoc comments to exported symbols in pkg/cmd/pr

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Kynan Ware 2026-03-04 15:53:47 -07:00
parent 16c6500c82
commit 5de7a10080
36 changed files with 200 additions and 5 deletions

View file

@ -19,6 +19,7 @@ import (
"github.com/spf13/cobra"
)
// CheckoutOptions holds the configuration for the pr checkout command.
type CheckoutOptions struct {
HttpClient func() (*http.Client, error)
GitClient *git.Client
@ -35,6 +36,7 @@ type CheckoutOptions struct {
BranchName string
}
// NewCmdCheckout creates the cobra command for checking out a pull request locally.
func NewCmdCheckout(f *cmdutil.Factory, runF func(*CheckoutOptions) error) *cobra.Command {
opts := &CheckoutOptions{
IO: f.IOStreams,
@ -290,6 +292,7 @@ func executeCmds(client *git.Client, credentialPattern git.CredentialPattern, cm
return nil
}
// PRResolver resolves a pull request and its base repository for checkout.
type PRResolver interface {
Resolve() (*api.PullRequest, ghrepo.Interface, error)
}
@ -299,6 +302,7 @@ type specificPRResolver struct {
selector string
}
// Resolve finds a pull request by its selector string and returns it along with its base repository.
func (r *specificPRResolver) Resolve() (*api.PullRequest, ghrepo.Interface, error) {
pr, baseRepo, err := r.prFinder.Find(shared.FindOptions{
Selector: r.selector,
@ -326,6 +330,7 @@ type promptingPRResolver struct {
baseRepo ghrepo.Interface
}
// Resolve interactively prompts the user to select a pull request from a list of open PRs.
func (r *promptingPRResolver) Resolve() (*api.PullRequest, ghrepo.Interface, error) {
r.io.StartProgressIndicator()
listResult, err := r.prLister.List(shared.ListOptions{

View file

@ -29,6 +29,7 @@ type checkCounts struct {
Canceled int
}
// ExportData returns the check's fields as a map for structured data export.
func (ch *check) ExportData(fields []string) map[string]interface{} {
return cmdutil.StructExportData(ch, fields)
}

View file

@ -32,6 +32,7 @@ var prCheckFields = []string{
"description",
}
// ChecksOptions holds the configuration for the pr checks command.
type ChecksOptions struct {
HttpClient func() (*http.Client, error)
IO *iostreams.IOStreams
@ -49,6 +50,7 @@ type ChecksOptions struct {
Required bool
}
// NewCmdChecks creates the cobra command for viewing CI status checks on a pull request.
func NewCmdChecks(f *cmdutil.Factory, runF func(*ChecksOptions) error) *cobra.Command {
var interval int
opts := &ChecksOptions{

View file

@ -14,6 +14,7 @@ import (
"github.com/spf13/cobra"
)
// CloseOptions holds the configuration for the pr close command.
type CloseOptions struct {
HttpClient func() (*http.Client, error)
GitClient *git.Client
@ -28,6 +29,7 @@ type CloseOptions struct {
DeleteLocalBranch bool
}
// NewCmdClose creates the cobra command for closing a pull request.
func NewCmdClose(f *cmdutil.Factory, runF func(*CloseOptions) error) *cobra.Command {
opts := &CloseOptions{
IO: f.IOStreams,

View file

@ -8,6 +8,7 @@ import (
"github.com/spf13/cobra"
)
// NewCmdComment creates the cobra command for adding a comment to a pull request.
func NewCmdComment(f *cmdutil.Factory, runF func(*shared.CommentableOptions) error) *cobra.Command {
opts := &shared.CommentableOptions{
IO: f.IOStreams,

View file

@ -30,6 +30,7 @@ import (
"github.com/spf13/cobra"
)
// CreateOptions holds the configuration and inputs for creating a pull request.
type CreateOptions struct {
// This struct stores user input and factory functions
Detector fd.Detector
@ -99,10 +100,12 @@ type baseRefs struct {
baseBranchName string
}
// BaseRef returns the name of the base branch.
func (r baseRefs) BaseRef() string {
return r.baseBranchName
}
// BaseRepo returns the base repository for the pull request.
func (r baseRefs) BaseRepo() *api.Repository {
return r.baseRepo
}
@ -114,10 +117,12 @@ type skipPushRefs struct {
qualifiedHeadRef shared.QualifiedHeadRef
}
// QualifiedHeadRef returns the fully qualified head ref string for cross-repo or same-repo PRs.
func (r skipPushRefs) QualifiedHeadRef() string {
return r.qualifiedHeadRef.String()
}
// UnqualifiedHeadRef returns the head branch name without any owner prefix.
func (r skipPushRefs) UnqualifiedHeadRef() string {
return r.qualifiedHeadRef.BranchName()
}
@ -132,6 +137,7 @@ type pushableRefs struct {
headBranchName string
}
// QualifiedHeadRef returns the head ref, prefixed with the owner if the head and base repos differ.
func (r pushableRefs) QualifiedHeadRef() string {
if ghrepo.IsSame(r.headRepo, r.baseRepo) {
return r.headBranchName
@ -139,10 +145,12 @@ func (r pushableRefs) QualifiedHeadRef() string {
return fmt.Sprintf("%s:%s", r.headRepo.RepoOwner(), r.headBranchName)
}
// UnqualifiedHeadRef returns the head branch name without any owner prefix.
func (r pushableRefs) UnqualifiedHeadRef() string {
return r.headBranchName
}
// HeadRepo returns the repository where the head branch resides.
func (r pushableRefs) HeadRepo() ghrepo.Interface {
return r.headRepo
}
@ -158,10 +166,12 @@ type forkableRefs struct {
qualifiedHeadRef shared.QualifiedHeadRef
}
// QualifiedHeadRef returns the fully qualified head ref string for a fork-based PR.
func (r forkableRefs) QualifiedHeadRef() string {
return r.qualifiedHeadRef.String()
}
// UnqualifiedHeadRef returns the head branch name without any owner prefix.
func (r forkableRefs) UnqualifiedHeadRef() string {
return r.qualifiedHeadRef.BranchName()
}
@ -190,6 +200,7 @@ type CreateContext struct {
GitClient *git.Client
}
// NewCmdCreate creates a new cobra.Command for opening a pull request.
func NewCmdCreate(f *cmdutil.Factory, runF func(*CreateOptions) error) *cobra.Command {
opts := &CreateOptions{
IO: f.IOStreams,
@ -641,6 +652,7 @@ func initDefaultTitleBody(ctx CreateContext, state *shared.IssueMetadataState, u
return nil
}
// NewIssueState creates an IssueMetadataState populated from the given CreateContext and CreateOptions.
func NewIssueState(ctx CreateContext, opts CreateOptions) (*shared.IssueMetadataState, error) {
var milestoneTitles []string
if opts.Milestone != "" {
@ -673,6 +685,7 @@ func NewIssueState(ctx CreateContext, opts CreateOptions) (*shared.IssueMetadata
return state, nil
}
// NewCreateContext initializes a CreateContext by resolving remotes, repos, and branch information.
func NewCreateContext(opts *CreateOptions) (*CreateContext, error) {
httpClient, err := opts.HttpClient()
if err != nil {

View file

@ -6,10 +6,12 @@ import (
"regexp"
)
// NewRegexpWriter creates a RegexpWriter that replaces matches of re with repl in the output.
func NewRegexpWriter(out io.Writer, re *regexp.Regexp, repl string) *RegexpWriter {
return &RegexpWriter{out: out, re: *re, repl: repl}
}
// RegexpWriter is an io.Writer that filters output by replacing lines matching a regexp.
type RegexpWriter struct {
out io.Writer
re regexp.Regexp
@ -17,6 +19,7 @@ type RegexpWriter struct {
buf []byte
}
// Write processes data by applying regexp replacements line by line and writing the result.
func (s *RegexpWriter) Write(data []byte) (int, error) {
if len(data) == 0 {
return 0, nil
@ -51,6 +54,7 @@ func (s *RegexpWriter) Write(data []byte) (int, error) {
return len(data), nil
}
// Flush writes any remaining buffered data after applying regexp replacements.
func (s *RegexpWriter) Flush() (int, error) {
if len(s.buf) > 0 {
repl := []byte(s.repl)

View file

@ -24,6 +24,7 @@ import (
"golang.org/x/text/transform"
)
// DiffOptions holds the configuration for the pr diff command.
type DiffOptions struct {
HttpClient func() (*http.Client, error)
IO *iostreams.IOStreams
@ -38,6 +39,7 @@ type DiffOptions struct {
BrowserMode bool
}
// NewCmdDiff creates the cobra command for viewing the diff of a pull request.
func NewCmdDiff(f *cmdutil.Factory, runF func(*DiffOptions) error) *cobra.Command {
opts := &DiffOptions{
IO: f.IOStreams,

View file

@ -21,6 +21,7 @@ import (
"golang.org/x/sync/errgroup"
)
// EditOptions holds the configuration for the pr edit command.
type EditOptions struct {
HttpClient func() (*http.Client, error)
IO *iostreams.IOStreams
@ -39,6 +40,7 @@ type EditOptions struct {
shared.Editable
}
// NewCmdEdit creates the cobra command for editing a pull request's properties.
func NewCmdEdit(f *cmdutil.Factory, runF func(*EditOptions) error) *cobra.Command {
opts := &EditOptions{
IO: f.IOStreams,
@ -529,6 +531,7 @@ func updatePullRequestReviewsREST(client *api.Client, repo ghrepo.Interface, num
return wg.Wait()
}
// Surveyor provides interactive prompts for selecting and editing pull request fields.
type Surveyor interface {
FieldsToEdit(*shared.Editable) error
EditFields(*shared.Editable, string) error
@ -538,24 +541,29 @@ type surveyor struct {
P shared.EditPrompter
}
// FieldsToEdit prompts the user to select which pull request fields to edit.
func (s surveyor) FieldsToEdit(editable *shared.Editable) error {
return shared.FieldsToEditSurvey(s.P, editable)
}
// EditFields prompts the user to provide new values for the selected pull request fields.
func (s surveyor) EditFields(editable *shared.Editable, editorCmd string) error {
return shared.EditFieldsSurvey(s.P, editable, editorCmd)
}
// EditableOptionsFetcher fetches the available options for editable pull request fields.
type EditableOptionsFetcher interface {
EditableOptionsFetch(*api.Client, ghrepo.Interface, *shared.Editable, gh.ProjectsV1Support) error
}
type fetcher struct{}
// EditableOptionsFetch retrieves the available options such as labels, assignees, and milestones for editing.
func (f fetcher) EditableOptionsFetch(client *api.Client, repo ghrepo.Interface, opts *shared.Editable, projectsV1Support gh.ProjectsV1Support) error {
return shared.FetchOptions(client, repo, opts, projectsV1Support)
}
// EditorRetriever retrieves the user's preferred text editor command.
type EditorRetriever interface {
Retrieve() (string, error)
}
@ -564,6 +572,7 @@ type editorRetriever struct {
config func() (gh.Config, error)
}
// Retrieve returns the user's configured editor command.
func (e editorRetriever) Retrieve() (string, error) {
return cmdutil.DetermineEditor(e.config)
}

View file

@ -20,6 +20,7 @@ import (
"github.com/spf13/cobra"
)
// ListOptions holds the configuration for the pr list command.
type ListOptions struct {
HttpClient func() (*http.Client, error)
IO *iostreams.IOStreams
@ -43,6 +44,7 @@ type ListOptions struct {
Now func() time.Time
}
// NewCmdList creates the cobra command for listing pull requests in a repository.
func NewCmdList(f *cmdutil.Factory, runF func(*ListOptions) error) *cobra.Command {
opts := &ListOptions{
IO: f.IOStreams,

View file

@ -9,21 +9,33 @@ import (
"github.com/shurcooL/githubv4"
)
// PullRequestMergeMethod represents the strategy used to merge a pull request.
type PullRequestMergeMethod int
const (
// PullRequestMergeMethodMerge creates a merge commit.
PullRequestMergeMethodMerge PullRequestMergeMethod = iota
// PullRequestMergeMethodRebase rebases the commits onto the base branch.
PullRequestMergeMethodRebase
// PullRequestMergeMethodSquash squashes all commits into a single commit.
PullRequestMergeMethodSquash
)
// Merge state status constants represent the GitHub GraphQL MergeStateStatus values.
const (
MergeStateStatusBehind = "BEHIND"
MergeStateStatusBlocked = "BLOCKED"
MergeStateStatusClean = "CLEAN"
MergeStateStatusDirty = "DIRTY"
// MergeStateStatusBehind indicates the head branch is behind the base branch.
MergeStateStatusBehind = "BEHIND"
// MergeStateStatusBlocked indicates the merge is blocked by branch protection or other rules.
MergeStateStatusBlocked = "BLOCKED"
// MergeStateStatusClean indicates the branch is clean and ready to merge.
MergeStateStatusClean = "CLEAN"
// MergeStateStatusDirty indicates the branch has merge conflicts.
MergeStateStatusDirty = "DIRTY"
// MergeStateStatusHasHooks indicates the branch has pre-receive hooks that must pass.
MergeStateStatusHasHooks = "HAS_HOOKS"
MergeStateStatusMerged = "MERGED"
// MergeStateStatusMerged indicates the pull request has already been merged.
MergeStateStatusMerged = "MERGED"
// MergeStateStatusUnstable indicates the branch has failing required status checks.
MergeStateStatusUnstable = "UNSTABLE"
)

View file

@ -23,6 +23,7 @@ type editor interface {
Edit(string, string) (string, error)
}
// MergeOptions holds the configuration for the pr merge command.
type MergeOptions struct {
HttpClient func() (*http.Client, error)
GitClient *git.Client
@ -57,6 +58,7 @@ type MergeOptions struct {
// ErrAlreadyInMergeQueue indicates that the pull request is already in a merge queue
var ErrAlreadyInMergeQueue = errors.New("already in merge queue")
// NewCmdMerge creates the cobra command for merging a pull request.
func NewCmdMerge(f *cmdutil.Factory, runF func(*MergeOptions) error) *cobra.Command {
opts := &MergeOptions{
IO: f.IOStreams,
@ -703,6 +705,7 @@ type userEditor struct {
config func() (gh.Config, error)
}
// Edit opens the user's preferred text editor to edit the given text and returns the result.
func (e *userEditor) Edit(filename, startingText string) (string, error) {
editorCommand, err := cmdutil.DetermineEditor(e.config)
if err != nil {

View file

@ -23,6 +23,7 @@ import (
"github.com/spf13/cobra"
)
// NewCmdPR creates the top-level cobra command for managing pull requests.
func NewCmdPR(f *cmdutil.Factory) *cobra.Command {
cmd := &cobra.Command{
Use: "pr <command>",

View file

@ -13,6 +13,7 @@ import (
"github.com/spf13/cobra"
)
// ReadyOptions holds the configuration for the pr ready command.
type ReadyOptions struct {
HttpClient func() (*http.Client, error)
IO *iostreams.IOStreams
@ -23,6 +24,7 @@ type ReadyOptions struct {
Undo bool
}
// NewCmdReady creates the cobra command for marking a pull request as ready for review.
func NewCmdReady(f *cmdutil.Factory, runF func(*ReadyOptions) error) *cobra.Command {
opts := &ReadyOptions{
IO: f.IOStreams,

View file

@ -12,6 +12,7 @@ import (
"github.com/spf13/cobra"
)
// ReopenOptions holds the configuration for the pr reopen command.
type ReopenOptions struct {
HttpClient func() (*http.Client, error)
IO *iostreams.IOStreams
@ -22,6 +23,7 @@ type ReopenOptions struct {
Comment string
}
// NewCmdReopen creates the cobra command for reopening a closed pull request.
func NewCmdReopen(f *cmdutil.Factory, runF func(*ReopenOptions) error) *cobra.Command {
opts := &ReopenOptions{
IO: f.IOStreams,

View file

@ -13,6 +13,7 @@ import (
"github.com/spf13/cobra"
)
// RevertOptions holds the configuration for the pr revert command.
type RevertOptions struct {
HttpClient func() (*http.Client, error)
IO *iostreams.IOStreams
@ -27,6 +28,7 @@ type RevertOptions struct {
IsDraft bool
}
// NewCmdRevert creates the cobra command for reverting a merged pull request.
func NewCmdRevert(f *cmdutil.Factory, runF func(*RevertOptions) error) *cobra.Command {
opts := &RevertOptions{
IO: f.IOStreams,

View file

@ -17,6 +17,7 @@ import (
"github.com/spf13/cobra"
)
// ReviewOptions holds the configuration for the pr review command.
type ReviewOptions struct {
HttpClient func() (*http.Client, error)
Config func() (gh.Config, error)
@ -31,6 +32,7 @@ type ReviewOptions struct {
Body string
}
// NewCmdReview creates the cobra command for submitting a review on a pull request.
func NewCmdReview(f *cmdutil.Factory, runF func(*ReviewOptions) error) *cobra.Command {
opts := &ReviewOptions{
IO: f.IOStreams,

View file

@ -20,20 +20,26 @@ import (
var errNoUserComments = errors.New("no comments found for current user")
var errDeleteNotConfirmed = errors.New("deletion not confirmed")
// InputType represents the method used for providing comment input.
type InputType int
const (
// InputTypeEditor indicates input will be provided via an external editor.
InputTypeEditor InputType = iota
// InputTypeInline indicates input is provided directly as a flag value.
InputTypeInline
// InputTypeWeb indicates the comment will be created via the browser.
InputTypeWeb
)
// Commentable represents an entity that can be commented on, such as an issue or pull request.
type Commentable interface {
Link() string
Identifier() string
CurrentUserComments() []api.Comment
}
// CommentableOptions holds the configuration for creating, updating, or deleting comments.
type CommentableOptions struct {
IO *iostreams.IOStreams
HttpClient func() (*http.Client, error)
@ -55,6 +61,7 @@ type CommentableOptions struct {
Host string
}
// CommentablePreRun validates flags and configures input type and interactivity before the command runs.
func CommentablePreRun(cmd *cobra.Command, opts *CommentableOptions) error {
inputFlags := 0
if cmd.Flags().Changed("body") {
@ -105,6 +112,7 @@ func CommentablePreRun(cmd *cobra.Command, opts *CommentableOptions) error {
return nil
}
// CommentableRun executes the comment action (create, update, or delete) based on the configured options.
func CommentableRun(opts *CommentableOptions) error {
commentable, repo, err := opts.RetrieveCommentable()
if err != nil {
@ -305,12 +313,14 @@ func deleteComment(commentable Commentable, opts *CommentableOptions) error {
return nil
}
// CommentableConfirmSubmitSurvey returns a function that prompts the user to confirm comment submission.
func CommentableConfirmSubmitSurvey(p Prompt) func() (bool, error) {
return func() (bool, error) {
return p.Confirm("Submit?", true)
}
}
// CommentableInteractiveEditSurvey returns a function that opens an editor for composing a comment interactively.
func CommentableInteractiveEditSurvey(cf func() (gh.Config, error), io *iostreams.IOStreams) func(string) (string, error) {
return func(initialValue string) (string, error) {
editorCommand, err := cmdutil.DetermineEditor(cf)
@ -324,12 +334,14 @@ func CommentableInteractiveEditSurvey(cf func() (gh.Config, error), io *iostream
}
}
// CommentableInteractiveCreateIfNoneSurvey returns a function that prompts the user to create a comment when none exist.
func CommentableInteractiveCreateIfNoneSurvey(p Prompt) func() (bool, error) {
return func() (bool, error) {
return p.Confirm("No comments found. Create one?", true)
}
}
// CommentableEditSurvey returns a function that opens an editor for composing a comment non-interactively.
func CommentableEditSurvey(cf func() (gh.Config, error), io *iostreams.IOStreams) func(string) (string, error) {
return func(initialValue string) (string, error) {
editorCommand, err := cmdutil.DetermineEditor(cf)
@ -340,6 +352,7 @@ func CommentableEditSurvey(cf func() (gh.Config, error), io *iostreams.IOStreams
}
}
// CommentableConfirmDeleteLastComment returns a function that prompts the user to confirm deletion of their last comment.
func CommentableConfirmDeleteLastComment(p Prompt) func(string) (bool, error) {
return func(body string) (bool, error) {
return p.Confirm(fmt.Sprintf("Delete the comment: %q?", body), true)

View file

@ -12,6 +12,7 @@ import (
"github.com/cli/cli/v2/pkg/markdown"
)
// Comment represents a comment on an issue or pull request with metadata accessors.
type Comment interface {
Identifier() string
AuthorLogin() string
@ -26,6 +27,7 @@ type Comment interface {
Status() string
}
// RawCommentList returns a plaintext representation of comments and reviews sorted by creation time.
func RawCommentList(comments api.Comments, reviews api.PullRequestReviews) string {
sortedComments := sortComments(comments, reviews)
var b strings.Builder
@ -50,6 +52,7 @@ func formatRawComment(comment Comment) string {
return b.String()
}
// CommentList returns a formatted string of comments and reviews, optionally showing only the newest.
func CommentList(io *iostreams.IOStreams, comments api.Comments, reviews api.PullRequestReviews, preview bool) (string, error) {
sortedComments := sortComments(comments, reviews)
if preview && len(sortedComments) > 0 {

View file

@ -11,6 +11,7 @@ import (
"github.com/cli/cli/v2/internal/ghrepo"
)
// RequestableReviewersForCompletion returns a sorted list of requestable reviewers for shell completion.
func RequestableReviewersForCompletion(httpClient *http.Client, repo ghrepo.Interface) ([]string, error) {
client := api.NewClientFromHTTP(api.NewCachedHTTPClient(httpClient, time.Minute*2))

View file

@ -9,6 +9,7 @@ import (
"github.com/cli/cli/v2/pkg/iostreams"
)
// StateTitleWithColor returns the title-cased PR state colorized appropriately, showing "Draft" for draft PRs.
func StateTitleWithColor(cs *iostreams.ColorScheme, pr api.PullRequest) string {
prStateColorFunc := cs.ColorFromString(ColorForPRState(pr))
if pr.State == "OPEN" && pr.IsDraft {
@ -17,6 +18,7 @@ func StateTitleWithColor(cs *iostreams.ColorScheme, pr api.PullRequest) string {
return prStateColorFunc(text.Title(pr.State))
}
// PrStateWithDraft returns the PR state string, substituting "DRAFT" for open draft PRs.
func PrStateWithDraft(pr *api.PullRequest) string {
if pr.IsDraft && pr.State == "OPEN" {
return "DRAFT"
@ -25,6 +27,7 @@ func PrStateWithDraft(pr *api.PullRequest) string {
return pr.State
}
// ColorForPRState returns a color name suitable for the given pull request's state and draft status.
func ColorForPRState(pr api.PullRequest) string {
switch pr.State {
case "OPEN":
@ -41,6 +44,7 @@ func ColorForPRState(pr api.PullRequest) string {
}
}
// ColorForIssueState returns a color name suitable for the given issue's state and state reason.
func ColorForIssueState(issue api.Issue) string {
switch issue.State {
case "OPEN":
@ -55,14 +59,17 @@ func ColorForIssueState(issue api.Issue) string {
}
}
// PrintHeader prints a bold header line to the IOStreams output.
func PrintHeader(io *iostreams.IOStreams, s string) {
fmt.Fprintln(io.Out, io.ColorScheme().Bold(s))
}
// PrintMessage prints a muted message line to the IOStreams output.
func PrintMessage(io *iostreams.IOStreams, s string) {
fmt.Fprintln(io.Out, io.ColorScheme().Muted(s))
}
// ListNoResults returns a NoResultsError with a message appropriate for whether filters were applied.
func ListNoResults(repoName string, itemName string, hasFilters bool) error {
if hasFilters {
return cmdutil.NewNoResultsError(fmt.Sprintf("no %ss match your search in %s", itemName, repoName))
@ -70,6 +77,7 @@ func ListNoResults(repoName string, itemName string, hasFilters bool) error {
return cmdutil.NewNoResultsError(fmt.Sprintf("no open %ss in %s", itemName, repoName))
}
// ListHeader returns a summary string describing how many items are shown out of the total, with filter context.
func ListHeader(repoName string, itemName string, matchCount int, totalMatchCount int, hasFilters bool) string {
if hasFilters {
matchVerb := "match"
@ -82,6 +90,7 @@ func ListHeader(repoName string, itemName string, matchCount int, totalMatchCoun
return fmt.Sprintf("Showing %d of %s in %s", matchCount, text.Pluralize(totalMatchCount, fmt.Sprintf("open %s", itemName)), repoName)
}
// PrCheckStatusSummaryWithColor returns a colorized one-line summary of a pull request's CI check status.
func PrCheckStatusSummaryWithColor(cs *iostreams.ColorScheme, checks api.PullRequestChecksStatus) string {
var summary = cs.Muted("No checks")
if checks.Total > 0 {

View file

@ -10,6 +10,7 @@ import (
"github.com/cli/cli/v2/pkg/set"
)
// Editable collects the editable fields of an issue or pull request for interactive or flag-driven editing.
type Editable struct {
Title EditableString
Body EditableString
@ -24,6 +25,7 @@ type Editable struct {
Metadata api.RepoMetadataResult
}
// EditableString holds a single string field that can be edited, along with its default value and valid options.
type EditableString struct {
Value string
Default string
@ -31,6 +33,7 @@ type EditableString struct {
Edited bool
}
// EditableSlice holds a list field that supports adding and removing values, along with defaults and valid options.
type EditableSlice struct {
Value []string
Add []string
@ -62,6 +65,7 @@ type EditableProjects struct {
ProjectItems map[string]string
}
// Dirty reports whether any field in the Editable has been modified.
func (e Editable) Dirty() bool {
return e.Title.Edited ||
e.Body.Edited ||
@ -73,6 +77,7 @@ func (e Editable) Dirty() bool {
e.Milestone.Edited
}
// TitleValue returns a pointer to the edited title, or nil if the title was not edited.
func (e Editable) TitleValue() *string {
if !e.Title.Edited {
return nil
@ -80,6 +85,7 @@ func (e Editable) TitleValue() *string {
return &e.Title.Value
}
// BodyValue returns a pointer to the edited body, or nil if the body was not edited.
func (e Editable) BodyValue() *string {
if !e.Body.Edited {
return nil
@ -87,6 +93,7 @@ func (e Editable) BodyValue() *string {
return &e.Body.Value
}
// AssigneeIds resolves the edited assignees to their node IDs, applying any add/remove modifications.
func (e Editable) AssigneeIds(client *api.Client, repo ghrepo.Interface) (*[]string, error) {
if !e.Assignees.Edited {
return nil, nil
@ -208,6 +215,7 @@ func (e Editable) ProjectV2Ids() (*[]string, *[]string, error) {
return &addIds, &removeIds, nil
}
// MilestoneId resolves the edited milestone title to its node ID, or returns an empty string to clear it.
func (e Editable) MilestoneId() (*string, error) {
if !e.Milestone.Edited {
return nil, nil
@ -288,6 +296,7 @@ func (ep *EditableProjects) clone() EditableProjects {
}
}
// EditPrompter defines the prompting interface used by interactive editing surveys.
type EditPrompter interface {
Select(string, string, []string) (int, error)
Input(string, string) (string, error)
@ -297,6 +306,7 @@ type EditPrompter interface {
Confirm(string, bool) (bool, error)
}
// EditFieldsSurvey interactively prompts the user to provide values for each edited field and confirms submission.
func EditFieldsSurvey(p EditPrompter, editable *Editable, editorCommand string) error {
var err error
if editable.Title.Edited {
@ -396,6 +406,7 @@ func EditFieldsSurvey(p EditPrompter, editable *Editable, editorCommand string)
return nil
}
// FieldsToEditSurvey prompts the user to select which fields they want to edit and marks them on the Editable.
func FieldsToEditSurvey(p EditPrompter, editable *Editable) error {
contains := func(s []string, str string) bool {
for _, v := range s {
@ -441,6 +452,7 @@ func FieldsToEditSurvey(p EditPrompter, editable *Editable) error {
return nil
}
// FetchOptions retrieves repository metadata (reviewers, assignees, labels, projects, milestones) needed for editing.
func FetchOptions(client *api.Client, repo ghrepo.Interface, editable *Editable, projectV1Support gh.ProjectsV1Support) error {
// Determine whether to fetch organization teams and reviewers.
// Interactive reviewer editing (Edited true, but no Add/Remove slices) still needs

View file

@ -9,6 +9,7 @@ import (
"golang.org/x/sync/errgroup"
)
// UpdateIssue applies the edited fields to an issue or pull request via the GitHub API, handling labels, projects, and other fields concurrently.
func UpdateIssue(httpClient *http.Client, repo ghrepo.Interface, id string, isPR bool, options Editable) error {
var wg errgroup.Group

View file

@ -36,6 +36,7 @@ func NewQualifiedHeadRef(owner string, branchName string) QualifiedHeadRef {
}
}
// NewQualifiedHeadRefWithoutOwner creates a QualifiedHeadRef with only a branch name and no owner.
func NewQualifiedHeadRefWithoutOwner(branchName string) QualifiedHeadRef {
return QualifiedHeadRef{
owner: o.None[string](),
@ -74,6 +75,7 @@ func (r QualifiedHeadRef) String() string {
return r.branchName
}
// BranchName returns the branch name component of the qualified head ref.
func (r QualifiedHeadRef) BranchName() string {
return r.branchName
}
@ -97,6 +99,7 @@ func (r PRFindRefs) QualifiedHeadRef() string {
return r.qualifiedHeadRef.String()
}
// UnqualifiedHeadRef returns the branch name without any owner prefix.
func (r PRFindRefs) UnqualifiedHeadRef() string {
return r.qualifiedHeadRef.BranchName()
}
@ -109,10 +112,12 @@ func (r PRFindRefs) Matches(baseBranchName, qualifiedHeadRef string) bool {
return headMatches && baseMatches
}
// BaseRepo returns the base repository used for finding the pull request.
func (r PRFindRefs) BaseRepo() ghrepo.Interface {
return r.baseRepo
}
// RemoteNameToRepoFn is a function type that resolves a git remote name to a repository interface.
type RemoteNameToRepoFn func(remoteName string) (ghrepo.Interface, error)
// PullRequestFindRefsResolver interrogates git configuration to try and determine
@ -122,6 +127,7 @@ type PullRequestFindRefsResolver struct {
RemoteNameToRepoFn RemoteNameToRepoFn
}
// NewPullRequestFindRefsResolver creates a PullRequestFindRefsResolver with the given git config client and remotes function.
func NewPullRequestFindRefsResolver(gitConfigClient GitConfigClient, remotesFn func() (ghContext.Remotes, error)) PullRequestFindRefsResolver {
return PullRequestFindRefsResolver{
GitConfigClient: gitConfigClient,
@ -245,6 +251,7 @@ type remoteToRepoResolver struct {
remoteNameToRepo RemoteNameToRepoFn
}
// NewRemoteToRepoResolver creates a remoteToRepoResolver that maps git remotes to repository interfaces.
func NewRemoteToRepoResolver(remotesFn func() (ghContext.Remotes, error)) remoteToRepoResolver {
return remoteToRepoResolver{
remoteNameToRepo: newRemoteNameToRepoFn(remotesFn),

View file

@ -26,6 +26,7 @@ import (
"golang.org/x/sync/errgroup"
)
// PRFinder finds a pull request by selector, returning the PR and its base repository.
type PRFinder interface {
Find(opts FindOptions) (*api.PullRequest, ghrepo.Interface, error)
}
@ -35,6 +36,7 @@ type progressIndicator interface {
StopProgressIndicator()
}
// GitConfigClient provides access to git branch configuration and push settings.
type GitConfigClient interface {
ReadBranchConfig(ctx context.Context, branchName string) (git.BranchConfig, error)
PushDefault(ctx context.Context) (git.PushDefault, error)
@ -55,6 +57,7 @@ type finder struct {
branchName string
}
// NewFinder creates a PRFinder backed by the given factory, or returns a test stub if one has been set.
func NewFinder(factory *cmdutil.Factory) PRFinder {
if finderForRunCommandStyleTests != nil {
f := finderForRunCommandStyleTests
@ -92,6 +95,7 @@ func StubFinderForRunCommandStyleTests(t *testing.T, selector string, pr *api.Pu
return finder
}
// FindOptions specifies the parameters for finding a pull request.
type FindOptions struct {
// Selector can be a number with optional `#` prefix, a branch name with optional `<owner>:` prefix, or
// a PR URL.
@ -108,6 +112,7 @@ type FindOptions struct {
Detector fd.Detector
}
// Find locates a pull request by number, URL, or branch name, fetching the requested GraphQL fields.
func (f *finder) Find(opts FindOptions) (*api.PullRequest, ghrepo.Interface, error) {
// If we have a URL, we don't need git stuff
if len(opts.Fields) == 0 {
@ -619,14 +624,17 @@ func preloadPrChecks(client *http.Client, repo ghrepo.Interface, pr *api.PullReq
return nil
}
// NotFoundError indicates that no pull request matching the query was found.
type NotFoundError struct {
error
}
// Unwrap returns the underlying error wrapped by NotFoundError.
func (err *NotFoundError) Unwrap() error {
return err.error
}
// NewMockFinder creates a mockFinder for use in tests, returning the given PR and repo when Find is called.
func NewMockFinder(selector string, pr *api.PullRequest, repo ghrepo.Interface) *mockFinder {
var err error
if pr == nil {
@ -649,6 +657,7 @@ type mockFinder struct {
err error
}
// Find returns the mocked pull request and repository, validating the selector and fields expectations.
func (m *mockFinder) Find(opts FindOptions) (*api.PullRequest, ghrepo.Interface, error) {
if m.err != nil {
return nil, nil, m.err
@ -672,6 +681,7 @@ func (m *mockFinder) Find(opts FindOptions) (*api.PullRequest, ghrepo.Interface,
return m.pr, m.repo, nil
}
// ExpectFields sets the expected GraphQL fields that Find must be called with.
func (m *mockFinder) ExpectFields(fields []string) {
m.expectFields = fields
}

View file

@ -8,11 +8,13 @@ import (
var _ GitConfigClient = &CachedBranchConfigGitConfigClient{}
// CachedBranchConfigGitConfigClient wraps a GitConfigClient to return a pre-fetched BranchConfig instead of querying git.
type CachedBranchConfigGitConfigClient struct {
CachedBranchConfig git.BranchConfig
GitConfigClient
}
// ReadBranchConfig returns the cached BranchConfig, ignoring the branch name argument.
func (c CachedBranchConfigGitConfigClient) ReadBranchConfig(ctx context.Context, branchName string) (git.BranchConfig, error) {
return c.CachedBranchConfig, nil
}

View file

@ -9,10 +9,12 @@ import (
api "github.com/cli/cli/v2/api"
)
// PRLister is the interface for listing pull requests from a repository.
type PRLister interface {
List(opt ListOptions) (*api.PullRequestAndTotalCount, error)
}
// ListOptions specifies filtering and pagination options for listing pull requests.
type ListOptions struct {
BaseRepo ghrepo.Interface
@ -29,12 +31,14 @@ type lister struct {
httpClient *http.Client
}
// NewLister creates a PRLister that fetches pull requests via the GitHub GraphQL API.
func NewLister(httpClient *http.Client) PRLister {
return &lister{
httpClient: httpClient,
}
}
// List retrieves pull requests from the repository matching the given options.
func (l *lister) List(opts ListOptions) (*api.PullRequestAndTotalCount, error) {
type response struct {
Repository struct {
@ -153,6 +157,7 @@ type mockLister struct {
err error
}
// NewMockLister creates a mock PRLister that returns the given result and error for testing.
func NewMockLister(result *api.PullRequestAndTotalCount, err error) *mockLister {
return &mockLister{
result: result,
@ -160,6 +165,7 @@ func NewMockLister(result *api.PullRequestAndTotalCount, err error) *mockLister
}
}
// List returns the preconfigured result and error, validating expected fields if set.
func (m *mockLister) List(opt ListOptions) (*api.PullRequestAndTotalCount, error) {
m.called = true
if m.err != nil {
@ -176,6 +182,7 @@ func (m *mockLister) List(opt ListOptions) (*api.PullRequestAndTotalCount, error
return m.result, m.err
}
// ExpectFields sets the GraphQL fields that the mock expects to receive in List calls.
func (m *mockLister) ExpectFields(fields []string) {
m.expectFields = fields
}

View file

@ -13,6 +13,7 @@ import (
"github.com/google/shlex"
)
// WithPrAndIssueQueryParams appends issue or pull request metadata from state as query parameters to the given URL.
func WithPrAndIssueQueryParams(client *api.Client, baseRepo ghrepo.Interface, baseURL string, state IssueMetadataState, projectsV1Support gh.ProjectsV1Support) (string, error) {
u, err := url.Parse(baseURL)
if err != nil {
@ -56,6 +57,7 @@ func ValidURL(urlStr string) bool {
return len(urlStr) < 8192
}
// AddMetadataToIssueParams resolves metadata such as assignees, labels, projects, milestones, and reviewers into IDs and adds them to the params map.
func AddMetadataToIssueParams(client *api.Client, baseRepo ghrepo.Interface, params map[string]interface{}, tb *IssueMetadataState, projectV1Support gh.ProjectsV1Support) error {
if !tb.HasMetadata() {
return nil
@ -139,6 +141,7 @@ func AddMetadataToIssueParams(client *api.Client, baseRepo ghrepo.Interface, par
return nil
}
// FilterOptions holds the filtering criteria used when listing issues or pull requests.
type FilterOptions struct {
Assignee string
Author string
@ -155,6 +158,7 @@ type FilterOptions struct {
State string
}
// IsDefault reports whether the filter options represent the default state with no custom filters applied.
func (opts *FilterOptions) IsDefault() bool {
if opts.State != "open" {
return false
@ -186,6 +190,7 @@ func (opts *FilterOptions) IsDefault() bool {
return true
}
// ListURLWithQuery builds a URL by appending a search query derived from FilterOptions to the given list URL.
func ListURLWithQuery(listURL string, options FilterOptions, advancedIssueSearchSyntax bool) (string, error) {
u, err := url.Parse(listURL)
if err != nil {
@ -199,6 +204,7 @@ func ListURLWithQuery(listURL string, options FilterOptions, advancedIssueSearch
return u.String(), nil
}
// SearchQueryBuild constructs a GitHub search query string from the given FilterOptions.
func SearchQueryBuild(options FilterOptions, advancedIssueSearchSyntax bool) string {
var is, state string
switch options.State {
@ -231,6 +237,7 @@ func SearchQueryBuild(options FilterOptions, advancedIssueSearchSyntax bool) str
return query.AdvancedIssueSearchString()
}
// QueryHasStateClause reports whether the search query contains an explicit state or merged filter clause.
func QueryHasStateClause(searchQuery string) bool {
argv, err := shlex.Split(searchQuery)
if err != nil {
@ -253,6 +260,7 @@ type MeReplacer struct {
login string
}
// NewMeReplacer creates a MeReplacer that resolves @me to the currently authenticated user's login.
func NewMeReplacer(apiClient *api.Client, hostname string) *MeReplacer {
return &MeReplacer{
apiClient: apiClient,
@ -272,6 +280,7 @@ func (r *MeReplacer) currentLogin() (string, error) {
return login, nil
}
// Replace substitutes "@me" with the current user's login, returning other handles unchanged.
func (r *MeReplacer) Replace(handle string) (string, error) {
if handle == "@me" {
return r.currentLogin()
@ -279,6 +288,7 @@ func (r *MeReplacer) Replace(handle string) (string, error) {
return handle, nil
}
// ReplaceSlice applies Replace to each element in the slice, substituting any "@me" occurrences.
func (r *MeReplacer) ReplaceSlice(handles []string) ([]string, error) {
res := make([]string, len(handles))
for i, h := range handles {

View file

@ -9,6 +9,7 @@ import (
"github.com/cli/cli/v2/pkg/iostreams"
)
// PreserveInput returns a cleanup function that saves issue or PR metadata state to a temporary file when a creation error occurs, enabling recovery with --recover.
func PreserveInput(io *iostreams.IOStreams, state *IssueMetadataState, createErr *error) func() {
return func() {
if !state.IsDirty() {

View file

@ -7,6 +7,7 @@ import (
"github.com/cli/cli/v2/api"
)
// ReactionGroupList formats a list of reaction groups into a human-readable string with emoji and counts.
func ReactionGroupList(rgs api.ReactionGroups) string {
var rs []string

View file

@ -11,10 +11,13 @@ import (
type metadataStateType int
const (
// IssueMetadata indicates that the metadata state is for an issue.
IssueMetadata metadataStateType = iota
// PRMetadata indicates that the metadata state is for a pull request.
PRMetadata
)
// IssueMetadataState holds the editable metadata for an issue or pull request during creation or editing.
type IssueMetadataState struct {
Type metadataStateType
@ -38,14 +41,17 @@ type IssueMetadataState struct {
dirty bool // whether user i/o has modified this
}
// MarkDirty flags the state as having been modified by user input.
func (tb *IssueMetadataState) MarkDirty() {
tb.dirty = true
}
// IsDirty reports whether the state has been modified by user input or has metadata set.
func (tb *IssueMetadataState) IsDirty() bool {
return tb.dirty || tb.HasMetadata()
}
// HasMetadata reports whether any reviewers, assignees, labels, projects, or milestones are set.
func (tb *IssueMetadataState) HasMetadata() bool {
return len(tb.Reviewers) > 0 ||
len(tb.Assignees) > 0 ||
@ -54,6 +60,7 @@ func (tb *IssueMetadataState) HasMetadata() bool {
len(tb.Milestones) > 0
}
// FillFromJSON populates the given IssueMetadataState by reading and unmarshaling a JSON recovery file.
func FillFromJSON(io *iostreams.IOStreams, recoverFile string, state *IssueMetadataState) error {
var data []byte
var err error

View file

@ -15,15 +15,23 @@ import (
"github.com/cli/cli/v2/pkg/surveyext"
)
// Action represents a user-selected action in a submission prompt.
type Action int
const (
// SubmitAction indicates the user chose to submit.
SubmitAction Action = iota
// PreviewAction indicates the user chose to continue in the browser.
PreviewAction
// CancelAction indicates the user cancelled the operation.
CancelAction
// MetadataAction indicates the user chose to add metadata.
MetadataAction
// EditCommitMessageAction indicates the user chose to edit the commit message.
EditCommitMessageAction
// EditCommitSubjectAction indicates the user chose to edit the commit subject.
EditCommitSubjectAction
// SubmitDraftAction indicates the user chose to submit as a draft.
SubmitDraftAction
noMilestone = "(none)"
@ -35,6 +43,7 @@ const (
cancelLabel = "Cancel"
)
// Prompt defines the interface for interactive user prompts used during issue and PR creation.
type Prompt interface {
Input(prompt string, defaultValue string) (string, error)
Select(prompt string, defaultValue string, options []string) (int, error)
@ -44,10 +53,12 @@ type Prompt interface {
MultiSelectWithSearch(prompt, searchPrompt string, defaults []string, persistentOptions []string, searchFunc func(string) prompter.MultiSelectSearchResult) ([]string, error)
}
// ConfirmIssueSubmission prompts the user to select a submission action for an issue.
func ConfirmIssueSubmission(p Prompt, allowPreview bool, allowMetadata bool) (Action, error) {
return confirmSubmission(p, allowPreview, allowMetadata, false, false)
}
// ConfirmPRSubmission prompts the user to select a submission action for a pull request.
func ConfirmPRSubmission(p Prompt, allowPreview, allowMetadata, isDraft bool) (Action, error) {
return confirmSubmission(p, allowPreview, allowMetadata, true, isDraft)
}
@ -89,6 +100,7 @@ func confirmSubmission(p Prompt, allowPreview, allowMetadata, allowDraft, isDraf
}
}
// BodySurvey prompts the user to edit the body of an issue or pull request using a markdown editor.
func BodySurvey(p Prompt, state *IssueMetadataState, templateContent string) error {
if templateContent != "" {
if state.Body != "" {
@ -113,6 +125,7 @@ func BodySurvey(p Prompt, state *IssueMetadataState, templateContent string) err
return nil
}
// TitleSurvey prompts the user to enter a required title for an issue or pull request.
func TitleSurvey(p Prompt, io *iostreams.IOStreams, state *IssueMetadataState) error {
var err error
result := ""
@ -135,6 +148,7 @@ func TitleSurvey(p Prompt, io *iostreams.IOStreams, state *IssueMetadataState) e
return nil
}
// MetadataFetcher fetches repository metadata such as assignees, labels, and projects for use in interactive prompts.
type MetadataFetcher struct {
IO *iostreams.IOStreams
APIClient *api.Client
@ -142,6 +156,7 @@ type MetadataFetcher struct {
State *IssueMetadataState
}
// RepoMetadataFetch retrieves repository metadata based on the given input and caches the result in State.
func (mf *MetadataFetcher) RepoMetadataFetch(input api.RepoMetadataInput) (*api.RepoMetadataResult, error) {
mf.IO.StartProgressIndicator()
metadataResult, err := api.RepoMetadata(mf.APIClient, mf.Repo, input)
@ -150,10 +165,12 @@ func (mf *MetadataFetcher) RepoMetadataFetch(input api.RepoMetadataInput) (*api.
return metadataResult, err
}
// RepoMetadataFetcher is the interface for fetching repository metadata used during metadata surveys.
type RepoMetadataFetcher interface {
RepoMetadataFetch(api.RepoMetadataInput) (*api.RepoMetadataResult, error)
}
// MetadataSurvey interactively prompts the user to select and set metadata such as reviewers, assignees, labels, projects, and milestones.
func MetadataSurvey(p Prompt, io *iostreams.IOStreams, baseRepo ghrepo.Interface, fetcher RepoMetadataFetcher, state *IssueMetadataState, projectsV1Support gh.ProjectsV1Support) error {
isChosen := func(m string) bool {
for _, c := range state.Metadata {
@ -360,15 +377,18 @@ func MetadataSurvey(p Prompt, io *iostreams.IOStreams, baseRepo ghrepo.Interface
return nil
}
// Editor is the interface for launching an external text editor.
type Editor interface {
Edit(filename, initialValue string) (string, error)
}
// UserEditor opens the user's preferred text editor for editing content.
type UserEditor struct {
IO *iostreams.IOStreams
Config func() (gh.Config, error)
}
// Edit opens the user's configured editor with the given filename and initial content, returning the edited result.
func (e *UserEditor) Edit(filename, initialValue string) (string, error) {
editorCommand, err := cmdutil.DetermineEditor(e.Config)
if err != nil {
@ -382,6 +402,7 @@ const editorHint = `
Please Enter the title on the first line and the body on subsequent lines.
Lines below dotted lines will be ignored, and an empty title aborts the creation process.`
// TitledEditSurvey returns a function that opens an editor for the user to provide a title and body.
func TitledEditSurvey(editor Editor) func(string, string) (string, string, error) {
return func(initialTitle, initialBody string) (string, string, error) {
initialValue := strings.Join([]string{initialTitle, initialBody, editorHintMarker, editorHint}, "\n")
@ -397,6 +418,7 @@ func TitledEditSurvey(editor Editor) func(string, string) (string, string, error
}
}
// InitEditorMode determines whether editor mode should be enabled based on flags and user configuration.
func InitEditorMode(f *cmdutil.Factory, editorMode bool, webMode bool, canPrompt bool) (bool, error) {
if err := cmdutil.MutuallyExclusive(
"specify only one of `--editor` or `--web`",

View file

@ -26,34 +26,42 @@ type pullRequestTemplate struct {
Gbody string `graphql:"body"`
}
// Name returns the display name of the issue template.
func (t *issueTemplate) Name() string {
return t.Gname
}
// NameForSubmit returns the template name to include when submitting an issue.
func (t *issueTemplate) NameForSubmit() string {
return t.Gname
}
// Body returns the content of the issue template as bytes.
func (t *issueTemplate) Body() []byte {
return []byte(t.Gbody)
}
// Title returns the default title of the issue template.
func (t *issueTemplate) Title() string {
return t.Gtitle
}
// Name returns the filename of the pull request template.
func (t *pullRequestTemplate) Name() string {
return t.Gname
}
// NameForSubmit returns an empty string since pull request templates do not have a submission name.
func (t *pullRequestTemplate) NameForSubmit() string {
return ""
}
// Body returns the content of the pull request template as bytes.
func (t *pullRequestTemplate) Body() []byte {
return []byte(t.Gbody)
}
// Title returns an empty string since pull request templates do not have a default title.
func (t *pullRequestTemplate) Title() string {
return ""
}
@ -114,6 +122,7 @@ func listPullRequestTemplates(httpClient *http.Client, repo ghrepo.Interface) ([
return templates, nil
}
// Template defines the interface for issue and pull request templates.
type Template interface {
Name() string
NameForSubmit() string
@ -141,6 +150,7 @@ type templateManager struct {
fetchError error
}
// NewTemplateManager creates a templateManager that discovers issue or PR templates from the API and optionally the filesystem.
func NewTemplateManager(httpClient *http.Client, repo ghrepo.Interface, p iprompter, dir string, allowFS bool, isPR bool) *templateManager {
cachedClient := api.NewCachedHTTPClient(httpClient, time.Hour*24)
return &templateManager{
@ -167,6 +177,7 @@ func (m *templateManager) hasAPI() (bool, error) {
return features.PullRequestTemplateQuery, nil
}
// HasTemplates reports whether any templates are available for the repository.
func (m *templateManager) HasTemplates() (bool, error) {
if err := m.memoizedFetch(); err != nil {
return false, err
@ -174,6 +185,7 @@ func (m *templateManager) HasTemplates() (bool, error) {
return len(m.templates) > 0, nil
}
// LegacyBody returns the body of the legacy template, or nil if no legacy template exists.
func (m *templateManager) LegacyBody() []byte {
if m.legacyTemplate == nil {
return nil
@ -181,6 +193,7 @@ func (m *templateManager) LegacyBody() []byte {
return m.legacyTemplate.Body()
}
// Choose prompts the user to interactively select a template from the available options.
func (m *templateManager) Choose() (Template, error) {
if err := m.memoizedFetch(); err != nil {
return nil, err
@ -210,6 +223,7 @@ func (m *templateManager) Choose() (Template, error) {
return m.templates[selectedOption], nil
}
// Select returns the template matching the given name, or an error if not found.
func (m *templateManager) Select(name string) (Template, error) {
if err := m.memoizedFetch(); err != nil {
return nil, err
@ -294,18 +308,22 @@ type filesystemTemplate struct {
path string
}
// Name returns the display name extracted from the filesystem template file.
func (t *filesystemTemplate) Name() string {
return githubtemplate.ExtractName(t.path)
}
// NameForSubmit returns an empty string since filesystem templates do not have a submission name.
func (t *filesystemTemplate) NameForSubmit() string {
return ""
}
// Body returns the contents of the filesystem template file.
func (t *filesystemTemplate) Body() []byte {
return githubtemplate.ExtractContents(t.path)
}
// Title returns the title extracted from the filesystem template file.
func (t *filesystemTemplate) Title() string {
return githubtemplate.ExtractTitle(t.path)
}

View file

@ -23,6 +23,7 @@ import (
"github.com/spf13/cobra"
)
// StatusOptions holds the configuration for the pr status command.
type StatusOptions struct {
HttpClient func() (*http.Client, error)
GitClient *git.Client
@ -39,6 +40,7 @@ type StatusOptions struct {
Detector fd.Detector
}
// NewCmdStatus creates the cobra command for showing the status of relevant pull requests.
func NewCmdStatus(f *cmdutil.Factory, runF func(*StatusOptions) error) *cobra.Command {
opts := &StatusOptions{
IO: f.IOStreams,

View file

@ -16,6 +16,7 @@ import (
"github.com/spf13/cobra"
)
// UpdateBranchOptions holds the configuration for the pr update-branch command.
type UpdateBranchOptions struct {
HttpClient func() (*http.Client, error)
IO *iostreams.IOStreams
@ -27,6 +28,7 @@ type UpdateBranchOptions struct {
Rebase bool
}
// NewCmdUpdateBranch creates the cobra command for updating a pull request branch with the latest base branch changes.
func NewCmdUpdateBranch(f *cmdutil.Factory, runF func(*UpdateBranchOptions) error) *cobra.Command {
opts := &UpdateBranchOptions{
IO: f.IOStreams,

View file

@ -20,6 +20,7 @@ import (
"github.com/spf13/cobra"
)
// ViewOptions holds the configuration for the pr view command.
type ViewOptions struct {
IO *iostreams.IOStreams
Browser browser.Browser
@ -37,6 +38,7 @@ type ViewOptions struct {
Now func() time.Time
}
// NewCmdView creates the cobra command for viewing a pull request's details.
func NewCmdView(f *cmdutil.Factory, runF func(*ViewOptions) error) *cobra.Command {
opts := &ViewOptions{
IO: f.IOStreams,