Compare commits

...

52 Commits

Author SHA1 Message Date
Cory Bennett ba08d51437 Updated Changelog 2016-12-18 09:48:21 -08:00
Cory Bennett 09d718b5d8 stabbing in the dark 2016-12-17 19:57:45 -08:00
Cory Bennett e3e84d7aa0 only test jira cloud service 2016-12-17 18:52:07 -08:00
Cory Bennett a4f1d754e4 debugging tests 2016-12-17 18:50:20 -08:00
Cory Bennett 683541de1e add verbose test 2016-12-17 18:40:21 -08:00
Cory Bennett e0fd6bab66 only warn about needing login when not already running the login command 2016-12-17 18:32:47 -08:00
Cory Bennett 5ca096ab6e use go-jira.atlassian.net for testing via travis 2016-12-17 17:25:11 -08:00
Cory Bennett ac515e2743 Merge branch 'master' of github.com:Netflix-Skunkworks/go-jira 2016-12-17 14:13:39 -08:00
Cory Bennett be4a5f9156 update for isolated xgo build 2016-12-17 14:13:00 -08:00
coryb 7f10eaa667 Merge pull request #61 from sylus/feature-proxy
fix(http): Add proxy transport
2016-12-17 14:06:45 -08:00
William Hearn b326623ed2 fix(http): Add proxy transport 2016-12-17 16:50:34 -05:00
William Hearn 72c78c6c1c fix(http): Add proxy transport 2016-12-17 14:13:20 -05:00
coryb 5df5a39405 Merge pull request #60 from facundoolano/patch-1
fix typo in readme
2016-12-15 14:00:31 -08:00
Facundo Olano bd54ecc4f6 fix typo in readme 2016-12-15 15:18:11 -03:00
Cory Bennett 073e0bdcce Updated Changelog 2016-11-24 00:11:53 -08:00
Cory Bennett 1347ebe6b6 [#12] integrate with keyring for password storage and provide http basic auth
credentials for every request since most jira services have websudo enabled with
does not allow cookie based authentication
2016-11-23 23:43:29 -08:00
coryb c7565b08a1 Merge pull request #53 from jshirley/master
Add support for a unix socket proxy
2016-09-22 19:14:07 -07:00
Jay Shirley 01067e859c Cleaning up usage 2016-09-22 18:54:52 -07:00
Jay Shirley 8b174625d9 Update usage 2016-09-20 06:46:04 -07:00
Cory Bennett 8acc177627 use gopkg.in for links to maintain version compatibility 2016-09-18 12:44:00 -07:00
Cory Bennett 8d9db0e399 remove extraneous tee 2016-08-29 01:00:35 -07:00
Cory Bennett 998e4601c0 reduce mega verbosity 2016-08-29 01:00:01 -07:00
Cory Bennett 2df70edd00 up plan count for the sleep 2016-08-29 00:58:23 -07:00
Cory Bennett f73b3a5dc8 add missing src directory 2016-08-29 00:49:10 -07:00
Cory Bennett e74c94b030 move start.log to directory 2016-08-29 00:23:05 -07:00
Cory Bennett c18d2140e4 try travis cache and log atlas-run start output 2016-08-29 00:19:27 -07:00
Cory Bennett 2b56833c1c break setup to debug in travis 2016-08-29 00:04:21 -07:00
Cory Bennett fe69ad1cec wait for docker service to start? 2016-08-28 23:47:34 -07:00
Cory Bennett 3b18a1863c make maven cache directory local so we can ensure it is always there for travis testing 2016-08-28 23:36:37 -07:00
Cory Bennett d6d6578b11 make prove verbose 2016-08-28 23:32:03 -07:00
Cory Bennett 2b433dda40 golint 2016-08-28 23:30:25 -07:00
Cory Bennett 08a24e7dc3 require docker, run prove after build 2016-08-28 23:24:12 -07:00
Cory Bennett a746ddc6fb use altas-run to build jira test service in docker container 2016-08-28 23:10:54 -07:00
Cory Bennett e254435734 add "rank" command allow ordering backlog issues in agile projects 2016-08-26 19:44:38 -07:00
Jay Shirley 14298bfa52 Adding a unixproxy mechanism 2016-08-25 10:27:59 -07:00
Cory Bennett a3633aa537 Updated Changelog 2016-08-24 12:27:42 -07:00
coryb 2a8b6521dc Merge pull request #52 from dbrower/master
Prefer transition names which match exactly
2016-08-24 12:26:56 -07:00
Don Brower 4cc172de6b Prefer transition names which match exactly
Some transitions are substrings of other transitions, and the current
loop sometimes never chose the correct one. For example, it would never
choose "QA" if there were also a transtion "Deploy to QA". Ditto for
"Open" and "Reopen".
2016-08-24 14:36:23 -04:00
Cory Bennett 0dd6061992 update tempates to make them more readable with space trimming added to go-1.6 2016-08-21 23:51:45 -07:00
Cory Bennett 9d12f56332 add simple test for the "table" list output 2016-08-21 23:51:30 -07:00
Cory Bennett 824dd2f725 Updated Changelog 2016-08-21 14:39:54 -07:00
Cory Bennett 657bc59c8f make "worklogs" command print output through template
allow "add worklog" command to open edit template
2016-08-21 14:31:30 -07:00
Cory Bennett ec1914dfde remove extra newline at end of worklogs template 2016-08-21 14:31:11 -07:00
Cory Bennett a22911a3f9 adding worklog related templates 2016-08-21 14:27:50 -07:00
Cory Bennett 1f6191425f add vet and link make targets 2016-08-21 14:22:27 -07:00
Cory Bennett f896555299 Updated Changelog 2016-08-21 12:42:47 -07:00
Cory Bennett e0b2c2d240 add GoDoc badge 2016-08-21 10:24:07 -07:00
Cory Bennett a5cb93f112 add travis badge 2016-08-21 10:16:06 -07:00
Cory Bennett cbbf335439 add travis build 2016-08-21 00:46:03 -07:00
Cory Bennett 92b5e38912 update for golint 2016-08-21 00:45:07 -07:00
Cory Bennett 6260e4964f fix for go vet 2016-08-19 10:19:04 -07:00
Cory Bennett 485d65f12b [#51] add missing data directory with generated structs 2016-08-19 08:52:42 -07:00
46 changed files with 2311 additions and 563 deletions
+4 -1
View File
@@ -9,4 +9,7 @@ jira
jira.exe
schemas/*.json
t/issue.props
t/.jira.d/templates
t/.jira.d/templates
dist/
src/
t/.maven-cache
+21
View File
@@ -0,0 +1,21 @@
sudo: true
before_install:
- sudo apt-get update && sudo apt-get install -y libgnome-keyring-dev
language: go
go:
- 1.6
matrix:
fast_finish: true
script:
- make vet
- make lint
- make
- JIRACLOUD=1 ./t/100basic.t -w -a 2>&1
cache:
directories:
- t/.maven-cache
+32
View File
@@ -1,5 +1,37 @@
# Changelog
## 0.1.9 - 2016-12-18
* only warn about needing login when not already running the login command [Cory Bennett] [[6c24e55](https://github.com/Netflix-Skunkworks/go-jira/commit/6c24e55)]
* fix(http): Add proxy transport [William Hearn] [[4bd740b](https://github.com/Netflix-Skunkworks/go-jira/commit/4bd740b)]
* fix(http): Add proxy transport [William Hearn] [[2dff6c9](https://github.com/Netflix-Skunkworks/go-jira/commit/2dff6c9)]
## 0.1.8 - 2016-11-24
* [[#12](https://github.com/Netflix-Skunkworks/go-jira/issues/12)] integrate with keyring for password storage and provide http basic auth credentials for every request since most jira services have websudo enabled with does not allow cookie based authentication [Cory Bennett] [[b8a6e57](https://github.com/Netflix-Skunkworks/go-jira/commit/b8a6e57)]
* Cleaning up usage [Jay Shirley] [[8add52b](https://github.com/Netflix-Skunkworks/go-jira/commit/8add52b)]
* Update usage [Jay Shirley] [[b56e32a](https://github.com/Netflix-Skunkworks/go-jira/commit/b56e32a)]
* use gopkg.in for links to maintain version compatibility [Cory Bennett] [[1414d1f](https://github.com/Netflix-Skunkworks/go-jira/commit/1414d1f)]
* golint [Cory Bennett] [[44cdebf](https://github.com/Netflix-Skunkworks/go-jira/commit/44cdebf)]
* add "rank" command allow ordering backlog issues in agile projects [Cory Bennett] [[e4cc9c6](https://github.com/Netflix-Skunkworks/go-jira/commit/e4cc9c6)]
* Adding a unixproxy mechanism [Jay Shirley] [[5b9c0dd](https://github.com/Netflix-Skunkworks/go-jira/commit/5b9c0dd)]
## 0.1.7 - 2016-08-24
* Prefer transition names which match exactly [Don Brower] [[e40f9c1](https://github.com/Netflix-Skunkworks/go-jira/commit/e40f9c1)]
* update tempates to make them more readable with space trimming added to go-1.6 [Cory Bennett] [[693b3e4](https://github.com/Netflix-Skunkworks/go-jira/commit/693b3e4)]
## 0.1.6 - 2016-08-21
* make "worklogs" command print output through template allow "add worklog" command to open edit template [Cory Bennett] [[cc3fbee](https://github.com/Netflix-Skunkworks/go-jira/commit/cc3fbee)]
* remove extra newline at end of worklogs template [Cory Bennett] [[d08ef15](https://github.com/Netflix-Skunkworks/go-jira/commit/d08ef15)]
* adding worklog related templates [Cory Bennett] [[ab1cd27](https://github.com/Netflix-Skunkworks/go-jira/commit/ab1cd27)]
## 0.1.5 - 2016-08-21
* update for golint [Cory Bennett] [[5a4e17c](https://github.com/Netflix-Skunkworks/go-jira/commit/5a4e17c)]
* fix for go vet [Cory Bennett] [[355fb42](https://github.com/Netflix-Skunkworks/go-jira/commit/355fb42)]
## 0.1.4 - 2016-08-12
* when running "dups" on a Process Management Project type, you have to start/stop the task to resolve it [Cory Bennett] [[2c91905](https://github.com/Netflix-Skunkworks/go-jira/commit/2c91905)]
+34 -16
View File
@@ -1,10 +1,10 @@
PLATFORMS= \
freebsd-amd64 \
linux-386 \
linux-amd64 \
windows-386 \
windows-amd64 \
darwin-amd64 \
freebsd/amd64 \
linux/386 \
linux/amd64 \
windows/386 \
windows/amd64 \
darwin/amd64 \
$(NULL)
# freebsd-386 \
@@ -45,7 +45,7 @@ else
GOBUILD=go build -v -ldflags "$(LDFLAGS) -s"
endif
build: src/github.com/Netflix-Skunkworks/go-jira
build: src/gopkg.in/Netflix-Skunkworks/go-jira.v0
$(GOBUILD) -o '$(BIN)' main/main.go
debug:
@@ -53,23 +53,41 @@ debug:
src/%:
mkdir -p $(@D)
test -L $@ || ln -sf '$(GOPATH)' $@
test -L $@ || ln -sf '../../..' $@
go get -v $* $*/main
vet:
@go vet .
@go vet ./data
@go vet ./main
lint:
@go get github.com/golang/lint/golint
@./bin/golint .
@./bin/golint ./data
@./bin/golint ./main
cross-setup:
for p in $(PLATFORMS); do \
echo Building for $$p"; \
cd $(GOROOT)/src && sudo GOROOT_BOOTSTRAP=$(GOROOT) GOOS=$${p/-*/} GOARCH=$${p/*-/} bash ./make.bash --no-clean; \
done
all:
rm -rf $(DIST); \
mkdir -p $(DIST); \
for p in $(PLATFORMS); do \
echo "Building for $$p"; \
${MAKE} build GOOS=$${p/-*/} GOARCH=$${p/*-/} BIN=$(DIST)/$(NAME)-$$p; \
done
for x in $(DIST)/jira-windows-*; do mv $$x $$x.exe; done
all: src/gopkg.in/Netflix-Skunkworks/go-jira.v0
docker pull karalabe/xgo-latest
rm -rf dist
mkdir -p dist
docker run --rm -e EXT_GOPATH=/gopath -v $$(pwd):/gopath -e TARGETS="$(PLATFORMS)" -v $$(pwd)/dist:/build karalabe/xgo-latest gopkg.in/Netflix-Skunkworks/go-jira.v0/main
cd $(DIST) && for x in main-*; do mv $$x jira-$$(echo $$x | cut -c 6-); done
# all:
# rm -rf $(DIST); \
# mkdir -p $(DIST); \
# for p in $(PLATFORMS); do \
# echo "Building for $$p"; \
# ${MAKE} build GOOS=$${p/-*/} GOARCH=$${p/*-/} BIN=$(DIST)/$(NAME)-$$p; \
# done
# for x in $(DIST)/jira-windows-*; do mv $$x $$x.exe; done
fmt:
gofmt -s -w main/*.go *.go
+4 -1
View File
@@ -1,4 +1,7 @@
[![Join the chat at https://gitter.im/go-jira-cli/help](https://badges.gitter.im/go-jira-cli/help.svg)](https://gitter.im/go-jira-cli/help?utm_source=badge&utm_medium=badge&utm_content=badge)
[![Build Status](https://travis-ci.org/Netflix-Skunkworks/go-jira.svg?branch=master)](https://travis-ci.org/Netflix-Skunkworks/go-jira)
[![GoDoc](https://godoc.org/gopkg.in/Netflix-Skunkworks/go-jira.v0?status.png)](https://godoc.org/gopkg.in/Netflix-Skunkworks/go-jira.v0)
# go-jira
simple command line client for Atlassian's Jira service written in Go
@@ -35,7 +38,7 @@ jira trans close GOJIRA-321 -o resolution="Won't Fix" --noedit
# same as above
jira close GOJIRA-321 -o resolution="Won't Fix"
jira repopen GOJIRA-321 -m "reopening" # reopen issue
jira reopen GOJIRA-321 -m "reopening" # reopen issue
jira watch GOJIRA-321 # add self as watcher to the issue
+180 -90
View File
@@ -5,9 +5,6 @@ import (
"crypto/tls"
"encoding/json"
"fmt"
"github.com/kballard/go-shellquote"
"gopkg.in/coryb/yaml.v2"
"gopkg.in/op/go-logging.v1"
"io/ioutil"
"net/http"
"net/http/cookiejar"
@@ -20,13 +17,20 @@ import (
"runtime"
"strings"
"time"
"github.com/kballard/go-shellquote"
"github.com/tmc/keyring"
"gopkg.in/coryb/yaml.v2"
"gopkg.in/op/go-logging.v1"
)
var (
log = logging.MustGetLogger("jira")
log = logging.MustGetLogger("jira")
// VERSION is the go-jira library version
VERSION string
)
// Cli is go-jira client object
type Cli struct {
endpoint *url.URL
opts map[string]interface{}
@@ -34,32 +38,43 @@ type Cli struct {
ua *http.Client
}
// New creates go-jira client object
func New(opts map[string]interface{}) *Cli {
homedir := homedir()
cookieJar, _ := cookiejar.New(nil)
endpoint, _ := opts["endpoint"].(string)
url, _ := url.Parse(strings.TrimRight(endpoint, "/"))
transport := &http.Transport{
TLSClientConfig: &tls.Config{},
}
if project, ok := opts["project"].(string); ok {
opts["project"] = strings.ToUpper(project)
}
if insecureSkipVerify, ok := opts["insecure"].(bool); ok {
transport.TLSClientConfig.InsecureSkipVerify = insecureSkipVerify
var ua *http.Client
if unixProxyPath, ok := opts["unixproxy"].(string); ok {
ua = &http.Client{
Jar: cookieJar,
Transport: UnixProxy(unixProxyPath),
}
} else {
transport := &http.Transport{
Proxy: http.ProxyFromEnvironment,
TLSClientConfig: &tls.Config{},
}
if insecureSkipVerify, ok := opts["insecure"].(bool); ok {
transport.TLSClientConfig.InsecureSkipVerify = insecureSkipVerify
}
ua = &http.Client{
Jar: cookieJar,
Transport: transport,
}
}
cli := &Cli{
endpoint: url,
opts: opts,
cookieFile: filepath.Join(homedir, ".jira.d", "cookies.js"),
ua: &http.Client{
Jar: cookieJar,
Transport: transport,
},
ua: ua,
}
cli.ua.Jar.SetCookies(url, cli.loadCookies())
@@ -120,7 +135,7 @@ func (c *Cli) loadCookies() []*http.Cookie {
log.Errorf("Failed to open %s: %s", c.cookieFile, err)
panic(err)
}
cookies := make([]*http.Cookie, 0)
cookies := []*http.Cookie{}
err = json.Unmarshal(bytes, &cookies)
if err != nil {
log.Errorf("Failed to parse json from file %s: %s", c.cookieFile, err)
@@ -140,44 +155,42 @@ func (c *Cli) put(uri string, content string) (*http.Response, error) {
return c.makeRequestWithContent("PUT", uri, content)
}
func (c *Cli) delete(uri string) (*http.Response, error) {
func (c *Cli) delete(uri string) (resp *http.Response, err error) {
method := "DELETE"
req, _ := http.NewRequest(method, uri, nil)
log.Infof("%s %s", req.Method, req.URL.String())
if resp, err := c.makeRequest(req); err != nil {
if resp, err = c.makeRequest(req); err != nil {
return nil, err
} else {
if resp.StatusCode == 401 {
if err := c.CmdLogin(); err != nil {
return nil, err
}
req, _ = http.NewRequest(method, uri, nil)
return c.makeRequest(req)
}
return resp, err
}
if resp.StatusCode == 401 {
if err = c.CmdLogin(); err != nil {
return nil, err
}
req, _ = http.NewRequest(method, uri, nil)
return c.makeRequest(req)
}
return resp, err
}
func (c *Cli) makeRequestWithContent(method string, uri string, content string) (*http.Response, error) {
func (c *Cli) makeRequestWithContent(method string, uri string, content string) (resp *http.Response, err error) {
buffer := bytes.NewBufferString(content)
req, _ := http.NewRequest(method, uri, buffer)
log.Infof("%s %s", req.Method, req.URL.String())
if resp, err := c.makeRequest(req); err != nil {
if resp, err = c.makeRequest(req); err != nil {
return nil, err
} else {
if resp.StatusCode == 401 {
if err := c.CmdLogin(); err != nil {
return nil, err
}
req, _ = http.NewRequest(method, uri, bytes.NewBufferString(content))
return c.makeRequest(req)
}
return resp, err
}
if resp.StatusCode == 401 {
if err = c.CmdLogin(); err != nil {
return nil, err
}
req, _ = http.NewRequest(method, uri, bytes.NewBufferString(content))
return c.makeRequest(req)
}
return resp, err
}
func (c *Cli) get(uri string) (*http.Response, error) {
func (c *Cli) get(uri string) (resp *http.Response, err error) {
req, _ := http.NewRequest("GET", uri, nil)
log.Infof("%s %s", req.Method, req.URL.String())
if log.IsEnabledFor(logging.DEBUG) {
@@ -186,23 +199,32 @@ func (c *Cli) get(uri string) (*http.Response, error) {
log.Debugf("%s", logBuffer)
}
if resp, err := c.makeRequest(req); err != nil {
if resp, err = c.makeRequest(req); err != nil {
return nil, err
} else {
if resp.StatusCode == 401 {
if err := c.CmdLogin(); err != nil {
return nil, err
}
return c.makeRequest(req)
}
return resp, err
}
if resp.StatusCode == 401 {
if err := c.CmdLogin(); err != nil {
return nil, err
}
return c.makeRequest(req)
}
return resp, err
}
func (c *Cli) makeRequest(req *http.Request) (resp *http.Response, err error) {
req.Header.Set("Accept", "application/json")
req.Header.Set("Content-Type", "application/json")
if val, ok := c.opts["password-keyring"].(bool); ok && val && !strings.HasSuffix(req.URL.Path, "/rest/auth/1/session") {
user, _ := c.opts["user"].(string)
password, _ := keyring.Get("go-jira", user)
if password == "" {
log.Warning("No password for user %s in keyring, please run the 'login' command first", user)
} else {
req.SetBasicAuth(user, password)
}
}
// this is actually done in http.send but doing it
// here so we can log it in DumpRequest for debugging
for _, cookie := range c.ua.Jar.Cookies(req.URL) {
@@ -217,18 +239,17 @@ func (c *Cli) makeRequest(req *http.Request) (resp *http.Response, err error) {
if resp, err = c.ua.Do(req); err != nil {
log.Errorf("Failed to %s %s: %s", req.Method, req.URL.String(), err)
return nil, err
} else {
if resp.StatusCode < 200 || resp.StatusCode >= 300 && resp.StatusCode != 401 {
log.Errorf("response status: %s", resp.Status)
}
}
if resp.StatusCode < 200 || resp.StatusCode >= 300 && resp.StatusCode != 401 {
log.Errorf("response status: %s", resp.Status)
}
runtime.SetFinalizer(resp, func(r *http.Response) {
r.Body.Close()
})
runtime.SetFinalizer(resp, func(r *http.Response) {
r.Body.Close()
})
if _, ok := resp.Header["Set-Cookie"]; ok {
c.saveCookies(resp)
}
if _, ok := resp.Header["Set-Cookie"]; ok {
c.saveCookies(resp)
}
if log.IsEnabledFor(logging.DEBUG) {
out, _ := httputil.DumpResponse(resp, true)
@@ -237,6 +258,7 @@ func (c *Cli) makeRequest(req *http.Request) (resp *http.Response, err error) {
return resp, nil
}
// GetTemplate will return the text/template for the given command name
func (c *Cli) GetTemplate(name string) string {
return c.getTemplate(name)
}
@@ -256,10 +278,9 @@ func (c *Cli) getTemplate(name string) string {
if override, ok := c.opts["template"].(string); ok {
if _, err := os.Stat(override); err == nil {
return readFile(override)
} else {
if t := getLookedUpTemplate(override, all_templates[override]); t != "" {
return t
}
}
if t := getLookedUpTemplate(override, allTemplates[override]); t != "" {
return t
}
}
// create-bug etc are special, if we dont find it in the path
@@ -267,9 +288,11 @@ func (c *Cli) getTemplate(name string) string {
if strings.HasPrefix(name, "create-") {
return getLookedUpTemplate(name, c.getTemplate("create"))
}
return getLookedUpTemplate(name, all_templates[name])
return getLookedUpTemplate(name, allTemplates[name])
}
// NoChangesFound is an error returned from when editing templates
// and no modifications were made while editing
type NoChangesFound struct{}
func (f NoChangesFound) Error() string {
@@ -360,27 +383,27 @@ func (c *Cli) editTemplate(template string, tmpFilePrefix string, templateData m
}
edited := make(map[string]interface{})
if fh, err := ioutil.ReadFile(tmpFileName); err != nil {
var data []byte
if data, err = ioutil.ReadFile(tmpFileName); err != nil {
log.Errorf("Failed to read tmpfile %s: %s", tmpFileName, err)
if editing && promptYN("edit again?", true) {
continue
}
return err
} else {
if err := yaml.Unmarshal(fh, &edited); err != nil {
log.Errorf("Failed to parse YAML: %s", err)
if editing && promptYN("edit again?", true) {
continue
}
return err
}
if err := yaml.Unmarshal(data, &edited); err != nil {
log.Errorf("Failed to parse YAML: %s", err)
if editing && promptYN("edit again?", true) {
continue
}
return err
}
if fixed, err := yamlFixup(edited); err != nil {
var fixed interface{}
if fixed, err = yamlFixup(edited); err != nil {
return err
} else {
edited = fixed.(map[string]interface{})
}
edited = fixed.(map[string]interface{})
// if you want to abort editing a jira issue then
// you can add the "abort: true" flag to the document
@@ -422,6 +445,7 @@ func (c *Cli) editTemplate(template string, tmpFilePrefix string, templateData m
return nil
}
// Browse will open up your default browser to the provided issue
func (c *Cli) Browse(issue string) error {
if val, ok := c.opts["browse"].(bool); ok && val {
if runtime.GOOS == "darwin" {
@@ -433,6 +457,7 @@ func (c *Cli) Browse(issue string) error {
return nil
}
// SaveData will write out the yaml formated --saveFile file with provided data
func (c *Cli) SaveData(data interface{}) error {
if val, ok := c.opts["saveFile"].(string); ok && val != "" {
yamlWrite(val, data)
@@ -440,33 +465,49 @@ func (c *Cli) SaveData(data interface{}) error {
return nil
}
// ViewIssueWorkLogs gets the worklog data for the given issue
func (c *Cli) ViewIssueWorkLogs(issue string) (interface{}, error) {
uri := fmt.Sprintf("%s/rest/api/2/issue/%s/worklog", c.endpoint, issue)
data, err := responseToJSON(c.get(uri))
if err != nil {
return nil, err
}
return data, nil
}
// ViewIssue will return the details for the given issue id
func (c *Cli) ViewIssue(issue string) (interface{}, error) {
uri := fmt.Sprintf("%s/rest/api/2/issue/%s", c.endpoint, issue)
if x := c.expansions(); len(x) > 0 {
uri = fmt.Sprintf("%s?expand=%s", uri, strings.Join(x, ","))
}
data, err := responseToJson(c.get(uri))
data, err := responseToJSON(c.get(uri))
if err != nil {
return nil, err
} else {
return data, nil
}
return data, nil
}
// FindIssues will return a list of issues that match the given options.
// If the "query" option is undefined it will generate a JQL query
// using any/all of the provide options: project, component, assignee,
// issuetype, watcher, reporter, sort
// Further it will restrict the fields being extracted from the jira
// response with the 'queryfields' option
func (c *Cli) FindIssues() (interface{}, error) {
var query string
var ok bool
// project = BAKERY and status not in (Resolved, Closed)
if query, ok = c.opts["query"].(string); !ok {
qbuff := bytes.NewBufferString("resolution = unresolved")
if project, ok := c.opts["project"]; !ok {
var project string
if project, ok = c.opts["project"].(string); !ok {
err := fmt.Errorf("Missing required arguments, either 'query' or 'project' are required")
log.Errorf("%s", err)
return nil, err
} else {
qbuff.WriteString(fmt.Sprintf(" AND project = '%s'", project))
}
qbuff.WriteString(fmt.Sprintf(" AND project = '%s'", project))
if component, ok := c.opts["component"]; ok {
qbuff.WriteString(fmt.Sprintf(" AND component = '%s'", component))
@@ -495,11 +536,9 @@ func (c *Cli) FindIssues() (interface{}, error) {
query = qbuff.String()
}
fields := make([]string, 0)
fields := []string{"summary"}
if qf, ok := c.opts["queryfields"].(string); ok {
fields = strings.Split(qf, ",")
} else {
fields = append(fields, "summary")
}
json, err := jsonEncode(map[string]interface{}{
@@ -514,13 +553,64 @@ func (c *Cli) FindIssues() (interface{}, error) {
}
uri := fmt.Sprintf("%s/rest/api/2/search", c.endpoint)
if data, err := responseToJson(c.post(uri, json)); err != nil {
var data interface{}
if data, err = responseToJSON(c.post(uri, json)); err != nil {
return nil, err
} else {
return data, nil
}
return data, nil
}
// RankOrder type used to specify before/after ranking arguments to RankIssue
type RankOrder int
const (
// RANKBEFORE should be used to rank issue before the target issue
RANKBEFORE RankOrder = iota
// RANKAFTER should be used to rank issue after the target issue
RANKAFTER RankOrder = iota
)
// RankIssue will modify issue to have rank before or after the target issue
func (c *Cli) RankIssue(issue, target string, order RankOrder) error {
type RankRequest struct {
Issues []string `json:"issues"`
Before string `json:"rankBeforeIssue,omitempty"`
After string `json:"rankAfterIssue,omitempty"`
}
req := &RankRequest{
Issues: []string{
issue,
},
}
if order == RANKBEFORE {
req.Before = target
} else {
req.After = target
}
json, err := jsonEncode(req)
if err != nil {
return err
}
uri := fmt.Sprintf("%s/rest/agile/1.0/issue/rank", c.endpoint)
if c.getOptBool("dryrun", false) {
log.Debugf("PUT: %s", json)
log.Debugf("Dryrun mode, skipping PUT")
return nil
}
resp, err := c.put(uri, json)
if err != nil {
return err
}
if resp.StatusCode != 204 {
return fmt.Errorf("failed to modify issue rank: %s", resp.Status)
}
return nil
}
// GetOptString will extract the string from the Cli object options
// otherwise return the provided default
func (c *Cli) GetOptString(optName string, dflt string) string {
return c.getOptString(optName, dflt)
}
@@ -528,11 +618,12 @@ func (c *Cli) GetOptString(optName string, dflt string) string {
func (c *Cli) getOptString(optName string, dflt string) string {
if val, ok := c.opts[optName].(string); ok {
return val
} else {
return dflt
}
return dflt
}
// GetOptBool will extract the boolean value from the Client object options
// otherwise return the provided default\
func (c *Cli) GetOptBool(optName string, dflt bool) bool {
return c.getOptBool(optName, dflt)
}
@@ -540,14 +631,13 @@ func (c *Cli) GetOptBool(optName string, dflt bool) bool {
func (c *Cli) getOptBool(optName string, dflt bool) bool {
if val, ok := c.opts[optName].(bool); ok {
return val
} else {
return dflt
}
return dflt
}
// expansions returns a comma-separated list of values for field expansion
func (c *Cli) expansions() []string {
expansions := make([]string, 0)
var expansions []string
if x, ok := c.opts["expand"].(string); ok {
expansions = strings.Split(x, ",")
}
+240 -120
View File
@@ -3,21 +3,23 @@ package jira
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"github.com/Netflix-Skunkworks/go-jira/data"
"github.com/howeyc/gopass"
"io/ioutil"
"net/http"
"net/url"
"os"
"strings"
"github.com/howeyc/gopass"
"github.com/tmc/keyring"
"gopkg.in/Netflix-Skunkworks/go-jira.v0/data"
// "github.com/kr/pretty"
)
// CmdLogin will attempt to login into jira server
func (c *Cli) CmdLogin() error {
uri := fmt.Sprintf("%s/rest/auth/1/session", c.endpoint)
for true {
for {
req, _ := http.NewRequest("GET", uri, nil)
user, _ := c.opts["user"].(string)
@@ -29,60 +31,71 @@ func (c *Cli) CmdLogin() error {
passwd := string(pw)
req.SetBasicAuth(user, passwd)
if resp, err := c.makeRequest(req); err != nil {
resp, err := c.makeRequest(req)
if err != nil {
return err
} else {
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
if reason := resp.Header.Get("X-Authentication-Denied-Reason"); reason != "" {
err := fmt.Errorf("Authenticaion Failed: %s", reason)
log.Errorf("%s", err)
return err
}
err := fmt.Errorf("Authentication Failed: Unknown Reason")
}
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
if reason := resp.Header.Get("X-Authentication-Denied-Reason"); reason != "" {
err := fmt.Errorf("Authenticaion Failed: %s", reason)
log.Errorf("%s", err)
return err
}
err := fmt.Errorf("Authentication Failed: Unknown Reason")
log.Errorf("%s", err)
return err
} else if resp.StatusCode == 200 {
// https://confluence.atlassian.com/display/JIRA043/JIRA+REST+API+%28Alpha%29+Tutorial#JIRARESTAPI%28Alpha%29Tutorial-CAPTCHAs
// probably bad password, try again
if reason := resp.Header.Get("X-Seraph-Loginreason"); reason == "AUTHENTICATION_DENIED" {
log.Warning("Authentication Failed: %s", reason)
continue
}
} else {
log.Warning("Login failed")
} else if resp.StatusCode == 200 {
// https://confluence.atlassian.com/display/JIRA043/JIRA+REST+API+%28Alpha%29+Tutorial#JIRARESTAPI%28Alpha%29Tutorial-CAPTCHAs
// probably bad password, try again
if reason := resp.Header.Get("X-Seraph-Loginreason"); reason == "AUTHENTICATION_DENIED" {
log.Warning("Authentication Failed: %s", reason)
continue
}
if val, ok := c.opts["password-keyring"].(bool); ok && val {
// save password in keychain so that it can be used for subsequent http requests
err := keyring.Set("go-jira", user, passwd)
if err != nil {
log.Errorf("Failed to set password in keyring: %s", err)
return err
}
}
break
} else {
log.Warning("Login failed")
continue
}
return nil
}
return nil
}
// CmdLogout will close any active sessions
func (c *Cli) CmdLogout() error {
uri := fmt.Sprintf("%s/rest/auth/1/session", c.endpoint)
req, _ := http.NewRequest("DELETE", uri, nil)
if resp, err := c.makeRequest(req); err != nil {
resp, err := c.makeRequest(req)
if err != nil {
return err
}
if resp.StatusCode == 401 || resp.StatusCode == 204 {
// 401 == no active session
// 204 == successfully logged out
} else {
if resp.StatusCode == 401 || resp.StatusCode == 204 {
// 401 == no active session
// 204 == successfully logged out
} else {
err := fmt.Errorf("Failed to Logout: %s", err)
return err
}
err := fmt.Errorf("Failed to Logout: %s", err)
return err
}
log.Notice("OK")
return nil
}
// CmdFields will send data from /rest/api/2/field API to "fields" template
func (c *Cli) CmdFields() error {
log.Debugf("fields called")
uri := fmt.Sprintf("%s/rest/api/2/field", c.endpoint)
data, err := responseToJson(c.get(uri))
data, err := responseToJSON(c.get(uri))
if err != nil {
return err
}
@@ -90,15 +103,17 @@ func (c *Cli) CmdFields() error {
return runTemplate(c.getTemplate("fields"), data, nil)
}
// CmdList will query jira and send data to "list" template
func (c *Cli) CmdList() error {
log.Debugf("list called")
if data, err := c.FindIssues(); err != nil {
data, err := c.FindIssues()
if err != nil {
return err
} else {
return runTemplate(c.getTemplate("list"), data, nil)
}
return runTemplate(c.getTemplate("list"), data, nil)
}
// CmdView will get issue data and send to "view" template
func (c *Cli) CmdView(issue string) error {
log.Debugf("view called")
c.Browse(issue)
@@ -109,23 +124,85 @@ func (c *Cli) CmdView(issue string) error {
return runTemplate(c.getTemplate("view"), data, nil)
}
// CmdWorklogs will get worklog data for given issue and sent to the "worklogs" template
func (c *Cli) CmdWorklogs(issue string) error {
log.Debugf("worklogs called")
c.Browse(issue)
data, err := c.ViewIssueWorkLogs(issue)
if err != nil {
return err
}
return runTemplate(c.getTemplate("worklogs"), data, nil)
}
// CmdWorklog will attempt to add (action=add) a worklog to the given issue.
// It will spawn the editor (unless --noedit isused) and post edited YAML
// content as JSON to the worklog endpoint
func (c *Cli) CmdWorklog(action string, issue string) error {
log.Debugf("%s worklog called", action)
c.Browse(issue)
if action == "add" {
uri := fmt.Sprintf("%s/rest/api/2/issue/%s/worklog", c.endpoint, issue)
worklogData := map[string]interface{}{
"issue": issue,
"comment": c.opts["comment"],
}
if v, ok := c.opts["time-spent"].(string); ok {
worklogData["timeSpent"] = v
}
return c.editTemplate(
c.getTemplate("worklog"),
fmt.Sprintf("%s-worklog-", issue),
worklogData,
func(json string) error {
if c.getOptBool("dryrun", false) {
log.Debugf("POST: %s", json)
log.Debugf("Dryrun mode, skipping POST")
return nil
}
resp, err := c.post(uri, json)
if err != nil {
return err
}
if resp.StatusCode == 201 {
c.Browse(issue)
if !c.opts["quiet"].(bool) {
fmt.Printf("OK %s %s/browse/%s\n", issue, c.endpoint, issue)
}
return nil
}
logBuffer := bytes.NewBuffer(make([]byte, 0))
resp.Write(logBuffer)
err = fmt.Errorf("Unexpected Response From POST")
log.Errorf("%s:\n%s", err, logBuffer)
return err
},
)
}
return nil
}
// CmdEdit will populate "edit" template with issue data and issue "editmeta" data.
// Then will parse yaml template and submit data to jira.
func (c *Cli) CmdEdit(issue string) error {
log.Debugf("edit called")
uri := fmt.Sprintf("%s/rest/api/2/issue/%s/editmeta", c.endpoint, issue)
editmeta, err := responseToJson(c.get(uri))
editmeta, err := responseToJSON(c.get(uri))
if err != nil {
return err
}
uri = fmt.Sprintf("%s/rest/api/2/issue/%s", c.endpoint, issue)
var issueData map[string]interface{}
if data, err := responseToJson(c.get(uri)); err != nil {
data, err := responseToJSON(c.get(uri))
if err != nil {
return err
} else {
issueData = data.(map[string]interface{})
}
issueData := data.(map[string]interface{})
issueData["meta"] = editmeta.(map[string]interface{})
issueData["overrides"] = c.opts
@@ -150,22 +227,22 @@ func (c *Cli) CmdEdit(issue string) error {
fmt.Printf("OK %s %s/browse/%s\n", issueData["key"], c.endpoint, issueData["key"])
}
return nil
} else {
logBuffer := bytes.NewBuffer(make([]byte, 0))
resp.Write(logBuffer)
err := fmt.Errorf("Unexpected Response From PUT")
log.Errorf("%s:\n%s", err, logBuffer)
return err
}
logBuffer := bytes.NewBuffer(make([]byte, 0))
resp.Write(logBuffer)
err = fmt.Errorf("Unexpected Response From PUT")
log.Errorf("%s:\n%s", err, logBuffer)
return err
},
)
}
// CmdEditMeta will send issue 'edit' metadata to the "editmeta" template
func (c *Cli) CmdEditMeta(issue string) error {
log.Debugf("editMeta called")
c.Browse(issue)
uri := fmt.Sprintf("%s/rest/api/2/issue/%s/editmeta", c.endpoint, issue)
data, err := responseToJson(c.get(uri))
data, err := responseToJSON(c.get(uri))
if err != nil {
return err
}
@@ -173,11 +250,12 @@ func (c *Cli) CmdEditMeta(issue string) error {
return runTemplate(c.getTemplate("editmeta"), data, nil)
}
// CmdTransitionMeta will send available transition metadata to the "transmeta" template
func (c *Cli) CmdTransitionMeta(issue string) error {
log.Debugf("tranisionMeta called")
c.Browse(issue)
uri := fmt.Sprintf("%s/rest/api/2/issue/%s/transitions?expand=transitions.fields", c.endpoint, issue)
data, err := responseToJson(c.get(uri))
data, err := responseToJSON(c.get(uri))
if err != nil {
return err
}
@@ -185,11 +263,12 @@ func (c *Cli) CmdTransitionMeta(issue string) error {
return runTemplate(c.getTemplate("transmeta"), data, nil)
}
// CmdIssueTypes will send issue 'create' metadata to the 'issuetypes'
func (c *Cli) CmdIssueTypes() error {
project := c.opts["project"].(string)
log.Debugf("issueTypes called")
uri := fmt.Sprintf("%s/rest/api/2/issue/createmeta?projectKeys=%s", c.endpoint, project)
data, err := responseToJson(c.get(uri))
data, err := responseToJSON(c.get(uri))
if err != nil {
return err
}
@@ -200,7 +279,7 @@ func (c *Cli) CmdIssueTypes() error {
func (c *Cli) defaultIssueType() string {
project := c.opts["project"].(string)
uri := fmt.Sprintf("%s/rest/api/2/issue/createmeta?projectKeys=%s", c.endpoint, project)
data, _ := responseToJson(c.get(uri))
data, _ := responseToJSON(c.get(uri))
issueTypeNames := make(map[string]bool)
if data, ok := data.(map[string]interface{}); ok {
@@ -226,6 +305,7 @@ func (c *Cli) defaultIssueType() string {
return ""
}
// CmdCreateMeta sends the 'create' metadata for the given project & issuetype and sends it to the 'createmeta' template
func (c *Cli) CmdCreateMeta() error {
project := c.opts["project"].(string)
issuetype := c.getOptString("issuetype", "")
@@ -235,7 +315,7 @@ func (c *Cli) CmdCreateMeta() error {
log.Debugf("createMeta called")
uri := fmt.Sprintf("%s/rest/api/2/issue/createmeta?projectKeys=%s&issuetypeNames=%s&expand=projects.issuetypes.fields", c.endpoint, project, url.QueryEscape(issuetype))
data, err := responseToJson(c.get(uri))
data, err := responseToJSON(c.get(uri))
if err != nil {
return err
}
@@ -254,16 +334,18 @@ func (c *Cli) CmdCreateMeta() error {
return runTemplate(c.getTemplate("createmeta"), data, nil)
}
// CmdComponents sends component data for given project and sends to the "components" template
func (c *Cli) CmdComponents(project string) error {
log.Debugf("Components called")
uri := fmt.Sprintf("%s/rest/api/2/project/%s/components", c.endpoint, project)
data, err := responseToJson(c.get(uri))
data, err := responseToJSON(c.get(uri))
if err != nil {
return err
}
return runTemplate(c.getTemplate("components"), data, nil)
}
// ValidTransitions will return a list of valid transitions for given issue.
func (c *Cli) ValidTransitions(issue string) (jiradata.Transitions, error) {
uri := fmt.Sprintf("%s/rest/api/2/issue/%s/transitions?expand=transitions.fields", c.endpoint, issue)
resp, err := c.get(uri)
@@ -284,18 +366,21 @@ func (c *Cli) ValidTransitions(issue string) (jiradata.Transitions, error) {
return transMeta.Transitions, nil
}
// CmdTransitions sends valid transtions for given issue to the "transitions" template
func (c *Cli) CmdTransitions(issue string) error {
log.Debugf("Transitions called")
// FIXME this should just call ValidTransitions then pass that data to templates
c.Browse(issue)
uri := fmt.Sprintf("%s/rest/api/2/issue/%s/transitions", c.endpoint, issue)
data, err := responseToJson(c.get(uri))
data, err := responseToJSON(c.get(uri))
if err != nil {
return err
}
return runTemplate(c.getTemplate("transitions"), data, nil)
}
// CmdCreate sends the create-metadata to the "create" template for editing, then
// will parse the edited document as YAML and submit the document to jira.
func (c *Cli) CmdCreate() error {
log.Debugf("create called")
project := c.opts["project"].(string)
@@ -305,7 +390,7 @@ func (c *Cli) CmdCreate() error {
}
uri := fmt.Sprintf("%s/rest/api/2/issue/createmeta?projectKeys=%s&issuetypeNames=%s&expand=projects.issuetypes.fields", c.endpoint, project, url.QueryEscape(issuetype))
data, err := responseToJson(c.get(uri))
data, err := responseToJSON(c.get(uri))
if err != nil {
return err
}
@@ -349,43 +434,43 @@ func (c *Cli) CmdCreate() error {
if resp.StatusCode == 201 {
// response: {"id":"410836","key":"PROJ-238","self":"https://jira/rest/api/2/issue/410836"}
if json, err := responseToJson(resp, nil); err != nil {
json, err := responseToJSON(resp, nil)
if err != nil {
return err
} else {
key := json.(map[string]interface{})["key"].(string)
link := fmt.Sprintf("%s/browse/%s", 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)
}
}
key := json.(map[string]interface{})["key"].(string)
link := fmt.Sprintf("%s/browse/%s", 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
} else {
logBuffer := bytes.NewBuffer(make([]byte, 0))
resp.Write(logBuffer)
err := fmt.Errorf("Unexpected Response From POST")
log.Errorf("%s:\n%s", err, logBuffer)
return err
}
logBuffer := bytes.NewBuffer(make([]byte, 0))
resp.Write(logBuffer)
err = fmt.Errorf("Unexpected Response From POST")
log.Errorf("%s:\n%s", err, logBuffer)
return err
},
)
return nil
}
// CmdIssueLinkTypes will send the issue link type data to the "issuelinktypes" template.
func (c *Cli) CmdIssueLinkTypes() error {
log.Debugf("Transitions called")
uri := fmt.Sprintf("%s/rest/api/2/issueLinkType", c.endpoint)
data, err := responseToJson(c.get(uri))
data, err := responseToJSON(c.get(uri))
if err != nil {
return err
}
return runTemplate(c.getTemplate("issuelinktypes"), data, nil)
}
// CmdBlocks will update the given issue as being "blocked" by the given blocker
func (c *Cli) CmdBlocks(blocker string, issue string) error {
log.Debugf("blocks called")
@@ -429,6 +514,8 @@ func (c *Cli) CmdBlocks(blocker string, issue string) error {
return nil
}
// CmdDups will update the given issue as being a duplicate by the given dup issue
// and will attempt to resolve the dup issue
func (c *Cli) CmdDups(duplicate string, issue string) error {
log.Debugf("dups called")
@@ -472,6 +559,8 @@ func (c *Cli) CmdDups(duplicate string, issue string) error {
return nil
}
// CmdWatch will add the given watcher to the issue (or remove the watcher
// given the 'remove' flag)
func (c *Cli) CmdWatch(issue string, watcher string, remove bool) error {
log.Debugf("watch called: watcher: %q, remove: %n", watcher, remove)
@@ -522,6 +611,7 @@ func (c *Cli) CmdWatch(issue string, watcher string, remove bool) error {
return nil
}
// CmdVote will add or remove a vote on an issue
func (c *Cli) CmdVote(issue string, up bool) error {
log.Debugf("vote called, with up: %n", up)
@@ -565,16 +655,41 @@ func (c *Cli) CmdVote(issue string, up bool) error {
return nil
}
// CmdRankAfter rank issue after target issue
func (c *Cli) CmdRankAfter(issue, after string) error {
err := c.RankIssue(issue, after, RANKAFTER)
if err != nil {
return nil
}
if !c.opts["quiet"].(bool) {
fmt.Printf("OK %s %s/browse/%s\n", issue, c.endpoint, issue)
}
return nil
}
// CmdRankBefore rank issue before target issue
func (c *Cli) CmdRankBefore(issue, before string) error {
err := c.RankIssue(issue, before, RANKBEFORE)
if err != nil {
return nil
}
if !c.opts["quiet"].(bool) {
fmt.Printf("OK %s %s/browse/%s\n", issue, c.endpoint, issue)
}
return nil
}
// CmdTransition will move state of the given issue to the given transtion
func (c *Cli) CmdTransition(issue string, trans string) error {
log.Debugf("transition called")
uri := fmt.Sprintf("%s/rest/api/2/issue/%s/transitions?expand=transitions.fields", c.endpoint, issue)
data, err := responseToJson(c.get(uri))
data, err := responseToJSON(c.get(uri))
if err != nil {
return err
}
transitions := data.(map[string]interface{})["transitions"].([]interface{})
var transId, transName string
var transID, transName string
var transMeta map[string]interface{}
found := make([]string, 0, len(transitions))
for _, transition := range transitions {
@@ -583,11 +698,14 @@ func (c *Cli) CmdTransition(issue string, trans string) error {
found = append(found, name)
if strings.Contains(strings.ToLower(name), strings.ToLower(trans)) {
transName = name
transId = id
transID = id
transMeta = transition.(map[string]interface{})
if strings.ToLower(name) == strings.ToLower(trans) {
break
}
}
}
if transId == "" {
if transID == "" {
err := fmt.Errorf("Invalid Transition '%s', Available: %s", trans, strings.Join(found, ", "))
log.Debugf("%s", err)
return err
@@ -620,12 +738,11 @@ func (c *Cli) CmdTransition(issue string, trans string) error {
}
uri = fmt.Sprintf("%s/rest/api/2/issue/%s", c.endpoint, issue)
var issueData map[string]interface{}
if data, err := responseToJson(c.get(uri)); err != nil {
data, err = responseToJSON(c.get(uri))
if err != nil {
return err
} else {
issueData = data.(map[string]interface{})
}
issueData := data.(map[string]interface{})
issueData["meta"] = transMeta
if c.GetOptString("defaultResolution", "") == "" {
// .meta.fields.resolution.allowedValues
@@ -648,7 +765,7 @@ func (c *Cli) CmdTransition(issue string, trans string) error {
issueData["overrides"] = c.opts
issueData["transition"] = map[string]interface{}{
"name": transName,
"id": transId,
"id": transID,
}
return c.editTemplate(
c.getTemplate("transition"),
@@ -658,6 +775,8 @@ func (c *Cli) CmdTransition(issue string, trans string) error {
)
}
// CmdComment will open up editor with "comment" template and submit
// YAML output to jira
func (c *Cli) CmdComment(issue string) error {
log.Debugf("comment called")
@@ -679,13 +798,12 @@ func (c *Cli) CmdComment(issue string) error {
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 POST")
log.Errorf("%s:\n%s", err, logBuffer)
return err
}
logBuffer := bytes.NewBuffer(make([]byte, 0))
resp.Write(logBuffer)
err = fmt.Errorf("Unexpected Response From POST")
log.Errorf("%s:\n%s", err, logBuffer)
return err
}
if comment, ok := c.opts["comment"]; ok && comment != "" {
@@ -696,24 +814,23 @@ func (c *Cli) CmdComment(issue string) error {
return err
}
return handlePost(json)
} else {
return c.editTemplate(
c.getTemplate("comment"),
fmt.Sprintf("%s-create-", issue),
map[string]interface{}{},
handlePost,
)
}
return nil
return c.editTemplate(
c.getTemplate("comment"),
fmt.Sprintf("%s-create-", issue),
map[string]interface{}{},
handlePost,
)
}
// CmdComponent will add a new component to given project
func (c *Cli) CmdComponent(action string, project string, name string, desc string, lead string) error {
log.Debugf("component called")
switch action {
case "add":
default:
return errors.New(fmt.Sprintf("CmdComponent: %q is not a valid action", action))
return fmt.Errorf("CmdComponent: %q is not a valid action", action)
}
json, err := jsonEncode(map[string]interface{}{
@@ -750,6 +867,7 @@ func (c *Cli) CmdComponent(action string, project string, name string, desc stri
return nil
}
// CmdLabels will add, remove or set labels on a given issue
func (c *Cli) CmdLabels(action string, issue string, labels []string) error {
log.Debugf("label called")
@@ -775,23 +893,22 @@ func (c *Cli) CmdLabels(action string, issue string, labels []string) error {
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.Errorf("%s:\n%s", err, logBuffer)
return err
}
logBuffer := bytes.NewBuffer(make([]byte, 0))
resp.Write(logBuffer)
err = fmt.Errorf("Unexpected Response From PUT")
log.Errorf("%s:\n%s", err, logBuffer)
return err
}
var labels_json string
var labelsJSON 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{}{
labelsJSON, err = jsonEncode(map[string]interface{}{
"labels": labelsActions,
})
} else {
@@ -802,18 +919,19 @@ func (c *Cli) CmdLabels(action string, issue string, labels []string) error {
}
labelsActions[i] = labelActionMap
}
labels_json, err = jsonEncode(map[string]interface{}{
labelsJSON, err = jsonEncode(map[string]interface{}{
"labels": labelsActions,
})
}
if err != nil {
return err
}
json := fmt.Sprintf("{ \"update\": %s }", labels_json)
json := fmt.Sprintf("{ \"update\": %s }", labelsJSON)
return handlePut(json)
}
// CmdAssign will assign the given user to be the owner of the given issue
func (c *Cli) CmdAssign(issue string, user string) error {
log.Debugf("assign called")
@@ -849,13 +967,14 @@ func (c *Cli) CmdAssign(issue string, user string) error {
return nil
}
// CmdExportTemplates will export the default templates to the template directory.
func (c *Cli) CmdExportTemplates() error {
dir := c.opts["directory"].(string)
if err := mkdir(dir); err != nil {
return err
}
for name, template := range all_templates {
for name, template := range allTemplates {
if wanted, ok := c.opts["template"]; ok && wanted != name {
continue
}
@@ -864,18 +983,19 @@ func (c *Cli) CmdExportTemplates() error {
log.Warning("Skipping %s, already exists", templateFile)
continue
}
if fh, err := os.OpenFile(templateFile, os.O_WRONLY|os.O_CREATE, 0644); err != nil {
fh, err := os.OpenFile(templateFile, os.O_WRONLY|os.O_CREATE, 0644)
if err != nil {
log.Errorf("Failed to open %s for writing: %s", templateFile, err)
return err
} else {
defer fh.Close()
log.Noticef("Creating %s", templateFile)
fh.Write([]byte(template))
}
defer fh.Close()
log.Noticef("Creating %s", templateFile)
fh.Write([]byte(template))
}
return nil
}
// CmdRequest will use the given uri to make a request and potentially send provided content.
func (c *Cli) CmdRequest(uri, content string) (err error) {
log.Debugf("request called")
@@ -886,11 +1006,11 @@ func (c *Cli) CmdRequest(uri, content string) (err error) {
method := strings.ToUpper(c.opts["method"].(string))
var data interface{}
if method == "GET" {
data, err = responseToJson(c.get(uri))
data, err = responseToJSON(c.get(uri))
} else if method == "POST" {
data, err = responseToJson(c.post(uri, content))
data, err = responseToJSON(c.post(uri, content))
} else if method == "PUT" {
data, err = responseToJson(c.put(uri, content))
data, err = responseToJSON(c.put(uri, content))
}
if err != nil {
return err
+19
View File
@@ -0,0 +1,19 @@
package jiradata
/////////////////////////////////////////////////////////////////////////
// This Code is Generated by SlipScheme Project:
// https://github.com/coryb/slipscheme
//
// Generated with command:
// slipscheme -pkg jiradata -overwrite ../schemas/TransitionsMeta.json
/////////////////////////////////////////////////////////////////////////
// DO NOT EDIT //
/////////////////////////////////////////////////////////////////////////
// AllowedValues defined from schema:
// {
// "title": "allowedValues",
// "type": "array",
// "items": {}
// }
type AllowedValues []interface{}
+87
View File
@@ -0,0 +1,87 @@
package jiradata
/////////////////////////////////////////////////////////////////////////
// This Code is Generated by SlipScheme Project:
// https://github.com/coryb/slipscheme
//
// Generated with command:
// slipscheme -pkg jiradata -overwrite ../schemas/TransitionsMeta.json
/////////////////////////////////////////////////////////////////////////
// DO NOT EDIT //
/////////////////////////////////////////////////////////////////////////
// FieldMeta defined from schema:
// {
// "title": "Field Meta",
// "type": "object",
// "properties": {
// "allowedValues": {
// "title": "allowedValues",
// "type": "array",
// "items": {}
// },
// "autoCompleteUrl": {
// "title": "autoCompleteUrl",
// "type": "string"
// },
// "hasDefaultValue": {
// "title": "hasDefaultValue",
// "type": "boolean"
// },
// "key": {
// "title": "key",
// "type": "string"
// },
// "name": {
// "title": "name",
// "type": "string"
// },
// "operations": {
// "title": "operations",
// "type": "array",
// "items": {
// "type": "string"
// }
// },
// "required": {
// "title": "required",
// "type": "boolean"
// },
// "schema": {
// "title": "Json Type",
// "type": "object",
// "properties": {
// "custom": {
// "title": "custom",
// "type": "string"
// },
// "customId": {
// "title": "customId",
// "type": "integer"
// },
// "items": {
// "title": "items",
// "type": "string"
// },
// "system": {
// "title": "system",
// "type": "string"
// },
// "type": {
// "title": "type",
// "type": "string"
// }
// }
// }
// }
// }
type FieldMeta struct {
AllowedValues AllowedValues `json:"allowedValues,omitempty" yaml:"allowedValues,omitempty"`
AutoCompleteURL string `json:"autoCompleteUrl,omitempty" yaml:"autoCompleteUrl,omitempty"`
HasDefaultValue bool `json:"hasDefaultValue,omitempty" yaml:"hasDefaultValue,omitempty"`
Key string `json:"key,omitempty" yaml:"key,omitempty"`
Name string `json:"name,omitempty" yaml:"name,omitempty"`
Operations Operations `json:"operations,omitempty" yaml:"operations,omitempty"`
Required bool `json:"required,omitempty" yaml:"required,omitempty"`
Schema *JSONType `json:"schema,omitempty" yaml:"schema,omitempty"`
}
+84
View File
@@ -0,0 +1,84 @@
package jiradata
/////////////////////////////////////////////////////////////////////////
// This Code is Generated by SlipScheme Project:
// https://github.com/coryb/slipscheme
//
// Generated with command:
// slipscheme -pkg jiradata -overwrite ../schemas/TransitionsMeta.json
/////////////////////////////////////////////////////////////////////////
// DO NOT EDIT //
/////////////////////////////////////////////////////////////////////////
// FieldMetaMap defined from schema:
// {
// "title": "fields",
// "type": "object",
// "patternProperties": {
// ".+": {
// "title": "Field Meta",
// "type": "object",
// "properties": {
// "allowedValues": {
// "title": "allowedValues",
// "type": "array",
// "items": {}
// },
// "autoCompleteUrl": {
// "title": "autoCompleteUrl",
// "type": "string"
// },
// "hasDefaultValue": {
// "title": "hasDefaultValue",
// "type": "boolean"
// },
// "key": {
// "title": "key",
// "type": "string"
// },
// "name": {
// "title": "name",
// "type": "string"
// },
// "operations": {
// "title": "operations",
// "type": "array",
// "items": {
// "type": "string"
// }
// },
// "required": {
// "title": "required",
// "type": "boolean"
// },
// "schema": {
// "title": "Json Type",
// "type": "object",
// "properties": {
// "custom": {
// "title": "custom",
// "type": "string"
// },
// "customId": {
// "title": "customId",
// "type": "integer"
// },
// "items": {
// "title": "items",
// "type": "string"
// },
// "system": {
// "title": "system",
// "type": "string"
// },
// "type": {
// "title": "type",
// "type": "string"
// }
// }
// }
// }
// }
// }
// }
type FieldMetaMap map[string]*FieldMeta
+46
View File
@@ -0,0 +1,46 @@
package jiradata
/////////////////////////////////////////////////////////////////////////
// This Code is Generated by SlipScheme Project:
// https://github.com/coryb/slipscheme
//
// Generated with command:
// slipscheme -pkg jiradata -overwrite ../schemas/TransitionsMeta.json
/////////////////////////////////////////////////////////////////////////
// DO NOT EDIT //
/////////////////////////////////////////////////////////////////////////
// JSONType defined from schema:
// {
// "title": "Json Type",
// "type": "object",
// "properties": {
// "custom": {
// "title": "custom",
// "type": "string"
// },
// "customId": {
// "title": "customId",
// "type": "integer"
// },
// "items": {
// "title": "items",
// "type": "string"
// },
// "system": {
// "title": "system",
// "type": "string"
// },
// "type": {
// "title": "type",
// "type": "string"
// }
// }
// }
type JSONType struct {
Custom string `json:"custom,omitempty" yaml:"custom,omitempty"`
CustomID int `json:"customId,omitempty" yaml:"customId,omitempty"`
Items string `json:"items,omitempty" yaml:"items,omitempty"`
System string `json:"system,omitempty" yaml:"system,omitempty"`
Type string `json:"type,omitempty" yaml:"type,omitempty"`
}
+21
View File
@@ -0,0 +1,21 @@
package jiradata
/////////////////////////////////////////////////////////////////////////
// This Code is Generated by SlipScheme Project:
// https://github.com/coryb/slipscheme
//
// Generated with command:
// slipscheme -pkg jiradata -overwrite ../schemas/TransitionsMeta.json
/////////////////////////////////////////////////////////////////////////
// DO NOT EDIT //
/////////////////////////////////////////////////////////////////////////
// Operations defined from schema:
// {
// "title": "operations",
// "type": "array",
// "items": {
// "type": "string"
// }
// }
type Operations []string
+78
View File
@@ -0,0 +1,78 @@
package jiradata
/////////////////////////////////////////////////////////////////////////
// This Code is Generated by SlipScheme Project:
// https://github.com/coryb/slipscheme
//
// Generated with command:
// slipscheme -pkg jiradata -overwrite ../schemas/TransitionsMeta.json
/////////////////////////////////////////////////////////////////////////
// DO NOT EDIT //
/////////////////////////////////////////////////////////////////////////
// Status defined from schema:
// {
// "title": "Status",
// "type": "object",
// "properties": {
// "description": {
// "title": "description",
// "type": "string"
// },
// "iconUrl": {
// "title": "iconUrl",
// "type": "string"
// },
// "id": {
// "title": "id",
// "type": "string"
// },
// "name": {
// "title": "name",
// "type": "string"
// },
// "self": {
// "title": "self",
// "type": "string"
// },
// "statusCategory": {
// "title": "Status Category",
// "type": "object",
// "properties": {
// "colorName": {
// "title": "colorName",
// "type": "string"
// },
// "id": {
// "title": "id",
// "type": "integer"
// },
// "key": {
// "title": "key",
// "type": "string"
// },
// "name": {
// "title": "name",
// "type": "string"
// },
// "self": {
// "title": "self",
// "type": "string"
// }
// }
// },
// "statusColor": {
// "title": "statusColor",
// "type": "string"
// }
// }
// }
type Status struct {
Description string `json:"description,omitempty" yaml:"description,omitempty"`
IconURL string `json:"iconUrl,omitempty" yaml:"iconUrl,omitempty"`
ID string `json:"id,omitempty" yaml:"id,omitempty"`
Name string `json:"name,omitempty" yaml:"name,omitempty"`
Self string `json:"self,omitempty" yaml:"self,omitempty"`
StatusCategory *StatusCategory `json:"statusCategory,omitempty" yaml:"statusCategory,omitempty"`
StatusColor string `json:"statusColor,omitempty" yaml:"statusColor,omitempty"`
}
+46
View File
@@ -0,0 +1,46 @@
package jiradata
/////////////////////////////////////////////////////////////////////////
// This Code is Generated by SlipScheme Project:
// https://github.com/coryb/slipscheme
//
// Generated with command:
// slipscheme -pkg jiradata -overwrite ../schemas/TransitionsMeta.json
/////////////////////////////////////////////////////////////////////////
// DO NOT EDIT //
/////////////////////////////////////////////////////////////////////////
// StatusCategory defined from schema:
// {
// "title": "Status Category",
// "type": "object",
// "properties": {
// "colorName": {
// "title": "colorName",
// "type": "string"
// },
// "id": {
// "title": "id",
// "type": "integer"
// },
// "key": {
// "title": "key",
// "type": "string"
// },
// "name": {
// "title": "name",
// "type": "string"
// },
// "self": {
// "title": "self",
// "type": "string"
// }
// }
// }
type StatusCategory struct {
ColorName string `json:"colorName,omitempty" yaml:"colorName,omitempty"`
ID int `json:"id,omitempty" yaml:"id,omitempty"`
Key string `json:"key,omitempty" yaml:"key,omitempty"`
Name string `json:"name,omitempty" yaml:"name,omitempty"`
Self string `json:"self,omitempty" yaml:"self,omitempty"`
}
+169
View File
@@ -0,0 +1,169 @@
package jiradata
/////////////////////////////////////////////////////////////////////////
// This Code is Generated by SlipScheme Project:
// https://github.com/coryb/slipscheme
//
// Generated with command:
// slipscheme -pkg jiradata -overwrite ../schemas/TransitionsMeta.json
/////////////////////////////////////////////////////////////////////////
// DO NOT EDIT //
/////////////////////////////////////////////////////////////////////////
// Transition defined from schema:
// {
// "title": "Transition",
// "type": "object",
// "properties": {
// "expand": {
// "title": "expand",
// "type": "string"
// },
// "fields": {
// "title": "fields",
// "type": "object",
// "patternProperties": {
// ".+": {
// "title": "Field Meta",
// "type": "object",
// "properties": {
// "allowedValues": {
// "title": "allowedValues",
// "type": "array",
// "items": {}
// },
// "autoCompleteUrl": {
// "title": "autoCompleteUrl",
// "type": "string"
// },
// "hasDefaultValue": {
// "title": "hasDefaultValue",
// "type": "boolean"
// },
// "key": {
// "title": "key",
// "type": "string"
// },
// "name": {
// "title": "name",
// "type": "string"
// },
// "operations": {
// "title": "operations",
// "type": "array",
// "items": {
// "type": "string"
// }
// },
// "required": {
// "title": "required",
// "type": "boolean"
// },
// "schema": {
// "title": "Json Type",
// "type": "object",
// "properties": {
// "custom": {
// "title": "custom",
// "type": "string"
// },
// "customId": {
// "title": "customId",
// "type": "integer"
// },
// "items": {
// "title": "items",
// "type": "string"
// },
// "system": {
// "title": "system",
// "type": "string"
// },
// "type": {
// "title": "type",
// "type": "string"
// }
// }
// }
// }
// }
// }
// },
// "hasScreen": {
// "title": "hasScreen",
// "type": "boolean"
// },
// "id": {
// "title": "id",
// "type": "string"
// },
// "name": {
// "title": "name",
// "type": "string"
// },
// "to": {
// "title": "Status",
// "type": "object",
// "properties": {
// "description": {
// "title": "description",
// "type": "string"
// },
// "iconUrl": {
// "title": "iconUrl",
// "type": "string"
// },
// "id": {
// "title": "id",
// "type": "string"
// },
// "name": {
// "title": "name",
// "type": "string"
// },
// "self": {
// "title": "self",
// "type": "string"
// },
// "statusCategory": {
// "title": "Status Category",
// "type": "object",
// "properties": {
// "colorName": {
// "title": "colorName",
// "type": "string"
// },
// "id": {
// "title": "id",
// "type": "integer"
// },
// "key": {
// "title": "key",
// "type": "string"
// },
// "name": {
// "title": "name",
// "type": "string"
// },
// "self": {
// "title": "self",
// "type": "string"
// }
// }
// },
// "statusColor": {
// "title": "statusColor",
// "type": "string"
// }
// }
// }
// }
// }
type Transition struct {
Expand string `json:"expand,omitempty" yaml:"expand,omitempty"`
Fields FieldMetaMap `json:"fields,omitempty" yaml:"fields,omitempty"`
HasScreen bool `json:"hasScreen,omitempty" yaml:"hasScreen,omitempty"`
ID string `json:"id,omitempty" yaml:"id,omitempty"`
Name string `json:"name,omitempty" yaml:"name,omitempty"`
To *Status `json:"to,omitempty" yaml:"to,omitempty"`
}
+166
View File
@@ -0,0 +1,166 @@
package jiradata
/////////////////////////////////////////////////////////////////////////
// This Code is Generated by SlipScheme Project:
// https://github.com/coryb/slipscheme
//
// Generated with command:
// slipscheme -pkg jiradata -overwrite ../schemas/TransitionsMeta.json
/////////////////////////////////////////////////////////////////////////
// DO NOT EDIT //
/////////////////////////////////////////////////////////////////////////
// Transitions defined from schema:
// {
// "title": "transitions",
// "type": "array",
// "items": {
// "title": "Transition",
// "type": "object",
// "properties": {
// "expand": {
// "title": "expand",
// "type": "string"
// },
// "fields": {
// "title": "fields",
// "type": "object",
// "patternProperties": {
// ".+": {
// "title": "Field Meta",
// "type": "object",
// "properties": {
// "allowedValues": {
// "title": "allowedValues",
// "type": "array",
// "items": {}
// },
// "autoCompleteUrl": {
// "title": "autoCompleteUrl",
// "type": "string"
// },
// "hasDefaultValue": {
// "title": "hasDefaultValue",
// "type": "boolean"
// },
// "key": {
// "title": "key",
// "type": "string"
// },
// "name": {
// "title": "name",
// "type": "string"
// },
// "operations": {
// "title": "operations",
// "type": "array",
// "items": {
// "type": "string"
// }
// },
// "required": {
// "title": "required",
// "type": "boolean"
// },
// "schema": {
// "title": "Json Type",
// "type": "object",
// "properties": {
// "custom": {
// "title": "custom",
// "type": "string"
// },
// "customId": {
// "title": "customId",
// "type": "integer"
// },
// "items": {
// "title": "items",
// "type": "string"
// },
// "system": {
// "title": "system",
// "type": "string"
// },
// "type": {
// "title": "type",
// "type": "string"
// }
// }
// }
// }
// }
// }
// },
// "hasScreen": {
// "title": "hasScreen",
// "type": "boolean"
// },
// "id": {
// "title": "id",
// "type": "string"
// },
// "name": {
// "title": "name",
// "type": "string"
// },
// "to": {
// "title": "Status",
// "type": "object",
// "properties": {
// "description": {
// "title": "description",
// "type": "string"
// },
// "iconUrl": {
// "title": "iconUrl",
// "type": "string"
// },
// "id": {
// "title": "id",
// "type": "string"
// },
// "name": {
// "title": "name",
// "type": "string"
// },
// "self": {
// "title": "self",
// "type": "string"
// },
// "statusCategory": {
// "title": "Status Category",
// "type": "object",
// "properties": {
// "colorName": {
// "title": "colorName",
// "type": "string"
// },
// "id": {
// "title": "id",
// "type": "integer"
// },
// "key": {
// "title": "key",
// "type": "string"
// },
// "name": {
// "title": "name",
// "type": "string"
// },
// "self": {
// "title": "self",
// "type": "string"
// }
// }
// },
// "statusColor": {
// "title": "statusColor",
// "type": "string"
// }
// }
// }
// }
// }
// }
type Transitions []*Transition
+18
View File
@@ -0,0 +1,18 @@
package jiradata
import (
"strings"
)
// Find will search the transitions for one that matches
// the given name. It will return a valid trantion that matches
// or nil
func (t Transitions) Find(name string) *Transition {
name = strings.ToLower(name)
for _, trans := range t {
if strings.Contains(strings.ToLower(trans.Name), name) {
return trans
}
}
return nil
}
+180
View File
@@ -0,0 +1,180 @@
package jiradata
/////////////////////////////////////////////////////////////////////////
// This Code is Generated by SlipScheme Project:
// https://github.com/coryb/slipscheme
//
// Generated with command:
// slipscheme -pkg jiradata -overwrite ../schemas/TransitionsMeta.json
/////////////////////////////////////////////////////////////////////////
// DO NOT EDIT //
/////////////////////////////////////////////////////////////////////////
// TransitionsMeta defined from schema:
// {
// "title": "Transitions Meta",
// "id": "https://docs.atlassian.com/jira/REST/schema/transitions-meta#",
// "type": "object",
// "properties": {
// "expand": {
// "title": "expand",
// "type": "string"
// },
// "transitions": {
// "title": "transitions",
// "type": "array",
// "items": {
// "title": "Transition",
// "type": "object",
// "properties": {
// "expand": {
// "title": "expand",
// "type": "string"
// },
// "fields": {
// "title": "fields",
// "type": "object",
// "patternProperties": {
// ".+": {
// "title": "Field Meta",
// "type": "object",
// "properties": {
// "allowedValues": {
// "title": "allowedValues",
// "type": "array",
// "items": {}
// },
// "autoCompleteUrl": {
// "title": "autoCompleteUrl",
// "type": "string"
// },
// "hasDefaultValue": {
// "title": "hasDefaultValue",
// "type": "boolean"
// },
// "key": {
// "title": "key",
// "type": "string"
// },
// "name": {
// "title": "name",
// "type": "string"
// },
// "operations": {
// "title": "operations",
// "type": "array",
// "items": {
// "type": "string"
// }
// },
// "required": {
// "title": "required",
// "type": "boolean"
// },
// "schema": {
// "title": "Json Type",
// "type": "object",
// "properties": {
// "custom": {
// "title": "custom",
// "type": "string"
// },
// "customId": {
// "title": "customId",
// "type": "integer"
// },
// "items": {
// "title": "items",
// "type": "string"
// },
// "system": {
// "title": "system",
// "type": "string"
// },
// "type": {
// "title": "type",
// "type": "string"
// }
// }
// }
// }
// }
// }
// },
// "hasScreen": {
// "title": "hasScreen",
// "type": "boolean"
// },
// "id": {
// "title": "id",
// "type": "string"
// },
// "name": {
// "title": "name",
// "type": "string"
// },
// "to": {
// "title": "Status",
// "type": "object",
// "properties": {
// "description": {
// "title": "description",
// "type": "string"
// },
// "iconUrl": {
// "title": "iconUrl",
// "type": "string"
// },
// "id": {
// "title": "id",
// "type": "string"
// },
// "name": {
// "title": "name",
// "type": "string"
// },
// "self": {
// "title": "self",
// "type": "string"
// },
// "statusCategory": {
// "title": "Status Category",
// "type": "object",
// "properties": {
// "colorName": {
// "title": "colorName",
// "type": "string"
// },
// "id": {
// "title": "id",
// "type": "integer"
// },
// "key": {
// "title": "key",
// "type": "string"
// },
// "name": {
// "title": "name",
// "type": "string"
// },
// "self": {
// "title": "self",
// "type": "string"
// }
// }
// },
// "statusColor": {
// "title": "statusColor",
// "type": "string"
// }
// }
// }
// }
// }
// }
// }
// }
type TransitionsMeta struct {
Expand string `json:"expand,omitempty" yaml:"expand,omitempty"`
Transitions Transitions `json:"transitions,omitempty" yaml:"transitions,omitempty"`
}
+30 -1
View File
@@ -3,7 +3,7 @@ package main
import (
"bytes"
"fmt"
"github.com/Netflix-Skunkworks/go-jira"
"gopkg.in/Netflix-Skunkworks/go-jira.v0"
"github.com/coryb/optigo"
"gopkg.in/coryb/yaml.v2"
"gopkg.in/op/go-logging.v1"
@@ -56,11 +56,14 @@ func main() {
Usage:
jira (ls|list) <Query Options>
jira view ISSUE
jira worklog ISSUE
jira add worklog ISSUE <Worklog Options>
jira edit [--noedit] <Edit Options> [ISSUE | <Query Options>]
jira create [--noedit] [-p PROJECT] <Create Options>
jira DUPLICATE dups ISSUE
jira BLOCKER blocks ISSUE
jira vote ISSUE [--down]
jira rank ISSUE (after|before) ISSUE
jira watch ISSUE [-w WATCHER] [--remove]
jira (trans|transition) TRANSITION ISSUE [--noedit] <Edit Options>
jira ack ISSUE [--edit] <Edit Options>
@@ -101,6 +104,7 @@ General Options:
-t --template=FILE Template file to use for output/editing
-u --user=USER Username to use for authenticaion (default: %s)
-v --verbose Increase output logging
--unixproxy=PATH Path for a unix-socket proxy (eg., --unixproxy /tmp/proxy.sock)
--version Print version
Query Options:
@@ -125,6 +129,10 @@ Create Options:
-m --comment=COMMENT Comment message for transition
-o --override=KEY=VAL Set custom key/value pairs
Worklog Options:
-T --time-spent=TIMESPENT Time spent working on issue
-m --comment=COMMENT Comment message for worklog
Command Options:
-d --directory=DIR Directory to export templates to (default: %s)
`, user, defaultQueryFields, defaultMaxResults, defaultSort, user, fmt.Sprintf("%s/.jira.d/templates", home))
@@ -177,6 +185,9 @@ Command Options:
"req": "request",
"request": "request",
"vote": "vote",
"rank": "rank",
"worklog": "worklog",
"addworklog": "addworklog",
}
defaults := map[string]interface{}{
@@ -229,7 +240,9 @@ Command Options:
"d|dir|directory=s": setopt,
"M|method=s": setopt,
"S|saveFile=s": setopt,
"T|time-spent=s": setopt,
"Q|quiet": setopt,
"unixproxy": setopt,
"down": setopt,
})
@@ -482,6 +495,15 @@ Command Options:
case "view":
requireArgs(1)
err = c.CmdView(args[0])
case "worklog":
if len(args) > 0 && args[0] == "add" {
setEditing(true)
requireArgs(2)
err = c.CmdWorklog(args[0], args[1])
} else {
requireArgs(1)
err = c.CmdWorklogs(args[0])
}
case "vote":
requireArgs(1)
if val, ok := opts["down"]; ok {
@@ -489,6 +511,13 @@ Command Options:
} else {
err = c.CmdVote(args[0], true)
}
case "rank":
requireArgs(3)
if args[1] == "after" {
err = c.CmdRankAfter(args[0], args[2])
} else {
err = c.CmdRankBefore(args[0], args[2])
}
case "request":
requireArgs(1)
data := ""
+1
View File
@@ -0,0 +1 @@
!src/
Regular → Executable
+9 -2
View File
@@ -1,2 +1,9 @@
endpoint: http://localhost:8080
user: gojira
#!/bin/sh
if [ -z "$JIRACLOUD" ]; then
echo endpoint: http://localhost:8080
echo user: gojira
echo password-keyring: true
else
echo endpoint: https://go-jira.atlassian.net
echo user: gojira@example.com
fi
+19 -11
View File
@@ -3,28 +3,36 @@ eval "$(curl -q -s https://raw.githubusercontent.com/coryb/osht/master/osht.sh)"
cd $(dirname $0)
jira="../jira --user admin"
SKIP test -n "$JIRACLOUD" # using Jira Cloud at go-jira.atlassian.net
PLAN 15
# clean out any old containers
RUNS sh -c "docker rm -f go-jira-test || true"
docker rm -f go-jira-test
# start newt jira service
RUNS docker run --detach --name go-jira-test --publish 8080:8080 go-jira-test:latest
RUNS docker build . -t go-jira-test
# wait a few seconds for it to bind to port 8080
RUNS sleep 10
mkdir -p $(pwd)/.maven-cache
# wait for healthchecks to pass, curl will retry 60 times over 1 min waiting
RUNS curl -q -L --retry 60 --retry-delay 1 -f -s "http://localhost:8080/rest/api/2/serverInfo?doHealthCheck=1"
# start newt jira service, cache the users m2 directory to make startup faster
RUNS docker run --detach -v $(pwd)/.maven-cache:/root/.m2/repository --name go-jira-test --publish 8080:8080 go-jira-test:latest
# wait for docker service to get started
RUNS sleep 5
echo "# Waiting for jira service to be listening on port 8080"
docker exec -i go-jira-test tail -f screenlog.0 | grep -m 1 'jira started successfully' | sed 's/^/# /'
# wait for healthchecks to pass, curl will retry 900 times over 15 min waiting
RUNS curl -q -L --retry 900 --retry-delay 1 -f -s "http://localhost:8080/rest/api/2/serverInfo?doHealthCheck=1"
# login to jira as admin user
echo "admin123" | RUNS $jira login
RUNS $jira login
# create gojira user
RUNS $jira req -M POST /rest/api/2/user '{"name":"gojira","password":"gojira123","emailAddress":"gojira@example.com","displayName":"Go Jira"}'
RUNS $jira req -M POST /rest/api/2/user '{"name":"gojira","password":"gojira123","emailAddress":"gojira@example.com","displayName":"GoJira"}'
# create mojira user (need secondary user for voting)
RUNS $jira req -M POST /rest/api/2/user '{"name":"mojira","password":"mojira123","emailAddress":"mojira@example.com","displayName":"Mo Jira"}'
# create mothra user (need secondary user for voting)
RUNS $jira req -M POST /rest/api/2/user '{"name":"mothra","password":"mothra123","emailAddress":"mothra@example.com","displayName":"Mothra"}'
# create SCRUM softwareproject
RUNS $jira req -M POST /rest/api/2/project '{"key":"SCRUM","name":"Scrum","projectTypeKey":"software","projectTemplateKey":"com.pyxis.greenhopper.jira:gh-scrum-template","lead":"gojira"}'
+3 -1
View File
@@ -3,6 +3,8 @@ eval "$(curl -q -s https://raw.githubusercontent.com/coryb/osht/master/osht.sh)"
cd $(dirname $0)
jira=../jira
SKIP test -n "$JIRACLOUD" # using Jira Cloud at go-jira.atlassian.net
PLAN 7
###############################################################################
@@ -24,6 +26,6 @@ echo "gojira123" | RUNS $jira login
RUNS $jira req /rest/auth/1/session </dev/null
GREP '"name": "gojira"'
GREP '"self": "http://localhost:8080/rest/api/latest/user?username=gojira"'
GREP "\"self\": \"$ENDPOINT/rest/api/latest/user?username=gojira\""
+53 -35
View File
@@ -4,23 +4,28 @@ cd $(dirname $0)
jira="../jira --project BASIC"
export JIRA_LOG_FORMAT="%{level:-5s} %{message}"
PLAN 84
ENDPOINT="http://localhost:8080"
if [ -n "$JIRACLOUD" ]; then
ENDPOINT="https://go-jira.atlassian.net"
fi
# cleanup from previous failed test executions
($jira ls | awk -F: '{print $1}' | while read issue; do ../jira done $issue; done) | sed 's/^/# CLEANUP: /g'
PLAN 86
# reset login
RUNS $jira logout
echo "gojira123" | RUNS $jira login
# cleanup from previous failed test executions
($jira ls | awk -F: '{print $1}' | while read issue; do ../jira done $issue; done) | sed 's/^/# CLEANUP: /g'
###############################################################################
## Create an issue
###############################################################################
RUNS $jira create -o summary=summary -o description=description --noedit --saveFile issue.props
RUNS $jira -v -v -v create -o summary=summary -o description=description --noedit --saveFile issue.props
issue=$(awk '/issue/{print $2}' issue.props)
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
OK $issue $ENDPOINT/browse/$issue
EOF
###############################################################################
@@ -52,6 +57,19 @@ DIFF <<EOF
$(printf %-12s $issue:) summary
EOF
###############################################################################
## List all issues, using the table template
###############################################################################
RUNS $jira ls --template table
DIFF <<EOF
+----------------+---------------------------------------------------------+--------------+--------------+------------+--------------+--------------+
| Issue | Summary | Priority | Status | Age | Reporter | Assignee |
+----------------+---------------------------------------------------------+--------------+--------------+------------+--------------+--------------+
| $(printf %-14s $issue) | summary | Medium | To Do | a minute | gojira | gojira |
+----------------+---------------------------------------------------------+--------------+--------------+------------+--------------+--------------+
EOF
###############################################################################
## Try to close the issue, bug Basic projects do not allow that state
###############################################################################
@@ -67,7 +85,7 @@ EOF
RUNS $jira done $issue
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
OK $issue $ENDPOINT/browse/$issue
EOF
###############################################################################
@@ -85,13 +103,13 @@ EOF
RUNS $jira create -o summary=summary -o description=description --noedit --saveFile issue.props
issue=$(awk '/issue/{print $2}' issue.props)
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
OK $issue $ENDPOINT/browse/$issue
EOF
RUNS $jira create -o summary=dup -o description=dup --noedit --saveFile issue.props
dup=$(awk '/issue/{print $2}' issue.props)
DIFF <<EOF
OK $dup http://localhost:8080/browse/$dup
OK $dup $ENDPOINT/browse/$dup
EOF
###############################################################################
@@ -102,8 +120,8 @@ EOF
RUNS $jira $dup dups $issue --noedit
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
OK $dup http://localhost:8080/browse/$dup
OK $issue $ENDPOINT/browse/$issue
OK $dup $ENDPOINT/browse/$dup
EOF
RUNS $jira $issue
@@ -140,7 +158,7 @@ EOF
RUNS $jira create -o summary=blocks -o description=blocks --noedit --saveFile issue.props
blocker=$(awk '/issue/{print $2}' issue.props)
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
OK $blocker $ENDPOINT/browse/$blocker
EOF
###############################################################################
@@ -149,7 +167,7 @@ EOF
RUNS $jira $blocker blocks $issue
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
OK $issue $ENDPOINT/browse/$issue
EOF
RUNS $jira $issue
@@ -181,13 +199,13 @@ $(printf %-12s $blocker:) blocks
EOF
###############################################################################
# reset login for mojira for voting
# reset login for mothra for voting
###############################################################################
jira="$jira --user mojira"
jira="$jira --user mothra"
RUNS $jira logout
echo "mojira123" | RUNS $jira login
echo "mothra123" | RUNS $jira login
###############################################################################
## vote for main issue, verify it shows when viewing the issue
@@ -195,7 +213,7 @@ echo "mojira123" | RUNS $jira login
RUNS $jira vote $issue
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
OK $issue $ENDPOINT/browse/$issue
EOF
RUNS $jira $issue
@@ -222,7 +240,7 @@ EOF
RUNS $jira vote $issue --down
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
OK $issue $ENDPOINT/browse/$issue
EOF
RUNS $jira $issue
@@ -244,19 +262,19 @@ description: |
EOF
###############################################################################
## set mojira user as watcher to issue and verify from REST api
## set mothra user as watcher to issue and verify from REST api
###############################################################################
RUNS $jira watch $issue
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
OK $issue $ENDPOINT/browse/$issue
EOF
# FIXME we probably need a watchers command to wrap this?
RUNS sh -c "$jira req /rest/api/2/issue/$issue/watchers | jq -r .watchers[].name"
DIFF <<EOF
gojira
mojira
mothra
EOF
###############################################################################
@@ -265,7 +283,7 @@ EOF
RUNS $jira trans "In Progress" $blocker --noedit
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
OK $blocker $ENDPOINT/browse/$blocker
EOF
###############################################################################
@@ -274,7 +292,7 @@ EOF
RUNS $jira todo $blocker
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
OK $blocker $ENDPOINT/browse/$blocker
EOF
###############################################################################
@@ -283,7 +301,7 @@ EOF
RUNS $jira trans "review" $blocker --noedit
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
OK $blocker $ENDPOINT/browse/$blocker
EOF
###############################################################################
@@ -292,7 +310,7 @@ EOF
RUNS $jira todo $blocker
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
OK $blocker $ENDPOINT/browse/$blocker
EOF
###############################################################################
@@ -301,7 +319,7 @@ EOF
RUNS $jira prog $blocker
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
OK $blocker $ENDPOINT/browse/$blocker
EOF
###############################################################################
@@ -310,7 +328,7 @@ EOF
RUNS $jira done $blocker
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
OK $blocker $ENDPOINT/browse/$blocker
EOF
###############################################################################
@@ -341,7 +359,7 @@ EOF
RUNS $jira comment $issue --noedit -m "Yo, Comment"
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
OK $issue $ENDPOINT/browse/$issue
EOF
RUNS $jira $issue
@@ -362,7 +380,7 @@ description: |
description
comments:
- | # mojira, a minute ago
- | # mothra, a minute ago
Yo, Comment
EOF
@@ -373,7 +391,7 @@ EOF
RUNS $jira add labels $blocker test-label another-label
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
OK $blocker $ENDPOINT/browse/$blocker
EOF
RUNS $jira $blocker
@@ -401,7 +419,7 @@ EOF
RUNS $jira remove labels $blocker another-label
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
OK $blocker $ENDPOINT/browse/$blocker
EOF
RUNS $jira $blocker
@@ -429,7 +447,7 @@ EOF
RUNS $jira set labels $blocker more-label better-label
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
OK $blocker $ENDPOINT/browse/$blocker
EOF
RUNS $jira $blocker
@@ -452,12 +470,12 @@ description: |
EOF
###############################################################################
## Verify that "mojira" user can take the issue (reassign to self)
## Verify that "mothra" user can take the issue (reassign to self)
###############################################################################
RUNS $jira take $blocker
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
OK $blocker $ENDPOINT/browse/$blocker
EOF
RUNS $jira $blocker
@@ -468,7 +486,7 @@ status: Done
summary: blocks
project: BASIC
issuetype: Bug
assignee: mojira
assignee: mothra
reporter: gojira
blockers:
depends: $issue[To Do]
@@ -485,7 +503,7 @@ EOF
RUNS $jira give $blocker gojira
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
OK $blocker $ENDPOINT/browse/$blocker
EOF
RUNS $jira $blocker
+48
View File
@@ -0,0 +1,48 @@
#!/bin/bash
eval "$(curl -q -s https://raw.githubusercontent.com/coryb/osht/master/osht.sh)"
cd $(dirname $0)
jira="../jira --project BASIC"
export JIRA_LOG_FORMAT="%{level:-5s} %{message}"
ENDPOINT="http://localhost:8080"
if [ -n "$JIRACLOUD" ]; then
ENDPOINT="https://go-jira.atlassian.net"
fi
PLAN 8
# reset login
RUNS $jira logout
echo "gojira123" | RUNS $jira login
# cleanup from previous failed test executions
($jira ls | awk -F: '{print $1}' | while read issue; do ../jira done $issue; done) | sed 's/^/# CLEANUP: /g'
###############################################################################
## Create an issue
###############################################################################
RUNS $jira create -o summary=summary -o description=description --noedit --saveFile issue.props
issue=$(awk '/issue/{print $2}' issue.props)
DIFF <<EOF
OK $issue $ENDPOINT/browse/$issue
EOF
###############################################################################
## Add a worklog to an issue
###############################################################################
RUNS $jira add worklog $issue --comment "work is hard" --time-spent "1h 12m" --noedit
DIFF <<EOF
OK $issue $ENDPOINT/browse/$issue
EOF
###############################################################################
## Verify worklog got added to issue
###############################################################################
RUNS $jira worklog $issue
DIFF <<EOF
- # gojira, a minute ago
comment: work is hard
timeSpent: 1h 12m
EOF
+35 -30
View File
@@ -4,6 +4,11 @@ cd $(dirname $0)
jira="../jira --project SCRUM"
export JIRA_LOG_FORMAT="%{level:-5s} %{message}"
ENDPOINT="http://localhost:8080"
if [ -n "$JIRACLOUD" ]; then
ENDPOINT="https://go-jira.atlassian.net"
fi
PLAN 84
# cleanup from previous failed test executions
@@ -20,7 +25,7 @@ RUNS $jira create -o summary=summary -o description=description --noedit --saveF
issue=$(awk '/issue/{print $2}' issue.props)
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
OK $issue $ENDPOINT/browse/$issue
EOF
###############################################################################
@@ -67,7 +72,7 @@ EOF
RUNS $jira done $issue
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
OK $issue $ENDPOINT/browse/$issue
EOF
###############################################################################
@@ -85,13 +90,13 @@ EOF
RUNS $jira create -o summary=summary -o description=description --noedit --saveFile issue.props
issue=$(awk '/issue/{print $2}' issue.props)
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
OK $issue $ENDPOINT/browse/$issue
EOF
RUNS $jira create -o summary=dup -o description=dup --noedit --saveFile issue.props
dup=$(awk '/issue/{print $2}' issue.props)
DIFF <<EOF
OK $dup http://localhost:8080/browse/$dup
OK $dup $ENDPOINT/browse/$dup
EOF
###############################################################################
@@ -102,8 +107,8 @@ EOF
RUNS $jira $dup dups $issue --noedit
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
OK $dup http://localhost:8080/browse/$dup
OK $issue $ENDPOINT/browse/$issue
OK $dup $ENDPOINT/browse/$dup
EOF
RUNS $jira $issue
@@ -140,7 +145,7 @@ EOF
RUNS $jira create -o summary=blocks -o description=blocks --noedit --saveFile issue.props
blocker=$(awk '/issue/{print $2}' issue.props)
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
OK $blocker $ENDPOINT/browse/$blocker
EOF
###############################################################################
@@ -149,7 +154,7 @@ EOF
RUNS $jira $blocker blocks $issue
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
OK $issue $ENDPOINT/browse/$issue
EOF
RUNS $jira $issue
@@ -181,13 +186,13 @@ $(printf %-12s $blocker:) blocks
EOF
###############################################################################
# reset login for mojira for voting
# reset login for mothra for voting
###############################################################################
jira="$jira --user mojira"
jira="$jira --user mothra"
RUNS $jira logout
echo "mojira123" | RUNS $jira login
echo "mothra123" | RUNS $jira login
###############################################################################
## vote for main issue, verify it shows when viewing the issue
@@ -195,7 +200,7 @@ echo "mojira123" | RUNS $jira login
RUNS $jira vote $issue
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
OK $issue $ENDPOINT/browse/$issue
EOF
RUNS $jira $issue
@@ -222,7 +227,7 @@ EOF
RUNS $jira vote $issue --down
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
OK $issue $ENDPOINT/browse/$issue
EOF
RUNS $jira $issue
@@ -244,19 +249,19 @@ description: |
EOF
###############################################################################
## set mojira user as watcher to issue and verify from REST api
## set mothra user as watcher to issue and verify from REST api
###############################################################################
RUNS $jira watch $issue
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
OK $issue $ENDPOINT/browse/$issue
EOF
# FIXME we probably need a watchers command to wrap this?
RUNS sh -c "$jira req /rest/api/2/issue/$issue/watchers | jq -r .watchers[].name"
DIFF <<EOF
gojira
mojira
mothra
EOF
###############################################################################
@@ -265,7 +270,7 @@ EOF
RUNS $jira trans "In Progress" $blocker --noedit
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
OK $blocker $ENDPOINT/browse/$blocker
EOF
###############################################################################
@@ -274,7 +279,7 @@ EOF
RUNS $jira todo $blocker
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
OK $blocker $ENDPOINT/browse/$blocker
EOF
###############################################################################
@@ -292,7 +297,7 @@ EOF
RUNS $jira todo $blocker
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
OK $blocker $ENDPOINT/browse/$blocker
EOF
###############################################################################
@@ -301,7 +306,7 @@ EOF
RUNS $jira prog $blocker
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
OK $blocker $ENDPOINT/browse/$blocker
EOF
###############################################################################
@@ -310,7 +315,7 @@ EOF
RUNS $jira done $blocker
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
OK $blocker $ENDPOINT/browse/$blocker
EOF
###############################################################################
@@ -341,7 +346,7 @@ EOF
RUNS $jira comment $issue --noedit -m "Yo, Comment"
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
OK $issue $ENDPOINT/browse/$issue
EOF
RUNS $jira $issue
@@ -362,7 +367,7 @@ description: |
description
comments:
- | # mojira, a minute ago
- | # mothra, a minute ago
Yo, Comment
EOF
@@ -373,7 +378,7 @@ EOF
RUNS $jira add labels $blocker test-label another-label
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
OK $blocker $ENDPOINT/browse/$blocker
EOF
RUNS $jira $blocker
@@ -401,7 +406,7 @@ EOF
RUNS $jira remove labels $blocker another-label
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
OK $blocker $ENDPOINT/browse/$blocker
EOF
RUNS $jira $blocker
@@ -429,7 +434,7 @@ EOF
RUNS $jira set labels $blocker more-label better-label
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
OK $blocker $ENDPOINT/browse/$blocker
EOF
RUNS $jira $blocker
@@ -452,12 +457,12 @@ description: |
EOF
###############################################################################
## Verify that "mojira" user can take the issue (reassign to self)
## Verify that "mothra" user can take the issue (reassign to self)
###############################################################################
RUNS $jira take $blocker
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
OK $blocker $ENDPOINT/browse/$blocker
EOF
RUNS $jira $blocker
@@ -468,7 +473,7 @@ status: Done
summary: blocks
project: SCRUM
issuetype: Bug
assignee: mojira
assignee: mothra
reporter: gojira
blockers:
depends: $issue[To Do]
@@ -485,7 +490,7 @@ EOF
RUNS $jira give $blocker gojira
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
OK $blocker $ENDPOINT/browse/$blocker
EOF
RUNS $jira $blocker
+35 -30
View File
@@ -4,6 +4,11 @@ cd $(dirname $0)
jira="../jira --project KANBAN"
export JIRA_LOG_FORMAT="%{level:-5s} %{message}"
ENDPOINT="http://localhost:8080"
if [ -n "$JIRACLOUD" ]; then
ENDPOINT="https://go-jira.atlassian.net"
fi
PLAN 86
# cleanup from previous failed test executions
@@ -20,7 +25,7 @@ RUNS $jira create -o summary=summary -o description=description --noedit --saveF
issue=$(awk '/issue/{print $2}' issue.props)
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
OK $issue $ENDPOINT/browse/$issue
EOF
###############################################################################
@@ -67,7 +72,7 @@ EOF
RUNS $jira done $issue
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
OK $issue $ENDPOINT/browse/$issue
EOF
###############################################################################
@@ -85,13 +90,13 @@ EOF
RUNS $jira create -o summary=summary -o description=description --noedit --saveFile issue.props
issue=$(awk '/issue/{print $2}' issue.props)
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
OK $issue $ENDPOINT/browse/$issue
EOF
RUNS $jira create -o summary=dup -o description=dup --noedit --saveFile issue.props
dup=$(awk '/issue/{print $2}' issue.props)
DIFF <<EOF
OK $dup http://localhost:8080/browse/$dup
OK $dup $ENDPOINT/browse/$dup
EOF
###############################################################################
@@ -102,8 +107,8 @@ EOF
RUNS $jira $dup dups $issue --noedit
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
OK $dup http://localhost:8080/browse/$dup
OK $issue $ENDPOINT/browse/$issue
OK $dup $ENDPOINT/browse/$dup
EOF
RUNS $jira $issue
@@ -140,7 +145,7 @@ EOF
RUNS $jira create -o summary=blocks -o description=blocks --noedit --saveFile issue.props
blocker=$(awk '/issue/{print $2}' issue.props)
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
OK $blocker $ENDPOINT/browse/$blocker
EOF
###############################################################################
@@ -149,7 +154,7 @@ EOF
RUNS $jira $blocker blocks $issue
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
OK $issue $ENDPOINT/browse/$issue
EOF
RUNS $jira $issue
@@ -181,13 +186,13 @@ $(printf %-12s $blocker:) blocks
EOF
###############################################################################
# reset login for mojira for voting
# reset login for mothra for voting
###############################################################################
jira="$jira --user mojira"
jira="$jira --user mothra"
RUNS $jira logout
echo "mojira123" | RUNS $jira login
echo "mothra123" | RUNS $jira login
###############################################################################
## vote for main issue, verify it shows when viewing the issue
@@ -195,7 +200,7 @@ echo "mojira123" | RUNS $jira login
RUNS $jira vote $issue
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
OK $issue $ENDPOINT/browse/$issue
EOF
RUNS $jira $issue
@@ -222,7 +227,7 @@ EOF
RUNS $jira vote $issue --down
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
OK $issue $ENDPOINT/browse/$issue
EOF
RUNS $jira $issue
@@ -244,19 +249,19 @@ description: |
EOF
###############################################################################
## set mojira user as watcher to issue and verify from REST api
## set mothra user as watcher to issue and verify from REST api
###############################################################################
RUNS $jira watch $issue
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
OK $issue $ENDPOINT/browse/$issue
EOF
# FIXME we probably need a watchers command to wrap this?
RUNS sh -c "$jira req /rest/api/2/issue/$issue/watchers | jq -r .watchers[].name"
DIFF <<EOF
gojira
mojira
mothra
EOF
###############################################################################
@@ -265,7 +270,7 @@ EOF
RUNS $jira trans "In Progress" $blocker --noedit
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
OK $blocker $ENDPOINT/browse/$blocker
EOF
###############################################################################
@@ -283,7 +288,7 @@ EOF
RUNS $jira backlog $blocker --noedit
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
OK $blocker $ENDPOINT/browse/$blocker
EOF
###############################################################################
@@ -301,7 +306,7 @@ EOF
RUNS $jira backlog $blocker
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
OK $blocker $ENDPOINT/browse/$blocker
EOF
###############################################################################
@@ -310,7 +315,7 @@ EOF
RUNS $jira prog $blocker
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
OK $blocker $ENDPOINT/browse/$blocker
EOF
###############################################################################
@@ -319,7 +324,7 @@ EOF
RUNS $jira done $blocker
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
OK $blocker $ENDPOINT/browse/$blocker
EOF
###############################################################################
@@ -350,7 +355,7 @@ EOF
RUNS $jira comment $issue --noedit -m "Yo, Comment"
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
OK $issue $ENDPOINT/browse/$issue
EOF
RUNS $jira $issue
@@ -371,7 +376,7 @@ description: |
description
comments:
- | # mojira, a minute ago
- | # mothra, a minute ago
Yo, Comment
EOF
@@ -382,7 +387,7 @@ EOF
RUNS $jira add labels $blocker test-label another-label
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
OK $blocker $ENDPOINT/browse/$blocker
EOF
RUNS $jira $blocker
@@ -410,7 +415,7 @@ EOF
RUNS $jira remove labels $blocker another-label
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
OK $blocker $ENDPOINT/browse/$blocker
EOF
RUNS $jira $blocker
@@ -438,7 +443,7 @@ EOF
RUNS $jira set labels $blocker more-label better-label
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
OK $blocker $ENDPOINT/browse/$blocker
EOF
RUNS $jira $blocker
@@ -461,12 +466,12 @@ description: |
EOF
###############################################################################
## Verify that "mojira" user can take the issue (reassign to self)
## Verify that "mothra" user can take the issue (reassign to self)
###############################################################################
RUNS $jira take $blocker
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
OK $blocker $ENDPOINT/browse/$blocker
EOF
RUNS $jira $blocker
@@ -477,7 +482,7 @@ status: Done
summary: blocks
project: KANBAN
issuetype: Bug
assignee: mojira
assignee: mothra
reporter: gojira
blockers:
depends: $issue[Backlog]
@@ -494,7 +499,7 @@ EOF
RUNS $jira give $blocker gojira
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
OK $blocker $ENDPOINT/browse/$blocker
EOF
RUNS $jira $blocker
+38 -33
View File
@@ -4,6 +4,11 @@ cd $(dirname $0)
jira="../jira --project PROJECT"
export JIRA_LOG_FORMAT="%{level:-5s} %{message}"
ENDPOINT="http://localhost:8080"
if [ -n "$JIRACLOUD" ]; then
ENDPOINT="https://go-jira.atlassian.net"
fi
PLAN 84
# cleanup from previous failed test executions
@@ -20,7 +25,7 @@ RUNS $jira create -o summary=summary -o description=description --noedit --saveF
issue=$(awk '/issue/{print $2}' issue.props)
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
OK $issue $ENDPOINT/browse/$issue
EOF
###############################################################################
@@ -67,7 +72,7 @@ EOF
RUNS $jira done $issue
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
OK $issue $ENDPOINT/browse/$issue
EOF
###############################################################################
@@ -85,13 +90,13 @@ EOF
RUNS $jira create -o summary=summary -o description=description --noedit --saveFile issue.props
issue=$(awk '/issue/{print $2}' issue.props)
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
OK $issue $ENDPOINT/browse/$issue
EOF
RUNS $jira create -o summary=dup -o description=dup --noedit --saveFile issue.props
dup=$(awk '/issue/{print $2}' issue.props)
DIFF <<EOF
OK $dup http://localhost:8080/browse/$dup
OK $dup $ENDPOINT/browse/$dup
EOF
###############################################################################
@@ -102,8 +107,8 @@ EOF
RUNS $jira $dup dups $issue --noedit
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
OK $dup http://localhost:8080/browse/$dup
OK $issue $ENDPOINT/browse/$issue
OK $dup $ENDPOINT/browse/$dup
EOF
RUNS $jira $issue
@@ -140,7 +145,7 @@ EOF
RUNS $jira create -o summary=blocks -o description=blocks --noedit --saveFile issue.props
blocker=$(awk '/issue/{print $2}' issue.props)
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
OK $blocker $ENDPOINT/browse/$blocker
EOF
###############################################################################
@@ -149,7 +154,7 @@ EOF
RUNS $jira $blocker blocks $issue
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
OK $issue $ENDPOINT/browse/$issue
EOF
RUNS $jira $issue
@@ -181,13 +186,13 @@ $(printf %-12s $blocker:) blocks
EOF
###############################################################################
# reset login for mojira for voting
# reset login for mothra for voting
###############################################################################
jira="$jira --user mojira"
jira="$jira --user mothra"
RUNS $jira logout
echo "mojira123" | RUNS $jira login
echo "mothra123" | RUNS $jira login
###############################################################################
## vote for main issue, verify it shows when viewing the issue
@@ -195,7 +200,7 @@ echo "mojira123" | RUNS $jira login
RUNS $jira vote $issue
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
OK $issue $ENDPOINT/browse/$issue
EOF
RUNS $jira $issue
@@ -222,7 +227,7 @@ EOF
RUNS $jira vote $issue --down
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
OK $issue $ENDPOINT/browse/$issue
EOF
RUNS $jira $issue
@@ -244,19 +249,19 @@ description: |
EOF
###############################################################################
## set mojira user as watcher to issue and verify from REST api
## set mothra user as watcher to issue and verify from REST api
###############################################################################
RUNS $jira watch $issue
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
OK $issue $ENDPOINT/browse/$issue
EOF
# FIXME we probably need a watchers command to wrap this?
RUNS sh -c "$jira req /rest/api/2/issue/$issue/watchers | jq -r .watchers[].name"
DIFF <<EOF
gojira
mojira
mothra
EOF
###############################################################################
@@ -279,12 +284,12 @@ ERROR Invalid Transition 'review', Available: Start Progress, Done
EOF
###############################################################################
## Set it to "Start Progress" and verify that assignee is set to mojira
## Set it to "Start Progress" and verify that assignee is set to mothra
###############################################################################
RUNS $jira start $blocker
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
OK $blocker $ENDPOINT/browse/$blocker
EOF
RUNS $jira $blocker
@@ -295,7 +300,7 @@ status: In Progress
summary: blocks
project: PROJECT
issuetype: Task
assignee: mojira
assignee: mothra
reporter: gojira
blockers:
depends: $issue[To Do]
@@ -311,7 +316,7 @@ EOF
RUNS $jira stop $blocker
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
OK $blocker $ENDPOINT/browse/$blocker
EOF
@@ -321,7 +326,7 @@ EOF
RUNS $jira done $blocker
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
OK $blocker $ENDPOINT/browse/$blocker
EOF
###############################################################################
@@ -352,7 +357,7 @@ EOF
RUNS $jira comment $issue --noedit -m "Yo, Comment"
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
OK $issue $ENDPOINT/browse/$issue
EOF
RUNS $jira $issue
@@ -373,7 +378,7 @@ description: |
description
comments:
- | # mojira, a minute ago
- | # mothra, a minute ago
Yo, Comment
EOF
@@ -384,7 +389,7 @@ EOF
RUNS $jira add labels $blocker test-label another-label
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
OK $blocker $ENDPOINT/browse/$blocker
EOF
RUNS $jira $blocker
@@ -395,7 +400,7 @@ status: Done
summary: blocks
project: PROJECT
issuetype: Task
assignee: mojira
assignee: mothra
reporter: gojira
blockers:
depends: $issue[To Do]
@@ -412,7 +417,7 @@ EOF
RUNS $jira remove labels $blocker another-label
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
OK $blocker $ENDPOINT/browse/$blocker
EOF
RUNS $jira $blocker
@@ -423,7 +428,7 @@ status: Done
summary: blocks
project: PROJECT
issuetype: Task
assignee: mojira
assignee: mothra
reporter: gojira
blockers:
depends: $issue[To Do]
@@ -440,7 +445,7 @@ EOF
RUNS $jira set labels $blocker more-label better-label
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
OK $blocker $ENDPOINT/browse/$blocker
EOF
RUNS $jira $blocker
@@ -451,7 +456,7 @@ status: Done
summary: blocks
project: PROJECT
issuetype: Task
assignee: mojira
assignee: mothra
reporter: gojira
blockers:
depends: $issue[To Do]
@@ -468,7 +473,7 @@ EOF
RUNS $jira give $blocker gojira
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
OK $blocker $ENDPOINT/browse/$blocker
EOF
RUNS $jira $blocker
@@ -491,12 +496,12 @@ description: |
EOF
###############################################################################
## Verify that "mojira" user can take the issue (reassign to self)
## Verify that "mothra" user can take the issue (reassign to self)
###############################################################################
RUNS $jira take $blocker
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
OK $blocker $ENDPOINT/browse/$blocker
EOF
RUNS $jira $blocker
@@ -507,7 +512,7 @@ status: Done
summary: blocks
project: PROJECT
issuetype: Task
assignee: mojira
assignee: mothra
reporter: gojira
blockers:
depends: $issue[To Do]
+35 -30
View File
@@ -4,6 +4,11 @@ cd $(dirname $0)
jira="../jira --project PROCESS"
export JIRA_LOG_FORMAT="%{level:-5s} %{message}"
ENDPOINT="http://localhost:8080"
if [ -n "$JIRACLOUD" ]; then
ENDPOINT="https://go-jira.atlassian.net"
fi
PLAN 84
# cleanup from previous failed test executions
@@ -21,7 +26,7 @@ RUNS $jira create -o summary=summary -o description=description --noedit --saveF
issue=$(awk '/issue/{print $2}' issue.props)
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
OK $issue $ENDPOINT/browse/$issue
EOF
###############################################################################
@@ -69,13 +74,13 @@ EOF
RUNS $jira start $issue
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
OK $issue $ENDPOINT/browse/$issue
EOF
RUNS $jira stop $issue
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
OK $issue $ENDPOINT/browse/$issue
EOF
###############################################################################
@@ -93,13 +98,13 @@ EOF
RUNS $jira create -o summary=summary -o description=description --noedit --saveFile issue.props
issue=$(awk '/issue/{print $2}' issue.props)
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
OK $issue $ENDPOINT/browse/$issue
EOF
RUNS $jira create -o summary=dup -o description=dup --noedit --saveFile issue.props
dup=$(awk '/issue/{print $2}' issue.props)
DIFF <<EOF
OK $dup http://localhost:8080/browse/$dup
OK $dup $ENDPOINT/browse/$dup
EOF
###############################################################################
@@ -112,9 +117,9 @@ EOF
RUNS $jira $dup dups $issue --noedit
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
OK $dup http://localhost:8080/browse/$dup
OK $dup http://localhost:8080/browse/$dup
OK $issue $ENDPOINT/browse/$issue
OK $dup $ENDPOINT/browse/$dup
OK $dup $ENDPOINT/browse/$dup
EOF
RUNS $jira $issue
@@ -151,7 +156,7 @@ EOF
RUNS $jira create -o summary=blocks -o description=blocks --noedit --saveFile issue.props
blocker=$(awk '/issue/{print $2}' issue.props)
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
OK $blocker $ENDPOINT/browse/$blocker
EOF
###############################################################################
@@ -160,7 +165,7 @@ EOF
RUNS $jira $blocker blocks $issue
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
OK $issue $ENDPOINT/browse/$issue
EOF
RUNS $jira $issue
@@ -192,13 +197,13 @@ $(printf %-12s $blocker:) blocks
EOF
###############################################################################
# reset login for mojira for voting
# reset login for mothra for voting
###############################################################################
jira="$jira --user mojira"
jira="$jira --user mothra"
RUNS $jira logout
echo "mojira123" | RUNS $jira login
echo "mothra123" | RUNS $jira login
###############################################################################
## vote for main issue, verify it shows when viewing the issue
@@ -206,7 +211,7 @@ echo "mojira123" | RUNS $jira login
RUNS $jira vote $issue
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
OK $issue $ENDPOINT/browse/$issue
EOF
RUNS $jira $issue
@@ -233,7 +238,7 @@ EOF
RUNS $jira vote $issue --down
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
OK $issue $ENDPOINT/browse/$issue
EOF
RUNS $jira $issue
@@ -255,19 +260,19 @@ description: |
EOF
###############################################################################
## set mojira user as watcher to issue and verify from REST api
## set mothra user as watcher to issue and verify from REST api
###############################################################################
RUNS $jira watch $issue
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
OK $issue $ENDPOINT/browse/$issue
EOF
# FIXME we probably need a watchers command to wrap this?
RUNS sh -c "$jira req /rest/api/2/issue/$issue/watchers | jq -r .watchers[].name"
DIFF <<EOF
gojira
mojira
mothra
EOF
###############################################################################
@@ -295,7 +300,7 @@ EOF
RUNS $jira start $blocker
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
OK $blocker $ENDPOINT/browse/$blocker
EOF
###############################################################################
@@ -304,7 +309,7 @@ EOF
RUNS $jira stop $blocker
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
OK $blocker $ENDPOINT/browse/$blocker
EOF
@@ -314,7 +319,7 @@ EOF
RUNS $jira reopen $blocker
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
OK $blocker $ENDPOINT/browse/$blocker
EOF
###############################################################################
@@ -345,7 +350,7 @@ EOF
RUNS $jira comment $issue --noedit -m "Yo, Comment"
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
OK $issue $ENDPOINT/browse/$issue
EOF
RUNS $jira $issue
@@ -366,7 +371,7 @@ description: |
description
comments:
- | # mojira, a minute ago
- | # mothra, a minute ago
Yo, Comment
EOF
@@ -377,7 +382,7 @@ EOF
RUNS $jira add labels $blocker test-label another-label
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
OK $blocker $ENDPOINT/browse/$blocker
EOF
RUNS $jira $blocker
@@ -405,7 +410,7 @@ EOF
RUNS $jira remove labels $blocker another-label
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
OK $blocker $ENDPOINT/browse/$blocker
EOF
RUNS $jira $blocker
@@ -433,7 +438,7 @@ EOF
RUNS $jira set labels $blocker more-label better-label
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
OK $blocker $ENDPOINT/browse/$blocker
EOF
RUNS $jira $blocker
@@ -456,12 +461,12 @@ description: |
EOF
###############################################################################
## Verify that "mojira" user can take the issue (reassign to self)
## Verify that "mothra" user can take the issue (reassign to self)
###############################################################################
RUNS $jira take $blocker
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
OK $blocker $ENDPOINT/browse/$blocker
EOF
RUNS $jira $blocker
@@ -472,7 +477,7 @@ status: Open
summary: blocks
project: PROCESS
issuetype: Task
assignee: mojira
assignee: mothra
reporter: gojira
blockers:
depends: $issue[Open]
@@ -489,7 +494,7 @@ EOF
RUNS $jira give $blocker gojira
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
OK $blocker $ENDPOINT/browse/$blocker
EOF
RUNS $jira $blocker
+31 -26
View File
@@ -4,6 +4,11 @@ cd $(dirname $0)
jira="../jira --project TASK"
export JIRA_LOG_FORMAT="%{level:-5s} %{message}"
ENDPOINT="http://localhost:8080"
if [ -n "$JIRACLOUD" ]; then
ENDPOINT="https://go-jira.atlassian.net"
fi
PLAN 82
# cleanup from previous failed test executions
@@ -20,7 +25,7 @@ RUNS $jira create -o summary=summary -o description=description --noedit --saveF
issue=$(awk '/issue/{print $2}' issue.props)
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
OK $issue $ENDPOINT/browse/$issue
EOF
###############################################################################
@@ -67,7 +72,7 @@ EOF
RUNS $jira done $issue
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
OK $issue $ENDPOINT/browse/$issue
EOF
###############################################################################
@@ -85,13 +90,13 @@ EOF
RUNS $jira create -o summary=summary -o description=description --noedit --saveFile issue.props
issue=$(awk '/issue/{print $2}' issue.props)
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
OK $issue $ENDPOINT/browse/$issue
EOF
RUNS $jira create -o summary=dup -o description=dup --noedit --saveFile issue.props
dup=$(awk '/issue/{print $2}' issue.props)
DIFF <<EOF
OK $dup http://localhost:8080/browse/$dup
OK $dup $ENDPOINT/browse/$dup
EOF
###############################################################################
@@ -104,8 +109,8 @@ EOF
RUNS $jira $dup dups $issue --noedit
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
OK $dup http://localhost:8080/browse/$dup
OK $issue $ENDPOINT/browse/$issue
OK $dup $ENDPOINT/browse/$dup
EOF
RUNS $jira $issue
@@ -142,7 +147,7 @@ EOF
RUNS $jira create -o summary=blocks -o description=blocks --noedit --saveFile issue.props
blocker=$(awk '/issue/{print $2}' issue.props)
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
OK $blocker $ENDPOINT/browse/$blocker
EOF
###############################################################################
@@ -151,7 +156,7 @@ EOF
RUNS $jira $blocker blocks $issue
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
OK $issue $ENDPOINT/browse/$issue
EOF
RUNS $jira $issue
@@ -183,13 +188,13 @@ $(printf %-12s $blocker:) blocks
EOF
###############################################################################
# reset login for mojira for voting
# reset login for mothra for voting
###############################################################################
jira="$jira --user mojira"
jira="$jira --user mothra"
RUNS $jira logout
echo "mojira123" | RUNS $jira login
echo "mothra123" | RUNS $jira login
###############################################################################
## vote for main issue, verify it shows when viewing the issue
@@ -197,7 +202,7 @@ echo "mojira123" | RUNS $jira login
RUNS $jira vote $issue
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
OK $issue $ENDPOINT/browse/$issue
EOF
RUNS $jira $issue
@@ -224,7 +229,7 @@ EOF
RUNS $jira vote $issue --down
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
OK $issue $ENDPOINT/browse/$issue
EOF
RUNS $jira $issue
@@ -246,19 +251,19 @@ description: |
EOF
###############################################################################
## set mojira user as watcher to issue and verify from REST api
## set mothra user as watcher to issue and verify from REST api
###############################################################################
RUNS $jira watch $issue
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
OK $issue $ENDPOINT/browse/$issue
EOF
# FIXME we probably need a watchers command to wrap this?
RUNS sh -c "$jira req /rest/api/2/issue/$issue/watchers | jq -r .watchers[].name"
DIFF <<EOF
gojira
mojira
mothra
EOF
###############################################################################
@@ -305,7 +310,7 @@ EOF
RUNS $jira done $blocker
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
OK $blocker $ENDPOINT/browse/$blocker
EOF
###############################################################################
@@ -336,7 +341,7 @@ EOF
RUNS $jira comment $issue --noedit -m "Yo, Comment"
DIFF <<EOF
OK $issue http://localhost:8080/browse/$issue
OK $issue $ENDPOINT/browse/$issue
EOF
RUNS $jira $issue
@@ -357,7 +362,7 @@ description: |
description
comments:
- | # mojira, a minute ago
- | # mothra, a minute ago
Yo, Comment
EOF
@@ -368,7 +373,7 @@ EOF
RUNS $jira add labels $blocker test-label another-label
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
OK $blocker $ENDPOINT/browse/$blocker
EOF
RUNS $jira $blocker
@@ -396,7 +401,7 @@ EOF
RUNS $jira remove labels $blocker another-label
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
OK $blocker $ENDPOINT/browse/$blocker
EOF
RUNS $jira $blocker
@@ -424,7 +429,7 @@ EOF
RUNS $jira set labels $blocker more-label better-label
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
OK $blocker $ENDPOINT/browse/$blocker
EOF
RUNS $jira $blocker
@@ -447,12 +452,12 @@ description: |
EOF
###############################################################################
## Verify that "mojira" user can take the issue (reassign to self)
## Verify that "mothra" user can take the issue (reassign to self)
###############################################################################
RUNS $jira take $blocker
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
OK $blocker $ENDPOINT/browse/$blocker
EOF
RUNS $jira $blocker
@@ -463,7 +468,7 @@ status: Done
summary: blocks
project: TASK
issuetype: Task
assignee: mojira
assignee: mothra
reporter: gojira
blockers:
depends: $issue[To Do]
@@ -480,7 +485,7 @@ EOF
RUNS $jira give $blocker gojira
DIFF <<EOF
OK $blocker http://localhost:8080/browse/$blocker
OK $blocker $ENDPOINT/browse/$blocker
EOF
RUNS $jira $blocker
+20
View File
@@ -0,0 +1,20 @@
FROM alpine:latest
RUN apk --update add openjdk8-jre curl screen && \
curl -s -L https://marketplace.atlassian.com/download/plugins/atlassian-plugin-sdk-tgz | tar xzf - && \
ln -s /atlassian* /atlassian
ENV PATH=/bin:/usr/bin:/atlassian/bin
# Copy in the serivce and also the root .m2 settings to force cache everything.
# We also copy in /root/.java settings to prevent the dumb spam prompt from
# the atlas-run command:
# Would you like to subscribe to the Atlassian developer mailing list? (Y/y/N/n) Y: :
COPY dockerroot /
WORKDIR /jiratestservice
EXPOSE 8080
# we wrap the command with screen so that the dumb atlas-run has a tty to watch. Without screen
# there is no tty so atlas-run will immediately read an EOF (aka CTRL-D) and interpret that to
# mean we want the service to begin the "graceful shutdown" and exit
CMD ["screen", "-DmL", "atlas-run", "--http-port", "8080", "--context-path", "ROOT", "--server", "localhost"]
-3
View File
@@ -1,3 +0,0 @@
RUN curl -Ls https://raw.githubusercontent.com/cptactionhank/docker-atlassian-jira-software/master/docker-entrypoint.sh -o /docker-entrypoint.sh \
&& chmod 755 /docker-entrypoint.sh \
&& echo jira.websudo.is.disabled = true >> /var/atlassian/jira/jira-config.properties
-3
View File
@@ -1,3 +0,0 @@
INCLUDE MERGE -VOLUME -COPY \
https://raw.githubusercontent.com/cptactionhank/docker-atlassian-jira-software/master/Dockerfile \
Dockerfile.inc
+16 -33
View File
@@ -2,39 +2,6 @@
The test are written using the `osht` bash testing framework. Please read the [documentation](https://github.com/coryb/osht/blob/master/README.md) for `osht`.
## Setup
These tests assume there is a jira service running at 127.0.0.1:8080 with user "gojira" and password "gojira123".
There should also be a poweruser "admin" with password "admin123"
The test Jira was setup following the instructions [here](https://github.com/cptactionhank/docker-atlassian-jira).
### build base docker image
```
docker run --rm -i -v $(pwd):/root:ro coryb/dfpp:1.0.2 Dockerfile.pre | docker build -t go-jira-base:latest -
```
### Initialize container
```
docker run --detach --name go-jira-test --publish 8080:8080 go-jira-base:latest
```
### create admin user
```
open http://localhost:8080
```
Then follow UI workflow to create "admin" user, skip intro and project creation.
### snapshot docker container
```
docker commit go-jira-test go-jira-test:latest
```
### Destroy base container
```
docker rm -f go-jira-test
```
## Running Test:
From the top level of the project you can run:
@@ -46,8 +13,24 @@ make
prove
```
### Running individual tests
To run a specific test you can run it directly like:
```
./100basic.t
```
There is a useful `-v` option to make the test more verbose and an `-a` option to casue the test to abort after the first failure.
The tests all require the jira service to be running from the docker container, so you will have to manually run the setup script:
```
./000setup.t
```
After than you can run the other tests over and over. The jira service is just a test instance started for local development. It comes with
a temporary license (I think it is 8 hours) so you will have to run the `./000setup.t` script at least once daily.
## API Documentation:
https://docs.atlassian.com/jira/REST/cloud/
https://docs.atlassian.com/jira-software/REST/cloud
## projectTempalteKey missing documentation
https://answers.atlassian.com/questions/36176301/jira-api-7.1.0-create-project
+6
View File
@@ -0,0 +1,6 @@
To avoid future confusion, we recommend that you include a license with your plugin.
This file is simply a reminder.
For a template license you can have a look at: http://www.opensource.org/licenses/
Atlassian releases most of its modules under the Apache2 license: http://opensource.org/licenses/Apache-2.0
+13
View File
@@ -0,0 +1,13 @@
You have successfully created an Atlassian Plugin!
Here are the SDK commands you'll use immediately:
* atlas-run -- installs this plugin into the product and starts it on localhost
* atlas-debug -- same as atlas-run, but allows a debugger to attach at port 5005
* atlas-cli -- after atlas-run or atlas-debug, opens a Maven command line window:
- 'pi' reinstalls the plugin into the running product instance
* atlas-help -- prints description for all commands in the SDK
Full documentation is always available at:
https://developer.atlassian.com/display/DOCS/Introduction+to+the+Atlassian+Plugin+SDK
+185
View File
@@ -0,0 +1,185 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.netflixskunkworks</groupId>
<artifactId>jiratestservice</artifactId>
<version>1.0</version>
<organization>
<name>Example Company</name>
<url>http://www.example.com/</url>
</organization>
<name>jiratestservice</name>
<description>This is the com.netflixskunkworks:jiratestservice plugin for Atlassian JIRA.</description>
<packaging>atlassian-plugin</packaging>
<dependencies>
<dependency>
<groupId>com.atlassian.jira</groupId>
<artifactId>jira-api</artifactId>
<version>${jira.version}</version>
<scope>provided</scope>
</dependency>
<!-- Add dependency on jira-core if you want access to JIRA implementation classes as well as the sanctioned API. -->
<!-- This is not normally recommended, but may be required eg when migrating a plugin originally developed against JIRA 4.x -->
<!--
<dependency>
<groupId>com.atlassian.jira</groupId>
<artifactId>jira-core</artifactId>
<version>${jira.version}</version>
<scope>provided</scope>
</dependency>
-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.atlassian.plugin</groupId>
<artifactId>atlassian-spring-scanner-annotation</artifactId>
<version>${atlassian.spring.scanner.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.atlassian.plugin</groupId>
<artifactId>atlassian-spring-scanner-runtime</artifactId>
<version>${atlassian.spring.scanner.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
<scope>provided</scope>
</dependency>
<!-- WIRED TEST RUNNER DEPENDENCIES -->
<dependency>
<groupId>com.atlassian.plugins</groupId>
<artifactId>atlassian-plugins-osgi-testrunner</artifactId>
<version>${plugin.testrunner.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>jsr311-api</artifactId>
<version>1.1.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.2.2-atlassian-1</version>
</dependency>
<!-- Uncomment to use TestKit in your project. Details at https://bitbucket.org/atlassian/jira-testkit -->
<!-- You can read more about TestKit at https://developer.atlassian.com/display/JIRADEV/Plugin+Tutorial+-+Smarter+integration+testing+with+TestKit -->
<!--
<dependency>
<groupId>com.atlassian.jira.tests</groupId>
<artifactId>jira-testkit-client</artifactId>
<version>${testkit.version}</version>
<scope>test</scope>
</dependency>
-->
</dependencies>
<build>
<plugins>
<plugin>
<groupId>com.atlassian.maven.plugins</groupId>
<artifactId>maven-jira-plugin</artifactId>
<version>${amps.version}</version>
<extensions>true</extensions>
<configuration>
<applications>
<application>
<applicationKey>jira-software</applicationKey>
<version>${jira.version}</version>
</application>
</applications>
<productVersion>${jira.version}</productVersion>
<productDataVersion>${jira.version}</productDataVersion>
<!-- Uncomment to install TestKit backdoor in JIRA. -->
<!--
<pluginArtifacts>
<pluginArtifact>
<groupId>com.atlassian.jira.tests</groupId>
<artifactId>jira-testkit-plugin</artifactId>
<version>${testkit.version}</version>
</pluginArtifact>
</pluginArtifacts>
-->
<enableQuickReload>true</enableQuickReload>
<enableFastdev>false</enableFastdev>
<!-- See here for an explanation of default instructions: -->
<!-- https://developer.atlassian.com/docs/advanced-topics/configuration-of-instructions-in-atlassian-plugins -->
<instructions>
<Atlassian-Plugin-Key>${atlassian.plugin.key}</Atlassian-Plugin-Key>
<!-- Add package to export here -->
<Export-Package>
com.netflixskunkworks.api,
</Export-Package>
<!-- Add package import here -->
<Import-Package>
org.springframework.osgi.*;resolution:="optional",
org.eclipse.gemini.blueprint.*;resolution:="optional",
*
</Import-Package>
<!-- Ensure plugin is spring powered -->
<Spring-Context>*</Spring-Context>
</instructions>
</configuration>
</plugin>
<plugin>
<groupId>com.atlassian.plugin</groupId>
<artifactId>atlassian-spring-scanner-maven-plugin</artifactId>
<version>${atlassian.spring.scanner.version}</version>
<executions>
<execution>
<goals>
<goal>atlassian-spring-scanner</goal>
</goals>
<phase>process-classes</phase>
</execution>
</executions>
<configuration>
<scannedDependencies>
<dependency>
<groupId>com.atlassian.plugin</groupId>
<artifactId>atlassian-spring-scanner-external-jar</artifactId>
</dependency>
</scannedDependencies>
<verbose>false</verbose>
</configuration>
</plugin>
</plugins>
</build>
<properties>
<jira.version>7.2.0</jira.version>
<amps.version>6.2.6</amps.version>
<plugin.testrunner.version>1.2.3</plugin.testrunner.version>
<atlassian.spring.scanner.version>1.2.13</atlassian.spring.scanner.version>
<!-- This key is used to keep the consistency between the key in atlassian-plugin.xml and the key to generate bundle. -->
<atlassian.plugin.key>${project.groupId}.${project.artifactId}</atlassian.plugin.key>
<!-- TestKit version 6.x for JIRA 6.x -->
<testkit.version>6.3.11</testkit.version>
</properties>
</project>
@@ -0,0 +1,7 @@
<atlassian-plugin key="${atlassian.plugin.key}" name="${project.name}" plugins-version="2">
<plugin-info>
<description>${project.description}</description>
<version>${project.version}</version>
<vendor name="${project.organization.name}" url="${project.organization.url}" />
</plugin-info>
</atlassian-plugin>
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE map SYSTEM "http://java.sun.com/dtd/preferences.dtd">
<map MAP_XML_VERSION="1.0">
<entry key="sdk-email-subscribe" value="true"/>
</map>
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE map SYSTEM "http://java.sun.com/dtd/preferences.dtd">
<map MAP_XML_VERSION="1.0">
<entry key="last_update_check" value="2016-08-29"/>
<entry key="sdk-pom-update-check-6.2.6-cbc3c672c37f65828d50132ed303cf7a" value="true"/>
</map>
+77
View File
@@ -0,0 +1,77 @@
<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
<profiles>
<!-- Default profile containing Atlassian servers -->
<profile>
<id>defaultProfile</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<repositories>
<repository>
<id>atlassian-public</id>
<url>https://maven.atlassian.com/repository/public</url>
<snapshots>
<enabled>true</enabled>
<updatePolicy>never</updatePolicy>
<checksumPolicy>warn</checksumPolicy>
</snapshots>
<releases>
<enabled>true</enabled>
<checksumPolicy>warn</checksumPolicy>
<updatePolicy>never</updatePolicy>
</releases>
</repository>
<repository>
<id>atlassian-plugin-sdk</id>
<url>file://${env.ATLAS_HOME}/repository</url>
<snapshots>
<enabled>true</enabled>
<updatePolicy>never</updatePolicy>
</snapshots>
<releases>
<enabled>true</enabled>
<checksumPolicy>warn</checksumPolicy>
<updatePolicy>never</updatePolicy>
</releases>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>atlassian-public</id>
<url>https://maven.atlassian.com/repository/public</url>
<releases>
<enabled>true</enabled>
<checksumPolicy>warn</checksumPolicy>
<updatePolicy>never</updatePolicy>
</releases>
<snapshots>
<updatePolicy>never</updatePolicy>
<checksumPolicy>warn</checksumPolicy>
</snapshots>
</pluginRepository>
<pluginRepository>
<id>atlassian-plugin-sdk</id>
<url>file://${env.ATLAS_HOME}/repository</url>
<releases>
<enabled>true</enabled>
<checksumPolicy>warn</checksumPolicy>
<updatePolicy>never</updatePolicy>
</releases>
<snapshots>
<enabled>true</enabled>
<updatePolicy>never</updatePolicy>
</snapshots>
</pluginRepository>
</pluginRepositories>
<properties>
<downloadSources>true</downloadSources>
<downloadJavadocs>true</downloadJavadocs>
</properties>
</profile>
</profiles>
</settings>
+128 -60
View File
@@ -1,60 +1,87 @@
package jira
var all_templates = map[string]string{
"debug": default_debug_template,
"fields": default_debug_template,
"editmeta": default_debug_template,
"transmeta": default_debug_template,
"createmeta": default_debug_template,
"issuelinktypes": default_debug_template,
"list": default_list_template,
"table": default_table_template,
"view": default_view_template,
"edit": default_edit_template,
"transitions": default_transitions_template,
"components": default_components_template,
"issuetypes": default_issuetypes_template,
"create": default_create_template,
"comment": default_comment_template,
"transition": default_transition_template,
"request": default_debug_template,
var allTemplates = map[string]string{
"debug": defaultDebugTemplate,
"fields": defaultDebugTemplate,
"editmeta": defaultDebugTemplate,
"transmeta": defaultDebugTemplate,
"createmeta": defaultDebugTemplate,
"issuelinktypes": defaultDebugTemplate,
"list": defaultListTemplate,
"table": defaultTableTemplate,
"view": defaultViewTemplate,
"edit": defaultEditTemplate,
"transitions": defaultTransitionsTemplate,
"components": defaultComponentsTemplate,
"issuetypes": defaultIssuetypesTemplate,
"create": defaultCreateTemplate,
"comment": defaultCommentTemplate,
"transition": defaultTransitionTemplate,
"request": defaultDebugTemplate,
"worklog": defaultWorklogTemplate,
"worklogs": defaultWorklogsTemplate,
}
const default_debug_template = "{{ . | toJson}}\n"
const defaultDebugTemplate = "{{ . | toJson}}\n"
const default_list_template = "{{ range .issues }}{{ .key | append \":\" | printf \"%-12s\"}} {{ .fields.summary }}\n{{ end }}"
const defaultListTemplate = "{{ range .issues }}{{ .key | append \":\" | printf \"%-12s\"}} {{ .fields.summary }}\n{{ end }}"
const default_table_template = `+{{ "-" | rep 16 }}+{{ "-" | rep 57 }}+{{ "-" | rep 14 }}+{{ "-" | rep 14 }}+{{ "-" | rep 12 }}+{{ "-" | rep 14 }}+{{ "-" | rep 14 }}+
const defaultTableTemplate = `+{{ "-" | rep 16 }}+{{ "-" | rep 57 }}+{{ "-" | rep 14 }}+{{ "-" | rep 14 }}+{{ "-" | rep 12 }}+{{ "-" | rep 14 }}+{{ "-" | rep 14 }}+
| {{ "Issue" | printf "%-14s" }} | {{ "Summary" | printf "%-55s" }} | {{ "Priority" | printf "%-12s" }} | {{ "Status" | printf "%-12s" }} | {{ "Age" | printf "%-10s" }} | {{ "Reporter" | printf "%-12s" }} | {{ "Assignee" | printf "%-12s" }} |
+{{ "-" | rep 16 }}+{{ "-" | rep 57 }}+{{ "-" | rep 14 }}+{{ "-" | rep 14 }}+{{ "-" | rep 12 }}+{{ "-" | rep 14 }}+{{ "-" | rep 14 }}+
{{ range .issues }}| {{ .key | printf "%-14s"}} | {{ .fields.summary | abbrev 55 | printf "%-55s" }} | {{.fields.priority.name | printf "%-12s" }} | {{.fields.status.name | printf "%-12s" }} | {{.fields.created | age | printf "%-10s" }} | {{if .fields.reporter}}{{ .fields.reporter.name | printf "%-12s"}}{{else}}<unassigned>{{end}} | {{if .fields.assignee }}{{.fields.assignee.name | printf "%-12s" }}{{else}}<unassigned>{{end}} |
{{ end }}+{{ "-" | rep 16 }}+{{ "-" | rep 57 }}+{{ "-" | rep 14 }}+{{ "-" | rep 14 }}+{{ "-" | rep 12 }}+{{ "-" | rep 14 }}+{{ "-" | rep 14 }}+
{{ range .issues -}}
| {{ .key | printf "%-14s"}} | {{ .fields.summary | abbrev 55 | printf "%-55s" }} | {{.fields.priority.name | printf "%-12s" }} | {{.fields.status.name | printf "%-12s" }} | {{.fields.created | age | printf "%-10s" }} | {{if .fields.reporter}}{{ .fields.reporter.name | printf "%-12s"}}{{else}}<unassigned>{{end}} | {{if .fields.assignee }}{{.fields.assignee.name | printf "%-12s" }}{{else}}<unassigned> {{end}} |
{{ end -}}
+{{ "-" | rep 16 }}+{{ "-" | rep 57 }}+{{ "-" | rep 14 }}+{{ "-" | rep 14 }}+{{ "-" | rep 12 }}+{{ "-" | rep 14 }}+{{ "-" | rep 14 }}+
`
const default_view_template = `issue: {{ .key }}{{if .fields.created}}
created: {{ .fields.created | age }} ago{{end}}{{if .fields.status}}
status: {{ .fields.status.name }}{{end}}
const defaultViewTemplate = `{{/* view template */ -}}
issue: {{ .key }}
{{if .fields.created -}}
created: {{ .fields.created | age }} ago
{{end -}}
{{if .fields.status -}}
status: {{ .fields.status.name }}
{{end -}}
summary: {{ .fields.summary }}
project: {{ .fields.project.key }}{{if .fields.components}}
components: {{ range .fields.components }}{{ .name }} {{end}}{{end}}{{if .fields.issuetype}}
issuetype: {{ .fields.issuetype.name }}{{end}}{{if .fields.assignee}}
assignee: {{ .fields.assignee.name }}{{end}}
reporter: {{ if .fields.reporter }}{{ .fields.reporter.name }}{{end}}{{if .fields.customfield_10110}}
watchers: {{ range .fields.customfield_10110 }}{{ .name }} {{end}}{{end}}{{if .fields.issuelinks}}
project: {{ .fields.project.key }}
{{if .fields.components -}}
components: {{ range .fields.components }}{{ .name }} {{end}}
{{end -}}
{{if .fields.issuetype -}}
issuetype: {{ .fields.issuetype.name }}
{{end -}}
{{if .fields.assignee -}}
assignee: {{ .fields.assignee.name }}
{{end -}}
reporter: {{ if .fields.reporter }}{{ .fields.reporter.name }}{{end}}
{{if .fields.customfield_10110 -}}
watchers: {{ range .fields.customfield_10110 }}{{ .name }} {{end}}
{{end -}}
{{if .fields.issuelinks -}}
blockers: {{ range .fields.issuelinks }}{{if .outwardIssue}}{{ .outwardIssue.key }}[{{.outwardIssue.fields.status.name}}]{{end}}{{end}}
depends: {{ range .fields.issuelinks }}{{if .inwardIssue}}{{ .inwardIssue.key }}[{{.inwardIssue.fields.status.name}}]{{end}}{{end}}{{end}}{{if .fields.priority}}
priority: {{ .fields.priority.name }}{{end}}{{if .fields.votes}}
votes: {{ .fields.votes.votes}}{{end}}{{if .fields.labels}}
labels: {{ join ", " .fields.labels }}{{end}}
depends: {{ range .fields.issuelinks }}{{if .inwardIssue}}{{ .inwardIssue.key }}[{{.inwardIssue.fields.status.name}}]{{end}}{{end}}
{{end -}}
{{if .fields.priority -}}
priority: {{ .fields.priority.name }}
{{end -}}
{{if .fields.votes -}}
votes: {{ .fields.votes.votes}}
{{end -}}
{{if .fields.labels -}}
labels: {{ join ", " .fields.labels }}
{{end -}}
description: |
{{ or .fields.description "" | indent 2 }}{{if .fields.comment.comments}}
{{ or .fields.description "" | indent 2 }}
{{if .fields.comment.comments}}
comments:
{{ range .fields.comment.comments }} - | # {{.author.name}}, {{.created | age}} ago
{{ or .body "" | indent 4}}
{{end}}{{end}}
{{end}}
{{end -}}
`
const default_edit_template = `# issue: {{ .key }}
const defaultEditTemplate = `{{/* edit template */ -}}
# issue: {{ .key }}
update:
comment:
- add:
@@ -67,8 +94,8 @@ fields:
- name: {{ .name }}{{end}}{{end}}
assignee:
name: {{ if .overrides.assignee }}{{.overrides.assignee}}{{else}}{{if .fields.assignee }}{{ .fields.assignee.name }}{{end}}{{end}}
# reporter:
# name: {{ if .overrides.reporter }}{{ .overrides.reporter }}{{else if .fields.reporter}}{{ .fields.reporter.name }}{{end}}
reporter:
name: {{ if .overrides.reporter }}{{ .overrides.reporter }}{{else if .fields.reporter}}{{ .fields.reporter.name }}{{end}}
# watchers
customfield_10110: {{ range .fields.customfield_10110 }}
- name: {{ .name }}{{end}}{{if .overrides.watcher}}
@@ -82,16 +109,17 @@ fields:
# {{ or .body "" | indent 4 | comment}}
# {{end}}
`
const default_transitions_template = `{{ range .transitions }}{{.id }}: {{.name}}
const defaultTransitionsTemplate = `{{ range .transitions }}{{.id }}: {{.name}}
{{end}}`
const default_components_template = `{{ range . }}{{.id }}: {{.name}}
const defaultComponentsTemplate = `{{ range . }}{{.id }}: {{.name}}
{{end}}`
const default_issuetypes_template = `{{ range .projects }}{{ range .issuetypes }}{{color "+bh"}}{{.name | append ":" | printf "%-13s" }}{{color "reset"}} {{.description}}
const defaultIssuetypesTemplate = `{{ range .projects }}{{ range .issuetypes }}{{color "+bh"}}{{.name | append ":" | printf "%-13s" }}{{color "reset"}} {{.description}}
{{end}}{{end}}`
const default_create_template = `fields:
const defaultCreateTemplate = `{{/* create template */ -}}
fields:
project:
key: {{ or .overrides.project "" }}
issuetype:
@@ -112,41 +140,81 @@ const default_create_template = `fields:
- name: {{.}}{{end}}
- name:{{end}}`
const default_comment_template = `body: |~
const defaultCommentTemplate = `body: |~
{{ or .overrides.comment "" | indent 2 }}
`
const default_transition_template = `update:
const defaultTransitionTemplate = `{{/* transition template */ -}}
update:
comment:
- add:
body: |~
{{ or .overrides.comment "" | indent 10 }}
fields:{{if .meta.fields.assignee}}
fields:
{{- if .meta.fields.assignee}}
assignee:
name: {{if .overrides.assignee}}{{.overrides.assignee}}{{else}}{{if .fields.assignee}}{{.fields.assignee.name}}{{end}}{{end}}{{end}}{{if .meta.fields.components}}
name: {{if .overrides.assignee}}{{.overrides.assignee}}{{else}}{{if .fields.assignee}}{{.fields.assignee.name}}{{end}}{{end}}
{{- end -}}
{{if .meta.fields.components}}
components: # Values: {{ range .meta.fields.components.allowedValues }}{{.name}}, {{end}}{{if .overrides.components }}{{ range (split "," .overrides.components)}}
- name: {{.}}{{end}}{{else}}{{ range .fields.components }}
- name: {{ .name }}{{end}}{{end}}{{end}}{{if .meta.fields.description}}
description: {{or .overrides.description .fields.description }}{{end}}{{if .meta.fields.fixVersions}}{{if .meta.fields.fixVersions.allowedValues}}
- name: {{ .name }}{{end}}{{end}}
{{- end -}}
{{if .meta.fields.description}}
description: {{or .overrides.description .fields.description }}
{{- end -}}
{{if .meta.fields.fixVersions -}}
{{if .meta.fields.fixVersions.allowedValues}}
fixVersions: # Values: {{ range .meta.fields.fixVersions.allowedValues }}{{.name}}, {{end}}{{if .overrides.fixVersions}}{{ range (split "," .overrides.fixVersions)}}
- name: {{.}}{{end}}{{else}}{{range .fields.fixVersions}}
- name: {{.}}{{end}}{{end}}{{end}}{{end}}{{if .meta.fields.issuetype}}
- name: {{.}}{{end}}{{end}}
{{- end -}}
{{- end -}}
{{if .meta.fields.issuetype}}
issuetype: # Values: {{ range .meta.fields.issuetype.allowedValues }}{{.name}}, {{end}}
name: {{if .overrides.issuetype}}{{.overrides.issuetype}}{{else}}{{if .fields.issuetype}}{{.fields.issuetype.name}}{{end}}{{end}}{{end}}{{if .meta.fields.labels}}
name: {{if .overrides.issuetype}}{{.overrides.issuetype}}{{else}}{{if .fields.issuetype}}{{.fields.issuetype.name}}{{end}}{{end}}
{{- end -}}
{{if .meta.fields.labels}}
labels: {{range .fields.labels}}
- {{.}}{{end}}{{if .overrides.labels}}{{range (split "," .overrides.labels)}}
- {{.}}{{end}}{{end}}{{end}}{{if .meta.fields.priority}}
- {{.}}{{end}}{{end}}
{{- end -}}
{{if .meta.fields.priority}}
priority: # Values: {{ range .meta.fields.priority.allowedValues }}{{.name}}, {{end}}
name: {{ or .overrides.priority "unassigned" }}{{end}}{{if .meta.fields.reporter}}
name: {{ or .overrides.priority "unassigned" }}
{{- end -}}
{{if .meta.fields.reporter}}
reporter:
name: {{if .overrides.reporter}}{{.overrides.reporter}}{{else}}{{if .fields.reporter}}{{.fields.reporter.name}}{{end}}{{end}}{{end}}{{if .meta.fields.resolution}}
name: {{if .overrides.reporter}}{{.overrides.reporter}}{{else}}{{if .fields.reporter}}{{.fields.reporter.name}}{{end}}{{end}}
{{- end -}}
{{if .meta.fields.resolution}}
resolution: # Values: {{ range .meta.fields.resolution.allowedValues }}{{.name}}, {{end}}
name: {{if .overrides.resolution}}{{.overrides.resolution}}{{else if .fields.resolution}}{{.fields.resolution.name}}{{else}}{{or .overrides.defaultResolution "Fixed"}}{{end}}{{end}}{{if .meta.fields.summary}}
summary: {{or .overrides.summary .fields.summary}}{{end}}{{if .meta.fields.versions.allowedValues}}
name: {{if .overrides.resolution}}{{.overrides.resolution}}{{else if .fields.resolution}}{{.fields.resolution.name}}{{else}}{{or .overrides.defaultResolution "Fixed"}}{{end}}
{{- end -}}
{{if .meta.fields.summary}}
summary: {{or .overrides.summary .fields.summary}}
{{- end -}}
{{if .meta.fields.versions.allowedValues}}
versions: # Values: {{ range .meta.fields.versions.allowedValues }}{{.name}}, {{end}}{{if .overrides.versions}}{{ range (split "," .overrides.versions)}}
- name: {{.}}{{end}}{{else}}{{range .fields.versions}}
- name: {{.}}{{end}}{{end}}{{end}}
- name: {{.}}{{end}}{{end}}
{{- end}}
transition:
id: {{ .transition.id }}
name: {{ .transition.name }}
`
const defaultWorklogTemplate = `{{/* worklog template */ -}}
# issue: {{ .issue }}
comment: |~
{{ or .comment "" }}
timeSpent: {{ or .timeSpent "" }}
started:
`
const defaultWorklogsTemplate = `{{/* worklogs template */ -}}
{{ range .worklogs }}- # {{.author.name}}, {{.created | age}} ago
comment: {{ or .comment "" }}
timeSpent: {{ .timeSpent }}
{{end}}`
+41
View File
@@ -0,0 +1,41 @@
package jira
import (
"fmt"
"net"
"net/http"
"os"
"time"
)
type Transport struct {
shadow http.Transport
}
func NewUnixProxyTransport(path string) *Transport {
dial := func(network, addr string) (net.Conn, error) {
return net.Dial("unix", path)
}
shadow := http.Transport{
Dial: dial,
DialTLS: dial,
DisableKeepAlives: true,
ResponseHeaderTimeout: 30 * time.Second,
ExpectContinueTimeout: 10 * time.Second,
}
return &Transport{shadow}
}
func UnixProxy(path string) *Transport {
return NewUnixProxyTransport(os.ExpandEnv(path))
}
func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) {
req2 := *req
url2 := *req.URL
req2.URL = &url2
req2.URL.Opaque = fmt.Sprintf("//%s%s", req.URL.Host, req.URL.EscapedPath())
return t.shadow.RoundTrip(&req2)
}
+41 -37
View File
@@ -4,7 +4,6 @@ import (
"bufio"
"bytes"
"encoding/json"
"errors"
"fmt"
"github.com/mgutz/ansi"
"gopkg.in/coryb/yaml.v2"
@@ -26,10 +25,12 @@ func homedir() string {
return os.Getenv("HOME")
}
// FindParentPaths will find all available paths from the current path up to the root
// that matches the given fileName path
func FindParentPaths(fileName string) []string {
cwd, _ := os.Getwd()
paths := make([]string, 0)
paths := []string{}
// special case if homedir is not in current path then check there anyway
homedir := homedir()
@@ -57,12 +58,14 @@ func FindParentPaths(fileName string) []string {
return paths
}
// FindClosestParentPath finds the path that matches the given fileName path that is
// closest to the current working directory
func FindClosestParentPath(fileName string) (string, error) {
paths := FindParentPaths(fileName)
if len(paths) > 0 {
return paths[len(paths)-1], nil
}
return "", errors.New(fmt.Sprintf("%s not found in parent directory hierarchy", fileName))
return "", fmt.Errorf("%s not found in parent directory hierarchy", fileName)
}
func readFile(file string) string {
@@ -92,35 +95,36 @@ func copyFile(src, dst string) (err error) {
}
func fuzzyAge(start string) (string, error) {
if t, err := time.Parse("2006-01-02T15:04:05.000-0700", start); err != nil {
t, err := time.Parse("2006-01-02T15:04:05.000-0700", start)
if err != nil {
return "", err
} else {
delta := time.Now().Sub(t)
if delta.Minutes() < 2 {
return "a minute", nil
} else if dm := delta.Minutes(); dm < 45 {
return fmt.Sprintf("%d minutes", int(dm)), nil
} else if dm := delta.Minutes(); dm < 90 {
return "an hour", nil
} else if dh := delta.Hours(); dh < 24 {
return fmt.Sprintf("%d hours", int(dh)), nil
} else if dh := delta.Hours(); dh < 48 {
return "a day", nil
} else {
return fmt.Sprintf("%d days", int(delta.Hours()/24)), nil
}
}
return "unknown", nil
delta := time.Now().Sub(t)
if delta.Minutes() < 2 {
return "a minute", nil
} else if dm := delta.Minutes(); dm < 45 {
return fmt.Sprintf("%d minutes", int(dm)), nil
} else if dm := delta.Minutes(); dm < 90 {
return "an hour", nil
} else if dh := delta.Hours(); dh < 24 {
return fmt.Sprintf("%d hours", int(dh)), nil
} else if dh := delta.Hours(); dh < 48 {
return "a day", nil
}
return fmt.Sprintf("%d days", int(delta.Hours()/24)), nil
}
func dateFormat(format string, content string) (string, error) {
if t, err := time.Parse("2006-01-02T15:04:05.000-0700", content); err != nil {
t, err := time.Parse("2006-01-02T15:04:05.000-0700", content)
if err != nil {
return "", err
} else {
return t.Format(format), nil
}
return t.Format(format), nil
}
// RunTemplate will run the give templateContent as a golang text/template
// and pass the provided data to the template execution. It will write
// the output to the provided "out" writer.
func RunTemplate(templateContent string, data interface{}, out io.Writer) error {
return runTemplate(templateContent, data, out)
}
@@ -132,11 +136,11 @@ func runTemplate(templateContent string, data interface{}, out io.Writer) error
funcs := map[string]interface{}{
"toJson": func(content interface{}) (string, error) {
if bytes, err := json.MarshalIndent(content, "", " "); err != nil {
bytes, err := json.MarshalIndent(content, "", " ")
if err != nil {
return "", err
} else {
return string(bytes), nil
}
return string(bytes), nil
},
"append": func(more string, content interface{}) (string, error) {
switch value := content.(type) {
@@ -145,13 +149,13 @@ func runTemplate(templateContent string, data interface{}, out io.Writer) error
case []byte:
return string(append(content.([]byte), []byte(more)...)), nil
default:
return "", errors.New(fmt.Sprintf("Unknown type: %s", value))
return "", fmt.Errorf("Unknown type: %s", value)
}
},
"indent": func(spaces int, content string) string {
indent := make([]rune, spaces+1, spaces+1)
indent[0] = '\n'
for i := 1; i < spaces+1; i += 1 {
for i := 1; i < spaces+1; i++ {
indent[i] = ' '
}
@@ -194,7 +198,7 @@ func runTemplate(templateContent string, data interface{}, out io.Writer) error
},
"rep": func(count int, content string) string {
var buffer bytes.Buffer
for i := 0; i < count; i += 1 {
for i := 0; i < count; i++ {
buffer.WriteString(content)
}
return buffer.String()
@@ -206,19 +210,19 @@ func runTemplate(templateContent string, data interface{}, out io.Writer) error
return dateFormat(format, content)
},
}
if tmpl, err := template.New("template").Funcs(funcs).Parse(templateContent); err != nil {
tmpl, err := template.New("template").Funcs(funcs).Parse(templateContent)
if err != nil {
log.Errorf("Failed to parse template: %s", err)
return err
} else {
if err := tmpl.Execute(out, data); err != nil {
log.Errorf("Failed to execute template: %s", err)
return err
}
}
if err := tmpl.Execute(out, data); err != nil {
log.Errorf("Failed to execute template: %s", err)
return err
}
return nil
}
func responseToJson(resp *http.Response, err error) (interface{}, error) {
func responseToJSON(resp *http.Response, err error) (interface{}, error) {
if err != nil {
return nil, err
}
@@ -367,7 +371,7 @@ func mkdir(dir string) error {
log.Errorf("Failed to stat %s: %s", dir, err)
return err
} else if err == nil && !stat.IsDir() {
err := fmt.Errorf("%s exists and is not a directory!", dir)
err := fmt.Errorf("%s exists and is not a directory", dir)
log.Errorf("%s", err)
return err
} else {