Add govet parsing

This commit is contained in:
Miguel de la Cruz 2020-04-27 17:47:13 +02:00
parent 4627bbbcb9
commit aad8c6b884
5 changed files with 126 additions and 52 deletions

View file

@ -5,20 +5,19 @@ import (
"encoding/csv" "encoding/csv"
"fmt" "fmt"
"os" "os"
"strconv"
"strings"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"git.ctrlz.es/mgdelacroix/campaigner/campaign" "git.ctrlz.es/mgdelacroix/campaigner/campaign"
"git.ctrlz.es/mgdelacroix/campaigner/model" "git.ctrlz.es/mgdelacroix/campaigner/model"
"git.ctrlz.es/mgdelacroix/campaigner/parsers"
) )
func GrepAddCmd() *cobra.Command { func GrepAddCmd() *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "grep", Use: "grep",
Short: "Generates the tickets reading grep's output from stdin", Short: "Generates the tickets reading grep's output from stdin",
Long: `Generates tickets for the campaign reading from the standard input the output grep. The grep command must be run with the -n flag. The generated ticket will contain three fields: Long: `Generates tickets for the campaign reading the output of grep from the standard input. The grep command must be run with the -n flag. The generated ticket will contain three fields:
- filename: the filename yield by grep - filename: the filename yield by grep
- lineNo: the line number yield by grep - lineNo: the line number yield by grep
@ -53,10 +52,15 @@ func GovetAddCmd() *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "govet", Use: "govet",
Short: "Generates the tickets reading govet's output from stdin", Short: "Generates the tickets reading govet's output from stdin",
Long: "Generates tickets for the campaign reading from the standard input the output grep. The grep command must be run with the -json flag", Long: `Generates tickets for the campaign reading the output of govet from the standard input. Govet usually writes to the standard error descriptor, so the output must be redirected. The generated ticket will contain three fields:
Example: ` govet -json ./... | campaigner add govet`,
- filename: the filename yield by grep
- lineNo: the line number yield by grep
- text: the text containing the govet error
`,
Example: ` govet ./... 2>&1 | campaigner add govet`,
Args: cobra.NoArgs, Args: cobra.NoArgs,
RunE: govetAddCmdF, Run: govetAddCmdF,
} }
cmd.Flags().BoolP("file-only", "f", false, "Generates one ticket per file instead of per match") cmd.Flags().BoolP("file-only", "f", false, "Generates one ticket per file instead of per match")
@ -91,46 +95,10 @@ func AddCmd() *cobra.Command {
return cmd return cmd
} }
func parseGrepLine(line string) (*model.Ticket, error) {
// ToDo: it would be great to be able to relate a line with its
// parent method, at least for JS and Golang
parts := strings.Split(line, ":")
if len(parts) < 3 {
return nil, fmt.Errorf("cannot parse line: %s", line)
}
filename := parts[0]
lineNo, err := strconv.Atoi(parts[1])
if err != nil {
return nil, err
}
text := strings.Join(parts[2:], "")
return &model.Ticket{
Data: map[string]interface{}{
"filename": filename,
"lineNo": lineNo,
"text": strings.TrimSpace(text),
},
}, nil
}
func parseGrep() []*model.Ticket {
tickets := []*model.Ticket{}
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
ticket, _ := parseGrepLine(scanner.Text())
if ticket != nil {
tickets = append(tickets, ticket)
}
}
return tickets
}
func grepAddCmdF(cmd *cobra.Command, _ []string) { func grepAddCmdF(cmd *cobra.Command, _ []string) {
fileOnly, _ := cmd.Flags().GetBool("file-only") fileOnly, _ := cmd.Flags().GetBool("file-only")
tickets := parseGrep() tickets := parsers.ParseWith(parsers.GREP)
cmp, err := campaign.Read() cmp, err := campaign.Read()
if err != nil { if err != nil {
@ -150,8 +118,23 @@ func agAddCmdF(_ *cobra.Command, _ []string) error {
return fmt.Errorf("not implemented yet") return fmt.Errorf("not implemented yet")
} }
func govetAddCmdF(_ *cobra.Command, _ []string) error { func govetAddCmdF(cmd *cobra.Command, _ []string) {
return fmt.Errorf("not implemented yet") fileOnly, _ := cmd.Flags().GetBool("file-only")
tickets := parsers.ParseWith(parsers.GOVET)
cmp, err := campaign.Read()
if err != nil {
ErrorAndExit(cmd, err)
}
cmp.Tickets = append(cmp.Tickets, tickets...)
cmp.Tickets = model.RemoveDuplicateTickets(cmp.Tickets, fileOnly)
if err := campaign.Save(cmp); err != nil {
ErrorAndExit(cmd, err)
}
cmd.Printf("%d tickets have been added\n", len(tickets))
} }
func csvAddCmdF(cmd *cobra.Command, args []string) { func csvAddCmdF(cmd *cobra.Command, args []string) {

View file

@ -71,6 +71,10 @@ func (c *GithubClient) PublishNextTicket(cmp *model.Campaign, dryRun bool) (bool
} }
ticket.GithubLink = issue.GetNumber() ticket.GithubLink = issue.GetNumber()
if user := issue.GetUser(); user != nil {
ticket.GithubAssignee = user.GetLogin()
}
ticket.GithubStatus = issue.GetState()
if err := campaign.Save(cmp); err != nil { if err := campaign.Save(cmp); err != nil {
return false, err return false, err
} }

View file

@ -135,6 +135,7 @@ func (c *JiraClient) PublishNextTicket(cmp *model.Campaign, dryRun bool) (bool,
ticket.JiraLink = issue.Key ticket.JiraLink = issue.Key
ticket.Summary = issue.Fields.Summary ticket.Summary = issue.Fields.Summary
ticket.Description = issue.Fields.Description ticket.Description = issue.Fields.Description
// ToDo: sync JiraStatus
if err := campaign.Save(cmp); err != nil { if err := campaign.Save(cmp); err != nil {
return false, err return false, err
} }

View file

@ -7,6 +7,7 @@ import (
type Ticket struct { type Ticket struct {
GithubLink int `json:"github_link,omitempty"` GithubLink int `json:"github_link,omitempty"`
GithubStatus string `json:"github_status,omitempty"` GithubStatus string `json:"github_status,omitempty"`
GithubAssignee string `json:"github_assignee,omitempty"`
JiraLink string `json:"jira_link,omitempty"` JiraLink string `json:"jira_link,omitempty"`
JiraStatus string `json:"jira_status,omitempty"` JiraStatus string `json:"jira_status,omitempty"`
Summary string `json:"summary,omitempty"` Summary string `json:"summary,omitempty"`

85
parsers/parsers.go Normal file
View file

@ -0,0 +1,85 @@
package parsers
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
"git.ctrlz.es/mgdelacroix/campaigner/model"
)
const (
GREP = "grep"
GOVET = "govet"
)
func parseGrepLine(line string) (*model.Ticket, error) {
// ToDo: it would be great to be able to relate a line with its
// parent method, at least for JS and Golang
parts := strings.Split(line, ":")
if len(parts) < 3 {
return nil, fmt.Errorf("cannot parse line: %s", line)
}
filename := parts[0]
lineNo, err := strconv.Atoi(parts[1])
if err != nil {
return nil, err
}
text := strings.Join(parts[2:], "")
return &model.Ticket{
Data: map[string]interface{}{
"filename": filename,
"lineNo": lineNo,
"text": strings.TrimSpace(text),
},
}, nil
}
func parseGovetLine(line string) (*model.Ticket, error) {
parts := strings.Split(line, ":")
if len(parts) < 4 {
return nil, fmt.Errorf("cannot parse line: %s", line)
}
filename := parts[0]
lineNo, err := strconv.Atoi(parts[1])
if err != nil {
return nil, err
}
text := strings.Join(parts[3:], "")
return &model.Ticket{
Data: map[string]interface{}{
"filename": filename,
"lineNo": lineNo,
"text": strings.TrimSpace(text),
},
}, nil
}
func ParseWith(parser string) []*model.Ticket {
var parseFn func(string) (*model.Ticket, error)
switch parser {
case GREP:
parseFn = parseGrepLine
case GOVET:
parseFn = parseGovetLine
default:
fmt.Fprintf(os.Stderr, "Unknown parser %s", parser)
return nil
}
tickets := []*model.Ticket{}
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
ticket, _ := parseFn(scanner.Text())
if ticket != nil {
tickets = append(tickets, ticket)
}
}
return tickets
}