package cmd import ( "errors" "fmt" "os" "strconv" "strings" "gitea.com/jolheiser/sip/modules/csv" "gitea.com/jolheiser/sip/modules/markdown" "gitea.com/jolheiser/sip/modules/sdk" "code.gitea.io/sdk/gitea" "github.com/AlecAivazis/survey/v2" "github.com/urfave/cli/v2" "go.jolheiser.com/beaver" "go.jolheiser.com/beaver/color" ) var Issues = cli.Command{ Name: "issues", Aliases: []string{"issue"}, Usage: "Commands for interacting with issues", Action: doIssuesSearch, Subcommands: []*cli.Command{ &IssuesCreate, }, Flags: []cli.Flag{ &cli.StringFlag{ Name: "csv", Usage: "Output results to a CSV file", }, }, } func doIssuesSearch(ctx *cli.Context) error { if _, err := issuesSearch(ctx, false); err != nil { return err } return nil } func issuesSearch(ctx *cli.Context, pulls bool) (*gitea.Issue, error) { client, err := getClient(ctx, false) if err != nil { return nil, err } typ := "issues" if pulls { typ = "pulls" } issues, err := queryIssues(ctx, client, pulls) if err != nil { return nil, err } if len(issues) == 0 { beaver.Errorf("No %s found!", typ) return nil, nil } if ctx.String("csv") != "" { fi, err := os.Create(ctx.String("csv")) if err != nil { return nil, err } if _, err := fi.WriteString(csv.Issues(issues)); err != nil { return nil, err } fmt.Println(color.FgCyan.Formatf("Matching %s were exported to", typ), color.Info.Format(ctx.String("csv"))) return nil, fi.Close() } issueMap := make(map[string]*gitea.Issue) for _, issue := range issues { index := color.New(color.FgCyan).Format("#" + strconv.Itoa(int(issue.Index))) title := color.New(color.FgYellow).Format(issue.Title) state := color.New(color.FgGreen).Format("[open]") if issue.PullRequest != nil && issue.PullRequest.HasMerged { state = color.New(color.FgMagenta).Format("[merged]") } else if issue.State == gitea.StateClosed { state = color.New(color.FgRed).Format("[closed]") } lbls := make([]string, len(issue.Labels)) for idx, label := range issue.Labels { lbls[idx] = label.Name } var labels string if len(lbls) > 0 { labels = color.New(color.FgHiBlack).Format("(" + strings.Join(lbls, ", ") + ")") } issueMap[fmt.Sprintf("%s %s %s %s", index, state, title, labels)] = issue } list := make([]string, 0) for key := range issueMap { list = append(list, key) } sel := &survey.Select{Options: list, Message: "Matching " + typ} var selection string if err := survey.AskOne(sel, &selection); err != nil { return nil, err } body, err := markdown.Render(issueMap[selection].Body) if err != nil { return nil, err } fmt.Println(body) return issueMap[selection], nil } func queryIssues(ctx *cli.Context, client *gitea.Client, pulls bool) ([]*gitea.Issue, error) { owner, repo, err := askOwnerRepo(ctx) if err != nil { return nil, err } question := &survey.Input{Message: "Search query"} var answer string if err := survey.AskOne(question, &answer); err != nil { return nil, err } filter := sdk.NewIssueFilter(answer) opts := gitea.ListIssueOption{KeyWord: filter.Query, State: "all"} issues, err := sdk.GetIssues(client, owner, repo, opts) if err != nil { return nil, err } filtered := make([]*gitea.Issue, 0) for _, issue := range issues { // Filter out issues if searching PRs and vice-versa if (pulls && issue.PullRequest == nil) || (!pulls && issue.PullRequest != nil) { continue } if !filter.Match(issue) { continue } filtered = append(filtered, issue) } return filtered, nil } func askOwnerRepo(ctx *cli.Context) (string, string, error) { question := []*survey.Question{ { Name: "repo", Prompt: &survey.Input{Message: "Full repository name", Default: fullName(ctx)}, Validate: validateFullName, }, } answer := struct { Repo string }{} if err := survey.Ask(question, &answer); err != nil { return "", "", err } ownerRepo := strings.Split(answer.Repo, "/") return ownerRepo[0], ownerRepo[1], nil } func validateFullName(ans interface{}) error { fullName := ans.(string) ownerRepo := strings.Split(fullName, "/") if len(ownerRepo) != 2 { return errors.New("full repo name should be in form `owner/repo`") } return nil }