From 7f8947b970a0de2e7894296e03a4759af68fc506 Mon Sep 17 00:00:00 2001 From: Olivier Tremblay Date: Wed, 4 Oct 2017 19:45:42 -0400 Subject: [PATCH] Add support for silent mode. Fixes #13 partially. Only missing STDIN reading as a special file. --- cmd/jkl/create.go | 9 +- cmd/jkl/edit.go | 11 +- cmd/jkl/editor.go | 44 +++++-- cmd/jkl/editor_test.go | 13 +- cmd/jkl/editor_test_file.issue | 2 + cmd/jkl/jkl.go | 1 + cmd/jkl/list.go | 5 +- issue.go | 227 ++++++++++++++++++--------------- jkl.go | 1 + 9 files changed, 188 insertions(+), 125 deletions(-) create mode 100644 cmd/jkl/editor_test_file.issue diff --git a/cmd/jkl/create.go b/cmd/jkl/create.go index 69c37ec..d36e0c7 100644 --- a/cmd/jkl/create.go +++ b/cmd/jkl/create.go @@ -4,19 +4,20 @@ import ( "bytes" "errors" "flag" + "fmt" "io" "os" - "fmt" "text/template" "otremblay.com/jkl" ) type CreateCmd struct { - args []string - project string - file string + args []string + project string + file string issuetype string + silent bool } func NewCreateCmd(args []string) (*CreateCmd, error) { diff --git a/cmd/jkl/edit.go b/cmd/jkl/edit.go index e06ec37..c467433 100644 --- a/cmd/jkl/edit.go +++ b/cmd/jkl/edit.go @@ -3,6 +3,7 @@ package main import ( "bytes" "flag" + "fmt" "os" "text/template" @@ -21,9 +22,9 @@ func NewEditCmd(args []string) (*EditCmd, error) { ccmd := &EditCmd{project: os.Getenv("JIRA_PROJECT")} f := flag.NewFlagSet("x", flag.ExitOnError) 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) - ccmd.taskKey = flag.Arg(0) + ccmd.taskKey = f.Arg(0) return ccmd, nil } @@ -31,7 +32,7 @@ func (ecmd *EditCmd) Edit() error { b := bytes.NewBuffer(nil) iss, err := jkl.GetIssue(ecmd.taskKey) if err != nil { - return err + return fmt.Errorf("Edit failed: %v", err) } err = editTmpl.Execute(b, iss) if err != nil { @@ -42,12 +43,12 @@ func (ecmd *EditCmd) Edit() error { iss, err = GetIssueFromFile(ecmd.file, b, iss.EditMeta) if err != nil { - return err + return fmt.Errorf("Error getting issue from file: %v", err) } } else { iss, err = GetIssueFromTmpFile(b, iss.EditMeta) if err != nil { - return err + return fmt.Errorf("Error getting issue from temp file: %v", err) } } diff --git a/cmd/jkl/editor.go b/cmd/jkl/editor.go index fedfc6e..3d45bb0 100644 --- a/cmd/jkl/editor.go +++ b/cmd/jkl/editor.go @@ -10,6 +10,7 @@ import ( "os/exec" "regexp" "strings" + "unicode" "reflect" @@ -17,6 +18,7 @@ import ( "otremblay.com/jkl" ) + // def get_editor do // [System.get_env("EDITOR"), "nano", "vim", "vi"] // |> 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) { f, err := ioutil.TempFile(os.TempDir(), "jkl") if err != nil { - return nil, err + return nil, fmt.Errorf("Error opening tempfile: %v", err) } copyInitial(f, initial) f2, err := GetTextFromFile(f) if err != nil { - return nil, err + return nil, fmt.Errorf("Error reading tempfile: %v", err) } 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) { - cmd := exec.Command(GetEditor(), file.Name()) - cmd.Stdin = os.Stdin - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - err := cmd.Run() - if err != nil { - fmt.Println(err) - return nil, err + var err error + if !*SilentMode { + cmd := exec.Command(GetEditor(), file.Name()) + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + err := cmd.Run() + if err != nil { + fmt.Println(err) + return nil, err + } + _, err = file.Seek(0, 0) } - _, err = file.Seek(0, 0) return file, err } @@ -108,6 +113,7 @@ func IssueFromReader(f io.Reader, editMeta *jkl.EditMeta) *jkl.JiraIssue { riss := reflect.ValueOf(iss).Elem() fieldsField := riss.FieldByName("Fields").Elem() currentField := reflect.Value{} + currFieldName := "" brd := bufio.NewReader(f) for { b, _, err := brd.ReadLine() @@ -131,6 +137,7 @@ func IssueFromReader(f io.Reader, editMeta *jkl.EditMeta) *jkl.JiraIssue { if len(parts) > 0 { iss.Fields.IssueType = &jkl.IssueType{} currentField = reflect.Value{} + currFieldName = potentialField f2 := newfield.Elem() f3 := f2.FieldByName("Name") 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 { iss.Fields.Project = &jkl.Project{} currentField = reflect.Value{} + currFieldName = potentialField f2 := newfield.Elem() f3 := f2.FieldByName("Key") 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 { iss.Fields.Parent = &jkl.JiraIssue{} currentField = reflect.Value{} + currFieldName = potentialField f2 := newfield.Elem() f3 := f2.FieldByName("Key") f3.SetString(strings.TrimSpace(strings.Join(parts, ":"))) } } else { + currFieldName = potentialField 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 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 diff --git a/cmd/jkl/editor_test.go b/cmd/jkl/editor_test.go index 35aa64f..4ca6988 100644 --- a/cmd/jkl/editor_test.go +++ b/cmd/jkl/editor_test.go @@ -12,7 +12,8 @@ hell Issue Type: Sometype this is ignored Summary: Dookienator -also ignored`, "\n")) +also ignored`, "\n"), nil) + AssertEqual(t, `Cowboys from hell`, iss.Fields.Description) @@ -34,3 +35,13 @@ Actual: %v`, expected, actual) 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) +} diff --git a/cmd/jkl/editor_test_file.issue b/cmd/jkl/editor_test_file.issue new file mode 100644 index 0000000..d558aee --- /dev/null +++ b/cmd/jkl/editor_test_file.issue @@ -0,0 +1,2 @@ +Summary: Dookie +Description: McDoogal \ No newline at end of file diff --git a/cmd/jkl/jkl.go b/cmd/jkl/jkl.go index 45da524..0df6212 100644 --- a/cmd/jkl/jkl.go +++ b/cmd/jkl/jkl.go @@ -14,6 +14,7 @@ import ( var verbose = flag.Bool("v", false, "Output debug information about jkl") var help = flag.Bool("h", false, "Outputs usage information message") +var SilentMode = flag.Bool("s", false, "Silent execution.") func main() { jkl.FindRCFile() diff --git a/cmd/jkl/list.go b/cmd/jkl/list.go index 592cf80..b5c5a61 100644 --- a/cmd/jkl/list.go +++ b/cmd/jkl/list.go @@ -39,6 +39,9 @@ func (l *listissue) Color() string { } return "" } +func (l *listissue) EFByName(name string) string { + return (*jkl.JiraIssue)(l).EFByName(name) +} type ListCmd struct { args []string @@ -76,7 +79,7 @@ func (l *ListCmd) List() error { if issues, err := jkl.List(strings.Join(l.args, " ")); err != nil { return err } else { - for _, issue := range issues { + for issue := range issues { var li listissue li = listissue(*issue) err := l.tmpl.Execute(os.Stdout, &li) diff --git a/issue.go b/issue.go index 6ca89e0..6e6d596 100644 --- a/issue.go +++ b/issue.go @@ -15,13 +15,15 @@ import ( ) type Search struct { - Issues []*JiraIssue `json:"issues"` + Issues []*JiraIssue `json:"issues"` + Total int + MaxResults int } type IssueType struct { - Name string `json:"name"` + Name string `json:"name"` IconURL string `json:",omitempty"` - Fields map[string]*FieldSpec + Fields map[string]*FieldSpec } func (it *IssueType) RangeFieldSpecs() string { @@ -38,12 +40,12 @@ func (it *IssueType) RangeFieldSpecs() string { } type AllowedValue struct { - Id string - Self string + Id string + Self string Value string } -func (a *AllowedValue) String() string{ +func (a *AllowedValue) String() string { return a.Value } @@ -51,24 +53,22 @@ type FieldSpec struct { Name string Required bool Schema struct { - Type string - Custom string + Type string + Custom string CustomId int - Items string + Items string } - Operations []string + Operations []string AllowedValues []*AllowedValue - } - type CreateMeta struct { Projects []*Project } type Project struct { Key string `json:"key,omitempty"` - Name string + Name string IssueTypes []*IssueType } @@ -82,9 +82,9 @@ func (a *Author) String() string { } type Comment struct { - Id string `json:"id"` + Id string `json:"id"` Author *Author `json:"author"` - Body string `json:"body"` + Body string `json:"body"` } type CommentColl struct { @@ -92,7 +92,7 @@ type CommentColl struct { } type Status struct { - Name string + Name string IconURL string `json:",omitempty"` } @@ -107,14 +107,14 @@ func (a *Attachment) String() string { type Attachment struct { Filename string - Author *Author - Content string + Author *Author + Content string } type LinkType struct { - Id string - Name string - Inward string + Id string + Name string + Inward string Outward string } @@ -129,8 +129,8 @@ func (i *IssueLink) String() string { } type IssueLink struct { - LinkType *LinkType `json:"type"` - InwardIssue *JiraIssue + LinkType *LinkType `json:"type"` + InwardIssue *JiraIssue OutwardIssue *JiraIssue } @@ -139,18 +139,18 @@ func (w *Worklog) String() string { } type Worklog struct { - Author *Author - Comment string - TimeSpent string + Author *Author + Comment string + TimeSpent string TimeSpentSeconds int - Started string + Started string } type Worklogs struct { Worklogs []*Worklog `json:",omitempty"` } -func (f *Fields) UnmarshalJSON(b []byte) error{ +func (f *Fields) UnmarshalJSON(b []byte) error { err := json.Unmarshal(b, &f.rawFields) if err != nil { fmt.Println("splosion") @@ -159,16 +159,18 @@ func (f *Fields) UnmarshalJSON(b []byte) error{ f.rawExtraFields = map[string]json.RawMessage{} vf := reflect.ValueOf(f).Elem() 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() { - objType := field.Type() - obj := reflect.New(objType).Interface() + objType := field.Type() + obj := reflect.New(objType).Interface() err := json.Unmarshal(mess, &obj) if err != nil { 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 { f.rawExtraFields[key] = mess } @@ -182,22 +184,22 @@ type Priority struct { } type Fields struct { - *IssueType `json:"issuetype,omitempty"` - Assignee *Author `json:",omitempty"` - Project *Project `json:"project,omitempty"` - Summary string `json:"summary,omitempty"` - Description string `json:"description,omitempty"` - Comment *CommentColl `json:"comment,omitempty"` - Parent *JiraIssue `json:",omitempty"` - Status *Status `json:",omitempty"` - TimeTracking *TimeTracking `json:"timetracking,omitempty"` - Attachment []*Attachment `json:"attachment,omitempty"` - IssueLinks []*IssueLink `json:"issueLinks,omitempty"` - Priority *Priority `json:",omitempty"` - Worklog *Worklogs `json:"worklog,omitempty"` - rawFields map[string]json.RawMessage + *IssueType `json:"issuetype,omitempty"` + Assignee *Author `json:",omitempty"` + Project *Project `json:"project,omitempty"` + Summary string `json:"summary,omitempty"` + Description string `json:"description,omitempty"` + Comment *CommentColl `json:"comment,omitempty"` + Parent *JiraIssue `json:",omitempty"` + Status *Status `json:",omitempty"` + TimeTracking *TimeTracking `json:"timetracking,omitempty"` + Attachment []*Attachment `json:"attachment,omitempty"` + IssueLinks []*IssueLink `json:"issueLinks,omitempty"` + Priority *Priority `json:",omitempty"` + Worklog *Worklogs `json:"worklog,omitempty"` + rawFields map[string]json.RawMessage rawExtraFields map[string]json.RawMessage - ExtraFields map[string]interface{} `json:"-"` + ExtraFields map[string]interface{} `json:"-"` } 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) } -type Transition struct{ - Id string `json:"id"` +type Transition struct { + Id string `json:"id"` Name string `json:"name"` } - type Schema struct { - System string - Custom string + System string + Custom string CustomId int } - type EditMeta struct { Fields map[string]*FieldSpec } - - type JiraIssue struct { - Key string `json:"key,omitempty"` - Fields *Fields `json:"fields,omitempty"` + Key string `json:"key,omitempty"` + Fields *Fields `json:"fields,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 { tmp := map[string]json.RawMessage{} if len(b) == 0 { return nil } i.Fields = &Fields{} - i.EditMeta = &EditMeta{Fields:map[string]*FieldSpec{}} + i.EditMeta = &EditMeta{Fields: map[string]*FieldSpec{}} err := json.Unmarshal(b, &tmp) if err != nil && *Verbose { 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) 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) - if err != nil && *Verbose{ + if err != nil && *Verbose { fmt.Fprintln(os.Stderr, errors.New(fmt.Sprintf("%s: %s", "Error unpacking EditMeta", err))) } err = json.Unmarshal(tmp["key"], &i.Key) @@ -282,34 +280,39 @@ func (i *JiraIssue) UnmarshalJSON(b []byte) error { if len(results) == 2 { i.Fields.ExtraFields[k] = results[1] } - } else {switch f.Schema.Type { - case "user": - a := &Author{} - json.Unmarshal(v, &a) - i.Fields.ExtraFields[k] = a - case "option": - val := &AllowedValue{} - err = json.Unmarshal(v, &val) - if err != nil {panic(err)} - i.Fields.ExtraFields[k] = val - case "array": - if f.Schema.Items == "option" { - val := []*AllowedValue{} - err = json.Unmarshal(v, &val) - if err != nil {panic(err)} - i.Fields.ExtraFields[k] = val - continue - } - fallthrough - default: - if string(v) != "null" { - i.Fields.ExtraFields[k] = string(v) + } else { + switch f.Schema.Type { + case "user": + a := &Author{} + json.Unmarshal(v, &a) + i.Fields.ExtraFields[k] = a + case "option": + val := &AllowedValue{} + err = json.Unmarshal(v, &val) + if err != nil { + panic(err) + } + i.Fields.ExtraFields[k] = val + case "array": + if f.Schema.Items == "option" { + val := []*AllowedValue{} + err = json.Unmarshal(v, &val) + if err != nil { + panic(err) + } + i.Fields.ExtraFields[k] = val + continue + } + fallthrough + default: + if string(v) != "null" { + i.Fields.ExtraFields[k] = string(v) + } } } - } } } - + return nil } @@ -331,7 +334,21 @@ func (i *JiraIssue) String() 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{} b := bytes.NewBuffer(nil) for k, v := range i.Fields.ExtraFields { @@ -350,7 +367,7 @@ func (i *JiraIssue) PrintExtraFields() 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}} ----------------- @@ -358,30 +375,40 @@ var commentTemplate = `{{if .Fields.Comment }}{{$k := .Key}}{{range .Fields.Comm {{end}}{{end}}` 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.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" + "\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[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 + "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" + - "URL: {{.URL}}\n\n" + + "URL: {{.URL}}\n\n" + "{{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" + "Time Remaining/Original Estimate:\t{{.Fields.PrettyRemaining}} / {{.Fields.PrettyOriginalEstimate}}\n\n" + - "{{.PrintExtraFields}}\n\n"+ + "{{.PrintExtraFields}}\n\n" + "Description: {{.Fields.Description}} \n\n" + - "Issue Links: \n{{range .Fields.IssueLinks}}\t{{.}}\n{{end}}\n\n" + - "Comments:\n\n" + commentTemplate+ + "Issue Links: \n{{range .Fields.IssueLinks}}\t{{.}}\n{{end}}\n\n" + + "Comments:\n\n" + commentTemplate + "Worklog:\n{{range .Fields.Worklog.Worklogs}}\t{{.}}\n{{end}}" var CommentIdSeparator = "~" -var issueTmpl = template.Must(template.New("issueTmpl").Parse(issueTmplTxt)) -var issueTmplNoColor = template.Must(template.New("issueTmplNoColor").Parse(issueTmplNoColorTxt)) \ No newline at end of file +var issueTmpl, issueTmplNoColor *template.Template + +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)) + } +} diff --git a/jkl.go b/jkl.go index 7823de3..bc1f855 100644 --- a/jkl.go +++ b/jkl.go @@ -239,6 +239,7 @@ func DoTransition(taskKey string, transitionName string) error { } func LogWork(taskKey string, workAmount string) error { + bootHttpClient() payload, err := serializePayload(map[string]interface{}{"timeSpent": workAmount}) resp, err := httpClient.Post("api/2/issue/"+taskKey+"/worklog", payload) if err != nil {