This commit is contained in:
Cory Bennett
2015-02-13 15:44:07 -08:00
parent 4c1e0ec93e
commit 97ad931f79
5 changed files with 187 additions and 153 deletions
+43 -37
View File
@@ -1,28 +1,28 @@
package cli
import (
"github.com/op/go-logging"
"net/http"
"net/http/cookiejar"
"bytes"
"encoding/json"
"fmt"
"github.com/op/go-logging"
"gopkg.in/yaml.v2"
"io/ioutil"
"net/http"
"net/http/cookiejar"
"net/url"
"os"
"os/exec"
"gopkg.in/yaml.v2"
"net/url"
"time"
"bytes"
"runtime"
"time"
)
var log = logging.MustGetLogger("jira.cli")
type Cli struct {
endpoint *url.URL
opts map[string]string
endpoint *url.URL
opts map[string]string
cookieFile string
ua *http.Client
ua *http.Client
}
func New(opts map[string]string) *Cli {
@@ -32,12 +32,12 @@ func New(opts map[string]string) *Cli {
url, _ := url.Parse(endpoint)
cli := &Cli{
endpoint: url,
opts: opts,
endpoint: url,
opts: opts,
cookieFile: fmt.Sprintf("%s/.jira.d/cookies.js", homedir),
ua: &http.Client{Jar: cookieJar},
ua: &http.Client{Jar: cookieJar},
}
cli.ua.Jar.SetCookies(url, cli.loadCookies())
return cli
@@ -49,17 +49,17 @@ func (c *Cli) saveCookies(cookies []*http.Cookie) {
for _, cookie := range cookies {
cookie.Expires = expiry
}
if currentCookies := c.loadCookies(); currentCookies != nil {
currentCookiesByName := make(map[string]*http.Cookie)
for _, cookie := range currentCookies {
currentCookiesByName[cookie.Name] = cookie
}
for _, cookie := range cookies {
currentCookiesByName[cookie.Name] = cookie
}
mergedCookies := make([]*http.Cookie, 0, len(currentCookiesByName))
for _, v := range currentCookiesByName {
mergedCookies = append(mergedCookies, v)
@@ -80,7 +80,7 @@ func (c *Cli) loadCookies() []*http.Cookie {
log.Error("Failed to open %s: %s", c.cookieFile, err)
os.Exit(1)
}
cookies := make([]*http.Cookie,0)
cookies := make([]*http.Cookie, 0)
err = json.Unmarshal(bytes, &cookies)
if err != nil {
log.Error("Failed to parse json from file %s: %s", c.cookieFile, err)
@@ -103,7 +103,7 @@ func (c *Cli) makeRequestWithContent(method string, uri string, content string)
log.Info("%s %s", req.Method, req.URL.String())
if log.IsEnabledFor(logging.DEBUG) {
logBuffer := bytes.NewBuffer(make([]byte,0,len(content)))
logBuffer := bytes.NewBuffer(make([]byte, 0, len(content)))
req.Write(logBuffer)
log.Debug("%s", logBuffer)
// need to recreate the buffer since the offset is now at the end
@@ -129,7 +129,7 @@ func (c *Cli) get(uri string) (*http.Response, error) {
req, _ := http.NewRequest("GET", uri, nil)
log.Info("%s %s", req.Method, req.URL.String())
if log.IsEnabledFor(logging.DEBUG) {
logBuffer := bytes.NewBuffer(make([]byte,0))
logBuffer := bytes.NewBuffer(make([]byte, 0))
req.Write(logBuffer)
log.Debug("%s", logBuffer)
}
@@ -149,7 +149,7 @@ func (c *Cli) get(uri string) (*http.Response, error) {
func (c *Cli) makeRequest(req *http.Request) (resp *http.Response, err error) {
req.Header.Set("Content-Type", "application/json")
if resp, err = c.ua.Do(req); err != nil {
if resp, err = c.ua.Do(req); err != nil {
log.Error("Failed to %s %s: %s", req.Method, req.URL.String(), err)
return nil, err
} else {
@@ -157,11 +157,11 @@ func (c *Cli) makeRequest(req *http.Request) (resp *http.Response, err error) {
log.Error("response status: %s", resp.Status)
resp.Write(os.Stderr)
}
runtime.SetFinalizer(resp, func(r *http.Response) {
r.Body.Close()
})
if _, ok := resp.Header["Set-Cookie"]; ok {
c.saveCookies(resp.Cookies())
}
@@ -189,32 +189,37 @@ func (c *Cli) getTemplate(path string, dflt string) string {
func (c *Cli) editTemplate(template string, tmpFilePrefix string, templateData map[string]interface{}, templateProcessor func(string) error) error {
tmpdir := fmt.Sprintf("%s/.jira.d/tmp", os.Getenv("HOME"))
fh, err := ioutil.TempFile(tmpdir, tmpFilePrefix); if err != nil {
fh, err := ioutil.TempFile(tmpdir, tmpFilePrefix)
if err != nil {
log.Error("Failed to make temp file in %s: %s", tmpdir, err)
return err
}
defer fh.Close()
tmpFileName := fmt.Sprintf("%s.yml", fh.Name())
if err := os.Rename(fh.Name(), tmpFileName); err != nil {
log.Error("Failed to rename %s to %s: %s", fh.Name(), fmt.Sprintf("%s.yml", fh.Name()), err)
return err
}
err = runTemplate(template, templateData, fh); if err != nil {
err = runTemplate(template, templateData, fh)
if err != nil {
return err
}
fh.Close()
editor, ok := c.opts["editor"]; if !ok {
editor = os.Getenv("JIRA_EDITOR"); if editor == "" {
editor = os.Getenv("EDITOR"); if editor == "" {
editor, ok := c.opts["editor"]
if !ok {
editor = os.Getenv("JIRA_EDITOR")
if editor == "" {
editor = os.Getenv("EDITOR")
if editor == "" {
editor = "vim"
}
}
}
for ; true ; {
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
@@ -225,7 +230,7 @@ func (c *Cli) editTemplate(template string, tmpFilePrefix string, templateData m
}
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)
@@ -242,7 +247,7 @@ func (c *Cli) editTemplate(template string, tmpFilePrefix string, templateData m
return err
}
}
if fixed, err := yamlFixup(edited); err != nil {
return err
} else {
@@ -251,7 +256,7 @@ 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{})
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)
@@ -264,7 +269,8 @@ func (c *Cli) editTemplate(template string, tmpFilePrefix string, templateData m
}
}
json, err := jsonEncode(edited); if err != nil {
json, err := jsonEncode(edited)
if err != nil {
return err
}
+91 -67
View File
@@ -1,23 +1,23 @@
package cli
import (
"net/http"
"fmt"
"code.google.com/p/gopass"
"bytes"
"strings"
"code.google.com/p/gopass"
"fmt"
"net/http"
"os"
"strings"
// "github.com/kr/pretty"
)
func (c *Cli) CmdLogin() (error) {
func (c *Cli) CmdLogin() error {
uri := fmt.Sprintf("%s/rest/auth/1/session", c.endpoint)
for ; true ; {
for true {
req, _ := http.NewRequest("GET", uri, nil)
user, _ := c.opts["user"]
prompt := fmt.Sprintf("Enter Password for %s: ", user)
passwd, _ := gopass.GetPass(prompt);
passwd, _ := gopass.GetPass(prompt)
req.SetBasicAuth(user, passwd)
if resp, err := c.makeRequest(req); err != nil {
@@ -32,7 +32,7 @@ func (c *Cli) CmdLogin() (error) {
}
log.Error("Authentication Failead: Unknown")
return fmt.Errorf("Authentication Failead")
}
if resp.StatusCode != 200 {
log.Warning("Login failed")
@@ -47,14 +47,14 @@ func (c *Cli) CmdLogin() (error) {
func (c *Cli) CmdFields() error {
log.Debug("fields called")
uri := fmt.Sprintf("%s/rest/api/2/field", c.endpoint)
data, err := responseToJson(c.get(uri)); if err != nil {
data, err := responseToJson(c.get(uri))
if err != nil {
return err
}
return runTemplate(c.getTemplate(".jira.d/templates/fields", default_fields_template), data, nil)
}
func (c *Cli) CmdList() error {
log.Debug("list called")
@@ -70,7 +70,7 @@ func (c *Cli) CmdList() error {
} else {
qbuff.WriteString(fmt.Sprintf(" AND project = '%s'", project))
}
if component, ok := c.opts["component"]; ok {
qbuff.WriteString(fmt.Sprintf(" AND component = '%s'", component))
}
@@ -86,25 +86,28 @@ func (c *Cli) CmdList() error {
}
json, err := jsonEncode(map[string]string{
"jql": query,
"startAt": "0",
"jql": query,
"startAt": "0",
"maxResults": "500",
}); if err != nil {
})
if err != nil {
return err
}
uri := fmt.Sprintf("%s/rest/api/2/search", c.endpoint)
data, err := responseToJson(c.post(uri, json)); if err != nil {
data, err := responseToJson(c.post(uri, json))
if err != nil {
return err
}
return runTemplate(c.getTemplate(".jira.d/templates/list", default_list_template), data, nil)
}
func (c *Cli) CmdView(issue string) error {
log.Debug("view called")
uri := fmt.Sprintf("%s/rest/api/2/issue/%s", c.endpoint, issue)
data, err := responseToJson(c.get(uri)); if err != nil {
data, err := responseToJson(c.get(uri))
if err != nil {
return err
}
@@ -115,7 +118,8 @@ func (c *Cli) CmdEdit(issue string) error {
log.Debug("edit called")
uri := fmt.Sprintf("%s/rest/api/2/issue/%s/editmeta", c.endpoint, issue)
editmeta, err := responseToJson(c.get(uri)); if err != nil {
editmeta, err := responseToJson(c.get(uri))
if err != nil {
return err
}
@@ -129,21 +133,22 @@ func (c *Cli) CmdEdit(issue string) error {
issueData["meta"] = editmeta.(map[string]interface{})
issueData["overrides"] = c.opts
return c.editTemplate(
c.getTemplate(".jira.d/templates/edit", default_edit_template),
fmt.Sprintf("%s-edit-", issue),
issueData,
func(json string) error {
resp, err := c.put(uri, json); if err != nil {
resp, err := c.put(uri, json)
if err != nil {
return err
}
if resp.StatusCode == 204 {
fmt.Printf("OK %s %s/browse/%s\n", issueData["key"], c.endpoint, issueData["key"])
return nil
} else {
logBuffer := bytes.NewBuffer(make([]byte,0))
logBuffer := bytes.NewBuffer(make([]byte, 0))
resp.Write(logBuffer)
err := fmt.Errorf("Unexpected Response From PUT")
log.Error("%s:\n%s", err, logBuffer)
@@ -156,27 +161,30 @@ func (c *Cli) CmdEdit(issue string) error {
func (c *Cli) CmdEditMeta(issue string) error {
log.Debug("editMeta called")
uri := fmt.Sprintf("%s/rest/api/2/issue/%s/editmeta", c.endpoint, issue)
data, err := responseToJson(c.get(uri)); if err != nil {
data, err := responseToJson(c.get(uri))
if err != nil {
return err
}
return runTemplate(c.getTemplate(".jira.d/templates/editmeta", default_fields_template), data, nil)
}
func (c *Cli) CmdTransitionMeta(issue string) error {
log.Debug("tranisionMeta called")
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 {
data, err := responseToJson(c.get(uri))
if err != nil {
return err
}
return runTemplate(c.getTemplate(".jira.d/templates/transmeta", default_fields_template), data, nil)
}
func (c *Cli) CmdIssueTypes(project string) error {
log.Debug("issueTypes called")
uri := fmt.Sprintf("%s/rest/api/2/issue/createmeta?projectKeys=%s", c.endpoint, project)
data, err := responseToJson(c.get(uri)); if err != nil {
data, err := responseToJson(c.get(uri))
if err != nil {
return err
}
@@ -186,7 +194,8 @@ func (c *Cli) CmdIssueTypes(project string) error {
func (c *Cli) CmdCreateMeta(project string, issuetype string) error {
log.Debug("createMeta called")
uri := fmt.Sprintf("%s/rest/api/2/issue/createmeta?projectKeys=%s&issuetypeNames=%s&expand=projects.issuetypes.fields", c.endpoint, project, issuetype)
data, err := responseToJson(c.get(uri)); if err != nil {
data, err := responseToJson(c.get(uri))
if err != nil {
return err
}
@@ -202,7 +211,8 @@ func (c *Cli) CmdCreateMeta(project string, issuetype string) error {
func (c *Cli) CmdTransitions(issue string) error {
log.Debug("Transitions called")
uri := fmt.Sprintf("%s/rest/api/2/issue/%s/transitions", c.endpoint, issue)
data, err := responseToJson(c.get(uri)); if err != nil {
data, err := responseToJson(c.get(uri))
if err != nil {
return err
}
return runTemplate(c.getTemplate(".jira.d/templates/transitions", default_transitions_template), data, nil)
@@ -212,7 +222,8 @@ func (c *Cli) CmdCreate(project string, issuetype string) error {
log.Debug("create called")
uri := fmt.Sprintf("%s/rest/api/2/issue/createmeta?projectKeys=%s&issuetypeNames=%s&expand=projects.issuetypes.fields", c.endpoint, project, issuetype)
data, err := responseToJson(c.get(uri)); if err != nil {
data, err := responseToJson(c.get(uri))
if err != nil {
return err
}
@@ -224,7 +235,7 @@ func (c *Cli) CmdCreate(project string, issuetype string) error {
issueData["meta"] = val.([]interface{})[0]
}
}
sanitizedType := strings.ToLower(strings.Replace(issuetype, " ", "", -1))
return c.editTemplate(
c.getTemplate(fmt.Sprintf(".jira.d/templates/create-%s", sanitizedType), default_create_template),
@@ -233,10 +244,11 @@ func (c *Cli) CmdCreate(project string, issuetype string) error {
func(json string) error {
log.Debug("JSON: %s", json)
uri := fmt.Sprintf("%s/rest/api/2/issue", c.endpoint)
resp, err := c.post(uri, json); if err != 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"}
if json, err := responseToJson(resp, nil); err != nil {
@@ -247,7 +259,7 @@ func (c *Cli) CmdCreate(project string, issuetype string) error {
}
return nil
} else {
logBuffer := bytes.NewBuffer(make([]byte,0))
logBuffer := bytes.NewBuffer(make([]byte, 0))
resp.Write(logBuffer)
err := fmt.Errorf("Unexpected Response From POST")
log.Error("%s:\n%s", err, logBuffer)
@@ -261,7 +273,8 @@ func (c *Cli) CmdCreate(project string, issuetype string) error {
func (c *Cli) CmdIssueLinkTypes() error {
log.Debug("Transitions called")
uri := fmt.Sprintf("%s/rest/api/2/issueLinkType", c.endpoint)
data, err := responseToJson(c.get(uri)); if err != nil {
data, err := responseToJson(c.get(uri))
if err != nil {
return err
}
return runTemplate(c.getTemplate(".jira.d/templates/issuelinktypes", default_fields_template), data, nil)
@@ -272,7 +285,7 @@ func (c *Cli) CmdBlocks(blocker string, issue string) error {
json, err := jsonEncode(map[string]interface{}{
"type": map[string]string{
"name": "Depends", // TODO This is probably not constant across Jira installs
"name": "Depends", // TODO This is probably not constant across Jira installs
},
"inwardIssue": map[string]string{
"key": issue,
@@ -280,18 +293,20 @@ func (c *Cli) CmdBlocks(blocker string, issue string) error {
"outwardIssue": map[string]string{
"key": blocker,
},
}); if err != nil {
})
if err != nil {
return err
}
uri := fmt.Sprintf("%s/rest/api/2/issueLink", c.endpoint)
resp, err := c.post(uri, json); if err != nil {
resp, err := c.post(uri, json)
if err != nil {
return err
}
if resp.StatusCode == 201 {
fmt.Printf("OK %s %s/browse/%s\n", issue, c.endpoint, issue)
} else {
logBuffer := bytes.NewBuffer(make([]byte,0))
logBuffer := bytes.NewBuffer(make([]byte, 0))
resp.Write(logBuffer)
err := fmt.Errorf("Unexpected Response From POST")
log.Error("%s:\n%s", err, logBuffer)
@@ -305,7 +320,7 @@ func (c *Cli) CmdDups(duplicate string, issue string) error {
json, err := jsonEncode(map[string]interface{}{
"type": map[string]string{
"name": "Duplicate", // TODO This is probably not constant across Jira installs
"name": "Duplicate", // TODO This is probably not constant across Jira installs
},
"inwardIssue": map[string]string{
"key": duplicate,
@@ -313,18 +328,20 @@ func (c *Cli) CmdDups(duplicate string, issue string) error {
"outwardIssue": map[string]string{
"key": issue,
},
}); if err != nil {
})
if err != nil {
return err
}
uri := fmt.Sprintf("%s/rest/api/2/issueLink", c.endpoint)
resp, err := c.post(uri, json); if err != nil {
resp, err := c.post(uri, json)
if err != nil {
return err
}
if resp.StatusCode == 201 {
fmt.Printf("OK %s %s/browse/%s\n", issue, c.endpoint, issue)
} else {
logBuffer := bytes.NewBuffer(make([]byte,0))
logBuffer := bytes.NewBuffer(make([]byte, 0))
resp.Write(logBuffer)
err := fmt.Errorf("Unexpected Response From POST")
log.Error("%s:\n%s", err, logBuffer)
@@ -333,22 +350,23 @@ func (c *Cli) CmdDups(duplicate string, issue string) error {
return nil
}
func (c *Cli) CmdWatch(issue string, watcher string) error {
log.Debug("watch called")
json, err := jsonEncode(watcher); if err != nil {
json, err := jsonEncode(watcher)
if err != nil {
return err
}
uri := fmt.Sprintf("%s/rest/api/2/issue/%s/watchers", c.endpoint, issue)
resp, err := c.post(uri, json); if err != nil {
resp, err := c.post(uri, json)
if err != nil {
return err
}
if resp.StatusCode == 204 {
fmt.Printf("OK %s %s/browse/%s\n", issue, c.endpoint, issue)
} else {
logBuffer := bytes.NewBuffer(make([]byte,0))
logBuffer := bytes.NewBuffer(make([]byte, 0))
resp.Write(logBuffer)
err := fmt.Errorf("Unexpected Response From POST")
log.Error("%s:\n%s", err, logBuffer)
@@ -360,16 +378,17 @@ 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)
data, err := responseToJson(c.get(uri)); if err != nil {
data, err := responseToJson(c.get(uri))
if err != nil {
return err
}
transitions := data.(map[string]interface{})["transitions"].([]interface{})
var transId string
var transId string
found := make([]string, 0, len(transitions))
for _, transition := range transitions {
name := transition.(map[string]interface{})["name"].(string)
id := transition.(map[string]interface{})["id"].(string)
id := transition.(map[string]interface{})["id"].(string)
found = append(found, name)
if strings.Contains(strings.ToLower(name), trans) {
transId = id
@@ -380,7 +399,6 @@ func (c *Cli) CmdTransition(issue string, trans string) error {
log.Error("%s", err)
return err
}
payload := map[string]interface{}{
"transition": map[string]interface{}{
@@ -400,18 +418,20 @@ func (c *Cli) CmdTransition(issue string, trans string) error {
}
}
json, err := jsonEncode(payload); if err != nil {
json, err := jsonEncode(payload)
if 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 {
resp, err := c.post(uri, json)
if err != nil {
return err
}
if resp.StatusCode == 204 {
fmt.Printf("OK %s %s/browse/%s\n", issue, c.endpoint, issue)
} else {
logBuffer := bytes.NewBuffer(make([]byte,0))
logBuffer := bytes.NewBuffer(make([]byte, 0))
resp.Write(logBuffer)
err := fmt.Errorf("Unexpected Response From POST")
log.Error("%s:\n%s", err, logBuffer)
@@ -422,19 +442,20 @@ func (c *Cli) CmdTransition(issue string, trans string) error {
func (c *Cli) CmdComment(issue string) error {
log.Debug("comment called")
handlePost := func(json string) error {
log.Debug("JSON: %s", json)
uri := fmt.Sprintf("%s/rest/api/2/issue/%s/comment", c.endpoint, issue)
resp, err := c.post(uri, json); if err != nil {
resp, err := c.post(uri, json)
if err != nil {
return err
}
if resp.StatusCode == 201 {
fmt.Printf("OK %s %s/browse/%s\n", issue, c.endpoint, issue)
return nil
} else {
logBuffer := bytes.NewBuffer(make([]byte,0))
logBuffer := bytes.NewBuffer(make([]byte, 0))
resp.Write(logBuffer)
err := fmt.Errorf("Unexpected Response From POST")
log.Error("%s:\n%s", err, logBuffer)
@@ -445,7 +466,8 @@ func (c *Cli) CmdComment(issue string) error {
if comment, ok := c.opts["comment"]; ok {
json, err := jsonEncode(map[string]interface{}{
"body": comment,
}); if err != nil {
})
if err != nil {
return err
}
return handlePost(json)
@@ -462,21 +484,23 @@ func (c *Cli) CmdComment(issue string) error {
func (c *Cli) CmdAssign(issue string, user string) error {
log.Debug("assign called")
json, err := jsonEncode(map[string]interface{}{
"name": user,
}); if err != nil {
})
if err != nil {
return err
}
uri := fmt.Sprintf("%s/rest/api/2/issue/%s/assignee", c.endpoint, issue)
resp, err := c.put(uri, json); if err != nil {
resp, err := c.put(uri, json)
if err != nil {
return err
}
if resp.StatusCode == 204 {
fmt.Printf("OK %s %s/browse/%s\n", issue, c.endpoint, issue)
} else {
logBuffer := bytes.NewBuffer(make([]byte,0))
logBuffer := bytes.NewBuffer(make([]byte, 0))
resp.Write(logBuffer)
err := fmt.Errorf("Unexpected Response From PUT")
log.Error("%s:\n%s", err, logBuffer)
@@ -508,7 +532,7 @@ func (c *Cli) CmdExportTemplates() error {
log.Warning("Skipping %s, already exists", templateFile)
continue
}
if fh, err := os.OpenFile(templateFile, os.O_WRONLY | os.O_CREATE, 0644); err != nil {
if fh, err := os.OpenFile(templateFile, os.O_WRONLY|os.O_CREATE, 0644); err != nil {
log.Error("Failed to open %s for writing: %s", templateFile, err)
return err
} else {
+7 -7
View File
@@ -1,14 +1,14 @@
package cli
var all_templates = map[string]string{
"fields": default_fields_template,
"list": default_list_template,
"view": default_view_template,
"edit": default_edit_template,
"fields": default_fields_template,
"list": default_list_template,
"view": default_view_template,
"edit": default_edit_template,
"transitions": default_transitions_template,
"issuetypes": default_issuetypes_template,
"create": default_create_template,
"comment": default_comment_template,
"issuetypes": default_issuetypes_template,
"create": default_create_template,
"comment": default_comment_template,
}
const default_fields_template = "{{ . | toJson}}\n"
+31 -27
View File
@@ -1,28 +1,28 @@
package cli
import (
"os"
"fmt"
"errors"
"strings"
"net/http"
"encoding/json"
"io/ioutil"
"text/template"
"io"
"bufio"
"bytes"
"encoding/json"
"errors"
"fmt"
"github.com/mgutz/ansi"
"io"
"io/ioutil"
"net/http"
"os"
"strings"
"text/template"
)
func FindParentPaths(fileName string) []string {
cwd, _ := os.Getwd()
paths := make([]string,0)
paths := make([]string, 0)
// special case if homedir is not in current path then check there anyway
homedir := os.Getenv("HOME")
if ! strings.HasPrefix(cwd, homedir) {
if !strings.HasPrefix(cwd, homedir) {
file := fmt.Sprintf("%s/%s", homedir, fileName)
if _, err := os.Stat(file); err == nil {
paths = append(paths, file)
@@ -44,9 +44,9 @@ func FindParentPaths(fileName string) []string {
return paths
}
func FindClosestParentPath(fileName string) (string,error) {
func FindClosestParentPath(fileName string) (string, error) {
paths := FindParentPaths(fileName)
if len(paths) > 0 {
if len(paths) > 0 {
return paths[len(paths)-1], nil
}
return "", errors.New(fmt.Sprintf("%s not found in parent directory hierarchy", fileName))
@@ -78,15 +78,18 @@ func runTemplate(templateContent string, data interface{}, out io.Writer) error
},
"append": func(more string, content interface{}) (string, error) {
switch value := content.(type) {
case string: return string(append([]byte(content.(string)), []byte(more)...)), nil
case []byte: return string(append(content.([]byte), []byte(more)...)), nil
default: return "", errors.New(fmt.Sprintf("Unknown type: %s", value))
case string:
return string(append([]byte(content.(string)), []byte(more)...)), nil
case []byte:
return string(append(content.([]byte), []byte(more)...)), nil
default:
return "", errors.New(fmt.Sprintf("Unknown type: %s", value))
}
},
"indent": func(spaces int, content string) string {
indent := make([]byte, spaces + 1, spaces +1)
indent := make([]byte, spaces+1, spaces+1)
indent[0] = '\n'
for i := 1; i < spaces + 1; i += 1 {
for i := 1; i < spaces+1; i += 1 {
indent[i] = ' '
}
return strings.Replace(content, "\n", string(indent), -1)
@@ -131,8 +134,9 @@ func jsonDecode(io io.Reader) interface{} {
func jsonEncode(data interface{}) (string, error) {
buffer := bytes.NewBuffer(make([]byte, 0))
enc := json.NewEncoder(buffer)
err := enc.Encode(data); if err != nil {
err := enc.Encode(data)
if err != nil {
log.Error("Failed to encode data %s: %s", data, err)
return "", err
}
@@ -140,7 +144,7 @@ func jsonEncode(data interface{}) (string, error) {
}
func jsonWrite(file string, data interface{}) {
fh, err := os.OpenFile(file, os.O_WRONLY | os.O_CREATE | os.O_TRUNC, 0600)
fh, err := os.OpenFile(file, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
defer fh.Close()
if err != nil {
log.Error("Failed to open %s: %s", file, err)
@@ -157,20 +161,20 @@ func promptYN(prompt string, yes bool) bool {
} else {
prompt = fmt.Sprintf("%s [Y/n]: ", prompt)
}
fmt.Printf("%s", prompt)
text, _ := reader.ReadString('\n')
ans := strings.ToLower(strings.TrimRight(text, "\n"))
if ans == "" {
return yes
}
if( strings.HasPrefix(ans, "y") ) {
if strings.HasPrefix(ans, "y") {
return true
}
return false
}
func yamlFixup( data interface{} ) (interface{}, error) {
func yamlFixup(data interface{}) (interface{}, error) {
switch d := data.(type) {
case map[interface{}]interface{}:
// need to copy this map into a string map so json can encode it
@@ -190,7 +194,7 @@ func yamlFixup( data interface{} ) (interface{}, error) {
}
}
return copy, nil
case map[string]interface{}:
case map[string]interface{}:
for k, v := range d {
if fixed, err := yamlFixup(v); err != nil {
return nil, err
@@ -208,7 +212,7 @@ func yamlFixup( data interface{} ) (interface{}, error) {
}
}
return data, nil
default: return d, nil
default:
return d, nil
}
}
+15 -15
View File
@@ -1,14 +1,14 @@
package main
import (
"fmt"
"github.com/Netflix-Skunkworks/go-jira/jira/cli"
"github.com/docopt/docopt-go"
"github.com/op/go-logging"
"fmt"
"gopkg.in/yaml.v2"
"io/ioutil"
"os"
"strings"
"io/ioutil"
"gopkg.in/yaml.v2"
"github.com/Netflix-Skunkworks/go-jira/jira/cli"
)
var log = logging.MustGetLogger("jira")
@@ -66,15 +66,16 @@ Command Options:
-q --query=JQL Jira Query Language expression for the search
-w --watcher=USER Watcher to add to issue (default: %s)
`, user, fmt.Sprintf("%s/.jira.d/templates", home), user)
args, err := docopt.Parse(usage, nil, true, "0.0.1", false, false); if err != nil {
args, err := docopt.Parse(usage, nil, true, "0.0.1", false, false)
if err != nil {
log.Error("Failed to parse options: %s", err)
os.Exit(1)
}
logBackend := logging.NewLogBackend(os.Stderr, "", 0)
logging.SetBackend(
logging.NewBackendFormatter(
logBackend,
logBackend,
logging.MustStringFormatter(format),
),
)
@@ -82,7 +83,7 @@ Command Options:
if verbose, ok := args["--verbose"]; ok {
if verbose.(int) > 1 {
logging.SetLevel(logging.DEBUG, "")
} else if verbose.(int) > 0 {
} else if verbose.(int) > 0 {
logging.SetLevel(logging.INFO, "")
}
}
@@ -94,7 +95,7 @@ Command Options:
// strip the "--" off the command line options
// and populate the opts that we pass to the cli ctor
for key,val := range args {
for key, val := range args {
if val != nil && strings.HasPrefix(key, "--") {
opt := key[2:]
if opt == "override" {
@@ -117,7 +118,7 @@ Command Options:
}
}
}
// cant use proper [default:x] syntax in docopt
// because only want to default if the option is not
// already specified in some .jira.d/config.yml file
@@ -133,10 +134,10 @@ Command Options:
if _, ok := opts["directory"]; !ok {
opts["directory"] = fmt.Sprintf("%s/.jira.d/templates", home)
}
c := cli.New(opts)
log.Debug("opts: %s", opts);
log.Debug("opts: %s", opts)
validCommand := func(cmd string) bool {
if val, ok := args[cmd]; ok && val.(bool) {
@@ -231,7 +232,7 @@ Command Options:
err = c.CmdView(val.(string))
}
if err != nil {
if err != nil {
os.Exit(1)
}
os.Exit(0)
@@ -249,8 +250,7 @@ func loadConfigs(opts map[string]string) {
// prepend
paths = append([]string{"/etc/jira-cli.yml"}, paths...)
for _, file := range(paths) {
for _, file := range paths {
parseYaml(file, opts)
}
}