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:
Cory Bennett
2015-02-17 10:19:54 -08:00
parent 9bf7533fc2
commit 45fb06f6bf
5 changed files with 149 additions and 69 deletions
+28 -19
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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))