work in progress, minor refactor. Added commands:

* login
* editmeta ISSUE
* edit ISSUE
* issuetypes [-p PROJECT]
* createmeta [-p PROJECT] [-i ISSUETYPE]
* transitions ISSUE

make --template argumetn work
This commit is contained in:
Cory Bennett
2015-02-12 15:50:08 -08:00
parent df67697fb7
commit 081fda79f3
6 changed files with 511 additions and 116 deletions
+89 -28
View File
@@ -10,7 +10,7 @@ import (
"os"
"net/url"
"time"
"io"
"bytes"
"runtime"
)
@@ -87,38 +87,99 @@ func (c *Cli) loadCookies() []*http.Cookie {
return cookies
}
func (c *Cli) post(uri string, content io.Reader) *http.Response {
req, _ := http.NewRequest("POST", uri, content)
return c.makeRequest(req)
func (c *Cli) post(uri string, content string) (*http.Response, error) {
return c.makeRequestWithContent("POST", uri, content)
}
func (c *Cli) get(uri string) *http.Response {
func (c *Cli) put(uri string, content string) (*http.Response, error) {
return c.makeRequestWithContent("PUT", uri, content)
}
func (c *Cli) makeRequestWithContent(method string, uri string, content string) (*http.Response, error) {
buffer := bytes.NewBufferString(content)
req, _ := http.NewRequest(method, uri, buffer)
log.Info("%s %s", req.Method, req.URL.String())
if log.IsEnabledFor(logging.DEBUG) {
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
// need to be able to rewind the buffer offset, dont know how yet
req, _ = http.NewRequest(method, uri, bytes.NewBufferString(content))
}
if resp, err := c.makeRequest(req); err != nil {
return nil, err
} else {
if resp.StatusCode == 401 {
if err := c.CmdLogin(); err != nil {
return nil, err
}
req, _ = http.NewRequest(method, uri, bytes.NewBufferString(content))
return c.makeRequest(req)
}
return resp, err
}
}
func (c *Cli) get(uri string) (*http.Response, error) {
req, _ := http.NewRequest("GET", uri, nil)
return c.makeRequest(req)
log.Info("%s %s", req.Method, req.URL.String())
if log.IsEnabledFor(logging.DEBUG) {
logBuffer := bytes.NewBuffer(make([]byte,0))
req.Write(logBuffer)
log.Debug("%s", logBuffer)
}
if resp, err := c.makeRequest(req); err != nil {
return nil, err
} else {
if resp.StatusCode == 401 {
if err := c.CmdLogin(); err != nil {
return nil, err
}
return c.makeRequest(req)
}
return resp, err
}
}
func (c *Cli) makeRequest(req *http.Request) *http.Response {
func (c *Cli) makeRequest(req *http.Request) (resp *http.Response, err error) {
req.Header.Set("Content-Type", "application/json")
resp, err := c.ua.Do(req)
if err != nil {
fmt.Printf("Error: %s", err)
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 {
if resp.StatusCode < 200 || resp.StatusCode >= 300 && resp.StatusCode != 401 {
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())
}
}
return resp, nil
}
func (c *Cli) getTemplate(path string, dflt string) string {
if override, ok := c.opts["template"]; ok {
if _, err := os.Stat(override); err == nil {
return readFile(override)
} else {
if file, err := FindClosestParentPath(fmt.Sprintf(".jira.d/templates/%s", override)); err == nil {
return readFile(file)
}
}
}
if file, err := FindClosestParentPath(path); err != nil {
return dflt
} else {
return readFile(file)
}
if resp.StatusCode != 200 {
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())
}
return resp
}
+204 -53
View File
@@ -2,17 +2,19 @@ package cli
import (
"net/http"
"encoding/json"
"fmt"
"bytes"
"os"
"code.google.com/p/gopass"
"os"
"bytes"
"os/exec"
"io/ioutil"
"gopkg.in/yaml.v1"
// "github.com/kr/pretty"
)
func (c *Cli) CmdLogin() {
func (c *Cli) CmdLogin() (error) {
uri := fmt.Sprintf("%s/rest/auth/1/session", c.endpoint)
resp := c.get(uri)
for ; resp.StatusCode != 200 ; {
for ; true ; {
req, _ := http.NewRequest("GET", uri, nil)
user, _ := c.opts["user"]
@@ -20,75 +22,224 @@ func (c *Cli) CmdLogin() {
passwd, _ := gopass.GetPass(prompt);
req.SetBasicAuth(user, passwd)
resp = c.makeRequest(req)
if resp.StatusCode == 403 {
// probably got this, need to redirect the user to login manually
// X-Authentication-Denied-Reason: CAPTCHA_CHALLENGE; login-url=https://jira/login.jsp
if reason := resp.Header.Get("X-Authentication-Denied-Reason"); reason != "" {
log.Error("Authentication Failed: %s", reason)
os.Exit(1)
if resp, err := c.makeRequest(req); err != nil {
return err
} else {
if resp.StatusCode == 403 {
// probably got this, need to redirect the user to login manually
// X-Authentication-Denied-Reason: CAPTCHA_CHALLENGE; login-url=https://jira/login.jsp
if reason := resp.Header.Get("X-Authentication-Denied-Reason"); reason != "" {
log.Error("Authentication Failed: %s", reason)
return fmt.Errorf("Authenticaion Failed: %s", reason)
}
log.Error("Authentication Failead: Unknown")
return fmt.Errorf("Authentication Failead")
}
if resp.StatusCode != 200 {
log.Warning("Login failed")
continue
}
log.Error("Authentication Failead: Unknown")
os.Exit(1)
}
if resp.StatusCode != 200 {
log.Error("Login failed")
}
return nil
}
return nil
}
func (c *Cli) CmdFields() {
func (c *Cli) CmdFields() error {
log.Debug("fields called")
resp := c.get(fmt.Sprintf("%s/rest/api/2/field", c.endpoint))
data := jsonDecode(resp.Body)
if templateFile, err := FindClosestParentPath(".jira.d/templates/fields"); err != nil {
runTemplate(default_fields_template, data)
} else {
log.Debug("Using Template: %s", templateFile)
runTemplate(readFile(templateFile), data)
uri := fmt.Sprintf("%s/rest/api/2/field", c.endpoint)
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() {
func (c *Cli) CmdList() error {
log.Debug("list called")
if query, ok := c.opts["query"]; !ok {
log.Error("No query argument found, either use --query or set query attribute in .jira file")
os.Exit(1)
return fmt.Errorf("Missing query")
} else {
buffer := bytes.NewBuffer(make([]byte, 0, len(query)))
enc := json.NewEncoder(buffer)
enc.Encode(map[string]string{
json, err := jsonEncode(map[string]string{
"jql": query,
"startAt": "0",
"maxResults": "500",
})
}); if err != nil {
return err
}
resp := c.post(fmt.Sprintf("%s/rest/api/2/search", c.endpoint), buffer)
data := jsonDecode(resp.Body)
if templateFile, err := FindClosestParentPath(".jira.d/templates/list"); err != nil {
runTemplate(default_list_template, data)
} else {
log.Debug("Using Template: %s", templateFile)
runTemplate(readFile(templateFile), data)
uri := fmt.Sprintf("%s/rest/api/2/search", c.endpoint)
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 {
return err
}
return runTemplate(c.getTemplate(".jira.d/templates/view", default_view_template), data, nil)
}
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 {
return err
}
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
} else {
issueData = data.(map[string]interface{})
}
issueData["meta"] = editmeta.(map[string]interface{})["fields"]
tmpdir := fmt.Sprintf("%s/.jira.d/tmp", os.Getenv("HOME"))
fh, err := ioutil.TempFile(tmpdir, fmt.Sprintf("%s-edit-", issue)); 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(c.getTemplate(".jira.d/templates/edit", default_edit_template), issueData, 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 = "vim"
}
}
}
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
}
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) {
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) {
continue
}
return err
}
}
if fixed, err := yamlFixup(edited); err != nil {
return err
} else {
edited = fixed.(map[string]interface{})
}
mf := editmeta.(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
}
return err
}
}
}
json, err := jsonEncode(edited); if err != nil {
return err
}
resp, err := c.put(uri, json); if err != nil {
return err
}
func (c *Cli) CmdView(issue string) {
log.Debug("view called")
resp := c.get(fmt.Sprintf("%s/rest/api/2/issue/%s", c.endpoint, issue))
data := jsonDecode(resp.Body)
if templateFile, err := FindClosestParentPath(".jira.d/templates/view"); err != nil {
runTemplate(default_view_template, data)
} else {
log.Debug("Using Template: %s", templateFile)
runTemplate(readFile(templateFile), data)
if resp.StatusCode == 204 {
fmt.Printf("OK %s %s", issueData["key"], issueData["self"])
return nil
} else {
logBuffer := bytes.NewBuffer(make([]byte,0))
resp.Write(logBuffer)
err := fmt.Errorf("Unexpected Response From PUT")
log.Error("%s:\n%s", err, logBuffer)
return err
}
}
return nil
}
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 {
return err
}
return runTemplate(c.getTemplate(".jira.d/templates/editmeta", 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 {
return err
}
return runTemplate(c.getTemplate(".jira.d/templates/issuetypes", default_issuetypes_template), data, nil)
}
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 {
return err
}
return runTemplate(c.getTemplate(".jira.d/templates/createmeta", default_fields_template), data, nil)
}
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 {
return err
}
return runTemplate(c.getTemplate(".jira.d/templates/transitions", default_transitions_template), data, nil)
}
+31 -3
View File
@@ -5,6 +5,7 @@ const default_fields_template = "{{ . | toJson}}\n"
const default_list_template = "{{ range .issues }}{{ .key | append \":\" | printf \"%-12s\"}} {{ .fields.summary }}\n{{ end }}"
const default_view_template = `issue: {{ .key }}
status: {{ .fields.status.name }}
summary: {{ .fields.summary }}
project: {{ .fields.project.key }}
components: {{ range .fields.components }}{{ .name }} {{end}}
@@ -12,13 +13,40 @@ issuetype: {{ .fields.issuetype.name }}
assignee: {{ .fields.assignee.name }}
reporter: {{ .fields.reporter.name }}
watchers: {{ range .fields.customfield_10110 }}{{ .name }} {{end}}
blockers: {{ range .fields.issuelinks }}{{if .outwardIssue}}{{ .outwardIssue.key }}{{end}}{{end}}
depends: {{ range .fields.issuelinks }}{{if .inwardIssue}}{{ .inwardIssue.key }}{{end}}{{end}}
blockers: {{ range .fields.issuelinks }}{{if .outwardIssue}}{{ .outwardIssue.key }}[{{.outwardIssue.fields.status.name}}]{{end}}{{end}}
depends: {{ range .fields.issuelinks }}{{if .inwardIssue}}{{ .inwardIssue.key }}[{{.inwardIssue.fields.status.name}}]{{end}}{{end}}
priority: {{ .fields.priority.name }}
description: |
{{ .fields.description | indent 2 }}
comments:
{{ range .fields.comment.comments }} - | # {{.author.name}} at {{.created}}
{{ .body | indent 4}}{{end}}
{{ .body | indent 4}}
{{end}}
`
const default_edit_template = `update:
comment:
- add:
body: |
fields:
summary: {{ .fields.summary }}
components: # {{ range .meta.components.allowedValues }}{{.name}}, {{end}}{{ range .fields.components }}
- name: {{ .name }}{{end}}
assignee:
name: {{ .fields.assignee.name }}
reporter:
name: {{ .fields.reporter.name }}
# watchers
customfield_10110: {{ range .fields.customfield_10110 }}
- name: {{ .name }}{{end}}
priority: # {{ range .meta.priority.allowedValues }}{{.name}}, {{end}}
name: {{ .fields.priority.name }}
description: |
{{ .fields.description | indent 4 }}
`
const default_transitions_template = `{{ range .transitions }}{{color "+bh"}}{{.name | printf "%-13s" }}{{color "reset"}} -> {{.to.name}}
{{end}}`
const default_issuetypes_template = `{{ range .projects }}{{ range .issuetypes }}{{color "+bh"}}{{.name | append ":" | printf "%-13s" }}{{color "reset"}} {{.description}}
{{end}}{{end}}`
+100 -6
View File
@@ -5,11 +5,13 @@ import (
"fmt"
"errors"
"strings"
"net/http"
"encoding/json"
"io/ioutil"
"text/template"
"io"
"bufio"
"bytes"
"github.com/mgutz/ansi"
)
@@ -18,6 +20,15 @@ func FindParentPaths(fileName string) []string {
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) {
file := fmt.Sprintf("%s/%s", homedir, fileName)
if _, err := os.Stat(file); err == nil {
paths = append(paths, file)
}
}
var dir string
for _, part := range strings.Split(cwd, string(os.PathSeparator)) {
if dir == "/" {
@@ -51,7 +62,12 @@ func readFile(file string) string {
return string(bytes)
}
func runTemplate(text string, data interface{}) {
func runTemplate(templateContent string, data interface{}, out io.Writer) error {
if out == nil {
out = os.Stdout
}
funcs := map[string]interface{}{
"toJson": func(content interface{}) (string, error) {
if bytes, err := json.MarshalIndent(content, "", " "); err != nil {
@@ -79,15 +95,24 @@ func runTemplate(text string, data interface{}) {
return ansi.ColorCode(color)
},
}
if tmpl, err := template.New("template").Funcs(funcs).Parse(text); err != nil {
if tmpl, err := template.New("template").Funcs(funcs).Parse(templateContent); err != nil {
log.Error("Failed to parse template: %s", err)
os.Exit(1)
return err
} else {
if err := tmpl.Execute(os.Stdout, data); err != nil {
if err := tmpl.Execute(out, data); err != nil {
log.Error("Failed to execute template: %s", err)
os.Exit(1)
return err
}
}
return nil
}
func responseToJson(resp *http.Response, err error) (interface{}, error) {
if err != nil {
return nil, err
} else {
return jsonDecode(resp.Body), nil
}
}
func jsonDecode(io io.Reader) interface{} {
@@ -100,6 +125,17 @@ func jsonDecode(io io.Reader) interface{} {
return data
}
func jsonEncode(data interface{}) (string, error) {
buffer := bytes.NewBuffer(make([]byte, 0))
enc := json.NewEncoder(buffer)
err := enc.Encode(data); if err != nil {
log.Error("Failed to encode data %s: %s", data, err)
return "", err
}
return buffer.String(), nil
}
func jsonWrite(file string, data interface{}) {
fh, err := os.OpenFile(file, os.O_WRONLY | os.O_CREATE | os.O_TRUNC, 0600)
defer fh.Close()
@@ -111,3 +147,61 @@ func jsonWrite(file string, data interface{}) {
enc.Encode(data)
}
func promptYN(prompt string, yes bool) bool {
reader := bufio.NewReader(os.Stdin)
if !yes {
prompt = fmt.Sprintf("%s [y/N]: ", prompt)
} else {
prompt = fmt.Sprintf("%s [Y/n]: ", prompt)
}
fmt.Printf("%s", prompt)
text, _ := reader.ReadString('\n')
ans := strings.ToLower(text)
if( strings.HasPrefix(ans, "y") ) {
return true
}
return false
}
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
copy := make(map[string]interface{})
for key, val := range d {
switch k := key.(type) {
case string:
if fixed, err := yamlFixup(val); err != nil {
return nil, err
} else {
copy[k] = fixed
}
default:
err := fmt.Errorf("YAML: key %s is type '%T', require 'string'", key, k)
log.Error("%s", err)
return nil, err
}
}
return copy, nil
case map[string]interface{}:
for k, v := range d {
if fixed, err := yamlFixup(v); err != nil {
return nil, err
} else {
d[k] = fixed
}
}
return d, nil
case []interface{}:
for i, val := range d {
if fixed, err := yamlFixup(val); err != nil {
return nil, err
} else {
d[i] = fixed
}
}
return data, nil
default: return d, nil
}
}
+87 -26
View File
@@ -14,32 +14,29 @@ import (
var log = logging.MustGetLogger("jira")
var format = "%{color}%{time:2006-01-02T15:04:05.000Z07:00} %{level:-5s} [%{shortfile}]%{color:reset} %{message}"
func parseYaml(file string, opts map[string]string) {
if fh, err := ioutil.ReadFile(file); err == nil {
log.Debug("Found Config file: %s", file)
yaml.Unmarshal(fh, &opts)
}
}
func loadConfigs(opts map[string]string) {
paths := cli.FindParentPaths(".jira.d/config.yml")
// prepend
paths = append([]string{"/etc/jira-cli.yml"}, paths...)
for _, file := range(paths) {
parseYaml(file, opts)
}
}
func main() {
user := os.Getenv("USER")
usage := fmt.Sprintf(`
Usage:
jira [-v ...] [-u USER] [-e URI] [-t FILE] fields
jira [-v ...] [-u USER] [-e URI] [-t FILE] ls [--query=JQL]
jira [-v ...] [-u USER] [-e URI] [-t FILE] login
jira [-v ...] [-u USER] [-e URI] [-t FILE] ls [-q JQL]
jira [-v ...] [-u USER] [-e URI] [-t FILE] view ISSUE
jira [-v ...] [-u USER] [-e URI] [-t FILE] ISSUE
jira [-v ...] [-u USER] [-e URI] [-t FILE] editmeta ISSUE
jira [-v ...] [-u USER] [-e URI] [-t FILE] edit ISSUE
jira [-v ...] [-u USER] [-e URI] [-t FILE] issuetypes [-p PROJECT]
jira [-v ...] [-u USER] [-e URI] [-t FILE] createmeta [-p PROJECT] [-i ISSUETYPE]
jira [-v ...] [-u USER] [-e URI] [-t FILE] transitions ISSUE
jira TODO [-v ...] [-u USER] [-e URI] [-t FILE] create [-p PROJECT] [-i ISSUETYPE]
jira TODO [-v ...] [-u USER] [-e URI] DUPLICATE dups ISSUE
jira TODO [-v ...] [-u USER] [-e URI] BLOCKER blocks ISSUE
jira TODO [-v ...] [-u USER] [-e URI] close ISSUE [-m COMMENT]
jira TODO [-v ...] [-u USER] [-e URI] resolve ISSUE [-m COMMENT]
jira TODO [-v ...] [-u USER] [-e URI] comment ISSUE [-m COMMENT]
jira TODO [-v ...] [-u USER] [-e URI] take ISSUE
jira TODO [-v ...] [-u USER] [-e URI] assign ISSUE ASSIGNEE
General Options:
-h --help Show this usage
@@ -49,8 +46,12 @@ General Options:
-e --endpoint=URI URI to use for jira (default: https://jira)
-t --template=FILE Template file to use for output
List options:
List Options:
-q --query=JQL Jira Query Language expression for the search
Create Options:
-p --project=PROJECT Jira Project Name
-i --issuetype=ISSUETYPE Jira Issue Type (default: Bug)
`, user)
args, _ := docopt.Parse(usage, nil, true, "0.0.1", false, false)
@@ -76,36 +77,96 @@ List options:
opts := make(map[string]string)
loadConfigs(opts)
// strip the "--" off the command line options
// and populate the opts that we pass to the cli ctor
for key,val := range args {
if val != nil && strings.HasPrefix(key, "--") {
opt := key[2:]
switch v := val.(type) {
// only deal with string opts, ignore
// other types, like int (for now) since
// they are only used for --verbose
case string:
opts[opt] = v
}
}
}
// 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
if _, ok := opts["endpoint"]; !ok {
opts["endpoint"] = "https://jira"
}
if _, ok := opts["user"]; !ok {
opts["user"] = user
}
if _, ok := opts["issuetype"]; !ok {
opts["issuetype"] = "Bug"
}
c := cli.New(opts)
log.Debug("opts: %s", opts);
c.CmdLogin()
if val, ok := args["fields"]; ok && val.(bool) {
c.CmdFields()
var err error
if val, ok := args["login"]; ok && val.(bool) {
err = c.CmdLogin()
} else if val, ok := args["fields"]; ok && val.(bool) {
err = c.CmdFields()
} else if val, ok := args["ls"]; ok && val.(bool) {
c.CmdList()
err = c.CmdList()
} else if val, ok := args["edit"]; ok && val.(bool) {
issue, _ := args["ISSUE"]
err = c.CmdEdit(issue.(string))
} else if val, ok := args["editmeta"]; ok && val.(bool) {
issue, _ := args["ISSUE"]
err = c.CmdEditMeta(issue.(string))
} else if val, ok := args["issuetypes"]; ok && val.(bool) {
var project interface{}
if project, ok = opts["project"]; !ok {
log.Error("missing PROJECT argument or \"project\" property in the config file")
os.Exit(1)
}
err = c.CmdIssueTypes(project.(string))
} else if val, ok := args["createmeta"]; ok && val.(bool) {
var project interface{}
if project, ok = opts["project"]; !ok {
log.Error("missing PROJECT argument or \"project\" property in the config file")
os.Exit(1)
}
var issuetype interface{}
if issuetype, ok = opts["issuetype"]; !ok {
issuetype = "Bug"
}
err = c.CmdCreateMeta(project.(string), issuetype.(string))
} else if val, ok := args["transitions"]; ok && val.(bool) {
issue, _ := args["ISSUE"]
err = c.CmdTransitions(issue.(string))
} else if val, ok := args["ISSUE"]; ok {
c.CmdView(val.(string))
err = c.CmdView(val.(string))
}
if err != nil {
os.Exit(1)
}
os.Exit(0)
}
func parseYaml(file string, opts map[string]string) {
if fh, err := ioutil.ReadFile(file); err == nil {
log.Debug("Found Config file: %s", file)
yaml.Unmarshal(fh, &opts)
}
}
func loadConfigs(opts map[string]string) {
paths := cli.FindParentPaths(".jira.d/config.yml")
// prepend
paths = append([]string{"/etc/jira-cli.yml"}, paths...)
for _, file := range(paths) {
parseYaml(file, opts)
}
}
View File