Example #1
0
func Run() {

	var err error
	logging.SetLevel(logging.NOTICE, "")

	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-ui ls <Query Options> 
  jira-ui ISSUE
  jira-ui

General Options:
  -e --endpoint=URI   URI to use for jira
  -h --help           Show this usage
  -u --user=USER      Username to use for authenticaion
  -v --verbose        Increase output logging
  --skiplogin         Skip the login check. You must have a valid session token (eg via 'jira login')
  --version           Print version

Ticket View Options:
  -t --template=FILE  Template file to use for viewing tickets
  -m --max_wrap=VAL   Maximum word-wrap width when viewing ticket text (0 disables)

Query Options:
  -q --query=JQL            Jira Query Language expression for the search
  -f --queryfields=FIELDS   Fields that are used in "list" view

`)
		printer(output)
	}

	jiraCommands := map[string]string{
		"list":     "list",
		"ls":       "list",
		"password": "******",
		"passwd":   "password",
	}

	cliOpts = make(map[string]interface{})
	setopt := func(name string, value interface{}) {
		cliOpts[name] = value
	}

	op := optigo.NewDirectAssignParser(map[string]interface{}{
		"h|help": usage,
		"version": func() {
			fmt.Println(fmt.Sprintf("version: %s", VERSION))
			os.Exit(0)
		},
		"v|verbose+": func() {
			logging.SetLevel(logging.GetLevel("")+1, "")
		},
		"u|user=s":        setopt,
		"endpoint=s":      setopt,
		"q|query=s":       setopt,
		"f|queryfields=s": setopt,
		"t|template=s":    setopt,
		"m|max_wrap=i":    setopt,
		"skip_login":      setopt,
	})

	if err := op.ProcessAll(os.Args[1:]); err != nil {
		log.Error("%s", err)
		usage(false)
	}
	args := op.Args

	var command string
	if len(args) > 0 {
		if alias, ok := jiraCommands[args[0]]; ok {
			command = alias
			args = args[1:]
		} else {
			command = "view"
			args = args[0:]
		}
	} else {
		command = "toplevel"
	}

	requireArgs := func(count int) {
		if len(args) < count {
			log.Error("Not enough arguments. %d required, %d provided", count, len(args))
			usage(false)
		}
	}

	if val, ok := cliOpts["skip_login"]; !ok || !val.(bool) {
		err = ensureLoggedIntoJira()
		if err != nil {
			log.Error("Login failed. Aborting")
			os.Exit(2)
		}
	}

	err = ui.Init()
	if err != nil {
		panic(err)
	}
	defer ui.Close()

	registerKeyboardHandlers()

	ticketQueryPage = new(QueryPage)
	passwordInputBox = new(PasswordInputBox)
	helpPage = new(HelpPage)
	commandBar = new(CommandBar)

	switch command {
	case "list":
		ticketListPage = new(TicketListPage)
		if query := cliOpts["query"]; query == nil {
			log.Error("Must supply a --query option to %q", command)
			os.Exit(1)
		} else {
			ticketListPage.ActiveQuery.JQL = query.(string)
			ticketListPage.ActiveQuery.Name = "adhoc"
			currentPage = ticketListPage
		}
	case "view":
		requireArgs(1)
		p := new(TicketShowPage)
		p.TicketId = args[0]
		currentPage = p
	case "toplevel":
		currentPage = ticketQueryPage
	case "password":
		currentPage = passwordInputBox
	default:
		log.Error("Unknown command %s", command)
		os.Exit(1)
	}

	for exitNow != true {

		currentPage.Create()
		ui.Loop()

	}

}
Example #2
0
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)
}
Example #3
0
func main() {
	logBackend := logging.NewLogBackend(os.Stderr, "", 0)
	format := os.Getenv("JIRA_LOG_FORMAT")
	if format == "" {
		format = defaultFormat
	}
	logging.SetBackend(
		logging.NewBackendFormatter(
			logBackend,
			logging.MustStringFormatter(format),
		),
	)
	logging.SetLevel(logging.NOTICE, "")

	user := os.Getenv("USER")
	home := os.Getenv("HOME")
	defaultQueryFields := "summary,created,updated,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 worklog ISSUE
  jira addworklog ISSUE time comment
  jira edit [--noedit] <Edit Options> [ISSUE | <Query Options>]
  jira create [--noedit] [-p PROJECT] <Create Options>
  jira DUPLICATE dups ISSUE
  jira BLOCKER blocks ISSUE
  jira vote ISSUE [--down]
  jira watch ISSUE [-w WATCHER] [--remove]
  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 todo ISSUE [--edit] <Edit Options>
  jira done ISSUE [--edit] <Edit Options>
  jira prog|progress|in-progress [--edit] <Edit Options>
  jira comment ISSUE [--noedit] <Edit Options>
  jira (set,add,remove) labels ISSUE [LABEL] ...
  jira take ISSUE
  jira (assign|give) ISSUE ASSIGNEE
  jira fields
  jira issuelinktypes
  jira transmeta ISSUE
  jira editmeta ISSUE
  jira add component [-p PROJECT] NAME DESCRIPTION LEAD
  jira components [-p PROJECT]
  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 logout
  jira request [-M METHOD] URI [DATA]
  jira ISSUE

General Options:
  -b --browse         Open your browser to the Jira issue
  -e --endpoint=URI   URI to use for jira
  -k --insecure       disable TLS certificate verification
  -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",
		"todo":             "todo",
		"done":             "done",
		"prog":             "in-progress",
		"progress":         "in-progress",
		"in-progress":      "in-progress",
		"comment":          "comment",
		"label":            "labels",
		"labels":           "labels",
		"component":        "component",
		"components":       "components",
		"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":            "******",
		"logout":           "logout",
		"req":              "request",
		"request":          "request",
		"vote":             "vote",
		"worklog":          "worklog",
		"addworklog":       "addworklog",
	}

	defaults := map[string]interface{}{
		"user":        user,
		"queryfields": defaultQueryFields,
		"directory":   fmt.Sprintf("%s/.jira.d/templates", home),
		"sort":        defaultSort,
		"max_results": defaultMaxResults,
		"method":      "GET",
		"quiet":       false,
	}
	opts := make(map[string]interface{})

	setopt := func(name string, value interface{}) {
		opts[name] = value
	}

	op := optigo.NewDirectAssignParser(map[string]interface{}{
		"h|help": usage,
		"version": func() {
			fmt.Println(fmt.Sprintf("version: %s", jira.VERSION))
			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,
		"k|insecure":            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,
		"remove":                setopt,
		"r|reporter=s":          setopt,
		"f|queryfields=s":       setopt,
		"x|expand=s":            setopt,
		"s|sort=s":              setopt,
		"l|limit|max_results=i": setopt,
		"o|override=s%":         &opts,
		"noedit":                setopt,
		"edit":                  setopt,
		"m|comment=s":           setopt,
		"d|dir|directory=s":     setopt,
		"M|method=s":            setopt,
		"S|saveFile=s":          setopt,
		"Q|quiet":               setopt,
		"down":                  setopt,
	})

	if err := op.ProcessAll(os.Args[1:]); err != nil {
		log.Errorf("%s", err)
		usage(false)
	}
	args := op.Args

	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
			// also for 'set/add/remove' actions like 'labels'
			if alias, ok := jiraCommands[args[1]]; ok {
				command = alias
				args = append(args[:1], args[2:]...)
			}
		}
	}

	if command == "" && len(args) > 0 {
		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 == "" {
		if command != "" {
			args = append([]string{command}, args...)
		}
		command = "view"
	}

	// apply defaults
	for k, v := range defaults {
		if _, ok := opts[k]; !ok {
			log.Debugf("Setting %q to %#v from defaults", k, v)
			opts[k] = v
		}
	}

	log.Debugf("opts: %v", opts)
	log.Debugf("args: %v", args)

	if _, ok := opts["endpoint"]; !ok {
		log.Errorf("endpoint option required.  Either use --endpoint or set a endpoint option in your ~/.jira.d/config.yml file")
		os.Exit(1)
	}

	c := jira.New(opts)

	log.Debugf("opts: %s", opts)

	setEditing := func(dflt bool) {
		log.Debugf("Default Editing: %t", dflt)
		if dflt {
			if val, ok := opts["noedit"].(bool); ok && val {
				log.Debugf("Setting edit = false")
				opts["edit"] = false
			} else {
				log.Debugf("Setting edit = true")
				opts["edit"] = true
			}
		} else {
			if _, ok := opts["edit"].(bool); !ok {
				log.Debugf("Setting edit = %t", dflt)
				opts["edit"] = dflt
			}
		}
	}

	requireArgs := func(count int) {
		if len(args) < count {
			log.Errorf("Not enough arguments. %d required, %d provided", count, len(args))
			usage(false)
		}
	}

	var err error
	switch command {
	case "login":
		err = c.CmdLogin()
	case "logout":
		err = c.CmdLogout()
	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 jira.NoChangesFound:
							log.Warning("No Changes found: %s", err)
							err = nil
							continue
						}
						break
					}
				}
			}
		}
	case "editmeta":
		requireArgs(1)
		err = c.CmdEditMeta(args[0])
	case "transmeta":
		requireArgs(1)
		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":
		requireArgs(1)
		err = c.CmdTransitions(args[0])
	case "blocks":
		requireArgs(2)
		err = c.CmdBlocks(args[0], args[1])
	case "dups":
		requireArgs(2)
		if err = c.CmdDups(args[0], args[1]); err == nil {
			opts["resolution"] = "Duplicate"
			err = c.CmdTransition(args[0], "close")
		}
	case "watch":
		requireArgs(1)
		watcher := c.GetOptString("watcher", opts["user"].(string))
		remove := c.GetOptBool("remove", false)
		err = c.CmdWatch(args[0], watcher, remove)
	case "transition":
		requireArgs(2)
		setEditing(true)
		err = c.CmdTransition(args[1], args[0])
	case "close":
		requireArgs(1)
		setEditing(false)
		err = c.CmdTransition(args[0], "close")
	case "acknowledge":
		requireArgs(1)
		setEditing(false)
		err = c.CmdTransition(args[0], "acknowledge")
	case "reopen":
		requireArgs(1)
		setEditing(false)
		err = c.CmdTransition(args[0], "reopen")
	case "resolve":
		requireArgs(1)
		setEditing(false)
		err = c.CmdTransition(args[0], "resolve")
	case "start":
		requireArgs(1)
		setEditing(false)
		err = c.CmdTransition(args[0], "start")
	case "stop":
		requireArgs(1)
		setEditing(false)
		err = c.CmdTransition(args[0], "stop")
	case "todo":
		requireArgs(1)
		setEditing(false)
		err = c.CmdTransition(args[0], "To Do")
	case "done":
		requireArgs(1)
		setEditing(false)
		err = c.CmdTransition(args[0], "Done")
	case "in-progress":
		requireArgs(1)
		setEditing(false)
		err = c.CmdTransition(args[0], "In Progress")
	case "comment":
		requireArgs(1)
		setEditing(true)
		err = c.CmdComment(args[0])
	case "labels":
		requireArgs(2)
		action := args[0]
		issue := args[1]
		labels := args[2:]
		err = c.CmdLabels(action, issue, labels)
	case "component":
		requireArgs(2)
		action := args[0]
		project := opts["project"].(string)
		name := args[1]
		var lead string
		var description string
		if len(args) > 2 {
			description = args[2]
		}
		if len(args) > 3 {
			lead = args[2]
		}
		err = c.CmdComponent(action, project, name, description, lead)
	case "components":
		project := opts["project"].(string)
		err = c.CmdComponents(project)
	case "take":
		requireArgs(1)
		err = c.CmdAssign(args[0], opts["user"].(string))
	case "browse":
		requireArgs(1)
		opts["browse"] = true
		err = c.Browse(args[0])
	case "export-templates":
		err = c.CmdExportTemplates()
	case "assign":
		requireArgs(2)
		err = c.CmdAssign(args[0], args[1])
	case "view":
		requireArgs(1)
		err = c.CmdView(args[0])
	case "worklog":
		requireArgs(1)
		err = c.CmdWorklogs(args[0])
	case "addworklog":
		requireArgs(4)
		timeInSeconds, err := strconv.Atoi(args[1])
		if err == nil {
			err = c.CmdNewWorklog(args[0], timeInSeconds, args[2], args[3])
		}
	case "vote":
		requireArgs(1)
		if val, ok := opts["down"]; ok {
			err = c.CmdVote(args[0], !val.(bool))
		} else {
			err = c.CmdVote(args[0], true)
		}
	case "request":
		requireArgs(1)
		data := ""
		if len(args) > 1 {
			data = args[1]
		}
		err = c.CmdRequest(args[0], data)
	default:
		log.Errorf("Unknown command %s", command)
		os.Exit(1)
	}

	if err != nil {
		log.Errorf("%s", err)
		os.Exit(1)
	}
	os.Exit(0)
}