mirror of
https://github.com/Threnklyn/jira.git
synced 2026-06-14 00:23:32 +02:00
add "transition" editable template so you can modify other fields during transitions
add --noedit/--edit options for various commands sanitize the yaml from the editor so we dont send empty strings when unedited
This commit is contained in:
+28
-19
@@ -235,29 +235,37 @@ func (c *Cli) editTemplate(template string, tmpFilePrefix string, templateData m
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
editing := true
|
||||
if val, ok := c.opts["edit"]; ok && val == "false" {
|
||||
editing = false
|
||||
}
|
||||
|
||||
for true {
|
||||
log.Debug("Running: %s %s", editor, tmpFileName)
|
||||
cmd := exec.Command(editor, tmpFileName)
|
||||
cmd.Stdout, cmd.Stderr, cmd.Stdin = os.Stdout, os.Stderr, os.Stdin
|
||||
if err := cmd.Run(); err != nil {
|
||||
log.Error("Failed to edit template with %s: %s", editor, err)
|
||||
if promptYN("edit again?", true) {
|
||||
continue
|
||||
if editing {
|
||||
log.Debug("Running: %s %s", editor, tmpFileName)
|
||||
cmd := exec.Command(editor, tmpFileName)
|
||||
cmd.Stdout, cmd.Stderr, cmd.Stdin = os.Stdout, os.Stderr, os.Stdin
|
||||
if err := cmd.Run(); err != nil {
|
||||
log.Error("Failed to edit template with %s: %s", editor, err)
|
||||
if promptYN("edit again?", true) {
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
edited := make(map[string]interface{})
|
||||
if fh, err := ioutil.ReadFile(tmpFileName); err != nil {
|
||||
log.Error("Failed to read tmpfile %s: %s", tmpFileName, err)
|
||||
if promptYN("edit again?", true) {
|
||||
if editing && promptYN("edit again?", true) {
|
||||
continue
|
||||
}
|
||||
return err
|
||||
} else {
|
||||
if err := yaml.Unmarshal(fh, &edited); err != nil {
|
||||
log.Error("Failed to parse YAML: %s", err)
|
||||
if promptYN("edit again?", true) {
|
||||
if editing && promptYN("edit again?", true) {
|
||||
continue
|
||||
}
|
||||
return err
|
||||
@@ -272,15 +280,16 @@ func (c *Cli) editTemplate(template string, tmpFilePrefix string, templateData m
|
||||
|
||||
if _, ok := templateData["meta"]; ok {
|
||||
mf := templateData["meta"].(map[string]interface{})["fields"]
|
||||
f := edited["fields"].(map[string]interface{})
|
||||
for k, _ := range f {
|
||||
if _, ok := mf.(map[string]interface{})[k]; !ok {
|
||||
err := fmt.Errorf("Field %s is not editable", k)
|
||||
log.Error("%s", err)
|
||||
if promptYN("edit again?", true) {
|
||||
continue
|
||||
if f, ok := edited["fields"].(map[string]interface{}); ok {
|
||||
for k, _ := range f {
|
||||
if _, ok := mf.(map[string]interface{})[k]; !ok {
|
||||
err := fmt.Errorf("Field %s is not editable", k)
|
||||
log.Error("%s", err)
|
||||
if editing && promptYN("edit again?", true) {
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -292,7 +301,7 @@ func (c *Cli) editTemplate(template string, tmpFilePrefix string, templateData m
|
||||
|
||||
if err := templateProcessor(json); err != nil {
|
||||
log.Error("%s", err)
|
||||
if promptYN("edit again?", true) {
|
||||
if editing && promptYN("edit again?", true) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
+41
-36
@@ -403,21 +403,24 @@ func (c *Cli) CmdWatch(issue string, watcher string) error {
|
||||
|
||||
func (c *Cli) CmdTransition(issue string, trans string) error {
|
||||
log.Debug("transition called")
|
||||
uri := fmt.Sprintf("%s/rest/api/2/issue/%s/transitions", c.endpoint, issue)
|
||||
uri := fmt.Sprintf("%s/rest/api/2/issue/%s/transitions?expand=transitions.fields", c.endpoint, issue)
|
||||
data, err := responseToJson(c.get(uri))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
transitions := data.(map[string]interface{})["transitions"].([]interface{})
|
||||
var transId string
|
||||
var transId, transName string
|
||||
var transMeta map[string]interface{}
|
||||
found := make([]string, 0, len(transitions))
|
||||
for _, transition := range transitions {
|
||||
name := transition.(map[string]interface{})["name"].(string)
|
||||
id := transition.(map[string]interface{})["id"].(string)
|
||||
found = append(found, name)
|
||||
if strings.Contains(strings.ToLower(name), trans) {
|
||||
transName = name
|
||||
transId = id
|
||||
transMeta = transition.(map[string]interface{})
|
||||
}
|
||||
}
|
||||
if transId == "" {
|
||||
@@ -426,45 +429,47 @@ func (c *Cli) CmdTransition(issue string, trans string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
payload := map[string]interface{}{
|
||||
"transition": map[string]interface{}{
|
||||
"id": transId,
|
||||
},
|
||||
}
|
||||
|
||||
if comment, ok := c.opts["comment"]; ok {
|
||||
payload["update"] = map[string]interface{}{
|
||||
"comment": []interface{}{
|
||||
map[string]interface{}{
|
||||
"add": map[string]interface{}{
|
||||
"body": comment,
|
||||
},
|
||||
},
|
||||
},
|
||||
handlePost := func(json string) error {
|
||||
log.Debug("POST: %s", json)
|
||||
// os.Exit(0)
|
||||
uri = fmt.Sprintf("%s/rest/api/2/issue/%s/transitions", c.endpoint, issue)
|
||||
resp, err := c.post(uri, json)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if resp.StatusCode == 204 {
|
||||
c.Browse(issue)
|
||||
fmt.Printf("OK %s %s/browse/%s\n", issue, c.endpoint, issue)
|
||||
} else {
|
||||
logBuffer := bytes.NewBuffer(make([]byte, 0))
|
||||
resp.Write(logBuffer)
|
||||
err := fmt.Errorf("Unexpected Response From POST")
|
||||
log.Error("%s:\n%s", err, logBuffer)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
json, err := jsonEncode(payload)
|
||||
if err != nil {
|
||||
|
||||
uri = fmt.Sprintf("%s/rest/api/2/issue/%s", c.endpoint, issue)
|
||||
var issueData map[string]interface{}
|
||||
if data, err := responseToJson(c.get(uri)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
uri = fmt.Sprintf("%s/rest/api/2/issue/%s/transitions", c.endpoint, issue)
|
||||
resp, err := c.post(uri, json)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if resp.StatusCode == 204 {
|
||||
c.Browse(issue)
|
||||
fmt.Printf("OK %s %s/browse/%s\n", issue, c.endpoint, issue)
|
||||
} else {
|
||||
logBuffer := bytes.NewBuffer(make([]byte, 0))
|
||||
resp.Write(logBuffer)
|
||||
err := fmt.Errorf("Unexpected Response From POST")
|
||||
log.Error("%s:\n%s", err, logBuffer)
|
||||
return err
|
||||
issueData = data.(map[string]interface{})
|
||||
}
|
||||
return nil
|
||||
issueData["meta"] = transMeta
|
||||
issueData["overrides"] = c.opts
|
||||
issueData["transition"] = map[string]interface{}{
|
||||
"name": transName,
|
||||
"id": transId,
|
||||
};
|
||||
|
||||
return c.editTemplate(
|
||||
c.getTemplate("transition"),
|
||||
fmt.Sprintf("%s-trans-%s-", issue, trans),
|
||||
issueData,
|
||||
handlePost,
|
||||
)
|
||||
}
|
||||
|
||||
func (c *Cli) CmdComment(issue string) error {
|
||||
|
||||
+37
-1
@@ -14,6 +14,7 @@ var all_templates = map[string]string{
|
||||
"issuetypes": default_issuetypes_template,
|
||||
"create": default_create_template,
|
||||
"comment": default_comment_template,
|
||||
"transition": default_transition_template,
|
||||
}
|
||||
|
||||
const default_debug_template = "{{ . | toJson}}\n"
|
||||
@@ -76,7 +77,7 @@ const default_create_template = `fields:
|
||||
name: {{ .overrides.issuetype }}
|
||||
summary: {{ or .overrides.summary "" }}
|
||||
priority: # {{ range .meta.fields.priority.allowedValues }}{{.name}}, {{end}}
|
||||
name: {{ or .overrides.priority "" }}
|
||||
name: {{ or .overrides.priority "unassigned" }}
|
||||
components: # {{ range .meta.fields.components.allowedValues }}{{.name}}, {{end}}{{ range split "," (or .overrides.components "")}}
|
||||
- name: {{ . }}{{end}}
|
||||
description: |
|
||||
@@ -93,3 +94,38 @@ const default_create_template = `fields:
|
||||
const default_comment_template = `body: |
|
||||
{{ or .overrides.comment | indent 2 }}
|
||||
`
|
||||
|
||||
const default_transition_template = `update:
|
||||
comment:
|
||||
- add:
|
||||
body: |
|
||||
{{ or .overrides.comment "" | indent 10 }}
|
||||
fields:{{if .meta.fields.assignee}}
|
||||
assignee:
|
||||
name: {{if .overrides.assignee}}{{.overrides.assignee}}{{else}}{{if .fields.assignee}}{{.fields.assignee.name}}{{end}}{{end}}{{end}}{{if .meta.fields.components}}
|
||||
components: # {{ range .meta.fields.components.allowedValues }}{{.name}}, {{end}}{{if .overrides.components }}{{ range (split "," .overrides.components)}}
|
||||
- name: {{.}}{{end}}{{else}}{{ range .fields.components }}
|
||||
- name: {{ .name }}{{end}}{{end}}{{end}}{{if .meta.fields.description}}
|
||||
description: {{or .overrides.description .fields.description }}{{end}}{{if .meta.fields.fixVersions}}{{if .meta.fields.fixVersions.allowedValues}}
|
||||
fixVersions: # {{ range .meta.fields.fixVersions.allowedValues }}{{.name}}, {{end}}{{if .overrides.fixVersions}}{{ range (split "," .overrides.fixVersions)}}
|
||||
- name: {{.}}{{end}}{{else}}{{range .fields.fixVersions}}
|
||||
- name: {{.}}{{end}}{{end}}{{end}}{{end}}{{if .meta.fields.issuetype}}
|
||||
issuetype: # {{ range .meta.fields.issuetype.allowedValues }}{{.name}}, {{end}}
|
||||
name: {{if .overrides.issuetype}}{{.overrides.issuetype}}{{else}}{{if .fields.issuetype}}{{.fields.issuetype.name}}{{end}}{{end}}{{end}}{{if .meta.fields.labels}}
|
||||
labels: {{range .fields.labels}}
|
||||
- {{.}}{{end}}{{if .overrides.labels}}{{range (split "," .overrides.labels)}}
|
||||
- {{.}}{{end}}{{end}}{{end}}{{if .meta.fields.priority}}
|
||||
priority: # {{ range .meta.fields.priority.allowedValues }}{{.name}}, {{end}}
|
||||
name: {{ or .overrides.priority "unassigned" }}{{end}}{{if .meta.fields.reporter}}
|
||||
reporter:
|
||||
name: {{if .overrides.reporter}}{{.overrides.reporter}}{{else}}{{if .fields.reporter}}{{.fields.reporter.name}}{{end}}{{end}}{{end}}{{if .meta.fields.resolution}}
|
||||
resolution: # {{ range .meta.fields.resolution.allowedValues }}{{.name}}, {{end}}
|
||||
name: {{if .overrides.resolution}}{{.overrides.resolution}}{{else if .fields.resolution}}{{.fields.resolution.name}}{{else}}Fixed{{end}}{{end}}{{if .meta.fields.summary}}
|
||||
summary: {{or .overrides.summary .fields.summary}}{{end}}{{if .meta.fields.versions.allowedValues}}
|
||||
versions: # {{ range .meta.fields.versions.allowedValues }}{{.name}}, {{end}}{{if .overrides.versions}}{{ range (split "," .overrides.versions)}}
|
||||
- name: {{.}}{{end}}{{else}}{{range .fields.versions}}
|
||||
- name: {{.}}{{end}}{{end}}{{end}}
|
||||
transition:
|
||||
id: {{ .transition.id }}
|
||||
name: {{ .transition.name }}
|
||||
`
|
||||
|
||||
+8
-3
@@ -184,7 +184,7 @@ func yamlFixup(data interface{}) (interface{}, error) {
|
||||
case string:
|
||||
if fixed, err := yamlFixup(val); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
} else if fixed != nil {
|
||||
copy[k] = fixed
|
||||
}
|
||||
default:
|
||||
@@ -198,7 +198,7 @@ func yamlFixup(data interface{}) (interface{}, error) {
|
||||
for k, v := range d {
|
||||
if fixed, err := yamlFixup(v); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
} else if fixed != nil {
|
||||
d[k] = fixed
|
||||
}
|
||||
}
|
||||
@@ -207,11 +207,16 @@ func yamlFixup(data interface{}) (interface{}, error) {
|
||||
for i, val := range d {
|
||||
if fixed, err := yamlFixup(val); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
} else if fixed != nil {
|
||||
d[i] = fixed
|
||||
}
|
||||
}
|
||||
return data, nil
|
||||
case string:
|
||||
if d == "" {
|
||||
return nil, nil
|
||||
}
|
||||
return d, nil
|
||||
default:
|
||||
return d, nil
|
||||
}
|
||||
|
||||
+35
-10
@@ -21,18 +21,18 @@ func main() {
|
||||
Usage:
|
||||
jira [-v ...] [-u USER] [-e URI] [-t FILE] (ls|list) ( [-q JQL] | [-p PROJECT] [-c COMPONENT] [-a ASSIGNEE] [-i ISSUETYPE] [-w WATCHER] [-r REPORTER])
|
||||
jira [-v ...] [-u USER] [-e URI] [-b] [-t FILE] view ISSUE
|
||||
jira [-v ...] [-u USER] [-e URI] [-b] [-t FILE] edit ISSUE [-m COMMENT] [-o KEY=VAL]...
|
||||
jira [-v ...] [-u USER] [-e URI] [-b] [-t FILE] create [-p PROJECT] [-i ISSUETYPE] [-o KEY=VAL]...
|
||||
jira [-v ...] [-u USER] [-e URI] [-b] [-t FILE] edit ISSUE [--noedit] [-m COMMENT] [-o KEY=VAL]...
|
||||
jira [-v ...] [-u USER] [-e URI] [-b] [-t FILE] create [--noedit] [-p PROJECT] [-i ISSUETYPE] [-o KEY=VAL]...
|
||||
jira [-v ...] [-u USER] [-e URI] [-b] DUPLICATE dups ISSUE
|
||||
jira [-v ...] [-u USER] [-e URI] [-b] BLOCKER blocks ISSUE
|
||||
jira [-v ...] [-u USER] [-e URI] [-b] watch ISSUE [-w WATCHER]
|
||||
jira [-v ...] [-u USER] [-e URI] [-b] (trans|transition) TRANSITION ISSUE [-m COMMENT]
|
||||
jira [-v ...] [-u USER] [-e URI] [-b] ack ISSUE [-m COMMENT]
|
||||
jira [-v ...] [-u USER] [-e URI] [-b] close ISSUE [-m COMMENT]
|
||||
jira [-v ...] [-u USER] [-e URI] [-b] resolve ISSUE [-m COMMENT]
|
||||
jira [-v ...] [-u USER] [-e URI] [-b] reopen ISSUE [-m COMMENT]
|
||||
jira [-v ...] [-u USER] [-e URI] [-b] start ISSUE [-m COMMENT]
|
||||
jira [-v ...] [-u USER] [-e URI] [-b] stop ISSUE [-m COMMENT]
|
||||
jira [-v ...] [-u USER] [-e URI] [-b] [-t FILE] (trans|transition) TRANSITION ISSUE [-m COMMENT] [--noedit]
|
||||
jira [-v ...] [-u USER] [-e URI] [-b] ack ISSUE [-m COMMENT] [--edit]
|
||||
jira [-v ...] [-u USER] [-e URI] [-b] close ISSUE [-m COMMENT] [--edit]
|
||||
jira [-v ...] [-u USER] [-e URI] [-b] resolve ISSUE [-m COMMENT] [--edit]
|
||||
jira [-v ...] [-u USER] [-e URI] [-b] reopen ISSUE [-m COMMENT] [--edit]
|
||||
jira [-v ...] [-u USER] [-e URI] [-b] start ISSUE [-m COMMENT] [--edit]
|
||||
jira [-v ...] [-u USER] [-e URI] [-b] stop ISSUE [-m COMMENT] [--edit]
|
||||
jira [-v ...] [-u USER] [-e URI] [-b] [-t FILE] comment ISSUE [-m COMMENT]
|
||||
jira [-v ...] [-u USER] [-e URI] [-b] take ISSUE
|
||||
jira [-v ...] [-u USER] [-e URI] [-b] (assign|give) ISSUE ASSIGNEE
|
||||
@@ -142,7 +142,7 @@ Command Options:
|
||||
log.Error("endpoint option required. Either use --endpoint or set a enpoint option in your ~/.jira.d/config.yml file")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
|
||||
c := cli.New(opts)
|
||||
|
||||
log.Debug("opts: %s", opts)
|
||||
@@ -165,6 +165,22 @@ Command Options:
|
||||
return dflt
|
||||
}
|
||||
|
||||
setEditing := func(dflt bool) {
|
||||
if dflt {
|
||||
if val, ok := opts["noedit"]; ok && val == "true" {
|
||||
opts["edit"] = "false"
|
||||
} else {
|
||||
opts["edit"] = "true"
|
||||
}
|
||||
} else {
|
||||
if val, ok := opts["edit"]; ok && val == "true" {
|
||||
opts["edit"] = "true"
|
||||
} else {
|
||||
opts["edit"] = "false"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if validCommand("login") {
|
||||
err = c.CmdLogin()
|
||||
} else if validCommand("fields") {
|
||||
@@ -172,6 +188,7 @@ Command Options:
|
||||
} else if validCommand("ls") || validCommand("list") {
|
||||
err = c.CmdList()
|
||||
} else if validCommand("edit") {
|
||||
setEditing(true)
|
||||
err = c.CmdEdit(args["ISSUE"].(string))
|
||||
} else if validCommand("editmeta") {
|
||||
err = c.CmdEditMeta(args["ISSUE"].(string))
|
||||
@@ -187,6 +204,7 @@ Command Options:
|
||||
validOpt("issuetype", "Bug").(string),
|
||||
)
|
||||
} else if validCommand("create") {
|
||||
setEditing(true)
|
||||
err = c.CmdCreate(
|
||||
validOpt("project", nil).(string),
|
||||
validOpt("issuetype", "Bug").(string),
|
||||
@@ -209,21 +227,28 @@ Command Options:
|
||||
validOpt("watcher", user).(string),
|
||||
)
|
||||
} else if validCommand("trans") || validCommand("transition") {
|
||||
setEditing(true)
|
||||
err = c.CmdTransition(
|
||||
args["ISSUE"].(string),
|
||||
args["TRANSITION"].(string),
|
||||
)
|
||||
} else if validCommand("close") {
|
||||
setEditing(false)
|
||||
err = c.CmdTransition(args["ISSUE"].(string), "close")
|
||||
} else if validCommand("ack") {
|
||||
setEditing(false)
|
||||
err = c.CmdTransition(args["ISSUE"].(string), "acknowledge")
|
||||
} else if validCommand("reopen") {
|
||||
setEditing(false)
|
||||
err = c.CmdTransition(args["ISSUE"].(string), "reopen")
|
||||
} else if validCommand("resolve") {
|
||||
setEditing(false)
|
||||
err = c.CmdTransition(args["ISSUE"].(string), "resolve")
|
||||
} else if validCommand("start") {
|
||||
setEditing(false)
|
||||
err = c.CmdTransition(args["ISSUE"].(string), "start")
|
||||
} else if validCommand("stop") {
|
||||
setEditing(false)
|
||||
err = c.CmdTransition(args["ISSUE"].(string), "stop")
|
||||
} else if validCommand("comment") {
|
||||
err = c.CmdComment(args["ISSUE"].(string))
|
||||
|
||||
Reference in New Issue
Block a user