1
0
mirror of https://gitea.com/jolheiser/sip synced 2024-11-22 19:51:58 +01:00

Add release support and CSV output (#16)

Add CSV output

Refactor requireToken and add release creation

Start releases

Signed-off-by: jolheiser <john.olheiser@gmail.com>

Co-authored-by: jolheiser <john.olheiser@gmail.com>
Reviewed-on: https://gitea.com/jolheiser/sip/pulls/16
This commit is contained in:
John Olheiser 2020-04-17 03:59:12 +00:00
parent 5ea204fb61
commit 0a6162ad05
17 changed files with 379 additions and 67 deletions

@ -27,6 +27,7 @@ steps:
GOPROXY: https://goproxy.cn
commands:
- make lint
- make vet
---
kind: pipeline

@ -28,6 +28,10 @@ lint:
fi
golangci-lint run --timeout 5m
.PHONY: vet
vet:
$(GO) vet ./...
.PHONY: fmt
fmt:
$(GO) fmt ./...

@ -54,12 +54,24 @@ var (
upstreamRepo []string
)
func requireToken(ctx *cli.Context) (string, error) {
if ctx.IsSet("token") {
return getToken(ctx.String("token")), nil
func getToken(name string) string {
for _, token := range config.Tokens {
if name == token.Name {
return token.Token
}
}
return ""
}
func getClient(ctx *cli.Context, requireToken bool) (*gitea.Client, error) {
if ctx.IsSet("token") {
return gitea.NewClient(ctx.String("url"), getToken(ctx.String("token"))), nil
}
var token string
if requireToken {
if len(config.Tokens) == 0 {
return "", errors.New(color.Error.Wrap("No tokens found! Add one with #{sip token create}", color.New(color.FgMagenta)))
return nil, errors.New(color.Error.Wrap("No tokens found! Add one with #{sip token create}", color.New(color.FgMagenta)))
}
tokenMap := make(map[string]config.Token)
opts := make([]string, len(config.Tokens))
@ -73,23 +85,13 @@ func requireToken(ctx *cli.Context) (string, error) {
var answer string
if err := survey.AskOne(question, &answer); err != nil {
return "", err
return nil, err
}
return tokenMap[answer].Token, nil
token = tokenMap[answer].Token
}
func getToken(name string) string {
for _, token := range config.Tokens {
if name == token.Name {
return token.Token
}
}
return ""
}
func getClient(ctx *cli.Context) *gitea.Client {
return gitea.NewClient(ctx.String("url"), getToken(ctx.String("token")))
return gitea.NewClient(ctx.String("url"), token), nil
}
func defRemote(remote, def string) string {

@ -4,12 +4,14 @@ import (
"code.gitea.io/sdk/gitea"
"errors"
"fmt"
"gitea.com/jolheiser/sip/modules/csv"
"gitea.com/jolheiser/sip/modules/markdown"
"gitea.com/jolheiser/sip/modules/sdk"
"github.com/AlecAivazis/survey/v2"
"github.com/urfave/cli/v2"
"go.jolheiser.com/beaver"
"go.jolheiser.com/beaver/color"
"os"
"strconv"
"strings"
)
@ -22,6 +24,12 @@ var Issues = cli.Command{
Subcommands: []*cli.Command{
&IssuesCreate,
},
Flags: []cli.Flag{
&cli.StringFlag{
Name: "csv",
Usage: "Output results to a CSV file",
},
},
}
func doIssuesSearch(ctx *cli.Context) error {
@ -32,11 +40,16 @@ func doIssuesSearch(ctx *cli.Context) error {
}
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, getClient(ctx), pulls)
issues, err := queryIssues(ctx, client, pulls)
if err != nil {
return nil, err
}
@ -46,6 +59,18 @@ func issuesSearch(ctx *cli.Context, pulls bool) (*gitea.Issue, error) {
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)))
@ -87,29 +112,20 @@ func issuesSearch(ctx *cli.Context, pulls bool) (*gitea.Issue, error) {
}
func queryIssues(ctx *cli.Context, client *gitea.Client, pulls bool) ([]*gitea.Issue, error) {
questions := []*survey.Question{
{
Name: "repo",
Prompt: &survey.Input{Message: "Full repository name", Default: fullName(ctx)},
Validate: validateFullName,
},
{
Name: "query",
Prompt: &survey.Input{Message: "Search query"},
},
}
answers := struct {
Repo string
Query string
}{}
if err := survey.Ask(questions, &answers); err != nil {
owner, repo, err := askOwnerRepo(ctx)
if err != nil {
return nil, err
}
filter := sdk.NewIssueFilter(answers.Query)
ownerRepo := strings.Split(answers.Repo, "/")
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, ownerRepo[0], ownerRepo[1], opts)
issues, err := sdk.GetIssues(client, owner, repo, opts)
if err != nil {
return nil, err
}
@ -132,6 +148,26 @@ func queryIssues(ctx *cli.Context, client *gitea.Client, pulls bool) ([]*gitea.I
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, "/")

@ -21,7 +21,7 @@ func doIssueCreate(ctx *cli.Context) error {
url := color.New(color.FgYellow).Format(fmt.Sprintf("%s/%s/%s", ctx.String("url"), ctx.String("owner"), ctx.String("repo")))
fmt.Println(color.New(color.FgCyan).Format("Creating a new issue for"), url)
token, err := requireToken(ctx)
client, err := getClient(ctx, true)
if err != nil {
return err
}
@ -65,8 +65,6 @@ func doIssueCreate(ctx *cli.Context) error {
}
}
client := gitea.NewClient(ctx.String("url"), token)
issue, err := client.CreateIssue(ctx.String("owner"), ctx.String("repo"), gitea.CreateIssueOption{Title: title, Body: body})
if err != nil {
return err

@ -14,6 +14,12 @@ var Pulls = cli.Command{
&PullsStatus,
&PullsCheckout,
},
Flags: []cli.Flag{
&cli.StringFlag{
Name: "csv",
Usage: "Output results to a CSV file",
},
},
}
func doPullsSearch(ctx *cli.Context) error {

@ -21,6 +21,11 @@ var PullsCheckout = cli.Command{
}
func doPullCheckout(ctx *cli.Context) error {
client, err := getClient(ctx, true)
if err != nil {
return err
}
var issue *gitea.Issue
questions := []*survey.Question{
{
@ -50,7 +55,7 @@ func doPullCheckout(ctx *cli.Context) error {
}
}
} else {
iss, err := getClient(ctx).GetIssue(upstreamRepo[1], upstreamRepo[2], prNum.Index)
iss, err := client.GetIssue(upstreamRepo[1], upstreamRepo[2], prNum.Index)
if err != nil {
return err
}

@ -22,13 +22,11 @@ func doPullCreate(ctx *cli.Context) error {
url := color.New(color.FgYellow).Format(fmt.Sprintf("%s/%s/%s", ctx.String("url"), ctx.String("owner"), ctx.String("repo")))
fmt.Println(color.New(color.FgCyan).Format("Creating a new pull request for"), url)
token, err := requireToken(ctx)
client, err := getClient(ctx, true)
if err != nil {
return err
}
client := gitea.NewClient(ctx.String("url"), token)
upstreams, err := client.ListRepoBranches(getUpstreamRepo()[1], getUpstreamRepo()[2])
if err != nil {
return err

@ -17,7 +17,11 @@ var PullsStatus = cli.Command{
}
func doPullStatus(ctx *cli.Context) error {
client := getClient(ctx)
client, err := getClient(ctx, false)
if err != nil {
return err
}
head := fmt.Sprintf("%s:%s", getOriginRepo()[1], git.Branch())
pulls, err := sdk.GetPulls(client, getUpstreamRepo()[1], getUpstreamRepo()[2], gitea.ListPullRequestsOptions{State: "all"})

80
cmd/release.go Normal file

@ -0,0 +1,80 @@
package cmd
import (
"code.gitea.io/sdk/gitea"
"fmt"
"gitea.com/jolheiser/sip/modules/csv"
"gitea.com/jolheiser/sip/modules/markdown"
"github.com/AlecAivazis/survey/v2"
"github.com/urfave/cli/v2"
"go.jolheiser.com/beaver/color"
"os"
)
var Release = cli.Command{
Name: "releases",
Aliases: []string{"release"},
Usage: "Commands for interacting with releases",
Action: doRelease,
Subcommands: []*cli.Command{
&ReleaseCreate,
},
Flags: []cli.Flag{
&cli.StringFlag{
Name: "csv",
Usage: "Output results to a CSV file",
},
},
}
func doRelease(ctx *cli.Context) error {
client, err := getClient(ctx, false)
if err != nil {
return err
}
owner, repo, err := askOwnerRepo(ctx)
if err != nil {
return err
}
releases, err := client.ListReleases(owner, repo)
if err != nil {
return err
}
if ctx.String("csv") != "" {
fi, err := os.Create(ctx.String("csv"))
if err != nil {
return err
}
if _, err := fi.WriteString(csv.Releases(releases)); err != nil {
return err
}
fmt.Println(color.FgCyan.Format("Releases were exported to"), color.Info.Format(ctx.String("csv")))
return fi.Close()
}
releaseMap := make(map[string]*gitea.Release)
releaseList := make([]string, len(releases))
for idx, release := range releases {
key := fmt.Sprintf("%s (%s)", release.Title, release.TagName)
releaseMap[key] = release
releaseList[idx] = key
}
sel := &survey.Select{Options: releaseList, Message: "Releases"}
var selection string
if err := survey.AskOne(sel, &selection); err != nil {
return err
}
note, err := markdown.Render(releaseMap[selection].Note)
if err != nil {
return err
}
fmt.Println(note)
fmt.Printf("Release Date: %s\n", releaseMap[selection].PublishedAt.Format(csv.TimeFormat))
return nil
}

87
cmd/release_create.go Normal file

@ -0,0 +1,87 @@
package cmd
import (
"code.gitea.io/sdk/gitea"
"fmt"
"gitea.com/jolheiser/sip/modules/git"
"github.com/AlecAivazis/survey/v2"
"github.com/urfave/cli/v2"
"go.jolheiser.com/beaver/color"
)
var ReleaseCreate = cli.Command{
Name: "create",
Aliases: []string{"new"},
Usage: "Create a new release",
Action: doReleaseCreate,
}
func doReleaseCreate(ctx *cli.Context) error {
client, err := getClient(ctx, true)
if err != nil {
return err
}
questions := []*survey.Question{
{
Name: "tag",
Prompt: &survey.Input{Message: "Tag Name"},
Validate: survey.Required,
},
{
Name: "target",
Prompt: &survey.Input{Message: "Target", Default: git.Branch(), Help: "Target ref, branch name or commit SHA"},
Validate: survey.Required,
},
{
Name: "title",
Prompt: &survey.Input{Message: "Title"},
Validate: survey.Required,
},
{
Name: "note",
Prompt: &survey.Multiline{Message: "Notes"},
},
{
Name: "draft",
Prompt: &survey.Confirm{Message: "Draft", Default: false},
Validate: survey.Required,
},
{
Name: "pre",
Prompt: &survey.Confirm{Message: "Pre-Release", Default: false},
Validate: survey.Required,
},
}
answers := struct {
Tag string
Target string
Title string
Note string
Draft bool
Pre bool
}{}
if err := survey.Ask(questions, &answers); err != nil {
return err
}
release, err := client.CreateRelease(ctx.String("owner"), ctx.String("repo"), gitea.CreateReleaseOption{
TagName: answers.Tag,
Target: answers.Target,
Title: answers.Title,
Note: answers.Note,
IsDraft: answers.Draft,
IsPrerelease: answers.Pre,
})
if err != nil {
return err
}
info := color.Info
cyan := color.New(color.FgCyan)
fmt.Println(info.Format("Release"), cyan.Format(release.TagName), info.Format("created!"))
// TODO Change to specific release page once supported
fmt.Println(cyan.Format(fmt.Sprintf("%s/%s/%s/releases/", ctx.String("url"), ctx.String("owner"), ctx.String("repo"))))
return nil
}

@ -19,7 +19,10 @@ var Repo = cli.Command{
}
func doRepo(ctx *cli.Context) error {
client := getClient(ctx)
client, err := getClient(ctx, false)
if err != nil {
return err
}
issues, err := sdk.GetIssues(client, ctx.String("owner"), ctx.String("repo"), gitea.ListIssueOption{State: "open"})
if err != nil {

@ -16,13 +16,11 @@ var RepoCreate = cli.Command{
}
func doRepoCreate(ctx *cli.Context) error {
token, err := requireToken(ctx)
client, err := getClient(ctx, true)
if err != nil {
return err
}
client := gitea.NewClient(ctx.String("url"), token)
questions := []*survey.Question{
{
Name: "name",

2
go.mod

@ -3,7 +3,7 @@ module gitea.com/jolheiser/sip
go 1.13
require (
code.gitea.io/sdk/gitea v0.11.0
code.gitea.io/sdk/gitea v0.11.2
github.com/AlecAivazis/survey/v2 v2.0.5
github.com/BurntSushi/toml v0.3.1
github.com/charmbracelet/glamour v0.1.0

8
go.sum

@ -1,7 +1,5 @@
code.gitea.io/sdk/gitea v0.11.0 h1:XgZtmImZsjMC+Z1WBfO6bYTCOJiGp+7w0HKmfhTwytw=
code.gitea.io/sdk/gitea v0.11.0/go.mod h1:z3uwDV/b9Ls47NGukYM9XhnHtqPh/J+t40lsUrR6JDY=
go.jolheiser.com/beaver v1.0.0 h1:IfNMhp7+DUaM0kaNwho4RWfuebCsa8A/kxtZBngFjHk=
go.jolheiser.com/beaver v1.0.0/go.mod h1:2mUGl6ZGKY/Y9u36iR4bqOPrHhr4C22cxkR8ei2G06I=
code.gitea.io/sdk/gitea v0.11.2 h1:D0xIRlHv3IckzdYOWzHK1bPvlkXdA4LD909UYyBdi1o=
code.gitea.io/sdk/gitea v0.11.2/go.mod h1:z3uwDV/b9Ls47NGukYM9XhnHtqPh/J+t40lsUrR6JDY=
github.com/AlecAivazis/survey/v2 v2.0.5 h1:xpZp+Q55wi5C7Iaze+40onHnEkex1jSc34CltJjOoPM=
github.com/AlecAivazis/survey/v2 v2.0.5/go.mod h1:WYBhg6f0y/fNYUuesWQc0PKbJcEliGcYHB9sNT3Bg74=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
@ -127,8 +125,6 @@ golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190530182044-ad28b68e88f1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4 h1:sfkvUWPNGwSV+8/fNqctR5lS2AqCSqYwXdrjCxp/dXo=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=

@ -23,6 +23,7 @@ func main() {
&cmd.Repo,
&cmd.Issues,
&cmd.Pulls,
&cmd.Release,
}
app.Flags = cmd.Flags
app.EnableBashCompletion = true

93
modules/csv/csv.go Normal file

@ -0,0 +1,93 @@
package csv
import (
"fmt"
"sort"
"strings"
"code.gitea.io/sdk/gitea"
)
const TimeFormat = "Jan 2, 2006 at 03:04 PM"
func Issues(issues []*gitea.Issue) string {
headers := "ID,Index,Author,Title,Body,Labels,Milestone,Assignee,State,Created,Updated,Closed\r\n"
// Sort the issues coming in
sort.Slice(issues, func(i, j int) bool {
return issues[i].ID < issues[j].ID
})
rows := make([]string, 0)
for _, issue := range issues {
cols := make([]string, 12)
cols[0] = fmt.Sprintf("%d", issue.ID)
cols[1] = fmt.Sprintf("%d", issue.Index)
if issue.Poster != nil {
cols[2] = issue.Poster.UserName
}
cols[3] = issue.Title
cols[4] = issue.Body
labels := make([]string, 0)
for _, lbl := range issue.Labels {
labels = append(labels, lbl.Name)
}
cols[5] = strings.Join(labels, ",")
if issue.Milestone != nil {
cols[6] = issue.Milestone.Title
}
if issue.Assignee != nil {
cols[7] = issue.Assignee.UserName
}
state := string(issue.State)
if issue.PullRequest != nil && issue.PullRequest.HasMerged {
state = "merged"
}
cols[8] = state
cols[9] = issue.Created.Format(TimeFormat)
cols[10] = issue.Updated.Format(TimeFormat)
if issue.Closed != nil {
cols[11] = issue.Closed.Format(TimeFormat)
}
for idx, col := range cols {
cols[idx] = escape(col)
}
rows = append(rows, strings.Join(cols, ","))
}
return headers + strings.Join(rows, "\r\n")
}
func Releases(releases []*gitea.Release) string {
headers := "ID,Tag Name,Target,Title,Body,Draft,Pre-Release,Created,Published,Publisher\r\n"
// Sort the releases coming in
sort.Slice(releases, func(i, j int) bool {
return releases[i].ID < releases[j].ID
})
rows := make([]string, 0)
for _, release := range releases {
cols := make([]string, 10)
cols[0] = fmt.Sprintf("%d", release.ID)
cols[1] = release.TagName
cols[2] = release.Target
cols[3] = release.Title
cols[4] = release.Note
cols[5] = fmt.Sprintf("%t", release.IsDraft)
cols[6] = fmt.Sprintf("%t", release.IsPrerelease)
cols[7] = release.CreatedAt.Format(TimeFormat)
cols[8] = release.PublishedAt.Format(TimeFormat)
if release.Publisher != nil {
cols[9] = release.Publisher.UserName
}
for idx, col := range cols {
cols[idx] = escape(col)
}
rows = append(rows, strings.Join(cols, ","))
}
return headers + strings.Join(rows, "\r\n")
}
func escape(input string) string {
return fmt.Sprintf(`"%s"`, strings.ReplaceAll(input, `"`, `""`))
}