cli

package
v0.5.0 Latest Latest
Warning

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

Go to latest
Published: Sep 23, 2026 License: GPL-3.0 Imports: 17 Imported by: 0

Documentation

Overview

Package cli implements the ghp-sync command line interface.

Index

Constants

This section is empty.

Variables

View Source
var PRFields = map[string]PRFieldDef{
	"PR#": {
		Type: gh.ItemValueTypeNumber,
		ComputeFn: func(ctx PRFieldContext) any {
			return ctx.PR.Number
		},
	},
	"Status": {
		Type: gh.ItemValueTypeSingleSelect,
		ComputeFn: func(ctx PRFieldContext) any {
			id, ok := ctx.Project.StatusIDs[ctx.Status]
			if !ok || id == "" {
				fmt.Printf("WARNING: status %q not found in project\n", ctx.Status)
				return nil
			}
			return id
		},
	},
	"User": {
		Type: gh.ItemValueTypeText,
		ComputeFn: func(ctx PRFieldContext) any {
			return ctx.PR.Author
		},
	},
	"Reviewed By": {
		Type: gh.ItemValueTypeText,
		ComputeFn: func(ctx PRFieldContext) any {
			if len(ctx.PR.ReviewedBy) == 0 {
				return nil
			}
			return strings.Join(ctx.PR.ReviewedBy, ", ")
		},
	},
	"Approved By": {
		Type: gh.ItemValueTypeText,
		ComputeFn: func(ctx PRFieldContext) any {
			if len(ctx.PR.ApprovedBy) == 0 {
				return nil
			}
			return strings.Join(ctx.PR.ApprovedBy, ", ")
		},
	},
	"Changes Requested By": {
		Type: gh.ItemValueTypeText,
		ComputeFn: func(ctx PRFieldContext) any {
			if len(ctx.PR.ChangesRequestedBy) == 0 {
				return nil
			}
			parts := make([]string, 0, len(ctx.PR.ChangesRequestedBy))
			for _, reviewer := range ctx.PR.ChangesRequestedBy {
				parts = append(parts, fmt.Sprintf("%s(×%d ✎%d)", reviewer.Login, reviewer.Requests, reviewer.Comments))
			}
			return strings.Join(parts, ", ")
		},
	},
	"Merged By": {
		Type: gh.ItemValueTypeText,
		ComputeFn: func(ctx PRFieldContext) any {
			if ctx.PR.MergedBy == "" {
				return nil
			}
			return ctx.PR.MergedBy
		},
	},
	"CI": {
		Type: gh.ItemValueTypeText,
		ComputeFn: func(ctx PRFieldContext) any {
			if !strings.EqualFold(ctx.PR.State, "open") {
				return ""
			}

			switch ctx.PR.CheckState {
			case "FAILURE", "ERROR":
				return "❌"
			case "PENDING", "EXPECTED":
				return "🕒"
			case "SUCCESS":
				return "✅"
			default:
				return ""
			}
		},
	},
	"Mergeable": {
		Type: gh.ItemValueTypeText,
		ComputeFn: func(ctx PRFieldContext) any {
			if !strings.EqualFold(ctx.PR.State, "open") {
				return ""
			}

			switch ctx.PR.Mergeable {
			case "CONFLICTING":
				return "❌"
			case "MERGEABLE":
				return "✅"
			default:
				return "?"
			}
		},
	},
	"Open Days": {
		Type: gh.ItemValueTypeNumber,
		ComputeFn: func(ctx PRFieldContext) any {
			return ctx.DaysOpen
		},
	},
	"Waiting Days": {
		Type: gh.ItemValueTypeNumber,
		ComputeFn: func(ctx PRFieldContext) any {
			return ctx.DaysWaiting
		},
	},
	"Comment Count": {
		Type: gh.ItemValueTypeNumber,
		ComputeFn: func(ctx PRFieldContext) any {
			return ctx.PR.TotalCommentCount
		},
	},
	"Review Count": {
		Type: gh.ItemValueTypeNumber,
		ComputeFn: func(ctx PRFieldContext) any {
			return ctx.PR.TotalReviewCount
		},
	},
	"Review Comment Count": {
		Type: gh.ItemValueTypeNumber,
		ComputeFn: func(ctx PRFieldContext) any {
			return ctx.PR.ReviewCommentCount
		},
	},
	"Created At": {
		Type: gh.ItemValueTypeDate,
		ComputeFn: func(ctx PRFieldContext) any {
			return ctx.PR.CreatedAt.Format(time.RFC3339)
		},
	},
	"Closed At": {
		Type: gh.ItemValueTypeDate,
		ComputeFn: func(ctx PRFieldContext) any {
			if strings.EqualFold(ctx.PR.State, "open") {
				return nil
			}
			return ctx.PR.ClosedAt.Format(time.RFC3339)
		},
	},
	"Merged At": {
		Type: gh.ItemValueTypeDate,
		ComputeFn: func(ctx PRFieldContext) any {
			if ctx.PR.MergedAt.IsZero() {
				return nil
			}
			return ctx.PR.MergedAt.Format(time.RFC3339)
		},
	},
	"Reviewed At": {
		Type: gh.ItemValueTypeDate,
		ComputeFn: func(ctx PRFieldContext) any {
			if ctx.PR.ReviewedAt.IsZero() {
				return nil
			}
			return ctx.PR.ReviewedAt.Format(time.RFC3339)
		},
	},
	"Last Reviewer": {
		Type: gh.ItemValueTypeText,
		ComputeFn: func(ctx PRFieldContext) any {
			if ctx.PR.LastReviewer == "" {
				return nil
			}
			return ctx.PR.LastReviewer
		},
	},
	"Filtered Review Count": {
		Type: gh.ItemValueTypeNumber,
		ComputeFn: func(ctx PRFieldContext) any {
			if ctx.PR.FilteredReviewCount == 0 {
				return nil
			}
			return ctx.PR.FilteredReviewCount
		},
	},
	"Filtered Review Comment Count": {
		Type: gh.ItemValueTypeNumber,
		ComputeFn: func(ctx PRFieldContext) any {
			if ctx.PR.FilteredReviewCount == 0 {
				return nil
			}
			return ctx.PR.FilteredReviewCommentCount
		},
	},
}

