mirror of
https://github.com/Threnklyn/jira.git
synced 2026-06-01 02:38:28 +02:00
Compare commits
28 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 |
@@ -1,5 +1,35 @@
|
|||||||
# 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
|
## 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)]
|
* [[#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)]
|
||||||
|
|||||||
@@ -14,20 +14,32 @@ PLATFORMS= \
|
|||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
DIST=$(shell pwd)/dist
|
DIST=$(shell pwd)/dist
|
||||||
|
|
||||||
export GOPATH=$(shell pwd)
|
export GOPATH=$(shell pwd)
|
||||||
GOBIN ?= $(shell pwd)/bin
|
|
||||||
|
GOBIN ?= $(shell pwd)
|
||||||
NAME=jira
|
NAME=jira
|
||||||
|
|
||||||
|
BIN ?= $(GOBIN)/$(NAME)
|
||||||
|
|
||||||
CURVER ?= $(shell [ -d .git ] && git describe --abbrev=0 --tags || grep ^\#\# CHANGELOG.md | awk '{print $$2; exit}')
|
CURVER ?= $(shell [ -d .git ] && git describe --abbrev=0 --tags || grep ^\#\# CHANGELOG.md | awk '{print $$2; exit}')
|
||||||
LDFLAGS:=-X main.buildVersion=$(CURVER)
|
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
|
build: src/github.com/Netflix-Skunkworks/go-jira
|
||||||
go build -ldflags "$(LDFLAGS)" -o $(GOBIN)/$(NAME) jira/main.go
|
$(GOBUILD) -o $(BIN) main/main.go
|
||||||
|
|
||||||
src/%:
|
src/%:
|
||||||
mkdir -p $(@D)
|
mkdir -p $(@D)
|
||||||
test -L $@ || ln -sf ../../.. $@
|
test -L $@ || ln -sf ../../.. $@
|
||||||
go get -v $*/jira
|
go get -v $* $*/main
|
||||||
|
|
||||||
cross-setup:
|
cross-setup:
|
||||||
for p in $(PLATFORMS); do \
|
for p in $(PLATFORMS); do \
|
||||||
@@ -40,33 +52,36 @@ all:
|
|||||||
mkdir -p $(DIST); \
|
mkdir -p $(DIST); \
|
||||||
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 "$(LDFLAGS) -s" -o $(DIST)/$(NAME)-$$p jira/main.go ; \
|
${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
|
||||||
|
|
||||||
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 src ./toolkit
|
rm -rf pkg dist bin src ./$(NAME)
|
||||||
|
|||||||
@@ -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,4 +1,4 @@
|
|||||||
package cli
|
package jira
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
@@ -80,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)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,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))
|
||||||
@@ -233,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]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -260,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 {
|
||||||
@@ -318,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)
|
||||||
@@ -359,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)
|
||||||
@@ -391,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)
|
||||||
@@ -418,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{})
|
||||||
@@ -445,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)
|
||||||
@@ -496,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))
|
||||||
@@ -526,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")
|
||||||
|
|
||||||
@@ -548,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)
|
||||||
@@ -585,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)
|
||||||
|
}
|
||||||
+37
-15
@@ -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"
|
||||||
@@ -14,10 +14,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
log = logging.MustGetLogger("jira")
|
log = logging.MustGetLogger("jira")
|
||||||
format = "%{color}%{time:2006-01-02T15:04:05.000Z07:00} %{level:-5s} [%{shortfile}]%{color:reset} %{message}"
|
format = "%{color}%{time:2006-01-02T15:04:05.000Z07:00} %{level:-5s} [%{shortfile}]%{color:reset} %{message}"
|
||||||
buildVersion string
|
)
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
logBackend := logging.NewLogBackend(os.Stderr, "", 0)
|
logBackend := logging.NewLogBackend(os.Stderr, "", 0)
|
||||||
@@ -66,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
|
||||||
@@ -78,11 +78,13 @@ 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)
|
||||||
@@ -136,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",
|
||||||
@@ -149,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{}{
|
||||||
@@ -157,11 +163,11 @@ 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
|
||||||
}
|
}
|
||||||
@@ -169,7 +175,7 @@ Command Options:
|
|||||||
op := optigo.NewDirectAssignParser(map[string]interface{}{
|
op := optigo.NewDirectAssignParser(map[string]interface{}{
|
||||||
"h|help": usage,
|
"h|help": usage,
|
||||||
"version": func() {
|
"version": func() {
|
||||||
fmt.Println(fmt.Sprintf("version: %s", buildVersion))
|
fmt.Println(fmt.Sprintf("version: %s", jira.VERSION))
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
},
|
},
|
||||||
"v|verbose+": func() {
|
"v|verbose+": func() {
|
||||||
@@ -180,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,
|
||||||
@@ -191,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 {
|
||||||
@@ -203,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 {
|
||||||
@@ -212,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:]...)
|
||||||
@@ -253,7 +263,7 @@ Command Options:
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
c := cli.New(opts)
|
c := jira.New(opts)
|
||||||
|
|
||||||
log.Debug("opts: %s", opts)
|
log.Debug("opts: %s", opts)
|
||||||
|
|
||||||
@@ -291,7 +301,6 @@ Command Options:
|
|||||||
case "list":
|
case "list":
|
||||||
err = c.CmdList()
|
err = c.CmdList()
|
||||||
case "edit":
|
case "edit":
|
||||||
requireArgs(1)
|
|
||||||
setEditing(true)
|
setEditing(true)
|
||||||
if len(args) > 0 {
|
if len(args) > 0 {
|
||||||
err = c.CmdEdit(args[0])
|
err = c.CmdEdit(args[0])
|
||||||
@@ -302,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
|
||||||
@@ -345,7 +354,7 @@ Command Options:
|
|||||||
case "transition":
|
case "transition":
|
||||||
requireArgs(2)
|
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)
|
requireArgs(1)
|
||||||
setEditing(false)
|
setEditing(false)
|
||||||
@@ -374,6 +383,12 @@ Command Options:
|
|||||||
requireArgs(1)
|
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)
|
requireArgs(1)
|
||||||
err = c.CmdAssign(args[0], opts["user"].(string))
|
err = c.CmdAssign(args[0], opts["user"].(string))
|
||||||
@@ -389,6 +404,13 @@ Command Options:
|
|||||||
case "view":
|
case "view":
|
||||||
requireArgs(1)
|
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)
|
||||||
@@ -430,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"
|
||||||
@@ -108,8 +109,11 @@ func dateFormat(format string, content string) (string, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func runTemplate(templateContent string, data interface{}, out io.Writer) error {
|
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
|
||||||
}
|
}
|
||||||
@@ -160,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
|
||||||
@@ -245,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 {
|
||||||
Reference in New Issue
Block a user