Add support for silent mode.

Fixes #13 partially.

Only missing STDIN reading as a special file.
This commit is contained in:
Olivier Tremblay 2017-10-04 19:45:42 -04:00 committed by Olivier Tremblay
parent edc5120921
commit 7f8947b970
No known key found for this signature in database
GPG key ID: D1C73ACB855E3A6D
9 changed files with 188 additions and 125 deletions

View file

@ -4,19 +4,20 @@ import (
"bytes" "bytes"
"errors" "errors"
"flag" "flag"
"fmt"
"io" "io"
"os" "os"
"fmt"
"text/template" "text/template"
"otremblay.com/jkl" "otremblay.com/jkl"
) )
type CreateCmd struct { type CreateCmd struct {
args []string args []string
project string project string
file string file string
issuetype string issuetype string
silent bool
} }
func NewCreateCmd(args []string) (*CreateCmd, error) { func NewCreateCmd(args []string) (*CreateCmd, error) {

View file

@ -3,6 +3,7 @@ package main
import ( import (
"bytes" "bytes"
"flag" "flag"
"fmt"
"os" "os"
"text/template" "text/template"
@ -21,9 +22,9 @@ func NewEditCmd(args []string) (*EditCmd, error) {
ccmd := &EditCmd{project: os.Getenv("JIRA_PROJECT")} ccmd := &EditCmd{project: os.Getenv("JIRA_PROJECT")}
f := flag.NewFlagSet("x", flag.ExitOnError) f := flag.NewFlagSet("x", flag.ExitOnError)
f.StringVar(&ccmd.project, "p", "", "Jira project key") f.StringVar(&ccmd.project, "p", "", "Jira project key")
f.StringVar(&ccmd.file, "f", "filename", "File to get issue description from") f.StringVar(&ccmd.file, "f", "", "File to get issue description from")
f.Parse(args) f.Parse(args)
ccmd.taskKey = flag.Arg(0) ccmd.taskKey = f.Arg(0)
return ccmd, nil return ccmd, nil
} }
@ -31,7 +32,7 @@ func (ecmd *EditCmd) Edit() error {
b := bytes.NewBuffer(nil) b := bytes.NewBuffer(nil)
iss, err := jkl.GetIssue(ecmd.taskKey) iss, err := jkl.GetIssue(ecmd.taskKey)
if err != nil { if err != nil {
return err return fmt.Errorf("Edit failed: %v", err)
} }
err = editTmpl.Execute(b, iss) err = editTmpl.Execute(b, iss)
if err != nil { if err != nil {
@ -42,12 +43,12 @@ func (ecmd *EditCmd) Edit() error {
iss, err = GetIssueFromFile(ecmd.file, b, iss.EditMeta) iss, err = GetIssueFromFile(ecmd.file, b, iss.EditMeta)
if err != nil { if err != nil {
return err return fmt.Errorf("Error getting issue from file: %v", err)
} }
} else { } else {
iss, err = GetIssueFromTmpFile(b, iss.EditMeta) iss, err = GetIssueFromTmpFile(b, iss.EditMeta)
if err != nil { if err != nil {
return err return fmt.Errorf("Error getting issue from temp file: %v", err)
} }
} }

View file

@ -10,6 +10,7 @@ import (
"os/exec" "os/exec"
"regexp" "regexp"
"strings" "strings"
"unicode"
"reflect" "reflect"
@ -17,6 +18,7 @@ import (
"otremblay.com/jkl" "otremblay.com/jkl"
) )
// def get_editor do // def get_editor do
// [System.get_env("EDITOR"), "nano", "vim", "vi"] // [System.get_env("EDITOR"), "nano", "vim", "vi"]
// |> Enum.find(nil, fn (ed) -> System.find_executable(ed) != nil end) // |> Enum.find(nil, fn (ed) -> System.find_executable(ed) != nil end)
@ -42,12 +44,12 @@ func copyInitial(dst io.WriteSeeker, initial io.Reader) {
func GetIssueFromTmpFile(initial io.Reader, editMeta *jkl.EditMeta) (*jkl.JiraIssue, error) { func GetIssueFromTmpFile(initial io.Reader, editMeta *jkl.EditMeta) (*jkl.JiraIssue, error) {
f, err := ioutil.TempFile(os.TempDir(), "jkl") f, err := ioutil.TempFile(os.TempDir(), "jkl")
if err != nil { if err != nil {
return nil, err return nil, fmt.Errorf("Error opening tempfile: %v", err)
} }
copyInitial(f, initial) copyInitial(f, initial)
f2, err := GetTextFromFile(f) f2, err := GetTextFromFile(f)
if err != nil { if err != nil {
return nil, err return nil, fmt.Errorf("Error reading tempfile: %v", err)
} }
return IssueFromReader(f2, editMeta), nil return IssueFromReader(f2, editMeta), nil
} }
@ -73,16 +75,19 @@ func GetTextFromSpecifiedFile(filename string, initial io.Reader) (io.Reader, er
} }
func GetTextFromFile(file *os.File) (io.Reader, error) { func GetTextFromFile(file *os.File) (io.Reader, error) {
cmd := exec.Command(GetEditor(), file.Name()) var err error
cmd.Stdin = os.Stdin if !*SilentMode {
cmd.Stdout = os.Stdout cmd := exec.Command(GetEditor(), file.Name())
cmd.Stderr = os.Stderr cmd.Stdin = os.Stdin
err := cmd.Run() cmd.Stdout = os.Stdout
if err != nil { cmd.Stderr = os.Stderr
fmt.Println(err) err := cmd.Run()
return nil, err if err != nil {
fmt.Println(err)
return nil, err
}
_, err = file.Seek(0, 0)
} }
_, err = file.Seek(0, 0)
return file, err return file, err
} }
@ -108,6 +113,7 @@ func IssueFromReader(f io.Reader, editMeta *jkl.EditMeta) *jkl.JiraIssue {
riss := reflect.ValueOf(iss).Elem() riss := reflect.ValueOf(iss).Elem()
fieldsField := riss.FieldByName("Fields").Elem() fieldsField := riss.FieldByName("Fields").Elem()
currentField := reflect.Value{} currentField := reflect.Value{}
currFieldName := ""
brd := bufio.NewReader(f) brd := bufio.NewReader(f)
for { for {
b, _, err := brd.ReadLine() b, _, err := brd.ReadLine()
@ -131,6 +137,7 @@ func IssueFromReader(f io.Reader, editMeta *jkl.EditMeta) *jkl.JiraIssue {
if len(parts) > 0 { if len(parts) > 0 {
iss.Fields.IssueType = &jkl.IssueType{} iss.Fields.IssueType = &jkl.IssueType{}
currentField = reflect.Value{} currentField = reflect.Value{}
currFieldName = potentialField
f2 := newfield.Elem() f2 := newfield.Elem()
f3 := f2.FieldByName("Name") f3 := f2.FieldByName("Name")
f3.SetString(strings.TrimSpace(strings.Join(parts, ":"))) f3.SetString(strings.TrimSpace(strings.Join(parts, ":")))
@ -139,6 +146,7 @@ func IssueFromReader(f io.Reader, editMeta *jkl.EditMeta) *jkl.JiraIssue {
if len(parts) > 0 { if len(parts) > 0 {
iss.Fields.Project = &jkl.Project{} iss.Fields.Project = &jkl.Project{}
currentField = reflect.Value{} currentField = reflect.Value{}
currFieldName = potentialField
f2 := newfield.Elem() f2 := newfield.Elem()
f3 := f2.FieldByName("Key") f3 := f2.FieldByName("Key")
f3.SetString(strings.TrimSpace(strings.Join(parts, ":"))) f3.SetString(strings.TrimSpace(strings.Join(parts, ":")))
@ -147,19 +155,27 @@ func IssueFromReader(f io.Reader, editMeta *jkl.EditMeta) *jkl.JiraIssue {
if len(parts) > 0 { if len(parts) > 0 {
iss.Fields.Parent = &jkl.JiraIssue{} iss.Fields.Parent = &jkl.JiraIssue{}
currentField = reflect.Value{} currentField = reflect.Value{}
currFieldName = potentialField
f2 := newfield.Elem() f2 := newfield.Elem()
f3 := f2.FieldByName("Key") f3 := f2.FieldByName("Key")
f3.SetString(strings.TrimSpace(strings.Join(parts, ":"))) f3.SetString(strings.TrimSpace(strings.Join(parts, ":")))
} }
} else { } else {
currFieldName = potentialField
currentField = newfield currentField = newfield
} }
} else if editMeta != nil { } else if editMeta != nil {
// If it's not valid, throw it at the createmeta. It will probably end up in ExtraFields. // If it's not valid, throw it at the createmeta. It will probably end up in ExtraFields.
} }
if currentField.IsValid() { if currentField.IsValid() {
currentField.SetString(strings.TrimSpace(currentField.String() + "\n" + strings.Join(parts, ":"))) newString := currentField.String() + "\n" + strings.Join(parts, ":")
if currFieldName != "Description" {
newString = strings.TrimSpace(newString)
} else if currentField.String() == "" {
newString = strings.TrimLeftFunc(newString, unicode.IsSpace)
}
currentField.SetString(newString)
} }
} }
return iss return iss

View file

@ -12,7 +12,8 @@ hell
Issue Type: Sometype Issue Type: Sometype
this is ignored this is ignored
Summary: Dookienator Summary: Dookienator
also ignored`, "\n")) also ignored`, "\n"), nil)
AssertEqual(t, `Cowboys AssertEqual(t, `Cowboys
from from
hell`, iss.Fields.Description) hell`, iss.Fields.Description)
@ -34,3 +35,13 @@ Actual: %v`, expected, actual)
func Assert(t *testing.T, fn func() bool) { func Assert(t *testing.T, fn func() bool) {
} }
func TestIssueFromFile(t *testing.T) {
*SilentMode = true
issue, err := GetIssueFromFile("editor_test_file.issue", nil, nil)
if err != nil {
t.Errorf("Apparently we effed up some! --> %v", err)
}
AssertEqual(t, "Dookie", issue.Fields.Summary)
AssertEqual(t, "McDoogal", issue.Fields.Description)
}

View file

@ -0,0 +1,2 @@
Summary: Dookie
Description: McDoogal

View file

@ -14,6 +14,7 @@ import (
var verbose = flag.Bool("v", false, "Output debug information about jkl") var verbose = flag.Bool("v", false, "Output debug information about jkl")
var help = flag.Bool("h", false, "Outputs usage information message") var help = flag.Bool("h", false, "Outputs usage information message")
var SilentMode = flag.Bool("s", false, "Silent execution.")
func main() { func main() {
jkl.FindRCFile() jkl.FindRCFile()

View file

@ -39,6 +39,9 @@ func (l *listissue) Color() string {
} }
return "" return ""
} }
func (l *listissue) EFByName(name string) string {
return (*jkl.JiraIssue)(l).EFByName(name)
}
type ListCmd struct { type ListCmd struct {
args []string args []string
@ -76,7 +79,7 @@ func (l *ListCmd) List() error {
if issues, err := jkl.List(strings.Join(l.args, " ")); err != nil { if issues, err := jkl.List(strings.Join(l.args, " ")); err != nil {
return err return err
} else { } else {
for _, issue := range issues { for issue := range issues {
var li listissue var li listissue
li = listissue(*issue) li = listissue(*issue)
err := l.tmpl.Execute(os.Stdout, &li) err := l.tmpl.Execute(os.Stdout, &li)

227
issue.go
View file

@ -15,13 +15,15 @@ import (
) )
type Search struct { type Search struct {
Issues []*JiraIssue `json:"issues"` Issues []*JiraIssue `json:"issues"`
Total int
MaxResults int
} }
type IssueType struct { type IssueType struct {
Name string `json:"name"` Name string `json:"name"`
IconURL string `json:",omitempty"` IconURL string `json:",omitempty"`
Fields map[string]*FieldSpec Fields map[string]*FieldSpec
} }
func (it *IssueType) RangeFieldSpecs() string { func (it *IssueType) RangeFieldSpecs() string {
@ -38,12 +40,12 @@ func (it *IssueType) RangeFieldSpecs() string {
} }
type AllowedValue struct { type AllowedValue struct {
Id string Id string
Self string Self string
Value string Value string
} }
func (a *AllowedValue) String() string{ func (a *AllowedValue) String() string {
return a.Value return a.Value
} }
@ -51,24 +53,22 @@ type FieldSpec struct {
Name string Name string
Required bool Required bool
Schema struct { Schema struct {
Type string Type string
Custom string Custom string
CustomId int CustomId int
Items string Items string
} }
Operations []string Operations []string
AllowedValues []*AllowedValue AllowedValues []*AllowedValue
} }
type CreateMeta struct { type CreateMeta struct {
Projects []*Project Projects []*Project
} }
type Project struct { type Project struct {
Key string `json:"key,omitempty"` Key string `json:"key,omitempty"`
Name string Name string
IssueTypes []*IssueType IssueTypes []*IssueType
} }
@ -82,9 +82,9 @@ func (a *Author) String() string {
} }
type Comment struct { type Comment struct {
Id string `json:"id"` Id string `json:"id"`
Author *Author `json:"author"` Author *Author `json:"author"`
Body string `json:"body"` Body string `json:"body"`
} }
type CommentColl struct { type CommentColl struct {
@ -92,7 +92,7 @@ type CommentColl struct {
} }
type Status struct { type Status struct {
Name string Name string
IconURL string `json:",omitempty"` IconURL string `json:",omitempty"`
} }
@ -107,14 +107,14 @@ func (a *Attachment) String() string {
type Attachment struct { type Attachment struct {
Filename string Filename string
Author *Author Author *Author
Content string Content string
} }
type LinkType struct { type LinkType struct {
Id string Id string
Name string Name string
Inward string Inward string
Outward string Outward string
} }
@ -129,8 +129,8 @@ func (i *IssueLink) String() string {
} }
type IssueLink struct { type IssueLink struct {
LinkType *LinkType `json:"type"` LinkType *LinkType `json:"type"`
InwardIssue *JiraIssue InwardIssue *JiraIssue
OutwardIssue *JiraIssue OutwardIssue *JiraIssue
} }
@ -139,18 +139,18 @@ func (w *Worklog) String() string {
} }
type Worklog struct { type Worklog struct {
Author *Author Author *Author
Comment string Comment string
TimeSpent string TimeSpent string
TimeSpentSeconds int TimeSpentSeconds int
Started string Started string
} }
type Worklogs struct { type Worklogs struct {
Worklogs []*Worklog `json:",omitempty"` Worklogs []*Worklog `json:",omitempty"`
} }
func (f *Fields) UnmarshalJSON(b []byte) error{ func (f *Fields) UnmarshalJSON(b []byte) error {
err := json.Unmarshal(b, &f.rawFields) err := json.Unmarshal(b, &f.rawFields)
if err != nil { if err != nil {
fmt.Println("splosion") fmt.Println("splosion")
@ -159,16 +159,18 @@ func (f *Fields) UnmarshalJSON(b []byte) error{
f.rawExtraFields = map[string]json.RawMessage{} f.rawExtraFields = map[string]json.RawMessage{}
vf := reflect.ValueOf(f).Elem() vf := reflect.ValueOf(f).Elem()
for key, mess := range f.rawFields { for key, mess := range f.rawFields {
field := vf.FieldByNameFunc(func(s string)bool{return strings.ToLower(key) == strings.ToLower(s)}) field := vf.FieldByNameFunc(func(s string) bool { return strings.ToLower(key) == strings.ToLower(s) })
if field.IsValid() { if field.IsValid() {
objType := field.Type() objType := field.Type()
obj := reflect.New(objType).Interface() obj := reflect.New(objType).Interface()
err := json.Unmarshal(mess, &obj) err := json.Unmarshal(mess, &obj)
if err != nil { if err != nil {
fmt.Fprintln(os.Stderr, objType, obj, string(mess)) fmt.Fprintln(os.Stderr, objType, obj, string(mess))
fmt.Fprintln(os.Stderr, errors.New(fmt.Sprintf("%s [%s]: %s","Error allocating field",key, err))) fmt.Fprintln(os.Stderr, errors.New(fmt.Sprintf("%s [%s]: %s", "Error allocating field", key, err)))
}
if obj != nil && !(reflect.ValueOf(obj) == reflect.Zero(reflect.TypeOf(obj))) {
field.Set(reflect.ValueOf(obj).Elem())
} }
field.Set(reflect.ValueOf(obj).Elem())
} else { } else {
f.rawExtraFields[key] = mess f.rawExtraFields[key] = mess
} }
@ -182,22 +184,22 @@ type Priority struct {
} }
type Fields struct { type Fields struct {
*IssueType `json:"issuetype,omitempty"` *IssueType `json:"issuetype,omitempty"`
Assignee *Author `json:",omitempty"` Assignee *Author `json:",omitempty"`
Project *Project `json:"project,omitempty"` Project *Project `json:"project,omitempty"`
Summary string `json:"summary,omitempty"` Summary string `json:"summary,omitempty"`
Description string `json:"description,omitempty"` Description string `json:"description,omitempty"`
Comment *CommentColl `json:"comment,omitempty"` Comment *CommentColl `json:"comment,omitempty"`
Parent *JiraIssue `json:",omitempty"` Parent *JiraIssue `json:",omitempty"`
Status *Status `json:",omitempty"` Status *Status `json:",omitempty"`
TimeTracking *TimeTracking `json:"timetracking,omitempty"` TimeTracking *TimeTracking `json:"timetracking,omitempty"`
Attachment []*Attachment `json:"attachment,omitempty"` Attachment []*Attachment `json:"attachment,omitempty"`
IssueLinks []*IssueLink `json:"issueLinks,omitempty"` IssueLinks []*IssueLink `json:"issueLinks,omitempty"`
Priority *Priority `json:",omitempty"` Priority *Priority `json:",omitempty"`
Worklog *Worklogs `json:"worklog,omitempty"` Worklog *Worklogs `json:"worklog,omitempty"`
rawFields map[string]json.RawMessage rawFields map[string]json.RawMessage
rawExtraFields map[string]json.RawMessage rawExtraFields map[string]json.RawMessage
ExtraFields map[string]interface{} `json:"-"` ExtraFields map[string]interface{} `json:"-"`
} }
func (f *Fields) PrettyRemaining() string { func (f *Fields) PrettyRemaining() string {
@ -218,41 +220,37 @@ func PrettySeconds(seconds int) string {
return fmt.Sprintf("%dd %2dh %2dm %2ds", days, hours, minutes, seconds) return fmt.Sprintf("%dd %2dh %2dm %2ds", days, hours, minutes, seconds)
} }
type Transition struct{ type Transition struct {
Id string `json:"id"` Id string `json:"id"`
Name string `json:"name"` Name string `json:"name"`
} }
type Schema struct { type Schema struct {
System string System string
Custom string Custom string
CustomId int CustomId int
} }
type EditMeta struct { type EditMeta struct {
Fields map[string]*FieldSpec Fields map[string]*FieldSpec
} }
type JiraIssue struct { type JiraIssue struct {
Key string `json:"key,omitempty"` Key string `json:"key,omitempty"`
Fields *Fields `json:"fields,omitempty"` Fields *Fields `json:"fields,omitempty"`
Transitions []*Transition `json:"transitions,omitempty"` Transitions []*Transition `json:"transitions,omitempty"`
EditMeta *EditMeta `json:"editmeta,omitempty"` EditMeta *EditMeta `json:"editmeta,omitempty"`
} }
var sprintRegexp = regexp.MustCompile(`name=([^,]+),`)
var sprintRegexp = regexp.MustCompile(`name=([^,]+),`)
func (i *JiraIssue) UnmarshalJSON(b []byte) error { func (i *JiraIssue) UnmarshalJSON(b []byte) error {
tmp := map[string]json.RawMessage{} tmp := map[string]json.RawMessage{}
if len(b) == 0 { if len(b) == 0 {
return nil return nil
} }
i.Fields = &Fields{} i.Fields = &Fields{}
i.EditMeta = &EditMeta{Fields:map[string]*FieldSpec{}} i.EditMeta = &EditMeta{Fields: map[string]*FieldSpec{}}
err := json.Unmarshal(b, &tmp) err := json.Unmarshal(b, &tmp)
if err != nil && *Verbose { if err != nil && *Verbose {
fmt.Fprintln(os.Stderr, errors.New(fmt.Sprintf("%s: %s", "Error unpacking raw json", err))) fmt.Fprintln(os.Stderr, errors.New(fmt.Sprintf("%s: %s", "Error unpacking raw json", err)))
@ -263,10 +261,10 @@ func (i *JiraIssue) UnmarshalJSON(b []byte) error {
} }
err = json.Unmarshal(tmp["transitions"], &i.Transitions) err = json.Unmarshal(tmp["transitions"], &i.Transitions)
if err != nil && *Verbose { if err != nil && *Verbose {
fmt.Fprintln(os.Stderr, errors.New(fmt.Sprintf("%s: %s", "Error unpacking transitions", err))) fmt.Fprintln(os.Stderr, errors.New(fmt.Sprintf("%s: %s", "Error unpacking transitions", err)))
} }
err = json.Unmarshal(tmp["editmeta"], &i.EditMeta) err = json.Unmarshal(tmp["editmeta"], &i.EditMeta)
if err != nil && *Verbose{ if err != nil && *Verbose {
fmt.Fprintln(os.Stderr, errors.New(fmt.Sprintf("%s: %s", "Error unpacking EditMeta", err))) fmt.Fprintln(os.Stderr, errors.New(fmt.Sprintf("%s: %s", "Error unpacking EditMeta", err)))
} }
err = json.Unmarshal(tmp["key"], &i.Key) err = json.Unmarshal(tmp["key"], &i.Key)
@ -282,34 +280,39 @@ func (i *JiraIssue) UnmarshalJSON(b []byte) error {
if len(results) == 2 { if len(results) == 2 {
i.Fields.ExtraFields[k] = results[1] i.Fields.ExtraFields[k] = results[1]
} }
} else {switch f.Schema.Type { } else {
case "user": switch f.Schema.Type {
a := &Author{} case "user":
json.Unmarshal(v, &a) a := &Author{}
i.Fields.ExtraFields[k] = a json.Unmarshal(v, &a)
case "option": i.Fields.ExtraFields[k] = a
val := &AllowedValue{} case "option":
err = json.Unmarshal(v, &val) val := &AllowedValue{}
if err != nil {panic(err)} err = json.Unmarshal(v, &val)
i.Fields.ExtraFields[k] = val if err != nil {
case "array": panic(err)
if f.Schema.Items == "option" { }
val := []*AllowedValue{} i.Fields.ExtraFields[k] = val
err = json.Unmarshal(v, &val) case "array":
if err != nil {panic(err)} if f.Schema.Items == "option" {
i.Fields.ExtraFields[k] = val val := []*AllowedValue{}
continue err = json.Unmarshal(v, &val)
} if err != nil {
fallthrough panic(err)
default: }
if string(v) != "null" { i.Fields.ExtraFields[k] = val
i.Fields.ExtraFields[k] = string(v) continue
}
fallthrough
default:
if string(v) != "null" {
i.Fields.ExtraFields[k] = string(v)
}
} }
} }
}
} }
} }
return nil return nil
} }
@ -331,7 +334,21 @@ func (i *JiraIssue) String() string {
return b.String() return b.String()
} }
func (i *JiraIssue) PrintExtraFields() string{ func (i *JiraIssue) EFByName(name string) string {
for k, f := range i.EditMeta.Fields {
if f.Name == name {
return fmt.Sprint(i.Fields.ExtraFields[k])
}
}
return ""
}
func (i *JiraIssue) StoryPoints() string {
return i.EFByName("Story Points")
}
func (i *JiraIssue) PrintExtraFields() string {
sorter := map[string]string{} sorter := map[string]string{}
b := bytes.NewBuffer(nil) b := bytes.NewBuffer(nil)
for k, v := range i.Fields.ExtraFields { for k, v := range i.Fields.ExtraFields {
@ -350,7 +367,7 @@ func (i *JiraIssue) PrintExtraFields() string{
return b.String() return b.String()
} }
var commentTemplate = `{{if .Fields.Comment }}{{$k := .Key}}{{range .Fields.Comment.Comments}}{{.Author.DisplayName}} [~{{.Author.Name}}] ({{$k}}`+CommentIdSeparator+`{{.Id}}): var commentTemplate = `{{if .Fields.Comment }}{{$k := .Key}}{{range .Fields.Comment.Comments}}{{.Author.DisplayName}} [~{{.Author.Name}}] ({{$k}}` + CommentIdSeparator + `{{.Id}}):
----------------- -----------------
{{.Body}} {{.Body}}
----------------- -----------------
@ -358,30 +375,40 @@ var commentTemplate = `{{if .Fields.Comment }}{{$k := .Key}}{{range .Fields.Comm
{{end}}{{end}}` {{end}}{{end}}`
var issueTmplTxt = "\x1b[1m{{.Key}}\x1b[0m\t{{if .Fields.IssueType}}[{{.Fields.IssueType.Name}}]{{end}}\t{{.Fields.Summary}}\n\n" + var issueTmplTxt = "\x1b[1m{{.Key}}\x1b[0m\t{{if .Fields.IssueType}}[{{.Fields.IssueType.Name}}]{{end}}\t{{.Fields.Summary}}\n\n" +
"\x1b[1mURL\x1b[0m: {{.URL}}\n\n" + "\x1b[1mURL\x1b[0m: {{.URL}}\n\n" +
"{{if .Fields.Status}}\x1b[1mStatus\x1b[0m:\t {{.Fields.Status.Name}}\n{{end}}" + "{{if .Fields.Status}}\x1b[1mStatus\x1b[0m:\t {{.Fields.Status.Name}}\n{{end}}" +
"{{if .Fields.Priority}}\x1b[1mStatus\x1b[0m:\t {{.Fields.Priority.Name}}\n{{end}}" + "{{if .Fields.Priority}}\x1b[1mStatus\x1b[0m:\t {{.Fields.Priority.Name}}\n{{end}}" +
"\x1b[1mTransitions\x1b[0m: {{range .Transitions}}[{{.Name}}] {{end}}\n"+ "\x1b[1mTransitions\x1b[0m: {{range .Transitions}}[{{.Name}}] {{end}}\n" +
"{{if .Fields.Assignee}}\x1b[1mAssignee:\x1b[0m\t{{.Fields.Assignee.Name}}\n{{end}}\n" + "{{if .Fields.Assignee}}\x1b[1mAssignee:\x1b[0m\t{{.Fields.Assignee.Name}}\n{{end}}\n" +
"\x1b[1mTime Remaining/Original Estimate:\x1b[0m\t{{.Fields.PrettyRemaining}} / {{.Fields.PrettyOriginalEstimate}}\n\n" + "\x1b[1mTime Remaining/Original Estimate:\x1b[0m\t{{.Fields.PrettyRemaining}} / {{.Fields.PrettyOriginalEstimate}}\n\n" +
"{{$i := .}}{{range $k, $v := .Fields.ExtraFields}}{{with index $i.EditMeta.Fields $k}}\x1b[1m{{.Name}}\x1b[0m{{end}}: {{$v}}\n{{end}}\n\n"+ "{{$i := .}}{{range $k, $v := .Fields.ExtraFields}}{{with index $i.EditMeta.Fields $k}}\x1b[1m{{.Name}}\x1b[0m{{end}}: {{$v}}\n{{end}}\n\n" +
"\x1b[1mDescription:\x1b[0m {{.Fields.Description}} \n\n" + "\x1b[1mDescription:\x1b[0m {{.Fields.Description}} \n\n" +
"\x1b[1mIssue Links\x1b[0m: \n{{range .Fields.IssueLinks}}\t{{.}}\n{{end}}\n\n" + "\x1b[1mIssue Links\x1b[0m: \n{{range .Fields.IssueLinks}}\t{{.}}\n{{end}}\n\n" +
"\x1b[1mComments:\x1b[0m\n\n" + commentTemplate + "\x1b[1mComments:\x1b[0m\n\n" + commentTemplate +
"Worklog:\n{{range .Fields.Worklog.Worklogs}}\t{{.}}\n{{end}}" "Worklog:\n{{range .Fields.Worklog.Worklogs}}\t{{.}}\n{{end}}"
var issueTmplNoColorTxt = "{{.Key}}\t{{if .Fields.IssueType}}[{{.Fields.IssueType.Name}}]{{end}}\t{{.Fields.Summary}}\n\n" + var issueTmplNoColorTxt = "{{.Key}}\t{{if .Fields.IssueType}}[{{.Fields.IssueType.Name}}]{{end}}\t{{.Fields.Summary}}\n\n" +
"URL: {{.URL}}\n\n" + "URL: {{.URL}}\n\n" +
"{{if .Fields.Status}}Status:\t {{.Fields.Status.Name}}\n{{end}}" + "{{if .Fields.Status}}Status:\t {{.Fields.Status.Name}}\n{{end}}" +
"Transitions: {{range .Transitions}}[{{.Name}}] {{end}}\n"+ "Transitions: {{range .Transitions}}[{{.Name}}] {{end}}\n" +
"{{if .Fields.Assignee}}Assignee:\t{{.Fields.Assignee.Name}}\n{{end}}\n" + "{{if .Fields.Assignee}}Assignee:\t{{.Fields.Assignee.Name}}\n{{end}}\n" +
"Time Remaining/Original Estimate:\t{{.Fields.PrettyRemaining}} / {{.Fields.PrettyOriginalEstimate}}\n\n" + "Time Remaining/Original Estimate:\t{{.Fields.PrettyRemaining}} / {{.Fields.PrettyOriginalEstimate}}\n\n" +
"{{.PrintExtraFields}}\n\n"+ "{{.PrintExtraFields}}\n\n" +
"Description: {{.Fields.Description}} \n\n" + "Description: {{.Fields.Description}} \n\n" +
"Issue Links: \n{{range .Fields.IssueLinks}}\t{{.}}\n{{end}}\n\n" + "Issue Links: \n{{range .Fields.IssueLinks}}\t{{.}}\n{{end}}\n\n" +
"Comments:\n\n" + commentTemplate+ "Comments:\n\n" + commentTemplate +
"Worklog:\n{{range .Fields.Worklog.Worklogs}}\t{{.}}\n{{end}}" "Worklog:\n{{range .Fields.Worklog.Worklogs}}\t{{.}}\n{{end}}"
var CommentIdSeparator = "~" var CommentIdSeparator = "~"
var issueTmpl = template.Must(template.New("issueTmpl").Parse(issueTmplTxt)) var issueTmpl, issueTmplNoColor *template.Template
var issueTmplNoColor = template.Must(template.New("issueTmplNoColor").Parse(issueTmplNoColorTxt))
func SetupTmpl() {
isstmpl := os.Getenv("JKL_ISSUE_TMPL")
if isstmpl != "" {
issueTmpl = template.Must(template.New("issueTmpl").Parse(isstmpl))
issueTmplNoColor = issueTmpl
} else {
issueTmpl = template.Must(template.New("issueTmpl").Parse(issueTmplTxt))
issueTmplNoColor = template.Must(template.New("issueTmplNoColor").Parse(issueTmplNoColorTxt))
}
}

1
jkl.go
View file

@ -239,6 +239,7 @@ func DoTransition(taskKey string, transitionName string) error {
} }
func LogWork(taskKey string, workAmount string) error { func LogWork(taskKey string, workAmount string) error {
bootHttpClient()
payload, err := serializePayload(map[string]interface{}{"timeSpent": workAmount}) payload, err := serializePayload(map[string]interface{}{"timeSpent": workAmount})
resp, err := httpClient.Post("api/2/issue/"+taskKey+"/worklog", payload) resp, err := httpClient.Post("api/2/issue/"+taskKey+"/worklog", payload)
if err != nil { if err != nil {