mirror of
https://github.com/Threnklyn/jira.git
synced 2026-05-19 20:53:27 +02:00
Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 937444f5bd | |||
| b68c44384f | |||
| 000b82fa19 | |||
| 07854d6be5 | |||
| b00021ccbd | |||
| 2f8fecbbfe | |||
| 37b138376b | |||
| 8a5e588ce2 | |||
| 67c86e4858 | |||
| 445f8f1f84 | |||
| 485f73181c | |||
| 4d321ec202 | |||
| d7bce222b6 | |||
| f231f55d74 | |||
| 28242c9c7e | |||
| 05951f1c0d | |||
| f47563048b | |||
| cd9976ae4e | |||
| f3aa2f4c1a | |||
| f6230ca8c6 | |||
| 412174f8a9 | |||
| 52085417e6 |
@@ -1,5 +1,30 @@
|
||||
# Changelog
|
||||
|
||||
## 0.1.15 - 2017-08-25
|
||||
|
||||
* merge in edit template changes from v1 branch [#105](https://github.com/Netflix-Skunkworks/go-jira/issues/105) [Cory Bennett] [[21dec2d](https://github.com/Netflix-Skunkworks/go-jira/commit/21dec2d)]
|
||||
* Handle keyring.ErrNotFound error instead of panicing. [Will Rouesnel] [[338cc4d](https://github.com/Netflix-Skunkworks/go-jira/commit/338cc4d)]
|
||||
* Use name field for fix version name value. Fixes [#89](https://github.com/Netflix-Skunkworks/go-jira/issues/89) [Bryan Baugher] [[fde82b2](https://github.com/Netflix-Skunkworks/go-jira/commit/fde82b2)]
|
||||
|
||||
## 0.1.14 - 2017-05-10
|
||||
|
||||
* fix unsafe casting for --quiet flag [Cory Bennett] [[6f29f43](https://github.com/Netflix-Skunkworks/go-jira/commit/6f29f43)]
|
||||
* [[#80](https://github.com/Netflix-Skunkworks/go-jira/issues/80)] add `jira unassign` and `jira give ISSUE --default` commands [Cory Bennett] [[03d8633](https://github.com/Netflix-Skunkworks/go-jira/commit/03d8633)]
|
||||
|
||||
## 0.1.13 - 2017-04-24
|
||||
|
||||
* work around `github.com/tmc/keyring` compile error for windows [Cory Bennett] [[85298e9](https://github.com/Netflix-Skunkworks/go-jira/commit/85298e9)]
|
||||
* Added generic issuelink command [David Reuss] [[cc54d11](https://github.com/Netflix-Skunkworks/go-jira/commit/cc54d11)]
|
||||
* Added --start parameter for pagination on results [David Reuss] [[9b94d9e](https://github.com/Netflix-Skunkworks/go-jira/commit/9b94d9e)]
|
||||
|
||||
## 0.1.12 - 2017-03-22
|
||||
|
||||
* Implement "browse" subcommand on Windows [Claus Brod] [[ca333d8](https://github.com/Netflix-Skunkworks/go-jira/commit/ca333d8)]
|
||||
|
||||
## 0.1.11 - 2017-02-26
|
||||
|
||||
* [[#69](https://github.com/Netflix-Skunkworks/go-jira/issues/69)] add subtask command [Cory Bennett] [[21a2ed5](https://github.com/Netflix-Skunkworks/go-jira/commit/21a2ed5)]
|
||||
|
||||
## 0.1.10 - 2017-02-08
|
||||
|
||||
* set GPG_TTY in .bashrc [Cory Bennett] [[b1e552f](https://github.com/Netflix-Skunkworks/go-jira/commit/b1e552f)]
|
||||
|
||||
@@ -451,6 +451,8 @@ func (c *Cli) Browse(issue string) error {
|
||||
return exec.Command("open", fmt.Sprintf("%s/browse/%s", c.endpoint, issue)).Run()
|
||||
} else if runtime.GOOS == "linux" {
|
||||
return exec.Command("xdg-open", fmt.Sprintf("%s/browse/%s", c.endpoint, issue)).Run()
|
||||
} else if runtime.GOOS == "windows" {
|
||||
return exec.Command("cmd", "/c", "start", fmt.Sprintf("%s/browse/%s", c.endpoint, issue)).Run()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -542,7 +544,7 @@ func (c *Cli) FindIssues() (interface{}, error) {
|
||||
|
||||
json, err := jsonEncode(map[string]interface{}{
|
||||
"jql": query,
|
||||
"startAt": "0",
|
||||
"startAt": c.opts["start_at"],
|
||||
"maxResults": c.opts["max_results"],
|
||||
"fields": fields,
|
||||
"expand": c.expansions(),
|
||||
|
||||
+167
-35
@@ -157,7 +157,7 @@ func (c *Cli) CmdWorklog(action string, issue string) error {
|
||||
|
||||
if resp.StatusCode == 201 {
|
||||
c.Browse(issue)
|
||||
if !c.opts["quiet"].(bool) {
|
||||
if !c.GetOptBool("quiet", false) {
|
||||
fmt.Printf("OK %s %s/browse/%s\n", issue, c.endpoint, issue)
|
||||
}
|
||||
return nil
|
||||
@@ -210,7 +210,7 @@ func (c *Cli) CmdEdit(issue string) error {
|
||||
|
||||
if resp.StatusCode == 204 {
|
||||
c.Browse(issueData["key"].(string))
|
||||
if !c.opts["quiet"].(bool) {
|
||||
if !c.GetOptBool("quiet", false) {
|
||||
fmt.Printf("OK %s %s/browse/%s\n", issueData["key"], c.endpoint, issueData["key"])
|
||||
}
|
||||
return nil
|
||||
@@ -376,31 +376,15 @@ func (c *Cli) CmdCreate() error {
|
||||
issuetype = c.defaultIssueType()
|
||||
}
|
||||
|
||||
uri := fmt.Sprintf("%s/rest/api/2/issue/createmeta?projectKeys=%s&issuetypeNames=%s&expand=projects.issuetypes.fields", c.endpoint, project, url.QueryEscape(issuetype))
|
||||
data, err := responseToJSON(c.get(uri))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
issueData := make(map[string]interface{})
|
||||
issueData["overrides"] = c.opts
|
||||
issueData["overrides"].(map[string]interface{})["issuetype"] = issuetype
|
||||
|
||||
if val, ok := data.(map[string]interface{})["projects"]; ok {
|
||||
if len(val.([]interface{})) == 0 {
|
||||
err = fmt.Errorf("Project '%s' or issuetype '%s' unknown. Unable to create issue.", project, issuetype)
|
||||
log.Errorf("%s", err)
|
||||
return err
|
||||
}
|
||||
if val, ok = val.([]interface{})[0].(map[string]interface{})["issuetypes"]; ok {
|
||||
if len(val.([]interface{})) == 0 {
|
||||
err = fmt.Errorf("Project '%s' does not support issuetype '%s'. Unable to create issue.", project, issuetype)
|
||||
log.Errorf("%s", err)
|
||||
return err
|
||||
}
|
||||
issueData["meta"] = val.([]interface{})[0]
|
||||
}
|
||||
meta, err := c.createIssueMetaData(project, issuetype)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
issueData["meta"] = meta
|
||||
|
||||
sanitizedType := strings.ToLower(strings.Replace(issuetype, " ", "", -1))
|
||||
return c.editTemplate(
|
||||
@@ -432,7 +416,97 @@ func (c *Cli) CmdCreate() error {
|
||||
"issue": key,
|
||||
"link": link,
|
||||
})
|
||||
if !c.opts["quiet"].(bool) {
|
||||
if !c.GetOptBool("quiet", false) {
|
||||
fmt.Printf("OK %s %s\n", key, link)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
logBuffer := bytes.NewBuffer(make([]byte, 0))
|
||||
resp.Write(logBuffer)
|
||||
err = fmt.Errorf("Unexpected Response From POST")
|
||||
log.Errorf("%s:\n%s", err, logBuffer)
|
||||
return err
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func (c *Cli) createIssueMetaData(project, issuetype string) (interface{}, error) {
|
||||
uri := fmt.Sprintf("%s/rest/api/2/issue/createmeta?projectKeys=%s&issuetypeNames=%s&expand=projects.issuetypes.fields", c.endpoint, project, url.QueryEscape(issuetype))
|
||||
metaData, err := responseToJSON(c.get(uri))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if val, ok := metaData.(map[string]interface{})["projects"]; ok {
|
||||
if len(val.([]interface{})) == 0 {
|
||||
err = fmt.Errorf("Project '%s' or issuetype '%s' unknown. Unable to create issue.", project, issuetype)
|
||||
log.Errorf("%s", err)
|
||||
return nil, err
|
||||
}
|
||||
if val, ok = val.([]interface{})[0].(map[string]interface{})["issuetypes"]; ok {
|
||||
if len(val.([]interface{})) == 0 {
|
||||
err = fmt.Errorf("Project '%s' does not support issuetype '%s'. Unable to create issue.", project, issuetype)
|
||||
log.Errorf("%s", err)
|
||||
return nil, err
|
||||
}
|
||||
return val.([]interface{})[0], nil
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// CmdSubtask sends the create-metadata to the "subtask" template for editing, then
|
||||
// will parse the edited document as YAML and submit the document to jira.
|
||||
func (c *Cli) CmdSubtask(issue string) error {
|
||||
log.Debugf("subtask called")
|
||||
|
||||
uri := fmt.Sprintf("%s/rest/api/2/issue/%s", c.endpoint, issue)
|
||||
parentData, err := responseToJSON(c.get(uri))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
subtaskData := make(map[string]interface{})
|
||||
subtaskData["parent"] = parentData
|
||||
subtaskData["overrides"] = c.opts
|
||||
|
||||
project := parentData.(map[string]interface{})["fields"].(map[string]interface{})["project"].(map[string]interface{})["key"].(string)
|
||||
meta, err := c.createIssueMetaData(project, "Sub-task")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
subtaskData["meta"] = meta
|
||||
|
||||
return c.editTemplate(
|
||||
c.getTemplate("subtask"),
|
||||
"subtask-",
|
||||
subtaskData,
|
||||
func(json string) error {
|
||||
uri := fmt.Sprintf("%s/rest/api/2/issue", c.endpoint)
|
||||
if c.getOptBool("dryrun", false) {
|
||||
log.Debugf("POST: %s", json)
|
||||
log.Debugf("Dryrun mode, skipping POST")
|
||||
return nil
|
||||
}
|
||||
resp, err := c.post(uri, json)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if resp.StatusCode == 201 {
|
||||
// response: {"id":"410836","key":"PROJ-238","self":"https://jira/rest/api/2/issue/410836"}
|
||||
json, err := responseToJSON(resp, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
key := json.(map[string]interface{})["key"].(string)
|
||||
link := fmt.Sprintf("%s/browse/%s", c.endpoint, key)
|
||||
c.Browse(key)
|
||||
c.SaveData(map[string]string{
|
||||
"issue": key,
|
||||
"link": link,
|
||||
})
|
||||
if !c.GetOptBool("quiet", false) {
|
||||
fmt.Printf("OK %s %s\n", key, link)
|
||||
}
|
||||
return nil
|
||||
@@ -457,6 +531,50 @@ func (c *Cli) CmdIssueLinkTypes() error {
|
||||
return runTemplate(c.getTemplate("issuelinktypes"), data, nil)
|
||||
}
|
||||
|
||||
// CmdIssueLink is a generic function for adding a link type to an issue
|
||||
func (c *Cli) CmdIssueLink(inwardIssue string, issueLinkTypeName string, outwardIssue string) error {
|
||||
log.Debugf("issuelink called")
|
||||
|
||||
json, err := jsonEncode(map[string]interface{}{
|
||||
"type": map[string]string{
|
||||
"name": issueLinkTypeName,
|
||||
},
|
||||
"inwardIssue": map[string]string{
|
||||
"key": inwardIssue,
|
||||
},
|
||||
"outwardIssue": map[string]string{
|
||||
"key": outwardIssue,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
uri := fmt.Sprintf("%s/rest/api/2/issueLink", c.endpoint)
|
||||
if c.getOptBool("dryrun", false) {
|
||||
log.Debugf("POST: %s", json)
|
||||
log.Debugf("Dryrun mode, skipping POST")
|
||||
return nil
|
||||
}
|
||||
resp, err := c.post(uri, json)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if resp.StatusCode == 201 {
|
||||
c.Browse(inwardIssue)
|
||||
if !c.GetOptBool("quiet", false) {
|
||||
fmt.Printf("OK %s %s/browse/%s\n", inwardIssue, c.endpoint, inwardIssue)
|
||||
}
|
||||
} else {
|
||||
logBuffer := bytes.NewBuffer(make([]byte, 0))
|
||||
resp.Write(logBuffer)
|
||||
err := fmt.Errorf("Unexpected Response From POST")
|
||||
log.Errorf("%s:\n%s", err, logBuffer)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CmdBlocks will update the given issue as being "blocked" by the given blocker
|
||||
func (c *Cli) CmdBlocks(blocker string, issue string) error {
|
||||
log.Debugf("blocks called")
|
||||
@@ -488,7 +606,7 @@ func (c *Cli) CmdBlocks(blocker string, issue string) error {
|
||||
}
|
||||
if resp.StatusCode == 201 {
|
||||
c.Browse(issue)
|
||||
if !c.opts["quiet"].(bool) {
|
||||
if !c.GetOptBool("quiet", false) {
|
||||
fmt.Printf("OK %s %s/browse/%s\n", issue, c.endpoint, issue)
|
||||
}
|
||||
} else {
|
||||
@@ -533,7 +651,7 @@ func (c *Cli) CmdDups(duplicate string, issue string) error {
|
||||
}
|
||||
if resp.StatusCode == 201 {
|
||||
c.Browse(issue)
|
||||
if !c.opts["quiet"].(bool) {
|
||||
if !c.GetOptBool("quiet", false) {
|
||||
fmt.Printf("OK %s %s/browse/%s\n", issue, c.endpoint, issue)
|
||||
}
|
||||
} else {
|
||||
@@ -581,7 +699,7 @@ func (c *Cli) CmdWatch(issue string, watcher string, remove bool) error {
|
||||
}
|
||||
if resp.StatusCode == 204 {
|
||||
c.Browse(issue)
|
||||
if !c.opts["quiet"].(bool) {
|
||||
if !c.GetOptBool("quiet", false) {
|
||||
fmt.Printf("OK %s %s/browse/%s\n", issue, c.endpoint, issue)
|
||||
}
|
||||
} else {
|
||||
@@ -625,7 +743,7 @@ func (c *Cli) CmdVote(issue string, up bool) error {
|
||||
}
|
||||
if resp.StatusCode == 204 {
|
||||
c.Browse(issue)
|
||||
if !c.opts["quiet"].(bool) {
|
||||
if !c.GetOptBool("quiet", false) {
|
||||
fmt.Printf("OK %s %s/browse/%s\n", issue, c.endpoint, issue)
|
||||
}
|
||||
} else {
|
||||
@@ -648,7 +766,7 @@ func (c *Cli) CmdRankAfter(issue, after string) error {
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
if !c.opts["quiet"].(bool) {
|
||||
if !c.GetOptBool("quiet", false) {
|
||||
fmt.Printf("OK %s %s/browse/%s\n", issue, c.endpoint, issue)
|
||||
}
|
||||
return nil
|
||||
@@ -660,7 +778,7 @@ func (c *Cli) CmdRankBefore(issue, before string) error {
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
if !c.opts["quiet"].(bool) {
|
||||
if !c.GetOptBool("quiet", false) {
|
||||
fmt.Printf("OK %s %s/browse/%s\n", issue, c.endpoint, issue)
|
||||
}
|
||||
return nil
|
||||
@@ -711,7 +829,7 @@ func (c *Cli) CmdTransition(issue string, trans string) error {
|
||||
}
|
||||
if resp.StatusCode == 204 {
|
||||
c.Browse(issue)
|
||||
if !c.opts["quiet"].(bool) {
|
||||
if !c.GetOptBool("quiet", false) {
|
||||
fmt.Printf("OK %s %s/browse/%s\n", issue, c.endpoint, issue)
|
||||
}
|
||||
} else {
|
||||
@@ -781,7 +899,7 @@ func (c *Cli) CmdComment(issue string) error {
|
||||
|
||||
if resp.StatusCode == 201 {
|
||||
c.Browse(issue)
|
||||
if !c.opts["quiet"].(bool) {
|
||||
if !c.GetOptBool("quiet", false) {
|
||||
fmt.Printf("OK %s %s/browse/%s\n", issue, c.endpoint, issue)
|
||||
}
|
||||
return nil
|
||||
@@ -841,7 +959,7 @@ func (c *Cli) CmdComponent(action string, project string, name string, desc stri
|
||||
return err
|
||||
}
|
||||
if resp.StatusCode == 201 {
|
||||
if !c.opts["quiet"].(bool) {
|
||||
if !c.GetOptBool("quiet", false) {
|
||||
fmt.Printf("OK %s %s\n", project, name)
|
||||
}
|
||||
} else {
|
||||
@@ -876,7 +994,7 @@ func (c *Cli) CmdLabels(action string, issue string, labels []string) error {
|
||||
|
||||
if resp.StatusCode == 204 {
|
||||
c.Browse(issue)
|
||||
if !c.opts["quiet"].(bool) {
|
||||
if !c.GetOptBool("quiet", false) {
|
||||
fmt.Printf("OK %s %s/browse/%s\n", issue, c.endpoint, issue)
|
||||
}
|
||||
return nil
|
||||
@@ -922,8 +1040,18 @@ func (c *Cli) CmdLabels(action string, issue string, labels []string) error {
|
||||
func (c *Cli) CmdAssign(issue string, user string) error {
|
||||
log.Debugf("assign called")
|
||||
|
||||
var userVal interface{} = user
|
||||
// https://docs.atlassian.com/jira/REST/cloud/#api/2/issue-assign
|
||||
// If the name is "-1" automatic assignee is used. A null name will remove the assignee.
|
||||
if user == "" {
|
||||
userVal = nil
|
||||
}
|
||||
if c.GetOptBool("default", false) {
|
||||
userVal = "-1"
|
||||
}
|
||||
|
||||
json, err := jsonEncode(map[string]interface{}{
|
||||
"name": user,
|
||||
"name": userVal,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -941,7 +1069,7 @@ func (c *Cli) CmdAssign(issue string, user string) error {
|
||||
}
|
||||
if resp.StatusCode == 204 {
|
||||
c.Browse(issue)
|
||||
if !c.opts["quiet"].(bool) {
|
||||
if !c.GetOptBool("quiet", false) {
|
||||
fmt.Printf("OK %s %s/browse/%s\n", issue, c.endpoint, issue)
|
||||
}
|
||||
} else {
|
||||
@@ -954,6 +1082,10 @@ func (c *Cli) CmdAssign(issue string, user string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Cli) CmdUnassign(issue string) error {
|
||||
return c.CmdAssign(issue, "")
|
||||
}
|
||||
|
||||
// CmdExportTemplates will export the default templates to the template directory.
|
||||
func (c *Cli) CmdExportTemplates() error {
|
||||
dir := c.opts["directory"].(string)
|
||||
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
// +build !windows
|
||||
|
||||
package jira
|
||||
|
||||
import "github.com/tmc/keyring"
|
||||
|
||||
func keyringGet(user string) (string, error) {
|
||||
password, err := keyring.Get("go-jira", user)
|
||||
if err != nil && err != keyring.ErrNotFound {
|
||||
return password, err
|
||||
}
|
||||
return password, nil
|
||||
}
|
||||
|
||||
func keyringSet(user, passwd string) error {
|
||||
return keyring.Set("go-jira", user, passwd)
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package jira
|
||||
|
||||
import "fmt"
|
||||
|
||||
func keyringGet(user string) (string, error) {
|
||||
return "", fmt.Errorf("Keyring is not supported for Windows, see: https://github.com/tmc/keyring")
|
||||
}
|
||||
|
||||
func keyringSet(user, passwd string) error {
|
||||
return fmt.Errorf("Keyring is not supported for Windows, see: https://github.com/tmc/keyring")
|
||||
}
|
||||
+30
-7
@@ -3,14 +3,15 @@ package main
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"gopkg.in/Netflix-Skunkworks/go-jira.v0"
|
||||
"github.com/coryb/optigo"
|
||||
"gopkg.in/coryb/yaml.v2"
|
||||
"gopkg.in/op/go-logging.v1"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
"github.com/coryb/optigo"
|
||||
"gopkg.in/Netflix-Skunkworks/go-jira.v0"
|
||||
"gopkg.in/coryb/yaml.v2"
|
||||
"gopkg.in/op/go-logging.v1"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -60,8 +61,10 @@ Usage:
|
||||
jira add worklog ISSUE <Worklog Options>
|
||||
jira edit [--noedit] <Edit Options> [ISSUE | <Query Options>]
|
||||
jira create [--noedit] [-p PROJECT] <Create Options>
|
||||
jira subtask ISSUE [--noedit] <Create Options>
|
||||
jira DUPLICATE dups ISSUE
|
||||
jira BLOCKER blocks ISSUE
|
||||
jira issuelink OUTWARDISSUE ISSUELINKTYPE INWARDISSUE
|
||||
jira vote ISSUE [--down]
|
||||
jira rank ISSUE (after|before) ISSUE
|
||||
jira watch ISSUE [-w WATCHER] [--remove]
|
||||
@@ -79,7 +82,8 @@ Usage:
|
||||
jira comment ISSUE [--noedit] <Edit Options>
|
||||
jira (set,add,remove) labels ISSUE [LABEL] ...
|
||||
jira take ISSUE
|
||||
jira (assign|give) ISSUE ASSIGNEE
|
||||
jira (assign|give) ISSUE [ASSIGNEE|--default]
|
||||
jira unassign ISSUE
|
||||
jira fields
|
||||
jira issuelinktypes
|
||||
jira transmeta ISSUE
|
||||
@@ -113,6 +117,7 @@ Query Options:
|
||||
-f --queryfields=FIELDS Fields that are used in "list" template: (default: %s)
|
||||
-i --issuetype=ISSUETYPE The Issue Type
|
||||
-l --limit=VAL Maximum number of results to return in query (default: %d)
|
||||
--start=START Start parameter for pagination
|
||||
-p --project=PROJECT Project to Search for
|
||||
-q --query=JQL Jira Query Language expression for the search
|
||||
-r --reporter=USER Reporter to search for
|
||||
@@ -145,8 +150,10 @@ Command Options:
|
||||
"view": "view",
|
||||
"edit": "edit",
|
||||
"create": "create",
|
||||
"subtask": "subtask",
|
||||
"dups": "dups",
|
||||
"blocks": "blocks",
|
||||
"issuelink": "issuelink",
|
||||
"watch": "watch",
|
||||
"trans": "transition",
|
||||
"transition": "transition",
|
||||
@@ -188,6 +195,7 @@ Command Options:
|
||||
"rank": "rank",
|
||||
"worklog": "worklog",
|
||||
"addworklog": "addworklog",
|
||||
"unassign": "unassign",
|
||||
}
|
||||
|
||||
defaults := map[string]interface{}{
|
||||
@@ -233,6 +241,7 @@ Command Options:
|
||||
"x|expand=s": setopt,
|
||||
"s|sort=s": setopt,
|
||||
"l|limit|max_results=i": setopt,
|
||||
"start|start_at=i": setopt,
|
||||
"o|override=s%": &opts,
|
||||
"noedit": setopt,
|
||||
"edit": setopt,
|
||||
@@ -244,6 +253,7 @@ Command Options:
|
||||
"Q|quiet": setopt,
|
||||
"unixproxy": setopt,
|
||||
"down": setopt,
|
||||
"default": setopt,
|
||||
})
|
||||
|
||||
if err := op.ProcessAll(os.Args[1:]); err != nil {
|
||||
@@ -332,6 +342,9 @@ Command Options:
|
||||
|
||||
var err error
|
||||
switch command {
|
||||
case "issuelink":
|
||||
requireArgs(3)
|
||||
err = c.CmdIssueLink(args[0], args[1], args[2])
|
||||
case "login":
|
||||
err = c.CmdLogin()
|
||||
case "logout":
|
||||
@@ -376,6 +389,9 @@ Command Options:
|
||||
case "create":
|
||||
setEditing(true)
|
||||
err = c.CmdCreate()
|
||||
case "subtask":
|
||||
setEditing(true)
|
||||
err = c.CmdSubtask(args[0])
|
||||
case "transitions":
|
||||
requireArgs(1)
|
||||
err = c.CmdTransitions(args[0])
|
||||
@@ -490,8 +506,15 @@ Command Options:
|
||||
case "export-templates":
|
||||
err = c.CmdExportTemplates()
|
||||
case "assign":
|
||||
requireArgs(2)
|
||||
err = c.CmdAssign(args[0], args[1])
|
||||
requireArgs(1)
|
||||
assignee := ""
|
||||
if len(args) > 1 {
|
||||
assignee = args[1]
|
||||
}
|
||||
err = c.CmdAssign(args[0], assignee)
|
||||
case "unassign":
|
||||
requireArgs(1)
|
||||
err = c.CmdUnassign(args[0])
|
||||
case "view":
|
||||
requireArgs(1)
|
||||
err = c.CmdView(args[0])
|
||||
|
||||
+6
-3
@@ -7,14 +7,17 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/howeyc/gopass"
|
||||
"github.com/tmc/keyring"
|
||||
)
|
||||
|
||||
func (c *Cli) GetPass(user string) string {
|
||||
passwd := ""
|
||||
if source, ok := c.opts["password-source"].(string); ok {
|
||||
if source == "keyring" {
|
||||
passwd, _ = keyring.Get("go-jira", user)
|
||||
var err error
|
||||
passwd, err = keyringGet(user)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
} else if source == "pass" {
|
||||
if bin, err := exec.LookPath("pass"); err == nil {
|
||||
buf := bytes.NewBufferString("")
|
||||
@@ -48,7 +51,7 @@ func (c *Cli) SetPass(user, passwd string) error {
|
||||
log.Debugf("password-source: %s", source)
|
||||
if source == "keyring" {
|
||||
// save password in keychain so that it can be used for subsequent http requests
|
||||
err := keyring.Set("go-jira", user, passwd)
|
||||
err := keyringSet(user, passwd)
|
||||
if err != nil {
|
||||
log.Errorf("Failed to set password in keyring: %s", err)
|
||||
return err
|
||||
|
||||
+37
-7
@@ -15,6 +15,7 @@ var allTemplates = map[string]string{
|
||||
"components": defaultComponentsTemplate,
|
||||
"issuetypes": defaultIssuetypesTemplate,
|
||||
"create": defaultCreateTemplate,
|
||||
"subtask": defaultSubtaskTemplate,
|
||||
"comment": defaultCommentTemplate,
|
||||
"transition": defaultTransitionTemplate,
|
||||
"request": defaultDebugTemplate,
|
||||
@@ -89,19 +90,24 @@ update:
|
||||
{{ or .overrides.comment "" | indent 10 }}
|
||||
fields:
|
||||
summary: {{ or .overrides.summary .fields.summary }}
|
||||
{{- if and .meta.fields.components .meta.fields.components.allowedValues }}
|
||||
components: # Values: {{ range .meta.fields.components.allowedValues }}{{.name}}, {{end}}{{if .overrides.components }}{{ range (split "," .overrides.components)}}
|
||||
- name: {{.}}{{end}}{{else}}{{ range .fields.components }}
|
||||
- name: {{ .name }}{{end}}{{end}}
|
||||
- name: {{ .name }}{{end}}{{end}}{{end}}
|
||||
{{- if .meta.fields.assignee}}
|
||||
assignee:
|
||||
name: {{ if .overrides.assignee }}{{.overrides.assignee}}{{else}}{{if .fields.assignee }}{{ .fields.assignee.name }}{{end}}{{end}}
|
||||
name: {{ if .overrides.assignee }}{{.overrides.assignee}}{{else}}{{if .fields.assignee }}{{ .fields.assignee.name }}{{end}}{{end}}{{end}}
|
||||
{{- if .meta.fields.reporter}}
|
||||
reporter:
|
||||
name: {{ if .overrides.reporter }}{{ .overrides.reporter }}{{else if .fields.reporter}}{{ .fields.reporter.name }}{{end}}
|
||||
name: {{ if .overrides.reporter }}{{ .overrides.reporter }}{{else if .fields.reporter}}{{ .fields.reporter.name }}{{end}}{{end}}
|
||||
{{- if .meta.fields.customfield_10110}}
|
||||
# watchers
|
||||
customfield_10110: {{ range .fields.customfield_10110 }}
|
||||
- name: {{ .name }}{{end}}{{if .overrides.watcher}}
|
||||
- name: {{ .overrides.watcher}}{{end}}
|
||||
- name: {{ .overrides.watcher}}{{end}}{{end}}
|
||||
{{- if .meta.fields.priority }}
|
||||
priority: # Values: {{ range .meta.fields.priority.allowedValues }}{{.name}}, {{end}}
|
||||
name: {{ or .overrides.priority .fields.priority.name }}
|
||||
name: {{ or .overrides.priority .fields.priority.name }}{{end}}
|
||||
description: |~
|
||||
{{ or .overrides.description (or .fields.description "") | indent 4 }}
|
||||
# comments:
|
||||
@@ -140,6 +146,30 @@ fields:
|
||||
- name: {{.}}{{end}}
|
||||
- name:{{end}}`
|
||||
|
||||
const defaultSubtaskTemplate = `{{/* create subtask template */ -}}
|
||||
fields:
|
||||
project:
|
||||
key: {{ .parent.fields.project.key }}
|
||||
summary: {{ or .overrides.summary "" }}{{if .meta.fields.priority.allowedValues}}
|
||||
priority: # Values: {{ range .meta.fields.priority.allowedValues }}{{.name}}, {{end}}
|
||||
name: {{ or .overrides.priority ""}}{{end}}{{if .meta.fields.components.allowedValues}}
|
||||
components: # Values: {{ range .meta.fields.components.allowedValues }}{{.name}}, {{end}}{{ range split "," (or .overrides.components "")}}
|
||||
- name: {{ . }}{{end}}{{end}}
|
||||
description: |~
|
||||
{{ or .overrides.description "" | indent 4 }}{{if .meta.fields.assignee}}
|
||||
assignee:
|
||||
name: {{ or .overrides.assignee "" }}{{end}}{{if .meta.fields.reporter}}
|
||||
reporter:
|
||||
name: {{ or .overrides.reporter .overrides.user }}{{end}}{{if .meta.fields.customfield_10110}}
|
||||
# watchers
|
||||
customfield_10110: {{ range split "," (or .overrides.watchers "")}}
|
||||
- name: {{.}}{{end}}
|
||||
- name:{{end}}
|
||||
issuetype:
|
||||
name: Sub-task
|
||||
parent:
|
||||
key: {{ .parent.key }}`
|
||||
|
||||
const defaultCommentTemplate = `body: |~
|
||||
{{ or .overrides.comment "" | indent 2 }}
|
||||
`
|
||||
@@ -166,8 +196,8 @@ fields:
|
||||
{{if .meta.fields.fixVersions -}}
|
||||
{{if .meta.fields.fixVersions.allowedValues}}
|
||||
fixVersions: # Values: {{ range .meta.fields.fixVersions.allowedValues }}{{.name}}, {{end}}{{if .overrides.fixVersions}}{{ range (split "," .overrides.fixVersions)}}
|
||||
- name: {{.}}{{end}}{{else}}{{range .fields.fixVersions}}
|
||||
- name: {{.}}{{end}}{{end}}
|
||||
- name: {{.name}}{{end}}{{else}}{{range .fields.fixVersions}}
|
||||
- name: {{.name}}{{end}}{{end}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{if .meta.fields.issuetype}}
|
||||
|
||||
Reference in New Issue
Block a user