func main() { logBackend := logging.NewLogBackend(os.Stderr, "", 0) logging.SetBackend( logging.NewBackendFormatter( logBackend, logging.MustStringFormatter(format), ), ) logging.SetLevel(logging.NOTICE, "") user := os.Getenv("USER") home := os.Getenv("HOME") defaultQueryFields := "summary,created,priority,status,reporter,assignee" defaultSort := "priority asc, created" defaultMaxResults := 500 usage := func(ok bool) { printer := fmt.Printf if !ok { printer = func(format string, args ...interface{}) (int, error) { return fmt.Fprintf(os.Stderr, format, args...) } defer func() { os.Exit(1) }() } else { defer func() { os.Exit(0) }() } output := fmt.Sprintf(` Usage: jira (ls|list) <Query Options> jira view ISSUE jira edit [--noedit] <Edit Options> [ISSUE | <Query Options>] jira create [--noedit] [-p PROJECT] <Create Options> jira DUPLICATE dups ISSUE jira BLOCKER blocks ISSUE jira watch ISSUE [-w WATCHER] jira (trans|transition) TRANSITION ISSUE [--noedit] <Edit Options> jira ack ISSUE [--edit] <Edit Options> jira close ISSUE [--edit] <Edit Options> jira resolve ISSUE [--edit] <Edit Options> jira reopen ISSUE [--edit] <Edit Options> jira start ISSUE [--edit] <Edit Options> jira stop ISSUE [--edit] <Edit Options> jira comment ISSUE [--noedit] <Edit Options> jira take ISSUE jira (assign|give) ISSUE ASSIGNEE jira fields jira issuelinktypes jira transmeta ISSUE jira editmeta ISSUE jira issuetypes [-p PROJECT] jira createmeta [-p PROJECT] [-i ISSUETYPE] jira transitions ISSUE jira export-templates [-d DIR] [-t template] jira (b|browse) ISSUE jira login jira ISSUE General Options: -b --browse Open your browser to the Jira issue -e --endpoint=URI URI to use for jira -h --help Show this usage -t --template=FILE Template file to use for output/editing -u --user=USER Username to use for authenticaion (default: %s) -v --verbose Increase output logging --version Print version Query Options: -a --assignee=USER Username assigned the issue -c --component=COMPONENT Component to Search for -f --queryfields=FIELDS Fields that are used in "list" template: (default: %s) -i --issuetype=ISSUETYPE The Issue Type -l --limit=VAL Maximum number of results to return in query (default: %d) -p --project=PROJECT Project to Search for -q --query=JQL Jira Query Language expression for the search -r --reporter=USER Reporter to search for -s --sort=ORDER For list operations, sort issues (default: %s) -w --watcher=USER Watcher to add to issue (default: %s) or Watcher to search for Edit Options: -m --comment=COMMENT Comment message for transition -o --override=KEY=VAL Set custom key/value pairs Create Options: -i --issuetype=ISSUETYPE Jira Issue Type (default: Bug) -m --comment=COMMENT Comment message for transition -o --override=KEY=VAL Set custom key/value pairs Command Options: -d --directory=DIR Directory to export templates to (default: %s) `, user, defaultQueryFields, defaultMaxResults, defaultSort, user, fmt.Sprintf("%s/.jira.d/templates", home)) printer(output) } jiraCommands := map[string]string{ "list": "list", "ls": "list", "view": "view", "edit": "edit", "create": "create", "dups": "dups", "blocks": "blocks", "watch": "watch", "trans": "transition", "transition": "transition", "ack": "acknowledge", "acknowledge": "acknowledge", "close": "close", "resolve": "resolve", "reopen": "reopen", "start": "start", "stop": "stop", "comment": "comment", "take": "take", "assign": "assign", "give": "assign", "fields": "fields", "issuelinktypes": "issuelinktypes", "transmeta": "transmeta", "editmeta": "editmeta", "issuetypes": "issuetypes", "createmeta": "createmeta", "transitions": "transitions", "export-templates": "export-templates", "browse": "browse", "login": "******", } defaults := map[string]interface{}{ "user": user, "queryfields": defaultQueryFields, "directory": fmt.Sprintf("%s/.jira.d/templates", home), "sort": defaultSort, "max_results": defaultMaxResults, } opts := make(map[string]interface{}) overrides := make(map[string]string) setopt := func(name string, value interface{}) { opts[name] = value } op := optigo.NewDirectAssignParser(map[string]interface{}{ "h|help": usage, "version": func() { fmt.Println("version: 0.0.12") os.Exit(0) }, "v|verbose+": func() { logging.SetLevel(logging.GetLevel("")+1, "") }, "dryrun": setopt, "b|browse": setopt, "editor=s": setopt, "u|user=s": setopt, "endpoint=s": setopt, "t|template=s": setopt, "q|query=s": setopt, "p|project=s": setopt, "c|component=s": setopt, "a|assignee=s": setopt, "i|issuetype=s": setopt, "w|watcher=s": setopt, "r|reporter=s": setopt, "f|queryfields=s": setopt, "s|sort=s": setopt, "l|limit|max_results=i": setopt, "o|override=s%": &overrides, "noedit": setopt, "edit": setopt, "m|comment=s": setopt, "d|dir|directory=s": setopt, }) if err := op.ProcessAll(os.Args[1:]); err != nil { log.Error("%s", err) usage(false) } args := op.Args for k, v := range overrides { opts[k] = v } var command string if len(args) > 0 { if alias, ok := jiraCommands[args[0]]; ok { command = alias args = args[1:] } else if len(args) > 1 { // look at second arg for "dups" and "blocks" commands if alias, ok := jiraCommands[args[1]]; ok { command = alias args = append(args[:1], args[2:]...) } } } if command == "" { command = args[0] args = args[1:] } os.Setenv("JIRA_OPERATION", command) loadConfigs(opts) // check to see if it was set in the configs: if value, ok := opts["command"].(string); ok { command = value } else if _, ok := jiraCommands[command]; !ok || command == "" { args = append([]string{command}, args...) command = "view" } // apply defaults for k, v := range defaults { if _, ok := opts[k]; !ok { log.Debug("Setting %q to %#v from defaults", k, v) opts[k] = v } } log.Debug("opts: %v", opts) log.Debug("args: %v", args) if _, ok := opts["endpoint"]; !ok { log.Error("endpoint option required. Either use --endpoint or set a enpoint option in your ~/.jira.d/config.yml file") os.Exit(1) } c := cli.New(opts) log.Debug("opts: %s", opts) setEditing := func(dflt bool) { log.Debug("Default Editing: %t", dflt) if dflt { if val, ok := opts["noedit"].(bool); ok && val { log.Debug("Setting edit = false") opts["edit"] = false } else { log.Debug("Setting edit = true") opts["edit"] = true } } else { if _, ok := opts["edit"].(bool); !ok { log.Debug("Setting edit = %t", dflt) opts["edit"] = dflt } } } var err error switch command { case "login": err = c.CmdLogin() case "fields": err = c.CmdFields() case "list": err = c.CmdList() case "edit": setEditing(true) if len(args) > 0 { err = c.CmdEdit(args[0]) } else { var data interface{} if data, err = c.FindIssues(); err == nil { issues := data.(map[string]interface{})["issues"].([]interface{}) for _, issue := range issues { if err = c.CmdEdit(issue.(map[string]interface{})["key"].(string)); err != nil { switch err.(type) { case cli.NoChangesFound: log.Warning("No Changes found: %s", err) err = nil continue } break } } } } case "editmeta": err = c.CmdEditMeta(args[0]) case "transmeta": err = c.CmdTransitionMeta(args[0]) case "issuelinktypes": err = c.CmdIssueLinkTypes() case "issuetypes": err = c.CmdIssueTypes() case "createmeta": err = c.CmdCreateMeta() case "create": setEditing(true) err = c.CmdCreate() case "transitions": err = c.CmdTransitions(args[0]) case "blocks": err = c.CmdBlocks(args[0], args[1]) case "dups": if err = c.CmdDups(args[0], args[1]); err == nil { opts["resolution"] = "Duplicate" err = c.CmdTransition(args[0], "close") } case "watch": err = c.CmdWatch(args[0]) case "transition": setEditing(true) err = c.CmdTransition(args[0], args[1]) case "close": setEditing(false) err = c.CmdTransition(args[0], "close") case "acknowledge": setEditing(false) err = c.CmdTransition(args[0], "acknowledge") case "reopen": setEditing(false) err = c.CmdTransition(args[0], "reopen") case "resolve": setEditing(false) err = c.CmdTransition(args[0], "resolve") case "start": setEditing(false) err = c.CmdTransition(args[0], "start") case "stop": setEditing(false) err = c.CmdTransition(args[0], "stop") case "comment": setEditing(true) err = c.CmdComment(args[0]) case "take": err = c.CmdAssign(args[0], opts["user"].(string)) case "browse": opts["browse"] = true err = c.Browse(args[0]) case "export-tempaltes": err = c.CmdExportTemplates() case "assign": err = c.CmdAssign(args[0], args[1]) case "view": err = c.CmdView(args[0]) default: log.Error("Unknown command %s", command) os.Exit(1) } if err != nil { log.Error("%s", err) os.Exit(1) } os.Exit(0) }
func main() { user := os.Getenv("USER") home := os.Getenv("HOME") usage := fmt.Sprintf(` Usage: jira [-v ...] [-u USER] [-e URI] [-t FILE] (ls|list) ( [-q JQL] | [-p PROJECT] [-c COMPONENT] [-a ASSIGNEE] [-i ISSUETYPE] [-w WATCHER] [-r REPORTER]) [-f FIELDS] [-s ORDER] jira [-v ...] [-u USER] [-e URI] [-b] [-t FILE] view ISSUE jira [-v ...] [-u USER] [-e URI] [-b] [-t FILE] edit ISSUE [--noedit] [-m COMMENT] [-o KEY=VAL]... jira [-v ...] [-u USER] [-e URI] [-b] [-t FILE] create [--noedit] [-p PROJECT] [-i ISSUETYPE] [-o KEY=VAL]... jira [-v ...] [-u USER] [-e URI] [-b] DUPLICATE dups ISSUE jira [-v ...] [-u USER] [-e URI] [-b] BLOCKER blocks ISSUE jira [-v ...] [-u USER] [-e URI] [-b] watch ISSUE [-w WATCHER] jira [-v ...] [-u USER] [-e URI] [-b] [-t FILE] (trans|transition) TRANSITION ISSUE [-m COMMENT] [-o KEY=VAL] [--noedit] jira [-v ...] [-u USER] [-e URI] [-b] ack ISSUE [-m COMMENT] [-o KEY=VAL] [--edit] jira [-v ...] [-u USER] [-e URI] [-b] close ISSUE [-m COMMENT] [-o KEY=VAL] [--edit] jira [-v ...] [-u USER] [-e URI] [-b] resolve ISSUE [-m COMMENT] [-o KEY=VAL] [--edit] jira [-v ...] [-u USER] [-e URI] [-b] reopen ISSUE [-m COMMENT] [-o KEY=VAL] [--edit] jira [-v ...] [-u USER] [-e URI] [-b] start ISSUE [-m COMMENT] [-o KEY=VAL] [--edit] jira [-v ...] [-u USER] [-e URI] [-b] stop ISSUE [-m COMMENT] [-o KEY=VAL] [--edit] jira [-v ...] [-u USER] [-e URI] [-b] [-t FILE] comment ISSUE [-m COMMENT] jira [-v ...] [-u USER] [-e URI] [-b] take ISSUE jira [-v ...] [-u USER] [-e URI] [-b] (assign|give) ISSUE ASSIGNEE jira [-v ...] [-u USER] [-e URI] [-t FILE] fields jira [-v ...] [-u USER] [-e URI] [-t FILE] issuelinktypes jira [-v ...] [-u USER] [-e URI] [-b][-t FILE] transmeta ISSUE jira [-v ...] [-u USER] [-e URI] [-b] [-t FILE] editmeta 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] [-b] [-t FILE] transitions ISSUE jira [-v ...] export-templates [-d DIR] [-t template] jira [-v ...] [-u USER] [-e URI] (b|browse) ISSUE jira [-v ...] [-u USER] [-e URI] [-t FILE] login jira [-v ...] [-u USER] [-e URI] [-b] [-t FILE] ISSUE General Options: -e --endpoint=URI URI to use for jira -h --help Show this usage -t --template=FILE Template file to use for output/editing -u --user=USER Username to use for authenticaion (default: %s) -v --verbose Increase output logging --version Show this version Command Options: -a --assignee=USER Username assigned the issue -b --browse Open your browser to the Jira issue -c --component=COMPONENT Component to Search for -d --directory=DIR Directory to export templates to (default: %s) -f --queryfields=FIELDS Fields that are used in "list" template: (default: summary,created,priority,status,reporter,assignee) -i --issuetype=ISSUETYPE Jira Issue Type (default: Bug) -m --comment=COMMENT Comment message for transition -o --override=KEY:VAL Set custom key/value pairs -p --project=PROJECT Project to Search for -q --query=JQL Jira Query Language expression for the search -r --reporter=USER Reporter to search for -s --sort=ORDER For list operations, sort issues (default: priority asc, created) -w --watcher=USER Watcher to add to issue (default: %s) or Watcher to search for `, user, fmt.Sprintf("%s/.jira.d/templates", home), user) args, err := docopt.Parse(usage, nil, true, "0.0.6", 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, logging.MustStringFormatter(format), ), ) logging.SetLevel(logging.NOTICE, "") if verbose, ok := args["--verbose"]; ok { if verbose.(int) > 1 { logging.SetLevel(logging.DEBUG, "") } else if verbose.(int) > 0 { logging.SetLevel(logging.INFO, "") } } log.Info("Args: %v", args) populateEnv(args) 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:] if opt == "override" { for _, v := range val.([]string) { if strings.Contains(v, "=") { kv := strings.SplitN(v, "=", 2) opts[kv[0]] = kv[1] } else { log.Error("Malformed override, expected KEY=VALUE, got %s", v) os.Exit(1) } } } else { switch v := val.(type) { case string: opts[opt] = v case int: opts[opt] = fmt.Sprintf("%d", v) case bool: opts[opt] = fmt.Sprintf("%t", 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["user"]; !ok { opts["user"] = user } if _, ok := opts["queryfields"]; !ok { opts["queryfields"] = "summary,created,priority,status,reporter,assignee" } if _, ok := opts["directory"]; !ok { opts["directory"] = fmt.Sprintf("%s/.jira.d/templates", home) } if _, ok := opts["sort"]; !ok { opts["sort"] = "priority asc, created" } if _, ok := opts["endpoint"]; !ok { log.Error("endpoint option required. Either use --endpoint or set a enpoint option in your ~/.jira.d/config.yml file") os.Exit(1) } c := cli.New(opts) log.Debug("opts: %s", opts) validCommand := func(cmd string) bool { if val, ok := args[cmd]; ok && val.(bool) { return true } return false } validOpt := func(opt string, dflt interface{}) interface{} { if val, ok := opts[opt]; ok { return val } if dflt == nil { log.Error("Missing required option --%s or \"%s\" property override in the config file", opt, opt) os.Exit(1) } return dflt } setEditing := func(dflt bool) { if dflt { if val, ok := opts["noedit"]; ok && val == "true" { opts["edit"] = "false" } else { opts["edit"] = "true" } } else { if val, ok := opts["edit"]; ok && val != "true" { opts["edit"] = "false" } } } if validCommand("login") { err = c.CmdLogin() } else if validCommand("fields") { err = c.CmdFields() } else if validCommand("ls") || validCommand("list") { err = c.CmdList() } else if validCommand("edit") { setEditing(true) err = c.CmdEdit(args["ISSUE"].(string)) } else if validCommand("editmeta") { err = c.CmdEditMeta(args["ISSUE"].(string)) } else if validCommand("transmeta") { err = c.CmdTransitionMeta(args["ISSUE"].(string)) } else if validCommand("issuelinktypes") { err = c.CmdIssueLinkTypes() } else if validCommand("issuetypes") { err = c.CmdIssueTypes(validOpt("project", nil).(string)) } else if validCommand("createmeta") { err = c.CmdCreateMeta( validOpt("project", nil).(string), validOpt("issuetype", "Bug").(string), ) } else if validCommand("create") { setEditing(true) err = c.CmdCreate( validOpt("project", nil).(string), validOpt("issuetype", "Bug").(string), ) } else if validCommand("transitions") { err = c.CmdTransitions(args["ISSUE"].(string)) } else if validCommand("blocks") { err = c.CmdBlocks( args["BLOCKER"].(string), args["ISSUE"].(string), ) } else if validCommand("dups") { if err = c.CmdDups( args["DUPLICATE"].(string), args["ISSUE"].(string), ); err == nil { opts["resolution"] = "Duplicate" err = c.CmdTransition( args["DUPLICATE"].(string), "close", ) } } else if validCommand("watch") { err = c.CmdWatch( args["ISSUE"].(string), validOpt("watcher", user).(string), ) } else if validCommand("trans") || validCommand("transition") { setEditing(true) err = c.CmdTransition( args["ISSUE"].(string), args["TRANSITION"].(string), ) } else if validCommand("close") { setEditing(false) err = c.CmdTransition(args["ISSUE"].(string), "close") } else if validCommand("ack") { setEditing(false) err = c.CmdTransition(args["ISSUE"].(string), "acknowledge") } else if validCommand("reopen") { setEditing(false) err = c.CmdTransition(args["ISSUE"].(string), "reopen") } else if validCommand("resolve") { setEditing(false) err = c.CmdTransition(args["ISSUE"].(string), "resolve") } else if validCommand("start") { setEditing(false) err = c.CmdTransition(args["ISSUE"].(string), "start") } else if validCommand("stop") { setEditing(false) err = c.CmdTransition(args["ISSUE"].(string), "stop") } else if validCommand("comment") { setEditing(true) err = c.CmdComment(args["ISSUE"].(string)) } else if validCommand("take") { err = c.CmdAssign(args["ISSUE"].(string), user) } else if validCommand("browse") || validCommand("b") { opts["browse"] = "true" err = c.Browse(args["ISSUE"].(string)) } else if validCommand("export-templates") { err = c.CmdExportTemplates() } else if validCommand("assign") || validCommand("give") { err = c.CmdAssign( args["ISSUE"].(string), args["ASSIGNEE"].(string), ) } else if val, ok := args["ISSUE"]; ok { err = c.CmdView(val.(string)) } if err != nil { os.Exit(1) } os.Exit(0) }