move commands from jiracli package to jiracmd package

This commit is contained in:
Cory Bennett
2017-09-02 09:14:54 -07:00
parent de1460ddc9
commit 0a5510b231
40 changed files with 417 additions and 384 deletions
-64
View File
@@ -1,64 +0,0 @@
package jiracli
import (
"fmt"
"github.com/coryb/figtree"
"github.com/coryb/oreo"
jira "gopkg.in/Netflix-Skunkworks/go-jira.v1"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
type AssignOptions struct {
GlobalOptions `yaml:",inline" json:",inline" figtree:",inline"`
Issue string `yaml:"issue,omitempty" json:"issue,omitempty"`
Assignee string `yaml:"assignee,omitempty" json:"assignee,omitempty"`
}
func CmdAssignRegistry(fig *figtree.FigTree, o *oreo.Client) *CommandRegistryEntry {
opts := AssignOptions{}
return &CommandRegistryEntry{
"Assign user to issue",
func() error {
return CmdAssign(o, &opts)
},
func(cmd *kingpin.CmdClause) error {
LoadConfigs(cmd, fig, &opts)
return CmdAssignUsage(cmd, &opts)
},
}
}
func CmdAssignUsage(cmd *kingpin.CmdClause, opts *AssignOptions) error {
if err := GlobalUsage(cmd, &opts.GlobalOptions); err != nil {
return err
}
BrowseUsage(cmd, &opts.GlobalOptions)
cmd.Flag("default", "use default user for assignee").PreAction(func(ctx *kingpin.ParseContext) error {
if flagValue(ctx, "default") == "true" {
opts.Assignee = "-1"
}
return nil
}).Bool()
cmd.Arg("ISSUE", "issue to assign").Required().StringVar(&opts.Issue)
cmd.Arg("ASSIGNEE", "user to assign to issue").StringVar(&opts.Assignee)
return nil
}
// CmdAssign will assign an issue to a user
func CmdAssign(o *oreo.Client, opts *AssignOptions) error {
err := jira.IssueAssign(o, opts.Endpoint.Value, opts.Issue, opts.Assignee)
if err != nil {
return err
}
fmt.Printf("OK %s %s/browse/%s\n", opts.Issue, opts.Endpoint.Value, opts.Issue)
if opts.Browse.Value {
return CmdBrowse(&BrowseOptions{opts.GlobalOptions, opts.Issue})
}
return nil
}
-80
View File
@@ -1,80 +0,0 @@
package jiracli
import (
"fmt"
"github.com/coryb/figtree"
"github.com/coryb/oreo"
jira "gopkg.in/Netflix-Skunkworks/go-jira.v1"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiradata"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
type BlockOptions struct {
GlobalOptions `yaml:",inline" json:",inline" figtree:",inline"`
jiradata.LinkIssueRequest `yaml:",inline" json:",inline" figtree:",inline"`
}
func CmdBlockRegistry(fig *figtree.FigTree, o *oreo.Client) *CommandRegistryEntry {
opts := BlockOptions{
GlobalOptions: GlobalOptions{
Template: figtree.NewStringOption("edit"),
},
LinkIssueRequest: jiradata.LinkIssueRequest{
Type: &jiradata.IssueLinkType{
Name: "Blocks",
},
InwardIssue: &jiradata.IssueRef{},
OutwardIssue: &jiradata.IssueRef{},
},
}
return &CommandRegistryEntry{
"Mark issues as blocker",
func() error {
return CmdBlock(o, &opts)
},
func(cmd *kingpin.CmdClause) error {
LoadConfigs(cmd, fig, &opts)
return CmdBlockUsage(cmd, &opts)
},
}
}
func CmdBlockUsage(cmd *kingpin.CmdClause, opts *BlockOptions) error {
if err := GlobalUsage(cmd, &opts.GlobalOptions); err != nil {
return err
}
BrowseUsage(cmd, &opts.GlobalOptions)
EditorUsage(cmd, &opts.GlobalOptions)
TemplateUsage(cmd, &opts.GlobalOptions)
cmd.Flag("comment", "Comment message when marking issue as blocker").Short('m').PreAction(func(ctx *kingpin.ParseContext) error {
opts.Comment = &jiradata.Comment{
Body: flagValue(ctx, "comment"),
}
return nil
}).String()
cmd.Arg("BLOCKER", "blocker issue").Required().StringVar(&opts.OutwardIssue.Key)
cmd.Arg("ISSUE", "issue that is blocked").Required().StringVar(&opts.InwardIssue.Key)
return nil
}
// CmdBlock will update the given issue as being a duplicate by the given dup issue
// and will attempt to resolve the dup issue
func CmdBlock(o *oreo.Client, opts *BlockOptions) error {
if err := jira.LinkIssues(o, opts.Endpoint.Value, &opts.LinkIssueRequest); err != nil {
return err
}
fmt.Printf("OK %s %s/browse/%s\n", opts.InwardIssue.Key, opts.Endpoint.Value, opts.InwardIssue.Key)
fmt.Printf("OK %s %s/browse/%s\n", opts.OutwardIssue.Key, opts.Endpoint.Value, opts.OutwardIssue.Key)
if opts.Browse.Value {
if err := CmdBrowse(&BrowseOptions{opts.GlobalOptions, opts.InwardIssue.Key}); err != nil {
return CmdBrowse(&BrowseOptions{opts.GlobalOptions, opts.OutwardIssue.Key})
}
}
return nil
}
-43
View File
@@ -1,43 +0,0 @@
package jiracli
import (
"fmt"
"github.com/coryb/figtree"
"github.com/pkg/browser"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
type BrowseOptions struct {
GlobalOptions `yaml:",inline" json:",inline" figtree:",inline"`
Issue string `yaml:"issue,omitempty" json:"issue,omitempty"`
}
func CmdBrowseRegistry(fig *figtree.FigTree) *CommandRegistryEntry {
opts := BrowseOptions{}
return &CommandRegistryEntry{
"Open issue in browser",
func() error {
return CmdBrowse(&opts)
},
func(cmd *kingpin.CmdClause) error {
LoadConfigs(cmd, fig, &opts)
return CmdBrowseUsage(cmd, &opts)
},
}
}
func CmdBrowseUsage(cmd *kingpin.CmdClause, opts *BrowseOptions) error {
if err := GlobalUsage(cmd, &opts.GlobalOptions); err != nil {
return err
}
cmd.Arg("ISSUE", "Issue to browse to").Required().StringVar(&opts.Issue)
return nil
}
// CmdBrowse open the default system browser to the provided issue
func CmdBrowse(opts *BrowseOptions) error {
return browser.OpenURL(fmt.Sprintf("%s/browse/%s", opts.Endpoint.Value, opts.Issue))
}
+1 -1
View File
@@ -187,7 +187,7 @@ func (o *GlobalOptions) editFile(fileName string) (changes bool, err error) {
return false, err
}
func editLoop(opts *GlobalOptions, input interface{}, output interface{}, submit func() error) error {
func EditLoop(opts *GlobalOptions, input interface{}, output interface{}, submit func() error) error {
tmpFile, err := tmpTemplate(opts.Template.Value, input)
if err != nil {
return err
-78
View File
@@ -1,78 +0,0 @@
package jiracli
import (
"fmt"
"github.com/coryb/figtree"
"github.com/coryb/oreo"
jira "gopkg.in/Netflix-Skunkworks/go-jira.v1"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiradata"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
type CommentOptions struct {
GlobalOptions `yaml:",inline" json:",inline" figtree:",inline"`
Overrides map[string]string `yaml:"overrides,omitempty" json:"overrides,omitempty"`
Issue string `yaml:"issue,omitempty" json:"issue,omitempty"`
}
func CmdCommentRegistry(fig *figtree.FigTree, o *oreo.Client) *CommandRegistryEntry {
opts := CommentOptions{
GlobalOptions: GlobalOptions{
Template: figtree.NewStringOption("comment"),
},
Overrides: map[string]string{},
}
return &CommandRegistryEntry{
"Add comment to issue",
func() error {
return CmdComment(o, &opts)
},
func(cmd *kingpin.CmdClause) error {
LoadConfigs(cmd, fig, &opts)
return CmdCommentUsage(cmd, &opts)
},
}
}
func CmdCommentUsage(cmd *kingpin.CmdClause, opts *CommentOptions) error {
if err := GlobalUsage(cmd, &opts.GlobalOptions); err != nil {
return err
}
BrowseUsage(cmd, &opts.GlobalOptions)
EditorUsage(cmd, &opts.GlobalOptions)
TemplateUsage(cmd, &opts.GlobalOptions)
cmd.Flag("comment", "Comment message for issue").Short('m').PreAction(func(ctx *kingpin.ParseContext) error {
opts.Overrides["comment"] = flagValue(ctx, "comment")
return nil
}).String()
cmd.Arg("ISSUE", "issue id to update").StringVar(&opts.Issue)
return nil
}
// CmdComment will update issue with comment
func CmdComment(o *oreo.Client, opts *CommentOptions) error {
comment := jiradata.Comment{}
input := struct {
Overrides map[string]string
}{
opts.Overrides,
}
err := editLoop(&opts.GlobalOptions, &input, &comment, func() error {
_, err := jira.IssueAddComment(o, opts.Endpoint.Value, opts.Issue, &comment)
return err
})
if err != nil {
return err
}
fmt.Printf("OK %s %s/browse/%s\n", opts.Issue, opts.Endpoint.Value, opts.Issue)
if opts.Browse.Value {
return CmdBrowse(&BrowseOptions{opts.GlobalOptions, opts.Issue})
}
return nil
}
-68
View File
@@ -1,68 +0,0 @@
package jiracli
import (
"fmt"
"github.com/coryb/figtree"
"github.com/coryb/oreo"
jira "gopkg.in/Netflix-Skunkworks/go-jira.v1"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiradata"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
type ComponentAddOptions struct {
GlobalOptions `yaml:",inline" json:",inline" figtree:",inline"`
jiradata.Component `yaml:",inline" json:",inline" figtree:",inline"`
}
func CmdComponentAddRegistry(fig *figtree.FigTree, o *oreo.Client) *CommandRegistryEntry {
opts := ComponentAddOptions{
GlobalOptions: GlobalOptions{
Template: figtree.NewStringOption("component-add"),
},
}
return &CommandRegistryEntry{
"Add component",
func() error {
return CmdComponentAdd(o, &opts)
},
func(cmd *kingpin.CmdClause) error {
LoadConfigs(cmd, fig, &opts)
return CmdComponentAddUsage(cmd, &opts)
},
}
}
func CmdComponentAddUsage(cmd *kingpin.CmdClause, opts *ComponentAddOptions) error {
if err := GlobalUsage(cmd, &opts.GlobalOptions); err != nil {
return err
}
EditorUsage(cmd, &opts.GlobalOptions)
TemplateUsage(cmd, &opts.GlobalOptions)
cmd.Flag("noedit", "Disable opening the editor").SetValue(&opts.SkipEditing)
cmd.Flag("project", "project to create component in").Short('p').StringVar(&opts.Project)
cmd.Flag("name", "name of component").Short('n').StringVar(&opts.Name)
cmd.Flag("description", "description of component").Short('d').StringVar(&opts.Description)
cmd.Flag("lead", "person that acts as lead for component").Short('l').StringVar(&opts.LeadUserName)
return nil
}
// CmdComponentAdd sends the provided overrides to the "component-add" template for editing, then
// will parse the edited document as YAML and submit the document to jira.
func CmdComponentAdd(o *oreo.Client, opts *ComponentAddOptions) error {
var err error
component := &jiradata.Component{}
var resp *jiradata.Component
err = editLoop(&opts.GlobalOptions, &opts.Component, component, func() error {
resp, err = jira.CreateComponent(o, opts.Endpoint.Value, component)
return err
})
if err != nil {
return err
}
fmt.Printf("OK %s %s\n", component.Project, component.Name)
return nil
}
-57
View File
@@ -1,57 +0,0 @@
package jiracli
import (
"fmt"
"github.com/coryb/figtree"
"github.com/coryb/oreo"
jira "gopkg.in/Netflix-Skunkworks/go-jira.v1"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
type ComponentsOptions struct {
GlobalOptions `yaml:",inline" json:",inline" figtree:",inline"`
Project string `yaml:"project,omitempty" json:"project,omitempty"`
}
func CmdComponentsRegistry(fig *figtree.FigTree, o *oreo.Client) *CommandRegistryEntry {
opts := ComponentsOptions{
GlobalOptions: GlobalOptions{
Template: figtree.NewStringOption("components"),
},
}
return &CommandRegistryEntry{
"Show components for a project",
func() error {
return CmdComponents(o, &opts)
},
func(cmd *kingpin.CmdClause) error {
LoadConfigs(cmd, fig, &opts)
return CmdComponentsUsage(cmd, &opts)
},
}
}
func CmdComponentsUsage(cmd *kingpin.CmdClause, opts *ComponentsOptions) error {
if err := GlobalUsage(cmd, &opts.GlobalOptions); err != nil {
return err
}
TemplateUsage(cmd, &opts.GlobalOptions)
cmd.Flag("project", "project to list components").Short('p').StringVar(&opts.Project)
return nil
}
// CmdComponents will get available components for project and send to the "components" template
func CmdComponents(o *oreo.Client, opts *ComponentsOptions) error {
if opts.Project == "" {
return fmt.Errorf("Project Required.")
}
data, err := jira.GetProjectComponents(o, opts.Endpoint.Value, opts.Project)
if err != nil {
return err
}
return runTemplate(opts.Template.Value, data, nil)
}
-152
View File
@@ -1,152 +0,0 @@
package jiracli
import (
"fmt"
"os"
"github.com/coryb/figtree"
"github.com/coryb/oreo"
jira "gopkg.in/Netflix-Skunkworks/go-jira.v1"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiradata"
kingpin "gopkg.in/alecthomas/kingpin.v2"
yaml "gopkg.in/coryb/yaml.v2"
)
type CreateOptions struct {
GlobalOptions `yaml:",inline" json:",inline" figtree:",inline"`
jiradata.IssueUpdate `yaml:",inline" json:",inline" figtree:",inline"`
Project string `yaml:"project,omitempty" json:"project,omitempty"`
IssueType string `yaml:"issuetype,omitempty" json:"issuetype,omitempty"`
Overrides map[string]string `yaml:"overrides,omitempty" json:"overrides,omitempty"`
SaveFile string `yaml:"savefile,omitempty" json:"savefile,omitempty"`
}
func CmdCreateRegistry(fig *figtree.FigTree, o *oreo.Client) *CommandRegistryEntry {
opts := CreateOptions{
GlobalOptions: GlobalOptions{
Template: figtree.NewStringOption("create"),
},
Overrides: map[string]string{},
}
return &CommandRegistryEntry{
"Create issue",
func() error {
return CmdCreate(o, &opts)
},
func(cmd *kingpin.CmdClause) error {
LoadConfigs(cmd, fig, &opts)
return CmdCreateUsage(cmd, &opts)
},
}
}
func CmdCreateUsage(cmd *kingpin.CmdClause, opts *CreateOptions) error {
if err := GlobalUsage(cmd, &opts.GlobalOptions); err != nil {
return err
}
BrowseUsage(cmd, &opts.GlobalOptions)
EditorUsage(cmd, &opts.GlobalOptions)
TemplateUsage(cmd, &opts.GlobalOptions)
cmd.Flag("noedit", "Disable opening the editor").SetValue(&opts.SkipEditing)
cmd.Flag("project", "project to create issue in").Short('p').StringVar(&opts.Project)
cmd.Flag("issuetype", "issuetype in to create").Short('i').StringVar(&opts.IssueType)
cmd.Flag("comment", "Comment message for issue").Short('m').PreAction(func(ctx *kingpin.ParseContext) error {
opts.Overrides["comment"] = flagValue(ctx, "comment")
return nil
}).String()
cmd.Flag("override", "Set issue property").Short('o').StringMapVar(&opts.Overrides)
cmd.Flag("saveFile", "Write issue as yaml to file").StringVar(&opts.SaveFile)
return nil
}
// CmdCreate sends the create-metadata to the "create" template for editing, then
// will parse the edited document as YAML and submit the document to jira.
func CmdCreate(o *oreo.Client, opts *CreateOptions) error {
type templateInput struct {
Meta *jiradata.IssueType `yaml:"meta" json:"meta"`
Overrides map[string]string `yaml:"overrides" json:"overrides"`
}
if err := defaultIssueType(o, opts.Endpoint.Value, &opts.Project, &opts.IssueType); err != nil {
return err
}
createMeta, err := jira.GetIssueCreateMetaIssueType(o, opts.Endpoint.Value, opts.Project, opts.IssueType)
if err != nil {
return err
}
issueUpdate := jiradata.IssueUpdate{}
input := templateInput{
Meta: createMeta,
Overrides: opts.Overrides,
}
input.Overrides["project"] = opts.Project
input.Overrides["issuetype"] = opts.IssueType
input.Overrides["user"] = opts.User.Value
var issueResp *jiradata.IssueCreateResponse
err = editLoop(&opts.GlobalOptions, &input, &issueUpdate, func() error {
issueResp, err = jira.CreateIssue(o, opts.Endpoint.Value, &issueUpdate)
return err
})
if err != nil {
return err
}
fmt.Printf("OK %s %s/browse/%s\n", issueResp.Key, opts.Endpoint.Value, issueResp.Key)
if opts.SaveFile != "" {
fh, err := os.Create(opts.SaveFile)
if err != nil {
return err
}
defer fh.Close()
out, err := yaml.Marshal(map[string]string{
"issue": issueResp.Key,
"link": fmt.Sprintf("%s/browse/%s", opts.Endpoint.Value, issueResp.Key),
})
if err != nil {
return err
}
fh.Write(out)
}
if opts.Browse.Value {
return CmdBrowse(&BrowseOptions{opts.GlobalOptions, issueResp.Key})
}
return nil
}
func defaultIssueType(o *oreo.Client, endpoint string, project, issuetype *string) error {
if project == nil || *project == "" {
return fmt.Errorf("Project undefined, please use --project argument or set the `project` config property")
}
if issuetype != nil && *issuetype != "" {
return nil
}
projectMeta, err := jira.GetIssueCreateMetaProject(o, endpoint, *project)
if err != nil {
return err
}
issueTypes := map[string]bool{}
for _, issuetype := range projectMeta.IssueTypes {
issueTypes[issuetype.Name] = true
}
// prefer "Bug" type
if _, ok := issueTypes["Bug"]; ok {
*issuetype = "Bug"
return nil
}
// next best default it "Task"
if _, ok := issueTypes["Task"]; ok {
*issuetype = "Task"
return nil
}
return fmt.Errorf("Unable to find default issueType of Bug or Task, please set --issuetype argument or set the `issuetype` config property")
}
-55
View File
@@ -1,55 +0,0 @@
package jiracli
import (
"github.com/coryb/figtree"
"github.com/coryb/oreo"
jira "gopkg.in/Netflix-Skunkworks/go-jira.v1"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
type CreateMetaOptions struct {
GlobalOptions `yaml:",inline" json:",inline" figtree:",inline"`
Project string `yaml:"project,omitempty" json:"project,omitempty"`
IssueType string `yaml:"issuetype,omitempty" json:"issuetype,omitempty"`
}
func CmdCreateMetaRegistry(fig *figtree.FigTree, o *oreo.Client) *CommandRegistryEntry {
opts := CreateMetaOptions{
GlobalOptions: GlobalOptions{
Template: figtree.NewStringOption("createmeta"),
},
}
return &CommandRegistryEntry{
"View 'create' metadata",
func() error {
return CmdCreateMeta(o, &opts)
},
func(cmd *kingpin.CmdClause) error {
LoadConfigs(cmd, fig, &opts)
return CmdCreateMetaUsage(cmd, &opts)
},
}
}
func CmdCreateMetaUsage(cmd *kingpin.CmdClause, opts *CreateMetaOptions) error {
if err := GlobalUsage(cmd, &opts.GlobalOptions); err != nil {
return err
}
TemplateUsage(cmd, &opts.GlobalOptions)
cmd.Flag("project", "project to fetch create metadata").Short('p').StringVar(&opts.Project)
cmd.Flag("issuetype", "issuetype in project to fetch create metadata").Short('i').StringVar(&opts.IssueType)
return nil
}
// Create will get issue create metadata and send to "createmeta" template
func CmdCreateMeta(o *oreo.Client, opts *CreateMetaOptions) error {
if err := defaultIssueType(o, opts.Endpoint.Value, &opts.Project, &opts.IssueType); err != nil {
return err
}
createMeta, err := jira.GetIssueCreateMetaIssueType(o, opts.Endpoint.Value, opts.Project, opts.IssueType)
if err != nil {
return err
}
return runTemplate(opts.Template.Value, createMeta, nil)
}
-102
View File
@@ -1,102 +0,0 @@
package jiracli
import (
"fmt"
"github.com/coryb/figtree"
"github.com/coryb/oreo"
jira "gopkg.in/Netflix-Skunkworks/go-jira.v1"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiradata"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
type DupOptions struct {
GlobalOptions `yaml:",inline" json:",inline" figtree:",inline"`
jiradata.LinkIssueRequest `yaml:",inline" json:",inline" figtree:",inline"`
Duplicate string `yaml:"duplicate,omitempty" json:"duplicate,omitempty"`
Issue string `yaml:"issue,omitempty" json:"issue,omitempty"`
}
func CmdDupRegistry(fig *figtree.FigTree, o *oreo.Client) *CommandRegistryEntry {
opts := DupOptions{
GlobalOptions: GlobalOptions{
Template: figtree.NewStringOption("edit"),
},
LinkIssueRequest: jiradata.LinkIssueRequest{
Type: &jiradata.IssueLinkType{
Name: "Duplicate",
},
InwardIssue: &jiradata.IssueRef{},
OutwardIssue: &jiradata.IssueRef{},
},
}
return &CommandRegistryEntry{
"Mark issues as duplicate",
func() error {
return CmdDup(o, &opts)
},
func(cmd *kingpin.CmdClause) error {
LoadConfigs(cmd, fig, &opts)
return CmdDupUsage(cmd, &opts)
},
}
}
func CmdDupUsage(cmd *kingpin.CmdClause, opts *DupOptions) error {
if err := GlobalUsage(cmd, &opts.GlobalOptions); err != nil {
return err
}
BrowseUsage(cmd, &opts.GlobalOptions)
EditorUsage(cmd, &opts.GlobalOptions)
TemplateUsage(cmd, &opts.GlobalOptions)
cmd.Flag("comment", "Comment message when marking issue as duplicate").Short('m').PreAction(func(ctx *kingpin.ParseContext) error {
opts.Comment = &jiradata.Comment{
Body: flagValue(ctx, "comment"),
}
return nil
}).String()
cmd.Arg("DUPLICATE", "duplicate issue to mark closed").Required().StringVar(&opts.InwardIssue.Key)
cmd.Arg("ISSUE", "duplicate issue to leave open").Required().StringVar(&opts.OutwardIssue.Key)
return nil
}
// CmdDups will update the given issue as being a duplicate by the given dup issue
// and will attempt to resolve the dup issue
func CmdDup(o *oreo.Client, opts *DupOptions) error {
if err := jira.LinkIssues(o, opts.Endpoint.Value, &opts.LinkIssueRequest); err != nil {
return err
}
fmt.Printf("OK %s %s/browse/%s\n", opts.OutwardIssue.Key, opts.Endpoint.Value, opts.OutwardIssue.Key)
meta, err := jira.GetIssueTransitions(o, opts.Endpoint.Value, opts.InwardIssue.Key)
if err != nil {
return err
}
for _, trans := range []string{"close", "done", "start", "stop"} {
transMeta := meta.Transitions.Find(trans)
if transMeta != nil {
issueUpdate := jiradata.IssueUpdate{
Transition: transMeta,
}
if err = jira.TransitionIssue(o, opts.Endpoint.Value, opts.InwardIssue.Key, &issueUpdate); err != nil {
return err
}
// if we just started the issue now we need to stop it
if trans != "start" {
break
}
}
}
fmt.Printf("OK %s %s/browse/%s\n", opts.InwardIssue.Key, opts.Endpoint.Value, opts.InwardIssue.Key)
if opts.Browse.Value {
if err := CmdBrowse(&BrowseOptions{opts.GlobalOptions, opts.OutwardIssue.Key}); err != nil {
return CmdBrowse(&BrowseOptions{opts.GlobalOptions, opts.InwardIssue.Key})
}
}
return nil
}
-123
View File
@@ -1,123 +0,0 @@
package jiracli
import (
"fmt"
"github.com/coryb/figtree"
"github.com/coryb/oreo"
jira "gopkg.in/Netflix-Skunkworks/go-jira.v1"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiradata"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
type EditOptions struct {
GlobalOptions `yaml:",inline" json:",inline" figtree:",inline"`
jiradata.IssueUpdate `yaml:",inline" json:",inline" figtree:",inline"`
jira.SearchOptions `yaml:",inline" json:",inline" figtree:",inline"`
Overrides map[string]string `yaml:"overrides,omitempty" json:"overrides,omitempty"`
Issue string `yaml:"issue,omitempty" json:"issue,omitempty"`
}
func CmdEditRegistry(fig *figtree.FigTree, o *oreo.Client) *CommandRegistryEntry {
opts := EditOptions{
GlobalOptions: GlobalOptions{
Template: figtree.NewStringOption("edit"),
},
Overrides: map[string]string{},
}
return &CommandRegistryEntry{
"Edit issue details",
func() error {
return CmdEdit(o, &opts)
},
func(cmd *kingpin.CmdClause) error {
LoadConfigs(cmd, fig, &opts)
return CmdEditUsage(cmd, &opts)
},
}
}
func CmdEditUsage(cmd *kingpin.CmdClause, opts *EditOptions) error {
if err := GlobalUsage(cmd, &opts.GlobalOptions); err != nil {
return err
}
BrowseUsage(cmd, &opts.GlobalOptions)
EditorUsage(cmd, &opts.GlobalOptions)
TemplateUsage(cmd, &opts.GlobalOptions)
cmd.Flag("noedit", "Disable opening the editor").SetValue(&opts.SkipEditing)
cmd.Flag("query", "Jira Query Language (JQL) expression for the search to edit multiple issues").Short('q').StringVar(&opts.Query)
cmd.Flag("comment", "Comment message for issue").Short('m').PreAction(func(ctx *kingpin.ParseContext) error {
opts.Overrides["comment"] = flagValue(ctx, "comment")
return nil
}).String()
cmd.Flag("override", "Set issue property").Short('o').StringMapVar(&opts.Overrides)
cmd.Arg("ISSUE", "issue id to edit").StringVar(&opts.Issue)
return nil
}
// Edit will get issue data and send to "edit" template
func CmdEdit(o *oreo.Client, opts *EditOptions) error {
type templateInput struct {
*jiradata.Issue `yaml:",inline"`
Meta *jiradata.EditMeta `yaml:"meta" json:"meta"`
Overrides map[string]string `yaml:"overrides" json:"overrides"`
}
if opts.Issue != "" {
issueData, err := jira.GetIssue(o, opts.Endpoint.Value, opts.Issue, nil)
if err != nil {
return err
}
editMeta, err := jira.GetIssueEditMeta(o, opts.Endpoint.Value, opts.Issue)
if err != nil {
return err
}
issueUpdate := jiradata.IssueUpdate{}
input := templateInput{
Issue: issueData,
Meta: editMeta,
Overrides: opts.Overrides,
}
err = editLoop(&opts.GlobalOptions, &input, &issueUpdate, func() error {
return jira.EditIssue(o, opts.Endpoint.Value, opts.Issue, &issueUpdate)
})
if err != nil {
return err
}
fmt.Printf("OK %s %s/browse/%s\n", opts.Issue, opts.Endpoint.Value, opts.Issue)
if opts.Browse.Value {
return CmdBrowse(&BrowseOptions{opts.GlobalOptions, opts.Issue})
}
}
results, err := jira.Search(o, opts.Endpoint.Value, opts)
if err != nil {
return err
}
for _, issueData := range results.Issues {
editMeta, err := jira.GetIssueEditMeta(o, opts.Endpoint.Value, issueData.Key)
if err != nil {
return err
}
issueUpdate := jiradata.IssueUpdate{}
input := templateInput{
Issue: issueData,
Meta: editMeta,
}
err = editLoop(&opts.GlobalOptions, &input, &issueUpdate, func() error {
return jira.EditIssue(o, opts.Endpoint.Value, issueData.Key, &issueUpdate)
})
if err != nil {
return err
}
fmt.Printf("OK %s %s/browse/%s\n", issueData.Key, opts.Endpoint.Value, issueData.Key)
if opts.Browse.Value {
return CmdBrowse(&BrowseOptions{opts.GlobalOptions, issueData.Key})
}
}
return nil
}
-58
View File
@@ -1,58 +0,0 @@
package jiracli
import (
"github.com/coryb/figtree"
"github.com/coryb/oreo"
jira "gopkg.in/Netflix-Skunkworks/go-jira.v1"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
type EditMetaOptions struct {
GlobalOptions `yaml:",inline" json:",inline" figtree:",inline"`
Issue string `yaml:"issue,omitempty" json:"issue,omitempty"`
}
func CmdEditMetaRegistry(fig *figtree.FigTree, o *oreo.Client) *CommandRegistryEntry {
opts := EditMetaOptions{
GlobalOptions: GlobalOptions{
Template: figtree.NewStringOption("editmeta"),
},
}
return &CommandRegistryEntry{
"View 'edit' metadata",
func() error {
return CmdEditMeta(o, &opts)
},
func(cmd *kingpin.CmdClause) error {
LoadConfigs(cmd, fig, &opts)
return CmdEditMetaUsage(cmd, &opts)
},
}
}
func CmdEditMetaUsage(cmd *kingpin.CmdClause, opts *EditMetaOptions) error {
if err := GlobalUsage(cmd, &opts.GlobalOptions); err != nil {
return err
}
BrowseUsage(cmd, &opts.GlobalOptions)
TemplateUsage(cmd, &opts.GlobalOptions)
cmd.Arg("ISSUE", "edit metadata for issue id").Required().StringVar(&opts.Issue)
return nil
}
// EditMeta will get issue edit metadata and send to "editmeta" template
func CmdEditMeta(o *oreo.Client, opts *EditMetaOptions) error {
editMeta, err := jira.GetIssueEditMeta(o, opts.Endpoint.Value, opts.Issue)
if err != nil {
return err
}
if err := runTemplate(opts.Template.Value, editMeta, nil); err != nil {
return err
}
if opts.Browse.Value {
return CmdBrowse(&BrowseOptions{opts.GlobalOptions, opts.Issue})
}
return nil
}
+1 -1
View File
@@ -6,7 +6,7 @@ type Error struct {
error
}
func cliError(cause error) error {
func CliError(cause error) error {
return &Error{
errors.WithStack(cause),
}
-68
View File
@@ -1,68 +0,0 @@
package jiracli
import (
"fmt"
"os"
"path"
"github.com/coryb/figtree"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
type ExportTemplatesOptions struct {
Template string `yaml:"template,omitempty" json:"template,omitempty"`
Dir string `yaml:"dir,omitempty" json:"dir,omitempty"`
}
func CmdExportTemplatesRegistry(fig *figtree.FigTree) *CommandRegistryEntry {
opts := ExportTemplatesOptions{}
return &CommandRegistryEntry{
"Export templates for customizations",
func() error {
return CmdExportTemplates(&opts)
},
func(cmd *kingpin.CmdClause) error {
LoadConfigs(cmd, fig, &opts)
if opts.Dir == "" {
opts.Dir = fmt.Sprintf("%s/.jira.d/templates", Homedir())
}
return CmdExportTemplatesUsage(cmd, &opts)
},
}
}
func CmdExportTemplatesUsage(cmd *kingpin.CmdClause, opts *ExportTemplatesOptions) error {
cmd.Flag("template", "Template to export").Short('t').StringVar(&opts.Template)
cmd.Flag("dir", "directory to write tempates to").Short('d').StringVar(&opts.Dir)
return nil
}
// CmdExportTemplates will export templates to directory
func CmdExportTemplates(opts *ExportTemplatesOptions) error {
if err := os.MkdirAll(opts.Dir, 0755); err != nil {
return err
}
for name, template := range allTemplates {
if opts.Template != "" && opts.Template != name {
continue
}
templateFile := path.Join(opts.Dir, name)
if _, err := os.Stat(templateFile); err == nil {
log.Warning("Skipping %s, already exists", templateFile)
continue
}
fh, err := os.OpenFile(templateFile, os.O_WRONLY|os.O_CREATE, 0644)
if err != nil {
log.Errorf("Failed to open %s for writing: %s", templateFile, err)
return err
}
defer fh.Close()
log.Noticef("Creating %s", templateFile)
fh.Write([]byte(template))
}
return nil
}
-35
View File
@@ -1,35 +0,0 @@
package jiracli
import (
"github.com/coryb/figtree"
"github.com/coryb/oreo"
jira "gopkg.in/Netflix-Skunkworks/go-jira.v1"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
func CmdFieldsRegistry(fig *figtree.FigTree, o *oreo.Client) *CommandRegistryEntry {
opts := GlobalOptions{
Template: figtree.NewStringOption("fields"),
}
return &CommandRegistryEntry{
"Prints all fields, both System and Custom",
func() error {
return CmdFields(o, &opts)
},
func(cmd *kingpin.CmdClause) error {
LoadConfigs(cmd, fig, &opts)
err := GlobalUsage(cmd, &opts)
TemplateUsage(cmd, &opts)
return err
},
}
}
// Fields will send data from /rest/api/2/field API to "fields" template
func CmdFields(o *oreo.Client, opts *GlobalOptions) error {
data, err := jira.GetFields(o, opts.Endpoint.Value)
if err != nil {
return err
}
return runTemplate(opts.Template.Value, data, nil)
}
-76
View File
@@ -1,76 +0,0 @@
package jiracli
import (
"fmt"
"github.com/coryb/figtree"
"github.com/coryb/oreo"
jira "gopkg.in/Netflix-Skunkworks/go-jira.v1"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiradata"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
type IssueLinkOptions struct {
GlobalOptions `yaml:",inline" json:",inline" figtree:",inline"`
jiradata.LinkIssueRequest `yaml:",inline" json:",inline" figtree:",inline"`
LinkType string `yaml:"linktype,omitempty" json:"linktype,omitempty"`
}
func CmdIssueLinkRegistry(fig *figtree.FigTree, o *oreo.Client) *CommandRegistryEntry {
opts := IssueLinkOptions{
LinkIssueRequest: jiradata.LinkIssueRequest{
Type: &jiradata.IssueLinkType{},
InwardIssue: &jiradata.IssueRef{},
OutwardIssue: &jiradata.IssueRef{},
},
}
return &CommandRegistryEntry{
"Link two issues",
func() error {
return CmdIssueLink(o, &opts)
},
func(cmd *kingpin.CmdClause) error {
LoadConfigs(cmd, fig, &opts)
return CmdIssueLinkUsage(cmd, &opts)
},
}
}
func CmdIssueLinkUsage(cmd *kingpin.CmdClause, opts *IssueLinkOptions) error {
if err := GlobalUsage(cmd, &opts.GlobalOptions); err != nil {
return err
}
BrowseUsage(cmd, &opts.GlobalOptions)
EditorUsage(cmd, &opts.GlobalOptions)
TemplateUsage(cmd, &opts.GlobalOptions)
cmd.Flag("comment", "Comment message when linking issue").Short('m').PreAction(func(ctx *kingpin.ParseContext) error {
opts.Comment = &jiradata.Comment{
Body: flagValue(ctx, "comment"),
}
return nil
}).String()
cmd.Arg("OUTWARDISSUE", "outward issue").Required().StringVar(&opts.OutwardIssue.Key)
cmd.Arg("ISSUELINKTYPE", "issue link type").Required().StringVar(&opts.Type.Name)
cmd.Arg("INWARDISSUE", "inward issue").Required().StringVar(&opts.InwardIssue.Key)
return nil
}
// CmdBlock will update the given issue as being a duplicate by the given dup issue
// and will attempt to resolve the dup issue
func CmdIssueLink(o *oreo.Client, opts *IssueLinkOptions) error {
if err := jira.LinkIssues(o, opts.Endpoint.Value, &opts.LinkIssueRequest); err != nil {
return err
}
fmt.Printf("OK %s %s/browse/%s\n", opts.InwardIssue.Key, opts.Endpoint.Value, opts.InwardIssue.Key)
fmt.Printf("OK %s %s/browse/%s\n", opts.OutwardIssue.Key, opts.Endpoint.Value, opts.OutwardIssue.Key)
if opts.Browse.Value {
if err := CmdBrowse(&BrowseOptions{opts.GlobalOptions, opts.OutwardIssue.Key}); err != nil {
return CmdBrowse(&BrowseOptions{opts.GlobalOptions, opts.InwardIssue.Key})
}
}
return nil
}
-42
View File
@@ -1,42 +0,0 @@
package jiracli
import (
"github.com/coryb/figtree"
"github.com/coryb/oreo"
jira "gopkg.in/Netflix-Skunkworks/go-jira.v1"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
func CmdIssueLinkTypesRegistry(fig *figtree.FigTree, o *oreo.Client) *CommandRegistryEntry {
opts := GlobalOptions{
Template: figtree.NewStringOption("issuelinktypes"),
}
return &CommandRegistryEntry{
"Show the issue link types",
func() error {
return CmdIssueLinkTypes(o, &opts)
},
func(cmd *kingpin.CmdClause) error {
LoadConfigs(cmd, fig, &opts)
return CmdIssueLinkTypesUsage(cmd, &opts)
},
}
}
func CmdIssueLinkTypesUsage(cmd *kingpin.CmdClause, opts *GlobalOptions) error {
if err := GlobalUsage(cmd, opts); err != nil {
return err
}
TemplateUsage(cmd, opts)
return nil
}
// CmdIssueLinkTypes will get issue link type data and send to "issuelinktypes" template
func CmdIssueLinkTypes(o *oreo.Client, opts *GlobalOptions) error {
data, err := jira.GetIssueLinkTypes(o, opts.Endpoint.Value)
if err != nil {
return err
}
return runTemplate(opts.Template.Value, data, nil)
}
-57
View File
@@ -1,57 +0,0 @@
package jiracli
import (
"fmt"
"github.com/coryb/figtree"
"github.com/coryb/oreo"
jira "gopkg.in/Netflix-Skunkworks/go-jira.v1"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
type IssueTypesOptions struct {
GlobalOptions `yaml:",inline" json:",inline" figtree:",inline"`
Project string `yaml:"project,omitempty" json:"project,omitempty"`
}
func CmdIssueTypesRegistry(fig *figtree.FigTree, o *oreo.Client) *CommandRegistryEntry {
opts := IssueTypesOptions{
GlobalOptions: GlobalOptions{
Template: figtree.NewStringOption("issuetypes"),
},
}
return &CommandRegistryEntry{
"Show issue types for a project",
func() error {
return CmdIssueTypes(o, &opts)
},
func(cmd *kingpin.CmdClause) error {
LoadConfigs(cmd, fig, &opts)
return CmdIssueTypesUsage(cmd, &opts)
},
}
}
func CmdIssueTypesUsage(cmd *kingpin.CmdClause, opts *IssueTypesOptions) error {
if err := GlobalUsage(cmd, &opts.GlobalOptions); err != nil {
return err
}
TemplateUsage(cmd, &opts.GlobalOptions)
cmd.Flag("project", "project to list issueTypes").Short('p').StringVar(&opts.Project)
return nil
}
// CmdIssueTypes will get available issueTypes for project and send to the "issueTypes" template
func CmdIssueTypes(o *oreo.Client, opts *IssueTypesOptions) error {
if opts.Project == "" {
return fmt.Errorf("Project Required.")
}
data, err := jira.GetIssueCreateMetaProject(o, opts.Endpoint.Value, opts.Project)
if err != nil {
return err
}
return runTemplate(opts.Template.Value, data, nil)
}
-66
View File
@@ -1,66 +0,0 @@
package jiracli
import (
"fmt"
"github.com/coryb/figtree"
"github.com/coryb/oreo"
jira "gopkg.in/Netflix-Skunkworks/go-jira.v1"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiradata"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
type LabelsAddOptions struct {
GlobalOptions `yaml:",inline" json:",inline" figtree:",inline"`
Issue string `yaml:"issue,omitempty" json:"issue,omitempty"`
Labels []string `yaml:"labels,omitempty" json:"labels,omitempty"`
}
func CmdLabelsAddRegistry(fig *figtree.FigTree, o *oreo.Client) *CommandRegistryEntry {
opts := LabelsAddOptions{}
return &CommandRegistryEntry{
"Add labels to an issue",
func() error {
return CmdLabelsAdd(o, &opts)
},
func(cmd *kingpin.CmdClause) error {
LoadConfigs(cmd, fig, &opts)
return CmdLabelsAddUsage(cmd, &opts)
},
}
}
func CmdLabelsAddUsage(cmd *kingpin.CmdClause, opts *LabelsAddOptions) error {
if err := GlobalUsage(cmd, &opts.GlobalOptions); err != nil {
return err
}
BrowseUsage(cmd, &opts.GlobalOptions)
cmd.Arg("ISSUE", "issue id to modify labels").Required().StringVar(&opts.Issue)
cmd.Arg("LABEL", "label to add to issue").Required().StringsVar(&opts.Labels)
return nil
}
// CmdLabels will add labels on a given issue
func CmdLabelsAdd(o *oreo.Client, opts *LabelsAddOptions) error {
ops := jiradata.FieldOperations{}
for _, label := range opts.Labels {
ops = append(ops, jiradata.FieldOperation{
"add": label,
})
}
issueUpdate := jiradata.IssueUpdate{
Update: jiradata.FieldOperationsMap{
"labels": ops,
},
}
if err := jira.EditIssue(o, opts.Endpoint.Value, opts.Issue, &issueUpdate); err != nil {
return err
}
fmt.Printf("OK %s %s/browse/%s\n", opts.Issue, opts.Endpoint.Value, opts.Issue)
if opts.Browse.Value {
return CmdBrowse(&BrowseOptions{opts.GlobalOptions, opts.Issue})
}
return nil
}
-67
View File
@@ -1,67 +0,0 @@
package jiracli
import (
"fmt"
"github.com/coryb/figtree"
"github.com/coryb/oreo"
jira "gopkg.in/Netflix-Skunkworks/go-jira.v1"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiradata"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
type LabelsRemoveOptions struct {
GlobalOptions `yaml:",inline" json:",inline" figtree:",inline"`
Issue string `yaml:"issue,omitempty" json:"issue,omitempty"`
Labels []string `yaml:"labels,omitempty" json:"labels,omitempty"`
}
func CmdLabelsRemoveRegistry(fig *figtree.FigTree, o *oreo.Client) *CommandRegistryEntry {
opts := LabelsRemoveOptions{}
return &CommandRegistryEntry{
"Remove labels from an issue",
func() error {
return CmdLabelsRemove(o, &opts)
},
func(cmd *kingpin.CmdClause) error {
LoadConfigs(cmd, fig, &opts)
return CmdLabelsRemoveUsage(cmd, &opts)
},
}
}
func CmdLabelsRemoveUsage(cmd *kingpin.CmdClause, opts *LabelsRemoveOptions) error {
if err := GlobalUsage(cmd, &opts.GlobalOptions); err != nil {
return err
}
BrowseUsage(cmd, &opts.GlobalOptions)
cmd.Arg("ISSUE", "issue id to modify labels").Required().StringVar(&opts.Issue)
cmd.Arg("LABEL", "label to remove from issue").Required().StringsVar(&opts.Labels)
return nil
}
// CmdLabels will remove labels on a given issue
func CmdLabelsRemove(o *oreo.Client, opts *LabelsRemoveOptions) error {
ops := jiradata.FieldOperations{}
for _, label := range opts.Labels {
ops = append(ops, jiradata.FieldOperation{
"remove": label,
})
}
issueUpdate := jiradata.IssueUpdate{
Update: jiradata.FieldOperationsMap{
"labels": ops,
},
}
err := jira.EditIssue(o, opts.Endpoint.Value, opts.Issue, &issueUpdate)
if err != nil {
return err
}
fmt.Printf("OK %s %s/browse/%s\n", opts.Issue, opts.Endpoint.Value, opts.Issue)
if opts.Browse.Value {
return CmdBrowse(&BrowseOptions{opts.GlobalOptions, opts.Issue})
}
return nil
}
-64
View File
@@ -1,64 +0,0 @@
package jiracli
import (
"fmt"
"github.com/coryb/figtree"
"github.com/coryb/oreo"
jira "gopkg.in/Netflix-Skunkworks/go-jira.v1"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiradata"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
type LabelsSetOptions struct {
GlobalOptions `yaml:",inline" json:",inline" figtree:",inline"`
Issue string `yaml:"issue,omitempty" json:"issue,omitempty"`
Labels []string `yaml:"labels,omitempty" json:"labels,omitempty"`
}
func CmdLabelsSetRegistry(fig *figtree.FigTree, o *oreo.Client) *CommandRegistryEntry {
opts := LabelsSetOptions{}
return &CommandRegistryEntry{
"Set labels on an issue",
func() error {
return CmdLabelsSet(o, &opts)
},
func(cmd *kingpin.CmdClause) error {
LoadConfigs(cmd, fig, &opts)
return CmdLabelsSetUsage(cmd, &opts)
},
}
}
func CmdLabelsSetUsage(cmd *kingpin.CmdClause, opts *LabelsSetOptions) error {
if err := GlobalUsage(cmd, &opts.GlobalOptions); err != nil {
return err
}
BrowseUsage(cmd, &opts.GlobalOptions)
cmd.Arg("ISSUE", "issue id to modify labels").Required().StringVar(&opts.Issue)
cmd.Arg("LABEL", "label to set on issue").Required().StringsVar(&opts.Labels)
return nil
}
// CmdLabels will set labels on a given issue
func CmdLabelsSet(o *oreo.Client, opts *LabelsSetOptions) error {
issueUpdate := jiradata.IssueUpdate{
Update: jiradata.FieldOperationsMap{
"labels": jiradata.FieldOperations{
jiradata.FieldOperation{
"set": opts.Labels,
},
},
},
}
if err := jira.EditIssue(o, opts.Endpoint.Value, opts.Issue, &issueUpdate); err != nil {
return err
}
fmt.Printf("OK %s %s/browse/%s\n", opts.Issue, opts.Endpoint.Value, opts.Issue)
if opts.Browse.Value {
return CmdBrowse(&BrowseOptions{opts.GlobalOptions, opts.Issue})
}
return nil
}
-68
View File
@@ -1,68 +0,0 @@
package jiracli
import (
"github.com/coryb/figtree"
"github.com/coryb/oreo"
jira "gopkg.in/Netflix-Skunkworks/go-jira.v1"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
type ListOptions struct {
GlobalOptions `yaml:",inline" json:",inline" figtree:",inline"`
jira.SearchOptions `yaml:",inline" json:",inline" figtree:",inline"`
}
func CmdListRegistry(fig *figtree.FigTree, o *oreo.Client) *CommandRegistryEntry {
opts := ListOptions{
GlobalOptions: GlobalOptions{
Template: figtree.NewStringOption("list"),
},
}
return &CommandRegistryEntry{
"Prints list of issues for given search criteria",
func() error {
return CmdList(o, &opts)
},
func(cmd *kingpin.CmdClause) error {
LoadConfigs(cmd, fig, &opts)
if opts.MaxResults == 0 {
opts.MaxResults = 500
}
if opts.QueryFields == "" {
opts.QueryFields = "assignee,created,priority,reporter,status,summary,updated"
}
if opts.Sort == "" {
opts.Sort = "priority asc, key"
}
return CmdListUsage(cmd, &opts)
},
}
}
func CmdListUsage(cmd *kingpin.CmdClause, opts *ListOptions) error {
if err := GlobalUsage(cmd, &opts.GlobalOptions); err != nil {
return err
}
TemplateUsage(cmd, &opts.GlobalOptions)
cmd.Flag("assignee", "User assigned the issue").Short('a').StringVar(&opts.Assignee)
cmd.Flag("component", "Component to search for").Short('c').StringVar(&opts.Component)
cmd.Flag("issuetype", "Issue type to search for").Short('i').StringVar(&opts.IssueType)
cmd.Flag("limit", "Maximum number of results to return in search").Short('l').IntVar(&opts.MaxResults)
cmd.Flag("project", "Project to search for").Short('p').StringVar(&opts.Project)
cmd.Flag("query", "Jira Query Language (JQL) expression for the search").Short('q').StringVar(&opts.Query)
cmd.Flag("queryfields", "Fields that are used in \"list\" template").Short('f').StringVar(&opts.QueryFields)
cmd.Flag("reporter", "Reporter to search for").Short('r').StringVar(&opts.Reporter)
cmd.Flag("sort", "Sort order to return").Short('s').StringVar(&opts.Sort)
cmd.Flag("watcher", "Watcher to search for").Short('w').StringVar(&opts.Watcher)
return nil
}
// List will query jira and send data to "list" template
func CmdList(o *oreo.Client, opts *ListOptions) error {
data, err := jira.Search(o, opts.Endpoint.Value, opts)
if err != nil {
return err
}
return runTemplate(opts.Template.Value, data, nil)
}
-67
View File
@@ -1,67 +0,0 @@
package jiracli
import (
"fmt"
"net/http"
"github.com/coryb/figtree"
"github.com/coryb/oreo"
"github.com/mgutz/ansi"
jira "gopkg.in/Netflix-Skunkworks/go-jira.v1"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
func CmdLoginRegistry(fig *figtree.FigTree, o *oreo.Client) *CommandRegistryEntry {
opts := GlobalOptions{}
return &CommandRegistryEntry{
"Attempt to login into jira server",
func() error {
return CmdLogin(o, &opts)
},
func(cmd *kingpin.CmdClause) error {
LoadConfigs(cmd, fig, &opts)
return GlobalUsage(cmd, &opts)
},
}
}
func authCallback(req *http.Request, resp *http.Response) (*http.Response, error) {
if resp.StatusCode == 403 {
defer resp.Body.Close()
// X-Authentication-Denied-Reason: CAPTCHA_CHALLENGE; login-url=https://jira/login.jsp
if reason := resp.Header.Get("X-Authentication-Denied-Reason"); reason != "" {
return resp, fmt.Errorf("Authenticaion Failed: " + reason)
}
return resp, fmt.Errorf("Authenticaion Failed: Unkown Reason")
} else if resp.StatusCode == 200 {
if reason := resp.Header.Get("X-Seraph-Loginreason"); reason == "AUTHENTICATION_DENIED" {
defer resp.Body.Close()
return resp, fmt.Errorf("Authentication Failed: " + reason)
}
}
return resp, nil
}
// CmdLogin will attempt to login into jira server
func CmdLogin(o *oreo.Client, opts *GlobalOptions) error {
ua := o.WithoutRedirect().WithRetries(0).WithoutCallbacks().WithPostCallback(authCallback)
for {
if session, err := jira.GetSession(o, opts.Endpoint.Value); err != nil {
// No active session so try to create a new one
_, err := jira.NewSession(ua, opts.Endpoint.Value, opts)
if err != nil {
// reset password on failed session
opts.SetPass("")
log.Errorf("%s", err)
continue
}
fmt.Println(ansi.Color("OK", "green"), "New session for", opts.User)
break
} else {
fmt.Println(ansi.Color("OK", "green"), "Found session for", session.Name)
break
}
}
return nil
}
-37
View File
@@ -1,37 +0,0 @@
package jiracli
import (
"fmt"
"github.com/coryb/figtree"
"github.com/coryb/oreo"
"github.com/mgutz/ansi"
jira "gopkg.in/Netflix-Skunkworks/go-jira.v1"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
func CmdLogoutRegistry(fig *figtree.FigTree, o *oreo.Client) *CommandRegistryEntry {
opts := GlobalOptions{}
return &CommandRegistryEntry{
"Deactivate sesssion with Jira server",
func() error {
return CmdLogout(o, &opts)
},
func(cmd *kingpin.CmdClause) error {
LoadConfigs(cmd, fig, &opts)
return GlobalUsage(cmd, &opts)
},
}
}
// CmdLogout will attempt to terminate an active Jira session
func CmdLogout(o *oreo.Client, opts *GlobalOptions) error {
ua := o.WithoutRedirect().WithRetries(0).WithoutCallbacks()
err := jira.DeleteSession(ua, opts.Endpoint.Value)
if err == nil {
fmt.Println(ansi.Color("OK", "green"), "Terminated session for", opts.User)
} else {
fmt.Printf("%s Failed to terminate session for %s: %s", ansi.Color("ERROR", "red"), opts.User, err)
}
return nil
}
-73
View File
@@ -1,73 +0,0 @@
package jiracli
import (
"fmt"
"github.com/coryb/figtree"
"github.com/coryb/oreo"
jira "gopkg.in/Netflix-Skunkworks/go-jira.v1"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiradata"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
type RankOptions struct {
GlobalOptions `yaml:",inline" json:",inline" figtree:",inline"`
First string `yaml:"first,omitempty" json:"first,omitempty"`
Second string `yaml:"second,omitempty" json:"second,omitempty"`
Order string `yaml:"order,omitempty" json:"order,omitempty"`
}
func CmdRankRegistry(fig *figtree.FigTree, o *oreo.Client) *CommandRegistryEntry {
opts := RankOptions{}
return &CommandRegistryEntry{
"Mark issues as blocker",
func() error {
return CmdRank(o, &opts)
},
func(cmd *kingpin.CmdClause) error {
LoadConfigs(cmd, fig, &opts)
return CmdRankUsage(cmd, &opts)
},
}
}
func CmdRankUsage(cmd *kingpin.CmdClause, opts *RankOptions) error {
if err := GlobalUsage(cmd, &opts.GlobalOptions); err != nil {
return err
}
BrowseUsage(cmd, &opts.GlobalOptions)
cmd.Arg("FIRST-ISSUE", "first issue").Required().StringVar(&opts.First)
cmd.Arg("after|before", "rank ordering").Required().HintOptions("after", "before").EnumVar(&opts.Order, "after", "before")
cmd.Arg("SECOND-ISSUE", "second issue").Required().StringVar(&opts.Second)
return nil
}
// CmdRank order two issue
func CmdRank(o *oreo.Client, opts *RankOptions) error {
req := &jiradata.RankRequest{
Issues: []string{opts.First},
}
if opts.Order == "after" {
req.RankAfterIssue = opts.Second
} else {
req.RankBeforeIssue = opts.Second
}
if err := jira.RankIssues(o, opts.Endpoint.Value, req); err != nil {
return err
}
fmt.Printf("OK %s %s/browse/%s\n", opts.First, opts.Endpoint.Value, opts.First)
fmt.Printf("OK %s %s/browse/%s\n", opts.Second, opts.Endpoint.Value, opts.Second)
if opts.Browse.Value {
if err := CmdBrowse(&BrowseOptions{opts.GlobalOptions, opts.First}); err != nil {
return CmdBrowse(&BrowseOptions{opts.GlobalOptions, opts.Second})
}
}
return nil
}
-93
View File
@@ -1,93 +0,0 @@
package jiracli
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/url"
"strings"
"github.com/coryb/figtree"
"github.com/coryb/oreo"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
type RequestOptions struct {
GlobalOptions `yaml:",inline" json:",inline" figtree:",inline"`
Method string `yaml:"method,omitempty" json:"method,omitempty"`
URI string `yaml:"uri,omitempty" json:"uri,omitempty"`
Data string `yaml:"data,omitempty" json:"data,omitempty"`
}
func CmdRequestRegistry(fig *figtree.FigTree, o *oreo.Client) *CommandRegistryEntry {
opts := RequestOptions{
GlobalOptions: GlobalOptions{
Template: figtree.NewStringOption("request"),
},
}
return &CommandRegistryEntry{
"Open issue in requestr",
func() error {
return CmdRequest(o, &opts)
},
func(cmd *kingpin.CmdClause) error {
LoadConfigs(cmd, fig, &opts)
if opts.Method == "" {
opts.Method = "GET"
}
return CmdRequestUsage(cmd, &opts)
},
}
}
func CmdRequestUsage(cmd *kingpin.CmdClause, opts *RequestOptions) error {
if err := GlobalUsage(cmd, &opts.GlobalOptions); err != nil {
return err
}
cmd.Flag("method", "HTTP request method to use").Short('m').EnumVar(&opts.Method, "GET", "PUT", "POST", "DELETE")
cmd.Arg("API", "Path to Jira API (ie: /rest/api/2/issue)").Required().StringVar(&opts.URI)
cmd.Arg("JSON", "JSON Content to send to API").Required().StringVar(&opts.Data)
return nil
}
// CmdRequest open the default system requestr to the provided issue
func CmdRequest(o *oreo.Client, opts *RequestOptions) error {
uri := opts.URI
if !strings.HasPrefix(uri, "http") {
uri = opts.Endpoint.Value + uri
}
parsedURI, err := url.Parse(uri)
if err != nil {
return err
}
builder := oreo.RequestBuilder(parsedURI).WithMethod(opts.Method)
if opts.Data != "" {
builder = builder.WithJSON(opts.Data)
}
resp, err := o.Do(builder.Build())
if err != nil {
return err
}
defer resp.Body.Close()
content, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
if len(content) == 0 {
fmt.Println("No Content")
return nil
}
var data interface{}
err = json.Unmarshal(content, &data)
if err != nil {
return fmt.Errorf("JSON Parse Error: %s from %q", err, content)
}
return runTemplate(opts.Template.Value, &data, nil)
}
-118
View File
@@ -1,118 +0,0 @@
package jiracli
import (
"fmt"
"github.com/coryb/figtree"
"github.com/coryb/oreo"
jira "gopkg.in/Netflix-Skunkworks/go-jira.v1"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiradata"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
type SubtaskOptions struct {
GlobalOptions `yaml:",inline" json:",inline" figtree:",inline"`
jiradata.IssueUpdate `yaml:",inline" json:",inline" figtree:",inline"`
Project string `yaml:"project,omitempty" json:"project,omitempty"`
IssueType string `yaml:"issuetype,omitempty" json:"issuetype,omitempty"`
Overrides map[string]string `yaml:"overrides,omitempty" json:"overrides,omitempty"`
Issue string `yaml:"issue,omitempty" json:"issue,omitempty"`
}
func CmdSubtaskRegistry(fig *figtree.FigTree, o *oreo.Client) *CommandRegistryEntry {
opts := SubtaskOptions{
GlobalOptions: GlobalOptions{
Template: figtree.NewStringOption("subtask"),
},
Overrides: map[string]string{},
}
return &CommandRegistryEntry{
"Subtask issue",
func() error {
return CmdSubtask(o, &opts)
},
func(cmd *kingpin.CmdClause) error {
LoadConfigs(cmd, fig, &opts)
if opts.IssueType == "" {
opts.IssueType = "Sub-task"
}
return CmdSubtaskUsage(cmd, &opts)
},
}
}
func CmdSubtaskUsage(cmd *kingpin.CmdClause, opts *SubtaskOptions) error {
if err := GlobalUsage(cmd, &opts.GlobalOptions); err != nil {
return err
}
BrowseUsage(cmd, &opts.GlobalOptions)
EditorUsage(cmd, &opts.GlobalOptions)
TemplateUsage(cmd, &opts.GlobalOptions)
cmd.Flag("noedit", "Disable opening the editor").SetValue(&opts.SkipEditing)
cmd.Flag("project", "project to subtask issue in").Short('p').StringVar(&opts.Project)
cmd.Flag("comment", "Comment message for issue").Short('m').PreAction(func(ctx *kingpin.ParseContext) error {
opts.Overrides["comment"] = flagValue(ctx, "comment")
return nil
}).String()
cmd.Flag("override", "Set issue property").Short('o').StringMapVar(&opts.Overrides)
cmd.Arg("ISSUE", "Parent issue for subtask").StringVar(&opts.Issue)
return nil
}
// CmdSubtask sends the subtask-metadata to the "subtask" template for editing, then
// will parse the edited document as YAML and submit the document to jira.
func CmdSubtask(o *oreo.Client, opts *SubtaskOptions) error {
type templateInput struct {
Meta *jiradata.IssueType `yaml:"meta" json:"meta"`
Overrides map[string]string `yaml:"overrides" json:"overrides"`
Parent *jiradata.Issue `yaml:"parent" json:"parent"`
}
parent, err := jira.GetIssue(o, opts.Endpoint.Value, opts.Issue, nil)
if err != nil {
return err
}
if project, ok := parent.Fields["project"].(map[string]interface{}); ok {
if key, ok := project["key"].(string); ok {
opts.Project = key
} else {
return fmt.Errorf("Failed to find Project Key in parent issue")
}
} else {
return fmt.Errorf("Failed to find Project field in parent issue")
}
createMeta, err := jira.GetIssueCreateMetaIssueType(o, opts.Endpoint.Value, opts.Project, opts.IssueType)
if err != nil {
return err
}
issueUpdate := jiradata.IssueUpdate{}
input := templateInput{
Meta: createMeta,
Overrides: opts.Overrides,
Parent: parent,
}
input.Overrides["project"] = opts.Project
input.Overrides["issuetype"] = opts.IssueType
input.Overrides["user"] = opts.User.Value
var issueResp *jiradata.IssueCreateResponse
err = editLoop(&opts.GlobalOptions, &input, &issueUpdate, func() error {
issueResp, err = jira.CreateIssue(o, opts.Endpoint.Value, &issueUpdate)
return err
})
if err != nil {
return err
}
fmt.Printf("OK %s %s/browse/%s\n", issueResp.Key, opts.Endpoint.Value, issueResp.Key)
if opts.Browse.Value {
return CmdBrowse(&BrowseOptions{opts.GlobalOptions, issueResp.Key})
}
return nil
}
-32
View File
@@ -1,32 +0,0 @@
package jiracli
import (
"github.com/coryb/figtree"
"github.com/coryb/oreo"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
func CmdTakeRegistry(fig *figtree.FigTree, o *oreo.Client) *CommandRegistryEntry {
opts := AssignOptions{}
return &CommandRegistryEntry{
"Assign issue to yourself",
func() error {
opts.Assignee = opts.User.Value
return CmdAssign(o, &opts)
},
func(cmd *kingpin.CmdClause) error {
LoadConfigs(cmd, fig, &opts)
return CmdAssignUsage(cmd, &opts)
},
}
}
func CmdTakeUsage(cmd *kingpin.CmdClause, opts *AssignOptions) error {
if err := GlobalUsage(cmd, &opts.GlobalOptions); err != nil {
return err
}
BrowseUsage(cmd, &opts.GlobalOptions)
cmd.Arg("ISSUE", "issue to assign").Required().StringVar(&opts.Issue)
return nil
}
+4 -4
View File
@@ -44,7 +44,7 @@ func getTemplate(name string) (string, error) {
} else if b != nil {
return string(b), nil
}
if s, ok := allTemplates[name]; ok {
if s, ok := AllTemplates[name]; ok {
return s, nil
}
return "", fmt.Errorf("No Template found for %q", name)
@@ -56,7 +56,7 @@ func tmpTemplate(templateName string, data interface{}) (string, error) {
return "", err
}
defer tmpFile.Close()
return tmpFile.Name(), runTemplate(templateName, data, tmpFile)
return tmpFile.Name(), RunTemplate(templateName, data, tmpFile)
}
func TemplateProcessor() *template.Template {
@@ -155,7 +155,7 @@ func TemplateProcessor() *template.Template {
return template.New("gojira").Funcs(funcs)
}
func runTemplate(templateName string, data interface{}, out io.Writer) error {
func RunTemplate(templateName string, data interface{}, out io.Writer) error {
templateContent, err := getTemplate(templateName)
if err != nil {
@@ -194,7 +194,7 @@ func runTemplate(templateName string, data interface{}, out io.Writer) error {
return nil
}
var allTemplates = map[string]string{
var AllTemplates = map[string]string{
"component-add": defaultComponentAddTemplate,
"debug": defaultDebugTemplate,
"fields": defaultDebugTemplate,
-142
View File
@@ -1,142 +0,0 @@
package jiracli
import (
"fmt"
"strings"
"github.com/coryb/figtree"
"github.com/coryb/oreo"
jira "gopkg.in/Netflix-Skunkworks/go-jira.v1"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiradata"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
type TransitionOptions struct {
GlobalOptions `yaml:",inline" json:",inline" figtree:",inline"`
Overrides map[string]string `yaml:"overrides,omitempty" json:"overrides,omitempty"`
Transition string `yaml:"transition,omitempty" json:"transition,omitempty"`
Issue string `yaml:"issue,omitempty" json:"issue,omitempty"`
Resolution string `yaml:"resolution,omitempty" json:"resolution,omitempty"`
}
func CmdTransitionRegistry(fig *figtree.FigTree, o *oreo.Client, transition string) *CommandRegistryEntry {
opts := TransitionOptions{
GlobalOptions: GlobalOptions{
Template: figtree.NewStringOption("transition"),
},
Overrides: map[string]string{},
}
help := "Transition issue to given state"
if transition != "" {
help = fmt.Sprintf("Transition issue to %s state", transition)
opts.SkipEditing = figtree.NewBoolOption(true)
}
return &CommandRegistryEntry{
help,
func() error {
return CmdTransition(o, &opts)
},
func(cmd *kingpin.CmdClause) error {
LoadConfigs(cmd, fig, &opts)
if opts.Transition == "" {
opts.Transition = transition
}
return CmdTransitionUsage(cmd, &opts)
},
}
}
func CmdTransitionUsage(cmd *kingpin.CmdClause, opts *TransitionOptions) error {
if err := GlobalUsage(cmd, &opts.GlobalOptions); err != nil {
return err
}
BrowseUsage(cmd, &opts.GlobalOptions)
TemplateUsage(cmd, &opts.GlobalOptions)
cmd.Flag("noedit", "Disable opening the editor").SetValue(&opts.SkipEditing)
cmd.Flag("comment", "Comment message for issue").Short('m').PreAction(func(ctx *kingpin.ParseContext) error {
opts.Overrides["comment"] = flagValue(ctx, "comment")
return nil
}).String()
cmd.Flag("override", "Set issue property").Short('o').StringMapVar(&opts.Overrides)
if opts.Transition == "" {
cmd.Arg("TRANSITION", "State to transition issue to").Required().StringVar(&opts.Transition)
}
cmd.Arg("ISSUE", "issue to transition").Required().StringVar(&opts.Issue)
return nil
}
// CmdTransition will move state of the given issue to the given transtion
func CmdTransition(o *oreo.Client, opts *TransitionOptions) error {
issueData, err := jira.GetIssue(o, opts.Endpoint.Value, opts.Issue, nil)
if err != nil {
return cliError(err)
}
meta, err := jira.GetIssueTransitions(o, opts.Endpoint.Value, opts.Issue)
if err != nil {
return cliError(err)
}
transMeta := meta.Transitions.Find(opts.Transition)
if transMeta == nil {
possible := []string{}
for _, trans := range meta.Transitions {
possible = append(possible, trans.Name)
}
if status, ok := issueData.Fields["status"].(map[string]interface{}); ok {
if name, ok := status["name"].(string); ok {
return cliError(fmt.Errorf("Invalid Transition %q from %q, Available: %s", opts.Transition, name, strings.Join(possible, ", ")))
}
}
return cliError(fmt.Errorf("No valid transition found matching %s", opts.Transition))
}
// need to default the Resolution, usually Fixed works but sometime need Done
if opts.Resolution == "" {
if resField, ok := transMeta.Fields["resolution"]; ok {
for _, allowedValueRaw := range resField.AllowedValues {
if allowedValue, ok := allowedValueRaw.(map[string]interface{}); ok {
if allowedValue["name"] == "Fixed" {
opts.Resolution = "Fixed"
} else if allowedValue["name"] == "Done" {
opts.Resolution = "Done"
}
}
}
}
}
opts.Overrides["resolution"] = opts.Resolution
type templateInput struct {
*jiradata.Issue `yaml:",inline"`
// Yes, Meta and Transition are redundant, but this is for backwards compatibility
// with old templates
Meta *jiradata.Transition `yaml:"meta,omitempty" json:"meta,omitemtpy"`
Transition *jiradata.Transition `yaml:"transition,omitempty" json:"transition,omitempty"`
Overrides map[string]string `yaml:"overrides,omitempty" json:"overrides,omitempty"`
}
issueUpdate := jiradata.IssueUpdate{}
input := templateInput{
Issue: issueData,
Meta: transMeta,
Transition: transMeta,
Overrides: opts.Overrides,
}
err = editLoop(&opts.GlobalOptions, &input, &issueUpdate, func() error {
return jira.TransitionIssue(o, opts.Endpoint.Value, opts.Issue, &issueUpdate)
})
if err != nil {
return cliError(err)
}
fmt.Printf("OK %s %s/browse/%s\n", issueData.Key, opts.Endpoint.Value, issueData.Key)
if opts.Browse.Value {
return CmdBrowse(&BrowseOptions{opts.GlobalOptions, opts.Issue})
}
return nil
}
-57
View File
@@ -1,57 +0,0 @@
package jiracli
import (
"github.com/coryb/figtree"
"github.com/coryb/oreo"
jira "gopkg.in/Netflix-Skunkworks/go-jira.v1"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
type TransitionsOptions struct {
GlobalOptions `yaml:",inline" json:",inline" figtree:",inline"`
Issue string `yaml:"issue,omitempty" json:"issue,omitempty"`
}
func CmdTransitionsRegistry(fig *figtree.FigTree, o *oreo.Client, defaultTemplate string) *CommandRegistryEntry {
opts := TransitionsOptions{
GlobalOptions: GlobalOptions{
Template: figtree.NewStringOption(defaultTemplate),
},
}
return &CommandRegistryEntry{
"List valid issue transitions",
func() error {
return CmdTransitions(o, &opts)
},
func(cmd *kingpin.CmdClause) error {
LoadConfigs(cmd, fig, &opts)
return CmdTransitionsUsage(cmd, &opts)
},
}
}
func CmdTransitionsUsage(cmd *kingpin.CmdClause, opts *TransitionsOptions) error {
if err := GlobalUsage(cmd, &opts.GlobalOptions); err != nil {
return err
}
BrowseUsage(cmd, &opts.GlobalOptions)
TemplateUsage(cmd, &opts.GlobalOptions)
cmd.Arg("ISSUE", "issue to list valid transitions").Required().StringVar(&opts.Issue)
return nil
}
// Transitions will get issue edit metadata and send to "editmeta" template
func CmdTransitions(o *oreo.Client, opts *TransitionsOptions) error {
editMeta, err := jira.GetIssueTransitions(o, opts.Endpoint.Value, opts.Issue)
if err != nil {
return err
}
if err := runTemplate(opts.Template.Value, editMeta, nil); err != nil {
return err
}
if opts.Browse.Value {
return CmdBrowse(&BrowseOptions{opts.GlobalOptions, opts.Issue})
}
return nil
}
-31
View File
@@ -1,31 +0,0 @@
package jiracli
import (
"github.com/coryb/figtree"
"github.com/coryb/oreo"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
func CmdUnassignRegistry(fig *figtree.FigTree, o *oreo.Client) *CommandRegistryEntry {
opts := AssignOptions{}
return &CommandRegistryEntry{
"Unassign an issue",
func() error {
return CmdAssign(o, &opts)
},
func(cmd *kingpin.CmdClause) error {
LoadConfigs(cmd, fig, &opts)
return CmdAssignUsage(cmd, &opts)
},
}
}
func CmdUnassignUsage(cmd *kingpin.CmdClause, opts *AssignOptions) error {
if err := GlobalUsage(cmd, &opts.GlobalOptions); err != nil {
return err
}
BrowseUsage(cmd, &opts.GlobalOptions)
cmd.Arg("ISSUE", "issue to unassign").Required().StringVar(&opts.Issue)
return nil
}
-58
View File
@@ -1,58 +0,0 @@
package jiracli
import (
"bytes"
"fmt"
"io/ioutil"
"os"
"path"
"github.com/coryb/figtree"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
func CmdUnexportTemplatesRegistry(fig *figtree.FigTree) *CommandRegistryEntry {
opts := ExportTemplatesOptions{}
return &CommandRegistryEntry{
"Remove unmodified exported templates",
func() error {
return CmdUnexportTemplates(&opts)
},
func(cmd *kingpin.CmdClause) error {
LoadConfigs(cmd, fig, &opts)
if opts.Dir != "" {
opts.Dir = fmt.Sprintf("%s/.jira.d/templates", Homedir())
}
return CmdExportTemplatesUsage(cmd, &opts)
},
}
}
// CmdUnexportTemplates will remove unmodified templates from export directory
func CmdUnexportTemplates(opts *ExportTemplatesOptions) error {
for name, template := range allTemplates {
if opts.Template != "" && opts.Template != name {
continue
}
templateFile := path.Join(opts.Dir, name)
if _, err := os.Stat(templateFile); err != nil {
log.Warning("Skipping %s, not found", templateFile)
continue
}
// open, read, compare
contents, err := ioutil.ReadFile(templateFile)
if err != nil {
return err
}
if bytes.Compare([]byte(template), contents) == 0 {
log.Warning("Removing %s, template identical to default", templateFile)
os.Remove(templateFile)
} else {
log.Warning("Skipping %s, found customizations to template", templateFile)
}
}
return nil
}
+1 -1
View File
@@ -50,7 +50,7 @@ func tmpYml(tmpFilePrefix string) (*os.File, error) {
return os.OpenFile(newFileName, os.O_RDWR|os.O_EXCL, 0600)
}
func flagValue(ctx *kingpin.ParseContext, name string) string {
func FlagValue(ctx *kingpin.ParseContext, name string) string {
for _, elem := range ctx.Elements {
if flag, ok := elem.Clause.(*kingpin.FlagClause); ok {
if flag.Model().Name == name {
-61
View File
@@ -1,61 +0,0 @@
package jiracli
import (
"github.com/coryb/figtree"
"github.com/coryb/oreo"
jira "gopkg.in/Netflix-Skunkworks/go-jira.v1"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
type ViewOptions struct {
GlobalOptions `yaml:",inline" json:",inline" figtree:",inline"`
jira.IssueOptions `yaml:",inline" json:",inline" figtree:",inline"`
Issue string `yaml:"issue,omitempty" json:"issue,omitempty"`
}
func CmdViewRegistry(fig *figtree.FigTree, o *oreo.Client) *CommandRegistryEntry {
opts := ViewOptions{
GlobalOptions: GlobalOptions{
Template: figtree.NewStringOption("view"),
},
}
return &CommandRegistryEntry{
"Prints issue details",
func() error {
return CmdView(o, &opts)
},
func(cmd *kingpin.CmdClause) error {
LoadConfigs(cmd, fig, &opts)
return CmdViewUsage(cmd, &opts)
},
}
}
func CmdViewUsage(cmd *kingpin.CmdClause, opts *ViewOptions) error {
if err := GlobalUsage(cmd, &opts.GlobalOptions); err != nil {
return err
}
BrowseUsage(cmd, &opts.GlobalOptions)
TemplateUsage(cmd, &opts.GlobalOptions)
cmd.Flag("expand", "field to expand for the issue").StringsVar(&opts.Expand)
cmd.Flag("field", "field to return for the issue").StringsVar(&opts.Fields)
cmd.Flag("property", "property to return for issue").StringsVar(&opts.Properties)
cmd.Arg("ISSUE", "issue id to view").Required().StringVar(&opts.Issue)
return nil
}
// View will get issue data and send to "view" template
func CmdView(o *oreo.Client, opts *ViewOptions) error {
data, err := jira.GetIssue(o, opts.Endpoint.Value, opts.Issue, opts)
if err != nil {
return err
}
if err := runTemplate(opts.Template.Value, data, nil); err != nil {
return err
}
if opts.Browse.Value {
return CmdBrowse(&BrowseOptions{opts.GlobalOptions, opts.Issue})
}
return nil
}
-74
View File
@@ -1,74 +0,0 @@
package jiracli
import (
"fmt"
"github.com/coryb/figtree"
"github.com/coryb/oreo"
jira "gopkg.in/Netflix-Skunkworks/go-jira.v1"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
type VoteAction int
const (
VoteUP VoteAction = iota
VoteDown
)
type VoteOptions struct {
GlobalOptions `yaml:",inline" json:",inline" figtree:",inline"`
Issue string `yaml:"issue,omitempty" json:"issue,omitempty"`
Action VoteAction `yaml:"-" json:"-"`
}
func CmdVoteRegistry(fig *figtree.FigTree, o *oreo.Client) *CommandRegistryEntry {
opts := VoteOptions{
GlobalOptions: GlobalOptions{},
Action: VoteUP,
}
return &CommandRegistryEntry{
"Vote up/down an issue",
func() error {
return CmdVote(o, &opts)
},
func(cmd *kingpin.CmdClause) error {
LoadConfigs(cmd, fig, &opts)
return CmdVoteUsage(cmd, &opts)
},
}
}
func CmdVoteUsage(cmd *kingpin.CmdClause, opts *VoteOptions) error {
if err := GlobalUsage(cmd, &opts.GlobalOptions); err != nil {
return err
}
BrowseUsage(cmd, &opts.GlobalOptions)
cmd.Flag("down", "downvote the issue").Short('d').PreAction(func(ctx *kingpin.ParseContext) error {
opts.Action = VoteDown
return nil
}).Bool()
cmd.Arg("ISSUE", "issue id to vote").StringVar(&opts.Issue)
return nil
}
// Vote will up/down vote an issue
func CmdVote(o *oreo.Client, opts *VoteOptions) error {
if opts.Action == VoteUP {
if err := jira.IssueAddVote(o, opts.Endpoint.Value, opts.Issue); err != nil {
return err
}
} else {
if err := jira.IssueRemoveVote(o, opts.Endpoint.Value, opts.Issue); err != nil {
return err
}
}
fmt.Printf("OK %s %s/browse/%s\n", opts.Issue, opts.Endpoint.Value, opts.Issue)
if opts.Browse.Value {
return CmdBrowse(&BrowseOptions{opts.GlobalOptions, opts.Issue})
}
return nil
}
-82
View File
@@ -1,82 +0,0 @@
package jiracli
import (
"fmt"
"github.com/coryb/figtree"
"github.com/coryb/oreo"
jira "gopkg.in/Netflix-Skunkworks/go-jira.v1"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
type WatchAction int
const (
WatcherAdd WatchAction = iota
WatcherRemove
)
type WatchOptions struct {
GlobalOptions `yaml:",inline" json:",inline" figtree:",inline"`
Issue string `yaml:"issue,omitempty" json:"issue,omitempty"`
Watcher string `yaml:"watcher,omitempty" json:"watcher,omitempty"`
Action WatchAction `yaml:"-" json:"-"`
}
func CmdWatchRegistry(fig *figtree.FigTree, o *oreo.Client) *CommandRegistryEntry {
opts := WatchOptions{
GlobalOptions: GlobalOptions{},
Action: WatcherAdd,
}
return &CommandRegistryEntry{
"Add/Remove watcher to issue",
func() error {
return CmdWatch(o, &opts)
},
func(cmd *kingpin.CmdClause) error {
LoadConfigs(cmd, fig, &opts)
return CmdWatchUsage(cmd, &opts)
},
}
}
func CmdWatchUsage(cmd *kingpin.CmdClause, opts *WatchOptions) error {
if err := GlobalUsage(cmd, &opts.GlobalOptions); err != nil {
return err
}
BrowseUsage(cmd, &opts.GlobalOptions)
cmd.Flag("remove", "remove watcher from issue").Short('r').PreAction(func(ctx *kingpin.ParseContext) error {
opts.Action = WatcherRemove
return nil
}).Bool()
cmd.Arg("ISSUE", "issue to add watcher").Required().StringVar(&opts.Issue)
cmd.Arg("WATCHER", "username of watcher to add to issue").StringVar(&opts.Watcher)
return nil
}
// CmdWatch will add the given watcher to the issue (or remove the watcher
// with the 'remove' flag)
func CmdWatch(o *oreo.Client, opts *WatchOptions) error {
if opts.Watcher == "" {
opts.Watcher = opts.User.Value
}
if opts.Action == WatcherAdd {
if err := jira.IssueAddWatcher(o, opts.Endpoint.Value, opts.Issue, opts.Watcher); err != nil {
return err
}
} else {
if err := jira.IssueRemoveWatcher(o, opts.Endpoint.Value, opts.Issue, opts.Watcher); err != nil {
return err
}
}
fmt.Printf("OK %s %s/browse/%s\n", opts.Issue, opts.Endpoint.Value, opts.Issue)
if opts.Browse.Value {
return CmdBrowse(&BrowseOptions{opts.GlobalOptions, opts.Issue})
}
return nil
}
-64
View File
@@ -1,64 +0,0 @@
package jiracli
import (
"github.com/coryb/figtree"
"github.com/coryb/oreo"
jira "gopkg.in/Netflix-Skunkworks/go-jira.v1"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiradata"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
type WorklogAddOptions struct {
GlobalOptions `yaml:",inline" json:",inline" figtree:",inline"`
jiradata.Worklog `yaml:",inline" json:",inline" figtree:",inline"`
Issue string `yaml:"issue,omitempty" json:"issue,omitempty"`
}
func CmdWorklogAddRegistry(fig *figtree.FigTree, o *oreo.Client) *CommandRegistryEntry {
opts := WorklogAddOptions{
GlobalOptions: GlobalOptions{
Template: figtree.NewStringOption("worklog"),
},
}
return &CommandRegistryEntry{
"Add a worklog to an issue",
func() error {
return CmdWorklogAdd(o, &opts)
},
func(cmd *kingpin.CmdClause) error {
LoadConfigs(cmd, fig, &opts)
return CmdWorklogAddUsage(cmd, &opts)
},
}
}
func CmdWorklogAddUsage(cmd *kingpin.CmdClause, opts *WorklogAddOptions) error {
if err := GlobalUsage(cmd, &opts.GlobalOptions); err != nil {
return err
}
BrowseUsage(cmd, &opts.GlobalOptions)
EditorUsage(cmd, &opts.GlobalOptions)
TemplateUsage(cmd, &opts.GlobalOptions)
cmd.Flag("noedit", "Disable opening the editor").SetValue(&opts.SkipEditing)
cmd.Flag("comment", "Comment message for worklog").Short('m').StringVar(&opts.Comment)
cmd.Flag("time-spent", "Time spent working on issue").Short('T').StringVar(&opts.TimeSpent)
cmd.Arg("ISSUE", "issue id to fetch worklogs").Required().StringVar(&opts.Issue)
return nil
}
// CmdWorklogAdd will attempt to add (action=add) a worklog to the given issue.
// It will spawn the editor (unless --noedit isused) and post edited YAML
// content as JSON to the worklog endpoint
func CmdWorklogAdd(o *oreo.Client, opts *WorklogAddOptions) error {
err := editLoop(&opts.GlobalOptions, &opts.Worklog, &opts.Worklog, func() error {
_, err := jira.AddIssueWorklog(o, opts.Endpoint.Value, opts.Issue, opts)
return err
})
if err != nil {
return err
}
if opts.Browse.Value {
return CmdBrowse(&BrowseOptions{opts.GlobalOptions, opts.Issue})
}
return nil
}
-56
View File
@@ -1,56 +0,0 @@
package jiracli
import (
"github.com/coryb/figtree"
"github.com/coryb/oreo"
jira "gopkg.in/Netflix-Skunkworks/go-jira.v1"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
type WorklogListOptions struct {
GlobalOptions `yaml:",inline" json:",inline" figtree:",inline"`
Issue string `yaml:"issue,omitempty" json:"issue,omitempty"`
}
func CmdWorklogListRegistry(fig *figtree.FigTree, o *oreo.Client) *CommandRegistryEntry {
opts := WorklogListOptions{
GlobalOptions: GlobalOptions{
Template: figtree.NewStringOption("worklogs"),
},
}
return &CommandRegistryEntry{
"Prints the worklog data for given issue",
func() error {
return CmdWorklogList(o, &opts)
},
func(cmd *kingpin.CmdClause) error {
LoadConfigs(cmd, fig, &opts)
return CmdWorklogListUsage(cmd, &opts)
},
}
}
func CmdWorklogListUsage(cmd *kingpin.CmdClause, opts *WorklogListOptions) error {
if err := GlobalUsage(cmd, &opts.GlobalOptions); err != nil {
return err
}
BrowseUsage(cmd, &opts.GlobalOptions)
TemplateUsage(cmd, &opts.GlobalOptions)
cmd.Arg("ISSUE", "issue id to fetch worklogs").Required().StringVar(&opts.Issue)
return nil
}
// // CmdWorklogList will get worklog data for given issue and sent to the "worklogs" template
func CmdWorklogList(o *oreo.Client, opts *WorklogListOptions) error {
data, err := jira.GetIssueWorklog(o, opts.Endpoint.Value, opts.Issue)
if err != nil {
return err
}
if err := runTemplate(opts.Template.Value, data, nil); err != nil {
return err
}
if opts.Browse.Value {
return CmdBrowse(&BrowseOptions{opts.GlobalOptions, opts.Issue})
}
return nil
}