PRFields is the registry of all available PR fields, keyed by field name (matches GitHub Project field name)

Functions

func CmdAdd added in v0.2.0

func CmdAdd(cmd *cobra.Command, args []string) error

func CmdIssues

func CmdIssues(_ *cobra.Command, _ []string) error

func CmdPRs

func CmdPRs(_ *cobra.Command, _ []string) error

func CmdPRsRefresh added in v0.2.0

func CmdPRsRefresh(_ *cobra.Command, _ []string) error

CmdPRsRefresh walks the project board itself and refreshes fields on PR items that are now closed or merged, rather than syncing PRs from a repo. This catches PRs that were added while open but have since closed, without crawling the repo's full PR history.

func CmdRateLimit

func CmdRateLimit(_ *cobra.Command, _ []string) error

func CmdSync

func CmdSync(_ *cobra.Command, args []string) error

func FilterByFlags

func FilterByFlags(f FlagData, prs *[]gh.PullRequest) (matched *[]gh.PullRequest, matchReasons map[int]string)

FilterByFlags returns the PRs matching the user filters along with a map of pr number -> why it matched. A nil map means no filters were active and everything was kept.

func GetStringSliceFixed

func GetStringSliceFixed(key string) []string

GetStringSliceFixed works around viper not correctly handling string slices from env vars the same way it does commandline flags, see https://github.com/spf13/viper/issues/380 env vars come through as a raw string which viper would split on whitespace, breaking values with spaces like "Approved By", so split those on commas only

func Make

func Make(cmdName string) (*cobra.Command, error)

func ValidateParams

func ValidateParams(params []string) func(cmd *cobra.Command, args []string) error

Types

type Filter

type Filter struct {
	Name  string
	Issue func(options github.Issue) (bool, error)
}

func GetFilterForAuthors

func GetFilterForAuthors(authors []string) *Filter

func GetFilterForLabels

func GetFilterForLabels(labels []string, and bool) *Filter

func GetFilterForLabelsAnd

func GetFilterForLabelsAnd(labels []string) *Filter

func GetFilterForLabelsOr

func GetFilterForLabelsOr(labels []string) *Filter

type Filters

type Filters struct {
	Authors   []string
	Assignees []string
	MergedBy  []string

	MergedSince string
	Reviewers   []string
	LabelsOr    []string
	LabelsAnd   []string
	States      []string

	ProjectStatusIs       string
	ProjectFieldPopulated []string

	FiltersOnly bool // don't auto-include prs already in the project
}

type FlagData

type FlagData struct {
	Token         string
	Repos         []string
	ProjectOwner  string
	ProjectNumber int
	ItemLimit     int
	DryRun        bool
	Strict        bool
	Filters       Filters

	// PR field population control
	PRPopulateFields []string // Only populate these fields (empty = all)
	PRSkipFields     []string // Skip these fields from population
	PRFields         []string // Resolved list of field names to populate

	// Linked issue field syncing
	SyncLinkedIssueFields []string // Copy these fields from linked issues
}

func GetFlags

func GetFlags() FlagData

func (FlagData) GetFilters

func (f FlagData) GetFilters() []Filter

type PRFieldContext

type PRFieldContext struct {
	PR          *gh.PullRequest
	Project     gh.Project
	DaysOpen    int
	DaysWaiting int
	Status      string // The computed status text (e.g., "In Progress", "Approved")
}

PRFieldContext holds all the data needed to compute field values

type PRFieldDef

type PRFieldDef struct {
	Type      gh.ItemValueType // Field type for GraphQL mutation
	ComputeFn func(ctx PRFieldContext) any
}

PRFieldDef defines a field that can be populated on a GitHub Project item

Jump to

Keyboard shortcuts

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