mirror of
https://github.com/Threnklyn/jira.git
synced 2026-05-28 00:38:29 +02:00
Compare commits
55 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b5417ef585 | |||
| c5af781c41 | |||
| f2c4df9b3e | |||
| 1dde7e06e6 | |||
| 7bc1897792 | |||
| 37aab3580b | |||
| 42990d8ca0 | |||
| 8e662462da | |||
| ad7bb2b724 | |||
| a8cce44178 | |||
| 35955a7a93 | |||
| f349e25bb9 | |||
| a92a93b282 | |||
| 8645ef11f1 | |||
| e042a3e62a | |||
| a738d1515e | |||
| d4f15ae5c6 | |||
| bc70b43868 | |||
| e24b431b7a | |||
| 101bc1da68 | |||
| 63e035c5c1 | |||
| 40bafc9b66 | |||
| 5d863ffed4 | |||
| 577394b0bd | |||
| c1a7e1bbdb | |||
| f904f3c089 | |||
| e35e518368 | |||
| 159d142f37 | |||
| df84d47552 | |||
| fa4ce5647d | |||
| 713d300a57 | |||
| c8ae7fc685 | |||
| 80322b648e | |||
| f2076a0977 | |||
| 886adb5db2 | |||
| 3bdbdbdaff | |||
| 544b923fab | |||
| 13a69e6f44 | |||
| fae9f94817 | |||
| 7d90672736 | |||
| 20faa959aa | |||
| aaff47d606 | |||
| 7bfb2946d4 | |||
| 9884281079 | |||
| 03fce96eb5 | |||
| 8f9e6f7d85 | |||
| 65c0240d34 | |||
| ccec749a0b | |||
| 6a9901f171 | |||
| 868764ac86 | |||
| f1e7514a00 | |||
| 92d10c3498 | |||
| a9c6b865b6 | |||
| 9cc55a13c1 | |||
| b116764d3e |
@@ -1,5 +1,62 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 0.0.20 - 2016-01-21
|
||||||
|
|
||||||
|
* [issue [#28](https://github.com/Netflix-Skunkworks/go-jira/issues/28)] check to make sure we got back issuetypes for create metadata [Cory Bennett] [[ee0e780](https://github.com/Netflix-Skunkworks/go-jira/commit/ee0e780)]
|
||||||
|
* Add insecure option for TLS endpoints [Brian Lalor] [[6a88bb9](https://github.com/Netflix-Skunkworks/go-jira/commit/6a88bb9)]
|
||||||
|
* Correct naming of parameter: set/add/remove are actions. [Mike Pountney] [[303784f](https://github.com/Netflix-Skunkworks/go-jira/commit/303784f)]
|
||||||
|
* Tweak CmdLabels args so that magic happens with CLI [Mike Pountney] [[40a7c65](https://github.com/Netflix-Skunkworks/go-jira/commit/40a7c65)]
|
||||||
|
* Expose ViewTicket as per FindIssues [Mike Pountney] [[8977f3d](https://github.com/Netflix-Skunkworks/go-jira/commit/8977f3d)]
|
||||||
|
* Add exposed versions of getTemplate and runTemplate [Mike Pountney] [[da6cbd5](https://github.com/Netflix-Skunkworks/go-jira/commit/da6cbd5)]
|
||||||
|
* Add 'labels' command to set/add/remove labels [Mike Pountney] [[230b52d](https://github.com/Netflix-Skunkworks/go-jira/commit/230b52d)]
|
||||||
|
* Add a 'join' func to the template engine [Mike Pountney] [[a7820fe](https://github.com/Netflix-Skunkworks/go-jira/commit/a7820fe)]
|
||||||
|
* make "jira" golang package, move code from jira/cli to root, move jira/main.go to main/main.go [Cory Bennett] [[7268b9e](https://github.com/Netflix-Skunkworks/go-jira/commit/7268b9e)]
|
||||||
|
|
||||||
|
## 0.0.19 - 2015-12-09
|
||||||
|
|
||||||
|
* fix jira trans TRANS ISSUE (case sensitivity issue), also go fmt [Cory Bennett] [[3c30f3b](https://github.com/Netflix-Skunkworks/go-jira/commit/3c30f3b)]
|
||||||
|
|
||||||
|
## 0.0.18 - 2015-12-03
|
||||||
|
|
||||||
|
* need to default "quiet" to false [Cory Bennett] [[4f4a89b](https://github.com/Netflix-Skunkworks/go-jira/commit/4f4a89b)]
|
||||||
|
|
||||||
|
## 0.0.17 - 2015-12-03
|
||||||
|
|
||||||
|
* add --quiet command to not print the OK .. add --saveFile option to print the issue/link to a file on create command [Cory Bennett] [[c9ac162](https://github.com/Netflix-Skunkworks/go-jira/commit/c9ac162)]
|
||||||
|
* fix overrides [Cory Bennett] [[eaddfe6](https://github.com/Netflix-Skunkworks/go-jira/commit/eaddfe6)]
|
||||||
|
* add abstract request wrapper to allow you to access/process random apis supported by Jira but not yet supported by go-jira [Cory Bennett] [[90ef56a](https://github.com/Netflix-Skunkworks/go-jira/commit/90ef56a)]
|
||||||
|
|
||||||
|
## 0.0.16 - 2015-11-23
|
||||||
|
|
||||||
|
* jira edit should not require one arguemnt (allow for --query) [Cory Bennett] [[a1eb4a1](https://github.com/Netflix-Skunkworks/go-jira/commit/a1eb4a1)]
|
||||||
|
|
||||||
|
## 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)]
|
||||||
|
* fix command line parser broken in 0.0.10 [Cory Bennett] [[15ae929](https://github.com/Netflix-Skunkworks/go-jira/commit/15ae929)]
|
||||||
|
|
||||||
## 0.0.10 - 2015-09-15
|
## 0.0.10 - 2015-09-15
|
||||||
|
|
||||||
* allow for command aliasing in conjunction with executable config files. Issue #5 [Cory Bennett] [[23590d4](https://github.com/Netflix-Skunkworks/go-jira/commit/23590d4)]
|
* allow for command aliasing in conjunction with executable config files. Issue #5 [Cory Bennett] [[23590d4](https://github.com/Netflix-Skunkworks/go-jira/commit/23590d4)]
|
||||||
|
|||||||
@@ -14,54 +14,74 @@ PLATFORMS= \
|
|||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
DIST=$(shell pwd)/dist
|
DIST=$(shell pwd)/dist
|
||||||
|
|
||||||
export GOPATH=$(shell pwd)
|
export GOPATH=$(shell pwd)
|
||||||
|
|
||||||
build:
|
GOBIN ?= $(shell pwd)
|
||||||
cd src/github.com/Netflix-Skunkworks/go-jira/jira; \
|
NAME=jira
|
||||||
go get -v
|
|
||||||
|
|
||||||
|
BIN ?= $(GOBIN)/$(NAME)
|
||||||
|
|
||||||
|
CURVER ?= $(shell [ -d .git ] && git describe --abbrev=0 --tags || grep ^\#\# CHANGELOG.md | awk '{print $$2; exit}')
|
||||||
|
LDFLAGS:=-X jira.VERSION=$(patsubst v%,%,$(CURVER)) -w
|
||||||
|
|
||||||
|
# use make DEBUG=1 and you can get a debuggable golang binary
|
||||||
|
# see https://github.com/mailgun/godebug
|
||||||
|
ifneq ($(DEBUG),)
|
||||||
|
GOBUILD=go get -v github.com/mailgun/godebug && ./bin/godebug build
|
||||||
|
else
|
||||||
|
GOBUILD=go build -v -ldflags "$(LDFLAGS) -s"
|
||||||
|
endif
|
||||||
|
|
||||||
|
build: src/github.com/Netflix-Skunkworks/go-jira
|
||||||
|
$(GOBUILD) -o $(BIN) main/main.go
|
||||||
|
|
||||||
|
src/%:
|
||||||
|
mkdir -p $(@D)
|
||||||
|
test -L $@ || ln -sf ../../.. $@
|
||||||
|
go get -v $* $*/main
|
||||||
|
|
||||||
cross-setup:
|
cross-setup:
|
||||||
for p in $(PLATFORMS); do \
|
for p in $(PLATFORMS); do \
|
||||||
echo "Building for $$p"; \
|
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
|
done
|
||||||
|
|
||||||
all:
|
all:
|
||||||
rm -rf $(DIST); \
|
rm -rf $(DIST); \
|
||||||
mkdir -p $(DIST); \
|
mkdir -p $(DIST); \
|
||||||
cd src/github.com/Netflix-Skunkworks/go-jira/jira; \
|
|
||||||
go get -d; \
|
|
||||||
for p in $(PLATFORMS); do \
|
for p in $(PLATFORMS); do \
|
||||||
echo "Building for $$p"; \
|
echo "Building for $$p"; \
|
||||||
GOOS=$${p/-*/} GOARCH=$${p/*-/} go build -v -ldflags -s -o $(DIST)/jira-$$p; \
|
${MAKE} build GOOS=$${p/-*/} GOARCH=$${p/*-/} BIN=$(DIST)/$(NAME)-$$p; \
|
||||||
done
|
done
|
||||||
|
|
||||||
fmt:
|
fmt:
|
||||||
gofmt -s -w jira
|
gofmt -s -w main/*.go *.go
|
||||||
|
|
||||||
install:
|
install:
|
||||||
export GOBIN=~/bin && ${MAKE} build
|
${MAKE} GOBIN=~/bin build
|
||||||
|
|
||||||
CURVER ?= $(shell git fetch --tags && git tag | tail -1)
|
|
||||||
NEWVER ?= $(shell echo $(CURVER) | awk -F. '{print $$1"."$$2"."$$3+1}')
|
NEWVER ?= $(shell echo $(CURVER) | awk -F. '{print $$1"."$$2"."$$3+1}')
|
||||||
TODAY := $(shell date +%Y-%m-%d)
|
TODAY := $(shell date +%Y-%m-%d)
|
||||||
|
|
||||||
changes:
|
changes:
|
||||||
@git log --pretty=format:"* %s [%cn] [%h]" --no-merges ^$(CURVER) HEAD jira | grep -v gofmt | grep -v "bump version"
|
@git log --pretty=format:"* %s [%cn] [%h]" --no-merges ^$(CURVER) HEAD main/*.go *.go | grep -vE 'gofmt|go fmt'
|
||||||
|
|
||||||
update-changelog:
|
update-changelog:
|
||||||
@echo "# Changelog" > CHANGELOG.md.new; \
|
@echo "# Changelog" > CHANGELOG.md.new; \
|
||||||
echo >> CHANGELOG.md.new; \
|
echo >> CHANGELOG.md.new; \
|
||||||
echo "## $(NEWVER) - $(TODAY)" >> CHANGELOG.md.new; \
|
echo "## $(NEWVER) - $(TODAY)" >> CHANGELOG.md.new; \
|
||||||
echo >> CHANGELOG.md.new; \
|
echo >> CHANGELOG.md.new; \
|
||||||
$(MAKE) changes | \
|
$(MAKE) --no-print-directory --silent changes | \
|
||||||
perl -pe 's{\[([a-f0-9]+)\]}{[[$$1](https://github.com/Netflix-Skunkworks/go-jira/commit/$$1)]}g' | \
|
perl -pe 's{\[([a-f0-9]+)\]}{[[$$1](https://github.com/Netflix-Skunkworks/go-jira/commit/$$1)]}g' | \
|
||||||
perl -pe 's{\#(\d+)}{[#$$1](https://github.com/Netflix-Skunkworks/go-jira/issues/$$1)}g' >> CHANGELOG.md.new; \
|
perl -pe 's{\#(\d+)}{[#$$1](https://github.com/Netflix-Skunkworks/go-jira/issues/$$1)}g' >> CHANGELOG.md.new; \
|
||||||
tail +2 CHANGELOG.md >> CHANGELOG.md.new; \
|
tail -n +2 CHANGELOG.md >> CHANGELOG.md.new; \
|
||||||
mv CHANGELOG.md.new CHANGELOG.md; \
|
mv CHANGELOG.md.new CHANGELOG.md; \
|
||||||
git commit -m "Updated Changelog" CHANGELOG.md; \
|
git commit -m "Updated Changelog" CHANGELOG.md; \
|
||||||
git tag $(NEWVER)
|
git tag v$(NEWVER)
|
||||||
|
|
||||||
|
version:
|
||||||
|
@echo $(patsubst v%,%,$(CURVER))
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf pkg dist bin && find src \! -path \*/go-jira\* -delete
|
rm -rf pkg dist bin src ./$(NAME)
|
||||||
|
|||||||
@@ -211,7 +211,7 @@ General Options:
|
|||||||
Query Options:
|
Query Options:
|
||||||
-a --assignee=USER Username assigned the issue
|
-a --assignee=USER Username assigned the issue
|
||||||
-c --component=COMPONENT Component to Search for
|
-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
|
-i --issuetype=ISSUETYPE The Issue Type
|
||||||
-l --limit=VAL Maximum number of results to return in query (default: 500)
|
-l --limit=VAL Maximum number of results to return in query (default: 500)
|
||||||
-p --project=PROJECT Project to Search for
|
-p --project=PROJECT Project to Search for
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
package cli
|
package jira
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"crypto/tls"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/kballard/go-shellquote"
|
"github.com/kballard/go-shellquote"
|
||||||
@@ -18,7 +19,10 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
var log = logging.MustGetLogger("jira.cli")
|
var (
|
||||||
|
log = logging.MustGetLogger("jira")
|
||||||
|
VERSION string
|
||||||
|
)
|
||||||
|
|
||||||
type Cli struct {
|
type Cli struct {
|
||||||
endpoint *url.URL
|
endpoint *url.URL
|
||||||
@@ -33,15 +37,26 @@ func New(opts map[string]interface{}) *Cli {
|
|||||||
endpoint, _ := opts["endpoint"].(string)
|
endpoint, _ := opts["endpoint"].(string)
|
||||||
url, _ := url.Parse(strings.TrimRight(endpoint, "/"))
|
url, _ := url.Parse(strings.TrimRight(endpoint, "/"))
|
||||||
|
|
||||||
|
transport := &http.Transport{
|
||||||
|
TLSClientConfig: &tls.Config{},
|
||||||
|
}
|
||||||
|
|
||||||
if project, ok := opts["project"].(string); ok {
|
if project, ok := opts["project"].(string); ok {
|
||||||
opts["project"] = strings.ToUpper(project)
|
opts["project"] = strings.ToUpper(project)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if insecureSkipVerify, ok := opts["insecure"].(bool); ok {
|
||||||
|
transport.TLSClientConfig.InsecureSkipVerify = insecureSkipVerify
|
||||||
|
}
|
||||||
|
|
||||||
cli := &Cli{
|
cli := &Cli{
|
||||||
endpoint: url,
|
endpoint: url,
|
||||||
opts: opts,
|
opts: opts,
|
||||||
cookieFile: fmt.Sprintf("%s/.jira.d/cookies.js", homedir),
|
cookieFile: fmt.Sprintf("%s/.jira.d/cookies.js", homedir),
|
||||||
ua: &http.Client{Jar: cookieJar},
|
ua: &http.Client{
|
||||||
|
Jar: cookieJar,
|
||||||
|
Transport: transport,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
cli.ua.Jar.SetCookies(url, cli.loadCookies())
|
cli.ua.Jar.SetCookies(url, cli.loadCookies())
|
||||||
@@ -174,6 +189,10 @@ func (c *Cli) makeRequest(req *http.Request) (resp *http.Response, err error) {
|
|||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Cli) GetTemplate(name string) string {
|
||||||
|
return c.getTemplate(name)
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Cli) getTemplate(name string) string {
|
func (c *Cli) getTemplate(name string) string {
|
||||||
if override, ok := c.opts["template"].(string); ok {
|
if override, ok := c.opts["template"].(string); ok {
|
||||||
if _, err := os.Stat(override); err == nil {
|
if _, err := os.Stat(override); err == nil {
|
||||||
@@ -354,6 +373,23 @@ func (c *Cli) Browse(issue string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Cli) SaveData(data interface{}) error {
|
||||||
|
if val, ok := c.opts["saveFile"].(string); ok && val != "" {
|
||||||
|
yamlWrite(val, data)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cli) ViewIssue(issue string) (interface{}, error) {
|
||||||
|
uri := fmt.Sprintf("%s/rest/api/2/issue/%s", c.endpoint, issue)
|
||||||
|
data, err := responseToJson(c.get(uri))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else {
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Cli) FindIssues() (interface{}, error) {
|
func (c *Cli) FindIssues() (interface{}, error) {
|
||||||
var query string
|
var query string
|
||||||
var ok bool
|
var ok bool
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
package cli
|
package jira
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"code.google.com/p/gopass"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"golang.org/x/crypto/ssh/terminal"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/http/httputil"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
// "github.com/kr/pretty"
|
// "github.com/kr/pretty"
|
||||||
@@ -16,13 +17,17 @@ func (c *Cli) CmdLogin() error {
|
|||||||
req, _ := http.NewRequest("GET", uri, nil)
|
req, _ := http.NewRequest("GET", uri, nil)
|
||||||
user, _ := c.opts["user"].(string)
|
user, _ := c.opts["user"].(string)
|
||||||
|
|
||||||
prompt := fmt.Sprintf("Enter Password for %s: ", user)
|
fmt.Printf("Enter Password for %s: ", user)
|
||||||
passwd, _ := gopass.GetPass(prompt)
|
pwbytes, _ := terminal.ReadPassword(int(os.Stdin.Fd()))
|
||||||
|
passwd := string(pwbytes)
|
||||||
|
|
||||||
req.SetBasicAuth(user, passwd)
|
req.SetBasicAuth(user, passwd)
|
||||||
|
log.Info("%s %s", req.Method, req.URL.String())
|
||||||
if resp, err := c.makeRequest(req); err != nil {
|
if resp, err := c.makeRequest(req); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
|
out, _ := httputil.DumpResponse(resp, true)
|
||||||
|
log.Debug("%s", out)
|
||||||
if resp.StatusCode == 403 {
|
if resp.StatusCode == 403 {
|
||||||
// probably got this, need to redirect the user to login manually
|
// probably got this, need to redirect the user to login manually
|
||||||
// X-Authentication-Denied-Reason: CAPTCHA_CHALLENGE; login-url=https://jira/login.jsp
|
// X-Authentication-Denied-Reason: CAPTCHA_CHALLENGE; login-url=https://jira/login.jsp
|
||||||
@@ -75,12 +80,10 @@ func (c *Cli) CmdList() error {
|
|||||||
func (c *Cli) CmdView(issue string) error {
|
func (c *Cli) CmdView(issue string) error {
|
||||||
log.Debug("view called")
|
log.Debug("view called")
|
||||||
c.Browse(issue)
|
c.Browse(issue)
|
||||||
uri := fmt.Sprintf("%s/rest/api/2/issue/%s", c.endpoint, issue)
|
data, err := c.ViewIssue(issue)
|
||||||
data, err := responseToJson(c.get(uri))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return runTemplate(c.getTemplate("view"), data, nil)
|
return runTemplate(c.getTemplate("view"), data, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,6 +113,7 @@ func (c *Cli) CmdEdit(issue string) error {
|
|||||||
issueData,
|
issueData,
|
||||||
func(json string) error {
|
func(json string) error {
|
||||||
if c.getOptBool("dryrun", false) {
|
if c.getOptBool("dryrun", false) {
|
||||||
|
log.Debug("PUT: %s", json)
|
||||||
log.Debug("Dryrun mode, skipping PUT")
|
log.Debug("Dryrun mode, skipping PUT")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -120,7 +124,9 @@ func (c *Cli) CmdEdit(issue string) error {
|
|||||||
|
|
||||||
if resp.StatusCode == 204 {
|
if resp.StatusCode == 204 {
|
||||||
c.Browse(issueData["key"].(string))
|
c.Browse(issueData["key"].(string))
|
||||||
fmt.Printf("OK %s %s/browse/%s\n", issueData["key"], c.endpoint, issueData["key"])
|
if !c.opts["quiet"].(bool) {
|
||||||
|
fmt.Printf("OK %s %s/browse/%s\n", issueData["key"], c.endpoint, issueData["key"])
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
} else {
|
} else {
|
||||||
logBuffer := bytes.NewBuffer(make([]byte, 0))
|
logBuffer := bytes.NewBuffer(make([]byte, 0))
|
||||||
@@ -218,7 +224,7 @@ func (c *Cli) CmdCreate() error {
|
|||||||
|
|
||||||
issueData := make(map[string]interface{})
|
issueData := make(map[string]interface{})
|
||||||
issueData["overrides"] = c.opts
|
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 val, ok := data.(map[string]interface{})["projects"]; ok {
|
||||||
if len(val.([]interface{})) == 0 {
|
if len(val.([]interface{})) == 0 {
|
||||||
@@ -227,6 +233,11 @@ func (c *Cli) CmdCreate() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if val, ok = val.([]interface{})[0].(map[string]interface{})["issuetypes"]; ok {
|
if val, ok = val.([]interface{})[0].(map[string]interface{})["issuetypes"]; ok {
|
||||||
|
if len(val.([]interface{})) == 0 {
|
||||||
|
err = fmt.Errorf("Project '%s' does not support issuetype '%s'. Unable to create issue.", project, issuetype)
|
||||||
|
log.Error("%s", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
issueData["meta"] = val.([]interface{})[0]
|
issueData["meta"] = val.([]interface{})[0]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -240,6 +251,7 @@ func (c *Cli) CmdCreate() error {
|
|||||||
log.Debug("JSON: %s", json)
|
log.Debug("JSON: %s", json)
|
||||||
uri := fmt.Sprintf("%s/rest/api/2/issue", c.endpoint)
|
uri := fmt.Sprintf("%s/rest/api/2/issue", c.endpoint)
|
||||||
if c.getOptBool("dryrun", false) {
|
if c.getOptBool("dryrun", false) {
|
||||||
|
log.Debug("POST: %s", json)
|
||||||
log.Debug("Dryrun mode, skipping POST")
|
log.Debug("Dryrun mode, skipping POST")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -253,10 +265,16 @@ func (c *Cli) CmdCreate() error {
|
|||||||
if json, err := responseToJson(resp, nil); err != nil {
|
if json, err := responseToJson(resp, nil); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
key := json.(map[string]interface{})["key"]
|
key := json.(map[string]interface{})["key"].(string)
|
||||||
c.Browse(key.(string))
|
link := fmt.Sprintf("%s/browse/%s", c.endpoint, key)
|
||||||
fmt.Printf("OK %s %s/browse/%s\n", key, c.endpoint, key)
|
c.Browse(key)
|
||||||
|
c.SaveData(map[string]string{
|
||||||
|
"issue": key,
|
||||||
|
"link": link,
|
||||||
|
})
|
||||||
|
if !c.opts["quiet"].(bool) {
|
||||||
|
fmt.Printf("OK %s %s\n", key, link)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
} else {
|
} else {
|
||||||
@@ -301,6 +319,7 @@ func (c *Cli) CmdBlocks(blocker string, issue string) error {
|
|||||||
|
|
||||||
uri := fmt.Sprintf("%s/rest/api/2/issueLink", c.endpoint)
|
uri := fmt.Sprintf("%s/rest/api/2/issueLink", c.endpoint)
|
||||||
if c.getOptBool("dryrun", false) {
|
if c.getOptBool("dryrun", false) {
|
||||||
|
log.Debug("POST: %s", json)
|
||||||
log.Debug("Dryrun mode, skipping POST")
|
log.Debug("Dryrun mode, skipping POST")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -310,7 +329,9 @@ func (c *Cli) CmdBlocks(blocker string, issue string) error {
|
|||||||
}
|
}
|
||||||
if resp.StatusCode == 201 {
|
if resp.StatusCode == 201 {
|
||||||
c.Browse(issue)
|
c.Browse(issue)
|
||||||
fmt.Printf("OK %s %s/browse/%s\n", issue, c.endpoint, issue)
|
if !c.opts["quiet"].(bool) {
|
||||||
|
fmt.Printf("OK %s %s/browse/%s\n", issue, c.endpoint, issue)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
logBuffer := bytes.NewBuffer(make([]byte, 0))
|
logBuffer := bytes.NewBuffer(make([]byte, 0))
|
||||||
resp.Write(logBuffer)
|
resp.Write(logBuffer)
|
||||||
@@ -341,6 +362,7 @@ func (c *Cli) CmdDups(duplicate string, issue string) error {
|
|||||||
|
|
||||||
uri := fmt.Sprintf("%s/rest/api/2/issueLink", c.endpoint)
|
uri := fmt.Sprintf("%s/rest/api/2/issueLink", c.endpoint)
|
||||||
if c.getOptBool("dryrun", false) {
|
if c.getOptBool("dryrun", false) {
|
||||||
|
log.Debug("POST: %s", json)
|
||||||
log.Debug("Dryrun mode, skipping POST")
|
log.Debug("Dryrun mode, skipping POST")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -350,7 +372,9 @@ func (c *Cli) CmdDups(duplicate string, issue string) error {
|
|||||||
}
|
}
|
||||||
if resp.StatusCode == 201 {
|
if resp.StatusCode == 201 {
|
||||||
c.Browse(issue)
|
c.Browse(issue)
|
||||||
fmt.Printf("OK %s %s/browse/%s\n", issue, c.endpoint, issue)
|
if !c.opts["quiet"].(bool) {
|
||||||
|
fmt.Printf("OK %s %s/browse/%s\n", issue, c.endpoint, issue)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
logBuffer := bytes.NewBuffer(make([]byte, 0))
|
logBuffer := bytes.NewBuffer(make([]byte, 0))
|
||||||
resp.Write(logBuffer)
|
resp.Write(logBuffer)
|
||||||
@@ -372,6 +396,7 @@ func (c *Cli) CmdWatch(issue string) error {
|
|||||||
|
|
||||||
uri := fmt.Sprintf("%s/rest/api/2/issue/%s/watchers", c.endpoint, issue)
|
uri := fmt.Sprintf("%s/rest/api/2/issue/%s/watchers", c.endpoint, issue)
|
||||||
if c.getOptBool("dryrun", false) {
|
if c.getOptBool("dryrun", false) {
|
||||||
|
log.Debug("POST: %s", json)
|
||||||
log.Debug("Dryrun mode, skipping POST")
|
log.Debug("Dryrun mode, skipping POST")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -381,7 +406,9 @@ func (c *Cli) CmdWatch(issue string) error {
|
|||||||
}
|
}
|
||||||
if resp.StatusCode == 204 {
|
if resp.StatusCode == 204 {
|
||||||
c.Browse(issue)
|
c.Browse(issue)
|
||||||
fmt.Printf("OK %s %s/browse/%s\n", issue, c.endpoint, issue)
|
if !c.opts["quiet"].(bool) {
|
||||||
|
fmt.Printf("OK %s %s/browse/%s\n", issue, c.endpoint, issue)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
logBuffer := bytes.NewBuffer(make([]byte, 0))
|
logBuffer := bytes.NewBuffer(make([]byte, 0))
|
||||||
resp.Write(logBuffer)
|
resp.Write(logBuffer)
|
||||||
@@ -408,7 +435,7 @@ func (c *Cli) CmdTransition(issue string, trans string) error {
|
|||||||
name := transition.(map[string]interface{})["name"].(string)
|
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)
|
found = append(found, name)
|
||||||
if strings.Contains(strings.ToLower(name), trans) {
|
if strings.Contains(strings.ToLower(name), strings.ToLower(trans)) {
|
||||||
transName = name
|
transName = name
|
||||||
transId = id
|
transId = id
|
||||||
transMeta = transition.(map[string]interface{})
|
transMeta = transition.(map[string]interface{})
|
||||||
@@ -425,6 +452,7 @@ func (c *Cli) CmdTransition(issue string, trans string) error {
|
|||||||
// os.Exit(0)
|
// os.Exit(0)
|
||||||
uri = fmt.Sprintf("%s/rest/api/2/issue/%s/transitions", c.endpoint, issue)
|
uri = fmt.Sprintf("%s/rest/api/2/issue/%s/transitions", c.endpoint, issue)
|
||||||
if c.getOptBool("dryrun", false) {
|
if c.getOptBool("dryrun", false) {
|
||||||
|
log.Debug("POST: %s", json)
|
||||||
log.Debug("Dryrun mode, skipping POST")
|
log.Debug("Dryrun mode, skipping POST")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -434,7 +462,9 @@ func (c *Cli) CmdTransition(issue string, trans string) error {
|
|||||||
}
|
}
|
||||||
if resp.StatusCode == 204 {
|
if resp.StatusCode == 204 {
|
||||||
c.Browse(issue)
|
c.Browse(issue)
|
||||||
fmt.Printf("OK %s %s/browse/%s\n", issue, c.endpoint, issue)
|
if !c.opts["quiet"].(bool) {
|
||||||
|
fmt.Printf("OK %s %s/browse/%s\n", issue, c.endpoint, issue)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
logBuffer := bytes.NewBuffer(make([]byte, 0))
|
logBuffer := bytes.NewBuffer(make([]byte, 0))
|
||||||
resp.Write(logBuffer)
|
resp.Write(logBuffer)
|
||||||
@@ -474,6 +504,7 @@ func (c *Cli) CmdComment(issue string) error {
|
|||||||
log.Debug("JSON: %s", json)
|
log.Debug("JSON: %s", json)
|
||||||
uri := fmt.Sprintf("%s/rest/api/2/issue/%s/comment", c.endpoint, issue)
|
uri := fmt.Sprintf("%s/rest/api/2/issue/%s/comment", c.endpoint, issue)
|
||||||
if c.getOptBool("dryrun", false) {
|
if c.getOptBool("dryrun", false) {
|
||||||
|
log.Debug("POST: %s", json)
|
||||||
log.Debug("Dryrun mode, skipping POST")
|
log.Debug("Dryrun mode, skipping POST")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -484,7 +515,9 @@ func (c *Cli) CmdComment(issue string) error {
|
|||||||
|
|
||||||
if resp.StatusCode == 201 {
|
if resp.StatusCode == 201 {
|
||||||
c.Browse(issue)
|
c.Browse(issue)
|
||||||
fmt.Printf("OK %s %s/browse/%s\n", issue, c.endpoint, issue)
|
if !c.opts["quiet"].(bool) {
|
||||||
|
fmt.Printf("OK %s %s/browse/%s\n", issue, c.endpoint, issue)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
} else {
|
} else {
|
||||||
logBuffer := bytes.NewBuffer(make([]byte, 0))
|
logBuffer := bytes.NewBuffer(make([]byte, 0))
|
||||||
@@ -514,6 +547,71 @@ func (c *Cli) CmdComment(issue string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Cli) CmdLabels(action string, issue string, labels []string) error {
|
||||||
|
log.Debug("label called")
|
||||||
|
|
||||||
|
if action != "add" && action != "remove" && action != "set" {
|
||||||
|
return fmt.Errorf("action must be 'add', 'set' or 'remove': %q is invalid", action)
|
||||||
|
}
|
||||||
|
|
||||||
|
handlePut := func(json string) error {
|
||||||
|
log.Debug("JSON: %s", json)
|
||||||
|
uri := fmt.Sprintf("%s/rest/api/2/issue/%s", c.endpoint, issue)
|
||||||
|
if c.getOptBool("dryrun", false) {
|
||||||
|
log.Debug("PUT: %s", json)
|
||||||
|
log.Debug("Dryrun mode, skipping POST")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
resp, err := c.put(uri, json)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode == 204 {
|
||||||
|
c.Browse(issue)
|
||||||
|
if !c.opts["quiet"].(bool) {
|
||||||
|
fmt.Printf("OK %s %s/browse/%s\n", issue, c.endpoint, issue)
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var labels_json string
|
||||||
|
var err error
|
||||||
|
if action == "set" {
|
||||||
|
labelsActions := make([]map[string][]string, 1)
|
||||||
|
labelsActions[0] = map[string][]string{
|
||||||
|
"set": labels,
|
||||||
|
}
|
||||||
|
labels_json, err = jsonEncode(map[string]interface{}{
|
||||||
|
"labels": labelsActions,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
labelsActions := make([]map[string]string, len(labels))
|
||||||
|
for i, label := range labels {
|
||||||
|
labelActionMap := map[string]string{
|
||||||
|
action: label,
|
||||||
|
}
|
||||||
|
labelsActions[i] = labelActionMap
|
||||||
|
}
|
||||||
|
labels_json, err = jsonEncode(map[string]interface{}{
|
||||||
|
"labels": labelsActions,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
json := fmt.Sprintf("{ \"update\": %s }", labels_json)
|
||||||
|
return handlePut(json)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Cli) CmdAssign(issue string, user string) error {
|
func (c *Cli) CmdAssign(issue string, user string) error {
|
||||||
log.Debug("assign called")
|
log.Debug("assign called")
|
||||||
|
|
||||||
@@ -526,6 +624,7 @@ func (c *Cli) CmdAssign(issue string, user string) error {
|
|||||||
|
|
||||||
uri := fmt.Sprintf("%s/rest/api/2/issue/%s/assignee", c.endpoint, issue)
|
uri := fmt.Sprintf("%s/rest/api/2/issue/%s/assignee", c.endpoint, issue)
|
||||||
if c.getOptBool("dryrun", false) {
|
if c.getOptBool("dryrun", false) {
|
||||||
|
log.Debug("PUT: %s", json)
|
||||||
log.Debug("Dryrun mode, skipping PUT")
|
log.Debug("Dryrun mode, skipping PUT")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -535,7 +634,9 @@ func (c *Cli) CmdAssign(issue string, user string) error {
|
|||||||
}
|
}
|
||||||
if resp.StatusCode == 204 {
|
if resp.StatusCode == 204 {
|
||||||
c.Browse(issue)
|
c.Browse(issue)
|
||||||
fmt.Printf("OK %s %s/browse/%s\n", issue, c.endpoint, issue)
|
if !c.opts["quiet"].(bool) {
|
||||||
|
fmt.Printf("OK %s %s/browse/%s\n", issue, c.endpoint, issue)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
logBuffer := bytes.NewBuffer(make([]byte, 0))
|
logBuffer := bytes.NewBuffer(make([]byte, 0))
|
||||||
resp.Write(logBuffer)
|
resp.Write(logBuffer)
|
||||||
@@ -572,3 +673,25 @@ func (c *Cli) CmdExportTemplates() error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Cli) CmdRequest(uri, content string) (err error) {
|
||||||
|
log.Debug("request called")
|
||||||
|
|
||||||
|
if !strings.HasPrefix(uri, "http") {
|
||||||
|
uri = fmt.Sprintf("%s%s", c.endpoint, uri)
|
||||||
|
}
|
||||||
|
|
||||||
|
method := strings.ToUpper(c.opts["method"].(string))
|
||||||
|
var data interface{}
|
||||||
|
if method == "GET" {
|
||||||
|
data, err = responseToJson(c.get(uri))
|
||||||
|
} else if method == "POST" {
|
||||||
|
data, err = responseToJson(c.post(uri, content))
|
||||||
|
} else if method == "PUT" {
|
||||||
|
data, err = responseToJson(c.put(uri, content))
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return runTemplate(c.getTemplate("request"), data, nil)
|
||||||
|
}
|
||||||
+76
-18
@@ -3,7 +3,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/Netflix-Skunkworks/go-jira/jira/cli"
|
"github.com/Netflix-Skunkworks/go-jira"
|
||||||
"github.com/coryb/optigo"
|
"github.com/coryb/optigo"
|
||||||
"github.com/op/go-logging"
|
"github.com/op/go-logging"
|
||||||
"gopkg.in/coryb/yaml.v2"
|
"gopkg.in/coryb/yaml.v2"
|
||||||
@@ -13,8 +13,10 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
var log = logging.MustGetLogger("jira")
|
var (
|
||||||
var format = "%{color}%{time:2006-01-02T15:04:05.000Z07:00} %{level:-5s} [%{shortfile}]%{color:reset} %{message}"
|
log = logging.MustGetLogger("jira")
|
||||||
|
format = "%{color}%{time:2006-01-02T15:04:05.000Z07:00} %{level:-5s} [%{shortfile}]%{color:reset} %{message}"
|
||||||
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
logBackend := logging.NewLogBackend(os.Stderr, "", 0)
|
logBackend := logging.NewLogBackend(os.Stderr, "", 0)
|
||||||
@@ -28,7 +30,7 @@ func main() {
|
|||||||
|
|
||||||
user := os.Getenv("USER")
|
user := os.Getenv("USER")
|
||||||
home := os.Getenv("HOME")
|
home := os.Getenv("HOME")
|
||||||
defaultQueryFields := "summary,created,priority,status,reporter,assignee"
|
defaultQueryFields := "summary,created,updated,priority,status,reporter,assignee"
|
||||||
defaultSort := "priority asc, created"
|
defaultSort := "priority asc, created"
|
||||||
defaultMaxResults := 500
|
defaultMaxResults := 500
|
||||||
|
|
||||||
@@ -63,6 +65,7 @@ Usage:
|
|||||||
jira start ISSUE [--edit] <Edit Options>
|
jira start ISSUE [--edit] <Edit Options>
|
||||||
jira stop ISSUE [--edit] <Edit Options>
|
jira stop ISSUE [--edit] <Edit Options>
|
||||||
jira comment ISSUE [--noedit] <Edit Options>
|
jira comment ISSUE [--noedit] <Edit Options>
|
||||||
|
jira (set,add,remove) labels ISSUE [LABEL] ...
|
||||||
jira take ISSUE
|
jira take ISSUE
|
||||||
jira (assign|give) ISSUE ASSIGNEE
|
jira (assign|give) ISSUE ASSIGNEE
|
||||||
jira fields
|
jira fields
|
||||||
@@ -75,15 +78,18 @@ Usage:
|
|||||||
jira export-templates [-d DIR] [-t template]
|
jira export-templates [-d DIR] [-t template]
|
||||||
jira (b|browse) ISSUE
|
jira (b|browse) ISSUE
|
||||||
jira login
|
jira login
|
||||||
|
jira request [-M METHOD] URI [DATA]
|
||||||
jira ISSUE
|
jira ISSUE
|
||||||
|
|
||||||
General Options:
|
General Options:
|
||||||
-b --browse Open your browser to the Jira issue
|
-b --browse Open your browser to the Jira issue
|
||||||
-e --endpoint=URI URI to use for jira
|
-e --endpoint=URI URI to use for jira
|
||||||
|
-k --insecure disable TLS certificate verification
|
||||||
-h --help Show this usage
|
-h --help Show this usage
|
||||||
-t --template=FILE Template file to use for output/editing
|
-t --template=FILE Template file to use for output/editing
|
||||||
-u --user=USER Username to use for authenticaion (default: %s)
|
-u --user=USER Username to use for authenticaion (default: %s)
|
||||||
-v --verbose Increase output logging
|
-v --verbose Increase output logging
|
||||||
|
--version Print version
|
||||||
|
|
||||||
Query Options:
|
Query Options:
|
||||||
-a --assignee=USER Username assigned the issue
|
-a --assignee=USER Username assigned the issue
|
||||||
@@ -132,6 +138,8 @@ Command Options:
|
|||||||
"start": "start",
|
"start": "start",
|
||||||
"stop": "stop",
|
"stop": "stop",
|
||||||
"comment": "comment",
|
"comment": "comment",
|
||||||
|
"label": "labels",
|
||||||
|
"labels": "labels",
|
||||||
"take": "take",
|
"take": "take",
|
||||||
"assign": "assign",
|
"assign": "assign",
|
||||||
"give": "assign",
|
"give": "assign",
|
||||||
@@ -145,6 +153,8 @@ Command Options:
|
|||||||
"export-templates": "export-templates",
|
"export-templates": "export-templates",
|
||||||
"browse": "browse",
|
"browse": "browse",
|
||||||
"login": "login",
|
"login": "login",
|
||||||
|
"req": "request",
|
||||||
|
"request": "request",
|
||||||
}
|
}
|
||||||
|
|
||||||
defaults := map[string]interface{}{
|
defaults := map[string]interface{}{
|
||||||
@@ -153,17 +163,21 @@ Command Options:
|
|||||||
"directory": fmt.Sprintf("%s/.jira.d/templates", home),
|
"directory": fmt.Sprintf("%s/.jira.d/templates", home),
|
||||||
"sort": defaultSort,
|
"sort": defaultSort,
|
||||||
"max_results": defaultMaxResults,
|
"max_results": defaultMaxResults,
|
||||||
|
"method": "GET",
|
||||||
|
"quiet": false,
|
||||||
}
|
}
|
||||||
opts := make(map[string]interface{})
|
opts := make(map[string]interface{})
|
||||||
|
|
||||||
overrides := make(map[string]string)
|
|
||||||
|
|
||||||
setopt := func(name string, value interface{}) {
|
setopt := func(name string, value interface{}) {
|
||||||
opts[name] = value
|
opts[name] = value
|
||||||
}
|
}
|
||||||
|
|
||||||
op := optigo.NewDirectAssignParser(map[string]interface{}{
|
op := optigo.NewDirectAssignParser(map[string]interface{}{
|
||||||
"h|help": usage,
|
"h|help": usage,
|
||||||
|
"version": func() {
|
||||||
|
fmt.Println(fmt.Sprintf("version: %s", jira.VERSION))
|
||||||
|
os.Exit(0)
|
||||||
|
},
|
||||||
"v|verbose+": func() {
|
"v|verbose+": func() {
|
||||||
logging.SetLevel(logging.GetLevel("")+1, "")
|
logging.SetLevel(logging.GetLevel("")+1, "")
|
||||||
},
|
},
|
||||||
@@ -172,6 +186,7 @@ Command Options:
|
|||||||
"editor=s": setopt,
|
"editor=s": setopt,
|
||||||
"u|user=s": setopt,
|
"u|user=s": setopt,
|
||||||
"endpoint=s": setopt,
|
"endpoint=s": setopt,
|
||||||
|
"k|insecure": setopt,
|
||||||
"t|template=s": setopt,
|
"t|template=s": setopt,
|
||||||
"q|query=s": setopt,
|
"q|query=s": setopt,
|
||||||
"p|project=s": setopt,
|
"p|project=s": setopt,
|
||||||
@@ -183,11 +198,14 @@ Command Options:
|
|||||||
"f|queryfields=s": setopt,
|
"f|queryfields=s": setopt,
|
||||||
"s|sort=s": setopt,
|
"s|sort=s": setopt,
|
||||||
"l|limit|max_results=i": setopt,
|
"l|limit|max_results=i": setopt,
|
||||||
"o|override=s%": &overrides,
|
"o|override=s%": &opts,
|
||||||
"noedit": setopt,
|
"noedit": setopt,
|
||||||
"edit": setopt,
|
"edit": setopt,
|
||||||
"m|comment=s": setopt,
|
"m|comment=s": setopt,
|
||||||
"d|dir|directory=s": setopt,
|
"d|dir|directory=s": setopt,
|
||||||
|
"M|method=s": setopt,
|
||||||
|
"S|saveFile=s": setopt,
|
||||||
|
"Q|quiet": setopt,
|
||||||
})
|
})
|
||||||
|
|
||||||
if err := op.ProcessAll(os.Args[1:]); err != nil {
|
if err := op.ProcessAll(os.Args[1:]); err != nil {
|
||||||
@@ -195,7 +213,6 @@ Command Options:
|
|||||||
usage(false)
|
usage(false)
|
||||||
}
|
}
|
||||||
args := op.Args
|
args := op.Args
|
||||||
opts["overrides"] = overrides
|
|
||||||
|
|
||||||
var command string
|
var command string
|
||||||
if len(args) > 0 {
|
if len(args) > 0 {
|
||||||
@@ -204,6 +221,7 @@ Command Options:
|
|||||||
args = args[1:]
|
args = args[1:]
|
||||||
} else if len(args) > 1 {
|
} else if len(args) > 1 {
|
||||||
// look at second arg for "dups" and "blocks" commands
|
// look at second arg for "dups" and "blocks" commands
|
||||||
|
// also for 'set/add/remove' actions like 'labels'
|
||||||
if alias, ok := jiraCommands[args[1]]; ok {
|
if alias, ok := jiraCommands[args[1]]; ok {
|
||||||
command = alias
|
command = alias
|
||||||
args = append(args[:1], args[2:]...)
|
args = append(args[:1], args[2:]...)
|
||||||
@@ -211,7 +229,7 @@ Command Options:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if command == "" {
|
if command == "" && len(args) > 0 {
|
||||||
command = args[0]
|
command = args[0]
|
||||||
args = args[1:]
|
args = args[1:]
|
||||||
}
|
}
|
||||||
@@ -222,8 +240,10 @@ Command Options:
|
|||||||
// check to see if it was set in the configs:
|
// check to see if it was set in the configs:
|
||||||
if value, ok := opts["command"].(string); ok {
|
if value, ok := opts["command"].(string); ok {
|
||||||
command = value
|
command = value
|
||||||
} else {
|
} else if _, ok := jiraCommands[command]; !ok || command == "" {
|
||||||
args = append([]string{command}, args...)
|
if command != "" {
|
||||||
|
args = append([]string{command}, args...)
|
||||||
|
}
|
||||||
command = "view"
|
command = "view"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -239,11 +259,11 @@ Command Options:
|
|||||||
log.Debug("args: %v", args)
|
log.Debug("args: %v", args)
|
||||||
|
|
||||||
if _, ok := opts["endpoint"]; !ok {
|
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)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
c := cli.New(opts)
|
c := jira.New(opts)
|
||||||
|
|
||||||
log.Debug("opts: %s", opts)
|
log.Debug("opts: %s", opts)
|
||||||
|
|
||||||
@@ -265,6 +285,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
|
var err error
|
||||||
switch command {
|
switch command {
|
||||||
case "login":
|
case "login":
|
||||||
@@ -284,7 +311,7 @@ Command Options:
|
|||||||
for _, issue := range issues {
|
for _, issue := range issues {
|
||||||
if err = c.CmdEdit(issue.(map[string]interface{})["key"].(string)); err != nil {
|
if err = c.CmdEdit(issue.(map[string]interface{})["key"].(string)); err != nil {
|
||||||
switch err.(type) {
|
switch err.(type) {
|
||||||
case cli.NoChangesFound:
|
case jira.NoChangesFound:
|
||||||
log.Warning("No Changes found: %s", err)
|
log.Warning("No Changes found: %s", err)
|
||||||
err = nil
|
err = nil
|
||||||
continue
|
continue
|
||||||
@@ -295,8 +322,10 @@ Command Options:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
case "editmeta":
|
case "editmeta":
|
||||||
|
requireArgs(1)
|
||||||
err = c.CmdEditMeta(args[0])
|
err = c.CmdEditMeta(args[0])
|
||||||
case "transmeta":
|
case "transmeta":
|
||||||
|
requireArgs(1)
|
||||||
err = c.CmdTransitionMeta(args[0])
|
err = c.CmdTransitionMeta(args[0])
|
||||||
case "issuelinktypes":
|
case "issuelinktypes":
|
||||||
err = c.CmdIssueLinkTypes()
|
err = c.CmdIssueLinkTypes()
|
||||||
@@ -308,51 +337,80 @@ Command Options:
|
|||||||
setEditing(true)
|
setEditing(true)
|
||||||
err = c.CmdCreate()
|
err = c.CmdCreate()
|
||||||
case "transitions":
|
case "transitions":
|
||||||
|
requireArgs(1)
|
||||||
err = c.CmdTransitions(args[0])
|
err = c.CmdTransitions(args[0])
|
||||||
case "blocks":
|
case "blocks":
|
||||||
|
requireArgs(2)
|
||||||
err = c.CmdBlocks(args[0], args[1])
|
err = c.CmdBlocks(args[0], args[1])
|
||||||
case "dups":
|
case "dups":
|
||||||
|
requireArgs(2)
|
||||||
if err = c.CmdDups(args[0], args[1]); err == nil {
|
if err = c.CmdDups(args[0], args[1]); err == nil {
|
||||||
opts["resolution"] = "Duplicate"
|
opts["resolution"] = "Duplicate"
|
||||||
err = c.CmdTransition(args[0], "close")
|
err = c.CmdTransition(args[0], "close")
|
||||||
}
|
}
|
||||||
case "watch":
|
case "watch":
|
||||||
|
requireArgs(1)
|
||||||
err = c.CmdWatch(args[0])
|
err = c.CmdWatch(args[0])
|
||||||
case "transition":
|
case "transition":
|
||||||
|
requireArgs(2)
|
||||||
setEditing(true)
|
setEditing(true)
|
||||||
err = c.CmdTransition(args[0], args[1])
|
err = c.CmdTransition(args[1], args[0])
|
||||||
case "close":
|
case "close":
|
||||||
|
requireArgs(1)
|
||||||
setEditing(false)
|
setEditing(false)
|
||||||
err = c.CmdTransition(args[0], "close")
|
err = c.CmdTransition(args[0], "close")
|
||||||
case "acknowledge":
|
case "acknowledge":
|
||||||
|
requireArgs(1)
|
||||||
setEditing(false)
|
setEditing(false)
|
||||||
err = c.CmdTransition(args[0], "acknowledge")
|
err = c.CmdTransition(args[0], "acknowledge")
|
||||||
case "reopen":
|
case "reopen":
|
||||||
|
requireArgs(1)
|
||||||
setEditing(false)
|
setEditing(false)
|
||||||
err = c.CmdTransition(args[0], "reopen")
|
err = c.CmdTransition(args[0], "reopen")
|
||||||
case "resolve":
|
case "resolve":
|
||||||
|
requireArgs(1)
|
||||||
setEditing(false)
|
setEditing(false)
|
||||||
err = c.CmdTransition(args[0], "resolve")
|
err = c.CmdTransition(args[0], "resolve")
|
||||||
case "start":
|
case "start":
|
||||||
|
requireArgs(1)
|
||||||
setEditing(false)
|
setEditing(false)
|
||||||
err = c.CmdTransition(args[0], "start")
|
err = c.CmdTransition(args[0], "start")
|
||||||
case "stop":
|
case "stop":
|
||||||
|
requireArgs(1)
|
||||||
setEditing(false)
|
setEditing(false)
|
||||||
err = c.CmdTransition(args[0], "stop")
|
err = c.CmdTransition(args[0], "stop")
|
||||||
case "comment":
|
case "comment":
|
||||||
|
requireArgs(1)
|
||||||
setEditing(true)
|
setEditing(true)
|
||||||
err = c.CmdComment(args[0])
|
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 "take":
|
case "take":
|
||||||
|
requireArgs(1)
|
||||||
err = c.CmdAssign(args[0], opts["user"].(string))
|
err = c.CmdAssign(args[0], opts["user"].(string))
|
||||||
case "browse":
|
case "browse":
|
||||||
|
requireArgs(1)
|
||||||
opts["browse"] = true
|
opts["browse"] = true
|
||||||
err = c.Browse(args[0])
|
err = c.Browse(args[0])
|
||||||
case "export-tempaltes":
|
case "export-templates":
|
||||||
err = c.CmdExportTemplates()
|
err = c.CmdExportTemplates()
|
||||||
case "assign":
|
case "assign":
|
||||||
|
requireArgs(2)
|
||||||
err = c.CmdAssign(args[0], args[1])
|
err = c.CmdAssign(args[0], args[1])
|
||||||
case "view":
|
case "view":
|
||||||
|
requireArgs(1)
|
||||||
err = c.CmdView(args[0])
|
err = c.CmdView(args[0])
|
||||||
|
case "request":
|
||||||
|
requireArgs(1)
|
||||||
|
data := ""
|
||||||
|
if len(args) > 1 {
|
||||||
|
data = args[1]
|
||||||
|
}
|
||||||
|
err = c.CmdRequest(args[0], data)
|
||||||
default:
|
default:
|
||||||
log.Error("Unknown command %s", command)
|
log.Error("Unknown command %s", command)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
@@ -394,9 +452,9 @@ func populateEnv(opts map[string]interface{}) {
|
|||||||
|
|
||||||
func loadConfigs(opts map[string]interface{}) {
|
func loadConfigs(opts map[string]interface{}) {
|
||||||
populateEnv(opts)
|
populateEnv(opts)
|
||||||
paths := cli.FindParentPaths(".jira.d/config.yml")
|
paths := jira.FindParentPaths(".jira.d/config.yml")
|
||||||
// prepend
|
// prepend
|
||||||
paths = append([]string{"/etc/jira-cli.yml"}, paths...)
|
paths = append([]string{"/etc/go-jira.yml"}, paths...)
|
||||||
|
|
||||||
// iterate paths in reverse
|
// iterate paths in reverse
|
||||||
for i := len(paths) - 1; i >= 0; i-- {
|
for i := len(paths) - 1; i >= 0; i-- {
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package cli
|
package jira
|
||||||
|
|
||||||
var all_templates = map[string]string{
|
var all_templates = map[string]string{
|
||||||
"debug": default_debug_template,
|
"debug": default_debug_template,
|
||||||
@@ -16,6 +16,7 @@ var all_templates = map[string]string{
|
|||||||
"create": default_create_template,
|
"create": default_create_template,
|
||||||
"comment": default_comment_template,
|
"comment": default_comment_template,
|
||||||
"transition": default_transition_template,
|
"transition": default_transition_template,
|
||||||
|
"request": default_debug_template,
|
||||||
}
|
}
|
||||||
|
|
||||||
const default_debug_template = "{{ . | toJson}}\n"
|
const default_debug_template = "{{ . | toJson}}\n"
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package cli
|
package jira
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/mgutz/ansi"
|
"github.com/mgutz/ansi"
|
||||||
|
"gopkg.in/coryb/yaml.v2"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
@@ -100,8 +101,19 @@ func fuzzyAge(start string) (string, error) {
|
|||||||
return "unknown", nil
|
return "unknown", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func runTemplate(templateContent string, data interface{}, out io.Writer) error {
|
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 {
|
||||||
|
return runTemplate(templateContent, data, out)
|
||||||
|
}
|
||||||
|
|
||||||
|
func runTemplate(templateContent string, data interface{}, out io.Writer) error {
|
||||||
if out == nil {
|
if out == nil {
|
||||||
out = os.Stdout
|
out = os.Stdout
|
||||||
}
|
}
|
||||||
@@ -152,6 +164,13 @@ func runTemplate(templateContent string, data interface{}, out io.Writer) error
|
|||||||
"split": func(sep string, content string) []string {
|
"split": func(sep string, content string) []string {
|
||||||
return strings.Split(content, sep)
|
return strings.Split(content, sep)
|
||||||
},
|
},
|
||||||
|
"join": func(sep string, content []interface{}) string {
|
||||||
|
vals := make([]string, len(content))
|
||||||
|
for i, v := range content {
|
||||||
|
vals[i] = v.(string)
|
||||||
|
}
|
||||||
|
return strings.Join(vals, sep)
|
||||||
|
},
|
||||||
"abbrev": func(max int, content string) string {
|
"abbrev": func(max int, content string) string {
|
||||||
if len(content) > max {
|
if len(content) > max {
|
||||||
var buffer bytes.Buffer
|
var buffer bytes.Buffer
|
||||||
@@ -171,6 +190,9 @@ func runTemplate(templateContent string, data interface{}, out io.Writer) error
|
|||||||
"age": func(content string) (string, error) {
|
"age": func(content string) (string, error) {
|
||||||
return fuzzyAge(content)
|
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 {
|
if tmpl, err := template.New("template").Funcs(funcs).Parse(templateContent); err != nil {
|
||||||
log.Error("Failed to parse template: %s", err)
|
log.Error("Failed to parse template: %s", err)
|
||||||
@@ -234,6 +256,21 @@ func jsonWrite(file string, data interface{}) {
|
|||||||
enc.Encode(data)
|
enc.Encode(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func yamlWrite(file string, data interface{}) {
|
||||||
|
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)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
if out, err := yaml.Marshal(data); err != nil {
|
||||||
|
log.Error("Failed to marshal yaml %v: %s", data, err)
|
||||||
|
os.Exit(1)
|
||||||
|
} else {
|
||||||
|
fh.Write(out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func promptYN(prompt string, yes bool) bool {
|
func promptYN(prompt string, yes bool) bool {
|
||||||
reader := bufio.NewReader(os.Stdin)
|
reader := bufio.NewReader(os.Stdin)
|
||||||
if !yes {
|
if !yes {
|
||||||
@@ -295,7 +332,7 @@ func yamlFixup(data interface{}) (interface{}, error) {
|
|||||||
}
|
}
|
||||||
return copy, nil
|
return copy, nil
|
||||||
case string:
|
case string:
|
||||||
if d == "" {
|
if d == "" || d == "\n" {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
return d, nil
|
return d, nil
|
||||||
Reference in New Issue
Block a user