mirror of
https://github.com/Threnklyn/jira.git
synced 2026-05-19 20:53:27 +02:00
Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| df84d47552 | |||
| fa4ce5647d | |||
| 713d300a57 | |||
| c8ae7fc685 | |||
| 80322b648e | |||
| f2076a0977 | |||
| 886adb5db2 | |||
| 3bdbdbdaff | |||
| 544b923fab | |||
| 13a69e6f44 | |||
| fae9f94817 | |||
| 7d90672736 | |||
| 20faa959aa | |||
| aaff47d606 | |||
| 7bfb2946d4 | |||
| 9884281079 | |||
| 03fce96eb5 | |||
| 8f9e6f7d85 | |||
| 65c0240d34 | |||
| ccec749a0b | |||
| 6a9901f171 |
@@ -1,5 +1,27 @@
|
||||
# Changelog
|
||||
|
||||
## 0.0.15 - 2015-11-23
|
||||
|
||||
* [[#17](https://github.com/Netflix-Skunkworks/go-jira/issues/17)] print usage on missing arguments [Cory Bennett] [[fd2a2fe](https://github.com/Netflix-Skunkworks/go-jira/commit/fd2a2fe)]
|
||||
|
||||
## 0.0.14 - 2015-11-17
|
||||
|
||||
* s/enpoint/endpoint/g [Oliver Schrenk] [[c5d251d](https://github.com/Netflix-Skunkworks/go-jira/commit/c5d251d)]
|
||||
* Implement dateFormat template command [Mike Pountney] [[68d3bae](https://github.com/Netflix-Skunkworks/go-jira/commit/68d3bae)]
|
||||
* Add 'updated' field to default queryfields. [Mike Pountney] [[91e2475](https://github.com/Netflix-Skunkworks/go-jira/commit/91e2475)]
|
||||
* Fix export-templates option (typo) [Mike Pountney] [[4d7fdb8](https://github.com/Netflix-Skunkworks/go-jira/commit/4d7fdb8)]
|
||||
* when yaml element resolves to "\n" strip it out so we dont post it to jira [Cory Bennett] [[47ced2f](https://github.com/Netflix-Skunkworks/go-jira/commit/47ced2f)]
|
||||
* print PUT/POST data when using --dryrun to help debug [Cory Bennett] [[618f245](https://github.com/Netflix-Skunkworks/go-jira/commit/618f245)]
|
||||
|
||||
## 0.0.13 - 2015-09-19
|
||||
|
||||
* replace dead/deprecated code.google.com/p/gopass with golang.org/x/crypto/ssh/terminal for reading password from stdin [Cory Bennett] [[909eb06](https://github.com/Netflix-Skunkworks/go-jira/commit/909eb06)]
|
||||
|
||||
## 0.0.12 - 2015-09-18
|
||||
|
||||
* fix exception from "jira create" [Cory Bennett] [[9348a4b](https://github.com/Netflix-Skunkworks/go-jira/commit/9348a4b)]
|
||||
* add some debug messages to help diagnose login failures [Cory Bennett] [[1c08a7d](https://github.com/Netflix-Skunkworks/go-jira/commit/1c08a7d)]
|
||||
|
||||
## 0.0.11 - 2015-09-16
|
||||
|
||||
* add --version [Cory Bennett] [[8385ee2](https://github.com/Netflix-Skunkworks/go-jira/commit/8385ee2)]
|
||||
|
||||
@@ -15,27 +15,33 @@ PLATFORMS= \
|
||||
|
||||
DIST=$(shell pwd)/dist
|
||||
export GOPATH=$(shell pwd)
|
||||
GOBIN ?= $(shell pwd)/bin
|
||||
NAME=jira
|
||||
|
||||
build:
|
||||
cd src/github.com/Netflix-Skunkworks/go-jira/jira; \
|
||||
go get -v
|
||||
CURVER ?= $(shell [ -d .git ] && git describe --abbrev=0 --tags || grep ^\#\# CHANGELOG.md | awk '{print $$2; exit}')
|
||||
LDFLAGS:=-X main.buildVersion=$(CURVER)
|
||||
|
||||
build: src/github.com/Netflix-Skunkworks/go-jira
|
||||
go build -ldflags "$(LDFLAGS)" -o $(GOBIN)/$(NAME) jira/main.go
|
||||
|
||||
src/%:
|
||||
mkdir -p $(@D)
|
||||
test -L $@ || ln -sf ../../.. $@
|
||||
go get -v $*/jira
|
||||
|
||||
cross-setup:
|
||||
for p in $(PLATFORMS); do \
|
||||
echo "Building for $$p"; \
|
||||
cd $(GOROOT)/src && sudo GOOS=$${p/-*/} GOARCH=$${p/*-/} bash ./make.bash --no-clean; \
|
||||
cd $(GOROOT)/src && sudo GOROOT_BOOTSTRAP=$(GOROOT) GOOS=$${p/-*/} GOARCH=$${p/*-/} bash ./make.bash --no-clean; \
|
||||
done
|
||||
|
||||
all:
|
||||
rm -rf $(DIST); \
|
||||
mkdir -p $(DIST); \
|
||||
cd src/github.com/Netflix-Skunkworks/go-jira/jira; \
|
||||
go get -d; \
|
||||
for p in $(PLATFORMS); do \
|
||||
echo "Building for $$p"; \
|
||||
GOOS=$${p/-*/} GOARCH=$${p/*-/} go build -v -ldflags -s -o $(DIST)/jira-$$p; \
|
||||
done
|
||||
GOOS=$${p/-*/} GOARCH=$${p/*-/} go build -v -ldflags "$(LDFLAGS) -s" -o $(DIST)/$(NAME)-$$p jira/main.go ; \
|
||||
done
|
||||
|
||||
fmt:
|
||||
gofmt -s -w jira
|
||||
@@ -43,11 +49,6 @@ fmt:
|
||||
install:
|
||||
export GOBIN=~/bin && ${MAKE} build
|
||||
|
||||
# need gsort on OSX (brew install coreutils) or newer sort on linux
|
||||
# that supports the -V option for version sorting
|
||||
SORT=gsort
|
||||
|
||||
CURVER ?= $(shell git fetch --tags && git tag | $(SORT) -V | tail -1)
|
||||
NEWVER ?= $(shell echo $(CURVER) | awk -F. '{print $$1"."$$2"."$$3+1}')
|
||||
TODAY := $(shell date +%Y-%m-%d)
|
||||
|
||||
@@ -65,9 +66,7 @@ update-changelog:
|
||||
tail +2 CHANGELOG.md >> CHANGELOG.md.new; \
|
||||
mv CHANGELOG.md.new CHANGELOG.md; \
|
||||
git commit -m "Updated Changelog" CHANGELOG.md; \
|
||||
perl -pi -e "s/version: $(CURVER)/version: $(NEWVER)/" jira/main.go; \
|
||||
git commit -m "bump version" jira/main.go; \
|
||||
git tag $(NEWVER)
|
||||
|
||||
clean:
|
||||
rm -rf pkg dist bin && find src \! -path \*/go-jira\* -delete
|
||||
rm -rf pkg dist bin src ./toolkit
|
||||
|
||||
@@ -211,7 +211,7 @@ General Options:
|
||||
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: summary,created,priority,status,reporter,assignee)
|
||||
-f --queryfields=FIELDS Fields that are used in "list" template: (default: summary,created,updated,priority,status,reporter,assignee)
|
||||
-i --issuetype=ISSUETYPE The Issue Type
|
||||
-l --limit=VAL Maximum number of results to return in query (default: 500)
|
||||
-p --project=PROJECT Project to Search for
|
||||
|
||||
+17
-4
@@ -2,9 +2,10 @@ package cli
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"code.google.com/p/gopass"
|
||||
"fmt"
|
||||
"golang.org/x/crypto/ssh/terminal"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"os"
|
||||
"strings"
|
||||
// "github.com/kr/pretty"
|
||||
@@ -16,13 +17,17 @@ func (c *Cli) CmdLogin() error {
|
||||
req, _ := http.NewRequest("GET", uri, nil)
|
||||
user, _ := c.opts["user"].(string)
|
||||
|
||||
prompt := fmt.Sprintf("Enter Password for %s: ", user)
|
||||
passwd, _ := gopass.GetPass(prompt)
|
||||
fmt.Printf("Enter Password for %s: ", user)
|
||||
pwbytes, _ := terminal.ReadPassword(int(os.Stdin.Fd()))
|
||||
passwd := string(pwbytes)
|
||||
|
||||
req.SetBasicAuth(user, passwd)
|
||||
log.Info("%s %s", req.Method, req.URL.String())
|
||||
if resp, err := c.makeRequest(req); err != nil {
|
||||
return err
|
||||
} else {
|
||||
out, _ := httputil.DumpResponse(resp, true)
|
||||
log.Debug("%s", out)
|
||||
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
|
||||
@@ -110,6 +115,7 @@ func (c *Cli) CmdEdit(issue string) error {
|
||||
issueData,
|
||||
func(json string) error {
|
||||
if c.getOptBool("dryrun", false) {
|
||||
log.Debug("PUT: %s", json)
|
||||
log.Debug("Dryrun mode, skipping PUT")
|
||||
return nil
|
||||
}
|
||||
@@ -218,7 +224,7 @@ func (c *Cli) CmdCreate() error {
|
||||
|
||||
issueData := make(map[string]interface{})
|
||||
issueData["overrides"] = c.opts
|
||||
issueData["overrides"].(map[string]string)["issuetype"] = issuetype
|
||||
issueData["overrides"].(map[string]interface{})["issuetype"] = issuetype
|
||||
|
||||
if val, ok := data.(map[string]interface{})["projects"]; ok {
|
||||
if len(val.([]interface{})) == 0 {
|
||||
@@ -240,6 +246,7 @@ func (c *Cli) CmdCreate() error {
|
||||
log.Debug("JSON: %s", json)
|
||||
uri := fmt.Sprintf("%s/rest/api/2/issue", c.endpoint)
|
||||
if c.getOptBool("dryrun", false) {
|
||||
log.Debug("POST: %s", json)
|
||||
log.Debug("Dryrun mode, skipping POST")
|
||||
return nil
|
||||
}
|
||||
@@ -301,6 +308,7 @@ func (c *Cli) CmdBlocks(blocker string, issue string) error {
|
||||
|
||||
uri := fmt.Sprintf("%s/rest/api/2/issueLink", c.endpoint)
|
||||
if c.getOptBool("dryrun", false) {
|
||||
log.Debug("POST: %s", json)
|
||||
log.Debug("Dryrun mode, skipping POST")
|
||||
return nil
|
||||
}
|
||||
@@ -341,6 +349,7 @@ func (c *Cli) CmdDups(duplicate string, issue string) error {
|
||||
|
||||
uri := fmt.Sprintf("%s/rest/api/2/issueLink", c.endpoint)
|
||||
if c.getOptBool("dryrun", false) {
|
||||
log.Debug("POST: %s", json)
|
||||
log.Debug("Dryrun mode, skipping POST")
|
||||
return nil
|
||||
}
|
||||
@@ -372,6 +381,7 @@ func (c *Cli) CmdWatch(issue string) error {
|
||||
|
||||
uri := fmt.Sprintf("%s/rest/api/2/issue/%s/watchers", c.endpoint, issue)
|
||||
if c.getOptBool("dryrun", false) {
|
||||
log.Debug("POST: %s", json)
|
||||
log.Debug("Dryrun mode, skipping POST")
|
||||
return nil
|
||||
}
|
||||
@@ -425,6 +435,7 @@ func (c *Cli) CmdTransition(issue string, trans string) error {
|
||||
// os.Exit(0)
|
||||
uri = fmt.Sprintf("%s/rest/api/2/issue/%s/transitions", c.endpoint, issue)
|
||||
if c.getOptBool("dryrun", false) {
|
||||
log.Debug("POST: %s", json)
|
||||
log.Debug("Dryrun mode, skipping POST")
|
||||
return nil
|
||||
}
|
||||
@@ -474,6 +485,7 @@ func (c *Cli) CmdComment(issue string) error {
|
||||
log.Debug("JSON: %s", json)
|
||||
uri := fmt.Sprintf("%s/rest/api/2/issue/%s/comment", c.endpoint, issue)
|
||||
if c.getOptBool("dryrun", false) {
|
||||
log.Debug("POST: %s", json)
|
||||
log.Debug("Dryrun mode, skipping POST")
|
||||
return nil
|
||||
}
|
||||
@@ -526,6 +538,7 @@ func (c *Cli) CmdAssign(issue string, user string) error {
|
||||
|
||||
uri := fmt.Sprintf("%s/rest/api/2/issue/%s/assignee", c.endpoint, issue)
|
||||
if c.getOptBool("dryrun", false) {
|
||||
log.Debug("PUT: %s", json)
|
||||
log.Debug("Dryrun mode, skipping PUT")
|
||||
return nil
|
||||
}
|
||||
|
||||
+12
-1
@@ -100,6 +100,14 @@ func fuzzyAge(start string) (string, error) {
|
||||
return "unknown", nil
|
||||
}
|
||||
|
||||
func dateFormat(format string, content string) (string, error) {
|
||||
if t, err := time.Parse("2006-01-02T15:04:05.000-0700", content); err != nil {
|
||||
return "", err
|
||||
} else {
|
||||
return t.Format(format), nil
|
||||
}
|
||||
}
|
||||
|
||||
func runTemplate(templateContent string, data interface{}, out io.Writer) error {
|
||||
|
||||
if out == nil {
|
||||
@@ -171,6 +179,9 @@ func runTemplate(templateContent string, data interface{}, out io.Writer) error
|
||||
"age": func(content string) (string, error) {
|
||||
return fuzzyAge(content)
|
||||
},
|
||||
"dateFormat": func(format string, content string) (string, error) {
|
||||
return dateFormat(format, content)
|
||||
},
|
||||
}
|
||||
if tmpl, err := template.New("template").Funcs(funcs).Parse(templateContent); err != nil {
|
||||
log.Error("Failed to parse template: %s", err)
|
||||
@@ -295,7 +306,7 @@ func yamlFixup(data interface{}) (interface{}, error) {
|
||||
}
|
||||
return copy, nil
|
||||
case string:
|
||||
if d == "" {
|
||||
if d == "" || d == "\n" {
|
||||
return nil, nil
|
||||
}
|
||||
return d, nil
|
||||
|
||||
+39
-8
@@ -13,8 +13,11 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
var log = logging.MustGetLogger("jira")
|
||||
var format = "%{color}%{time:2006-01-02T15:04:05.000Z07:00} %{level:-5s} [%{shortfile}]%{color:reset} %{message}"
|
||||
var (
|
||||
log = logging.MustGetLogger("jira")
|
||||
format = "%{color}%{time:2006-01-02T15:04:05.000Z07:00} %{level:-5s} [%{shortfile}]%{color:reset} %{message}"
|
||||
buildVersion string
|
||||
)
|
||||
|
||||
func main() {
|
||||
logBackend := logging.NewLogBackend(os.Stderr, "", 0)
|
||||
@@ -28,7 +31,7 @@ func main() {
|
||||
|
||||
user := os.Getenv("USER")
|
||||
home := os.Getenv("HOME")
|
||||
defaultQueryFields := "summary,created,priority,status,reporter,assignee"
|
||||
defaultQueryFields := "summary,created,updated,priority,status,reporter,assignee"
|
||||
defaultSort := "priority asc, created"
|
||||
defaultMaxResults := 500
|
||||
|
||||
@@ -166,7 +169,7 @@ Command Options:
|
||||
op := optigo.NewDirectAssignParser(map[string]interface{}{
|
||||
"h|help": usage,
|
||||
"version": func() {
|
||||
fmt.Println("version: 0.0.11")
|
||||
fmt.Println(fmt.Sprintf("version: %s", buildVersion))
|
||||
os.Exit(0)
|
||||
},
|
||||
"v|verbose+": func() {
|
||||
@@ -216,7 +219,7 @@ Command Options:
|
||||
}
|
||||
}
|
||||
|
||||
if command == "" {
|
||||
if command == "" && len(args) > 0 {
|
||||
command = args[0]
|
||||
args = args[1:]
|
||||
}
|
||||
@@ -228,7 +231,9 @@ Command Options:
|
||||
if value, ok := opts["command"].(string); ok {
|
||||
command = value
|
||||
} else if _, ok := jiraCommands[command]; !ok || command == "" {
|
||||
args = append([]string{command}, args...)
|
||||
if command != "" {
|
||||
args = append([]string{command}, args...)
|
||||
}
|
||||
command = "view"
|
||||
}
|
||||
|
||||
@@ -244,7 +249,7 @@ Command Options:
|
||||
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")
|
||||
log.Error("endpoint option required. Either use --endpoint or set a endpoint option in your ~/.jira.d/config.yml file")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
@@ -270,6 +275,13 @@ Command Options:
|
||||
}
|
||||
}
|
||||
|
||||
requireArgs := func(count int) {
|
||||
if len(args) < count {
|
||||
log.Error("Not enough arguments. %d required, %d provided", count, len(args))
|
||||
usage(false)
|
||||
}
|
||||
}
|
||||
|
||||
var err error
|
||||
switch command {
|
||||
case "login":
|
||||
@@ -279,6 +291,7 @@ Command Options:
|
||||
case "list":
|
||||
err = c.CmdList()
|
||||
case "edit":
|
||||
requireArgs(1)
|
||||
setEditing(true)
|
||||
if len(args) > 0 {
|
||||
err = c.CmdEdit(args[0])
|
||||
@@ -300,8 +313,10 @@ Command Options:
|
||||
}
|
||||
}
|
||||
case "editmeta":
|
||||
requireArgs(1)
|
||||
err = c.CmdEditMeta(args[0])
|
||||
case "transmeta":
|
||||
requireArgs(1)
|
||||
err = c.CmdTransitionMeta(args[0])
|
||||
case "issuelinktypes":
|
||||
err = c.CmdIssueLinkTypes()
|
||||
@@ -313,50 +328,66 @@ Command Options:
|
||||
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)
|
||||
err = c.CmdWatch(args[0])
|
||||
case "transition":
|
||||
requireArgs(2)
|
||||
setEditing(true)
|
||||
err = c.CmdTransition(args[0], args[1])
|
||||
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 "comment":
|
||||
requireArgs(1)
|
||||
setEditing(true)
|
||||
err = c.CmdComment(args[0])
|
||||
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-tempaltes":
|
||||
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])
|
||||
default:
|
||||
log.Error("Unknown command %s", command)
|
||||
|
||||
Reference in New Issue
Block a user