mirror of
https://github.com/Threnklyn/jira.git
synced 2026-06-13 16:13:34 +02:00
Compare commits
52 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f896555299 | |||
| e0b2c2d240 | |||
| a5cb93f112 | |||
| cbbf335439 | |||
| 92b5e38912 | |||
| 6260e4964f | |||
| 485d65f12b | |||
| 1f33400288 | |||
| 37332354b7 | |||
| 7530b309e2 | |||
| e93bf71fea | |||
| d022f0ad70 | |||
| 4d5076230c | |||
| 3cbd2f85a4 | |||
| 970876851b | |||
| f74c45d7d7 | |||
| 4e7e52288d | |||
| 63f41e5e88 | |||
| dbf6a5a265 | |||
| b2056be287 | |||
| 6beb941d82 | |||
| b297d5a4ef | |||
| bf7f38de87 | |||
| f7ed1ed8d8 | |||
| 280c0f24b3 | |||
| 6e296052f5 | |||
| 189b0d252c | |||
| 0e453a45d3 | |||
| 179596ff12 | |||
| 50bac02419 | |||
| 4b7e24a199 | |||
| b9bf8455bd | |||
| 9111231545 | |||
| 986528d4ea | |||
| 9c1f028be2 | |||
| e3c5051e5e | |||
| 580ea50b37 | |||
| be31acde65 | |||
| a2f8b7ef65 | |||
| c28d46fe8f | |||
| 108a5b4976 | |||
| e3d11357e1 | |||
| dfb10740f5 | |||
| adc08935b4 | |||
| 073c8a3694 | |||
| c4a31a498e | |||
| bcad37089a | |||
| b2ba8de15d | |||
| 6016bda571 | |||
| 34ca09cf1a | |||
| d7fb88ee41 | |||
| e58625b00c |
@@ -7,3 +7,8 @@ src/github.com/op/
|
|||||||
src/gopkg.in/
|
src/gopkg.in/
|
||||||
jira
|
jira
|
||||||
jira.exe
|
jira.exe
|
||||||
|
schemas/*.json
|
||||||
|
t/issue.props
|
||||||
|
t/.jira.d/templates
|
||||||
|
dist/
|
||||||
|
src/
|
||||||
+15
@@ -0,0 +1,15 @@
|
|||||||
|
language: go
|
||||||
|
|
||||||
|
go:
|
||||||
|
- 1.6
|
||||||
|
|
||||||
|
matrix:
|
||||||
|
fast_finish: true
|
||||||
|
|
||||||
|
before_install:
|
||||||
|
- go get github.com/golang/lint/golint
|
||||||
|
|
||||||
|
script:
|
||||||
|
- go vet -x ./...
|
||||||
|
- $HOME/gopath/bin/golint ./...
|
||||||
|
- make
|
||||||
@@ -1,5 +1,38 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 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)]
|
||||||
|
* allow for defaultResolution option for transition command [Cory Bennett] [[a328c2d](https://github.com/Netflix-Skunkworks/go-jira/commit/a328c2d)]
|
||||||
|
* add "backlog" command for Kanban related Issues [Cory Bennett] [[5d39b23](https://github.com/Netflix-Skunkworks/go-jira/commit/5d39b23)]
|
||||||
|
* fix --noedit flag with "dups" command [Cory Bennett] [[37c07fa](https://github.com/Netflix-Skunkworks/go-jira/commit/37c07fa)]
|
||||||
|
* add "votes" and "labels" to default view template [Cory Bennett] [[6f73b8c](https://github.com/Netflix-Skunkworks/go-jira/commit/6f73b8c)]
|
||||||
|
* add "blockerType" config param, for issueLinkType use for "blocks" command [Cory Bennett] [[30fd301](https://github.com/Netflix-Skunkworks/go-jira/commit/30fd301)]
|
||||||
|
* update gitter room [Cory Bennett] [[4b822b1](https://github.com/Netflix-Skunkworks/go-jira/commit/4b822b1)]
|
||||||
|
* default issuetype to "Bug" for project that have Bug, otherwise try "Task" [Cory Bennett] [[0c807b4](https://github.com/Netflix-Skunkworks/go-jira/commit/0c807b4)]
|
||||||
|
* make view template only show fields that have values [Cory Bennett] [[8238fe8](https://github.com/Netflix-Skunkworks/go-jira/commit/8238fe8)]
|
||||||
|
* make default create template only display fields if they are valid fields for the project [Cory Bennett] [[adc2ace](https://github.com/Netflix-Skunkworks/go-jira/commit/adc2ace)]
|
||||||
|
* ignore empty json fields when processing templates [Cory Bennett] [[f5f3e28](https://github.com/Netflix-Skunkworks/go-jira/commit/f5f3e28)]
|
||||||
|
* allow JIRA_LOG_FORMAT env variable to control log output format [Cory Bennett] [[469def0](https://github.com/Netflix-Skunkworks/go-jira/commit/469def0)]
|
||||||
|
* remove extraneous debug [Cory Bennett] [[752a94d](https://github.com/Netflix-Skunkworks/go-jira/commit/752a94d)]
|
||||||
|
* add logout command modify password prompt to echo masked password [Cory Bennett] [[8ad91be](https://github.com/Netflix-Skunkworks/go-jira/commit/8ad91be)]
|
||||||
|
* tweak cookies to store hostname dump all http request/response with --verbose [Cory Bennett] [[f93fe79](https://github.com/Netflix-Skunkworks/go-jira/commit/f93fe79)]
|
||||||
|
* load configs in order of closest to cwd (/etc/go-jira.yml is last) [Cory Bennett] [[f54267b](https://github.com/Netflix-Skunkworks/go-jira/commit/f54267b)]
|
||||||
|
|
||||||
|
## 0.1.3 - 2016-07-30
|
||||||
|
|
||||||
|
* [[#43](https://github.com/Netflix-Skunkworks/go-jira/issues/43)] add support for jira done|todo|prog commands [Cory Bennett] [[dd7d1cc](https://github.com/Netflix-Skunkworks/go-jira/commit/dd7d1cc)]
|
||||||
|
* Reporter is not generally editable. [Mike Pountney] [[a637b43](https://github.com/Netflix-Skunkworks/go-jira/commit/a637b43)]
|
||||||
|
|
||||||
|
## 0.1.2 - 2016-06-29
|
||||||
|
|
||||||
|
* [[#44](https://github.com/Netflix-Skunkworks/go-jira/issues/44)] Close tmpfile before rename to work around "The process cannot access the file because it is being used by another process" error on windows. [Cory Bennett] [[0980f8e](https://github.com/Netflix-Skunkworks/go-jira/commit/0980f8e)]
|
||||||
|
|
||||||
## 0.1.1 - 2016-06-28
|
## 0.1.1 - 2016-06-28
|
||||||
|
|
||||||
* use USERPROFILE instead of HOME for windows, rework paths to use filepath.Join for better cross platform support [Cory Bennett] [[adcedc4](https://github.com/Netflix-Skunkworks/go-jira/commit/adcedc4)]
|
* use USERPROFILE instead of HOME for windows, rework paths to use filepath.Join for better cross platform support [Cory Bennett] [[adcedc4](https://github.com/Netflix-Skunkworks/go-jira/commit/adcedc4)]
|
||||||
|
|||||||
@@ -1,18 +1,19 @@
|
|||||||
PLATFORMS= \
|
PLATFORMS= \
|
||||||
freebsd-386 \
|
|
||||||
freebsd-amd64 \
|
freebsd-amd64 \
|
||||||
freebsd-arm \
|
|
||||||
linux-386 \
|
linux-386 \
|
||||||
linux-amd64 \
|
linux-amd64 \
|
||||||
linux-arm \
|
|
||||||
openbsd-386 \
|
|
||||||
openbsd-amd64 \
|
|
||||||
windows-386 \
|
windows-386 \
|
||||||
windows-amd64 \
|
windows-amd64 \
|
||||||
darwin-386 \
|
|
||||||
darwin-amd64 \
|
darwin-amd64 \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
|
# freebsd-386 \
|
||||||
|
# freebsd-arm \
|
||||||
|
# linux-arm \
|
||||||
|
# openbsd-386 \
|
||||||
|
# openbsd-amd64 \
|
||||||
|
# darwin-386
|
||||||
|
|
||||||
NAME=jira
|
NAME=jira
|
||||||
|
|
||||||
OS=$(shell uname -s)
|
OS=$(shell uname -s)
|
||||||
@@ -33,8 +34,8 @@ DIST=$(CWD)$(SEP)dist
|
|||||||
|
|
||||||
GOBIN ?= $(CWD)
|
GOBIN ?= $(CWD)
|
||||||
|
|
||||||
CURVER ?= $(shell [ -d .git ] && git describe --abbrev=0 --tags || grep ^\#\# CHANGELOG.md | awk '{print $$2; exit}')
|
CURVER ?= $(patsubst v%,%,$(shell [ -d .git ] && git describe --abbrev=0 --tags || grep ^\#\# CHANGELOG.md | awk '{print $$2; exit}'))
|
||||||
LDFLAGS:=-X jira.VERSION=$(patsubst v%,%,$(CURVER)) -w
|
LDFLAGS:=-X jira.VERSION=$(CURVER) -w
|
||||||
|
|
||||||
# use make DEBUG=1 and you can get a debuggable golang binary
|
# use make DEBUG=1 and you can get a debuggable golang binary
|
||||||
# see https://github.com/mailgun/godebug
|
# see https://github.com/mailgun/godebug
|
||||||
@@ -47,6 +48,9 @@ endif
|
|||||||
build: src/github.com/Netflix-Skunkworks/go-jira
|
build: src/github.com/Netflix-Skunkworks/go-jira
|
||||||
$(GOBUILD) -o '$(BIN)' main/main.go
|
$(GOBUILD) -o '$(BIN)' main/main.go
|
||||||
|
|
||||||
|
debug:
|
||||||
|
$(MAKE) DEBUG=1
|
||||||
|
|
||||||
src/%:
|
src/%:
|
||||||
mkdir -p $(@D)
|
mkdir -p $(@D)
|
||||||
test -L $@ || ln -sf '$(GOPATH)' $@
|
test -L $@ || ln -sf '$(GOPATH)' $@
|
||||||
@@ -65,18 +69,19 @@ all:
|
|||||||
echo "Building for $$p"; \
|
echo "Building for $$p"; \
|
||||||
${MAKE} build GOOS=$${p/-*/} GOARCH=$${p/*-/} BIN=$(DIST)/$(NAME)-$$p; \
|
${MAKE} build GOOS=$${p/-*/} GOARCH=$${p/*-/} BIN=$(DIST)/$(NAME)-$$p; \
|
||||||
done
|
done
|
||||||
|
for x in $(DIST)/jira-windows-*; do mv $$x $$x.exe; done
|
||||||
|
|
||||||
fmt:
|
fmt:
|
||||||
gofmt -s -w main/*.go *.go
|
gofmt -s -w main/*.go *.go
|
||||||
|
|
||||||
install:
|
install:
|
||||||
${MAKE} GOBIN=~/bin build
|
${MAKE} GOBIN=$$HOME/bin build
|
||||||
|
|
||||||
NEWVER ?= $(shell echo $(CURVER) | awk -F. '{print $$1"."$$2"."$$3+1}')
|
NEWVER ?= $(shell echo $(CURVER) | awk -F. '{print $$1"."$$2"."$$3+1}')
|
||||||
TODAY := $(shell date +%Y-%m-%d)
|
TODAY := $(shell date +%Y-%m-%d)
|
||||||
|
|
||||||
changes:
|
changes:
|
||||||
@git log --pretty=format:"* %s [%cn] [%h]" --no-merges ^$(CURVER) HEAD main/*.go *.go | grep -vE 'gofmt|go fmt'
|
@git log --pretty=format:"* %s [%cn] [%h]" --no-merges ^v$(CURVER) HEAD main/*.go *.go | grep -vE 'gofmt|go fmt'
|
||||||
|
|
||||||
update-changelog:
|
update-changelog:
|
||||||
@echo "# Changelog" > CHANGELOG.md.new; \
|
@echo "# Changelog" > CHANGELOG.md.new; \
|
||||||
@@ -92,7 +97,7 @@ update-changelog:
|
|||||||
git tag v$(NEWVER)
|
git tag v$(NEWVER)
|
||||||
|
|
||||||
version:
|
version:
|
||||||
@echo $(patsubst v%,%,$(CURVER))
|
@echo $(CURVER)
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf pkg dist bin src ./$(NAME)
|
rm -rf pkg dist bin src ./$(NAME)
|
||||||
|
|||||||
@@ -1,3 +1,8 @@
|
|||||||
|
[](https://gitter.im/go-jira-cli/help?utm_source=badge&utm_medium=badge&utm_content=badge)
|
||||||
|
[](https://travis-ci.org/Netflix-Skunkworks/go-jira)
|
||||||
|
[](https://godoc.org/gopkg.in/Netflix-Skunkworks/go-jira.v0)
|
||||||
|
|
||||||
|
|
||||||
# go-jira
|
# go-jira
|
||||||
simple command line client for Atlassian's Jira service written in Go
|
simple command line client for Atlassian's Jira service written in Go
|
||||||
|
|
||||||
@@ -157,6 +162,26 @@ When running a command like `jira edit` it will look through the current directo
|
|||||||
if found it will use that file as the template, otherwise it will use the default **edit** template hard-coded into **go-jira**. You can export the default
|
if found it will use that file as the template, otherwise it will use the default **edit** template hard-coded into **go-jira**. You can export the default
|
||||||
hard-coded templates with `jira export-templates` which will write them to **~/.jira.d/templates/**.
|
hard-coded templates with `jira export-templates` which will write them to **~/.jira.d/templates/**.
|
||||||
|
|
||||||
|
#### Writing/Editing Templates
|
||||||
|
|
||||||
|
First the basic templating functionality is defined by the Go language 'text/template' library. The library reference documentation can be found [here](https://golang.org/pkg/text/template/), and there is a good primer document [here](https://gohugo.io/templates/go-templates/). `go-jira` also provides a few extra helper functions to make it a bit easlier to format the data, those functions are defined [here](https://github.com/Netflix-Skunkworks/go-jira/blob/master/util.go#L133).
|
||||||
|
|
||||||
|
Knowing what data and fields are available to any given template is not obvious. The easiest approach to determine what is available is to use the `debug` template on any given operation. For eample to find out what is available to the "view" templates, you can use:
|
||||||
|
```
|
||||||
|
jira view GOJIRA-321 -t debug
|
||||||
|
```
|
||||||
|
|
||||||
|
This will print out the data in JSON format that is available to the template. You can do this for any other operation, like "list":
|
||||||
|
```
|
||||||
|
jira list -t debug
|
||||||
|
```
|
||||||
|
|
||||||
|
Figuring out what is available to input templates (like for the `create` operation) is a bit more tricky, but similar. To find the data available for a `create` template you can run:
|
||||||
|
```
|
||||||
|
jira create --dryrun -t debug --editor /bin/cat
|
||||||
|
```
|
||||||
|
This will attempt to fetch metadata for your default project (you can provide any options that you would normally specify for the `create` operation). It uses the `--dryrun` option to prevent any actual updates being sent to Jira. The `-t debug` is like before to cause the input to be serialized to JSON and printed for your inspection. Finally the `--editor /bin/cat` will cause `go-jira` to just print the template rather than open up an editor and wait for you to edit/save it.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/cookiejar"
|
"net/http/cookiejar"
|
||||||
|
"net/http/httputil"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
@@ -22,10 +23,12 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
log = logging.MustGetLogger("jira")
|
log = logging.MustGetLogger("jira")
|
||||||
|
// VERSION is the go-jira library version
|
||||||
VERSION string
|
VERSION string
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Cli is go-jira client object
|
||||||
type Cli struct {
|
type Cli struct {
|
||||||
endpoint *url.URL
|
endpoint *url.URL
|
||||||
opts map[string]interface{}
|
opts map[string]interface{}
|
||||||
@@ -33,6 +36,7 @@ type Cli struct {
|
|||||||
ua *http.Client
|
ua *http.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// New creates go-jira client object
|
||||||
func New(opts map[string]interface{}) *Cli {
|
func New(opts map[string]interface{}) *Cli {
|
||||||
homedir := homedir()
|
homedir := homedir()
|
||||||
cookieJar, _ := cookiejar.New(nil)
|
cookieJar, _ := cookiejar.New(nil)
|
||||||
@@ -66,7 +70,22 @@ func New(opts map[string]interface{}) *Cli {
|
|||||||
return cli
|
return cli
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cli) saveCookies(cookies []*http.Cookie) {
|
func (c *Cli) saveCookies(resp *http.Response) {
|
||||||
|
if _, ok := resp.Header["Set-Cookie"]; !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
cookies := resp.Cookies()
|
||||||
|
for _, cookie := range cookies {
|
||||||
|
if cookie.Domain == "" {
|
||||||
|
// if it is host:port then we need to split off port
|
||||||
|
parts := strings.Split(resp.Request.URL.Host, ":")
|
||||||
|
host := parts[0]
|
||||||
|
log.Debugf("Setting DOMAIN to %s for Cookie: %s", host, cookie)
|
||||||
|
cookie.Domain = host
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// expiry in one week from now
|
// expiry in one week from now
|
||||||
expiry := time.Now().Add(24 * 7 * time.Hour)
|
expiry := time.Now().Add(24 * 7 * time.Hour)
|
||||||
for _, cookie := range cookies {
|
for _, cookie := range cookies {
|
||||||
@@ -76,11 +95,11 @@ func (c *Cli) saveCookies(cookies []*http.Cookie) {
|
|||||||
if currentCookies := c.loadCookies(); currentCookies != nil {
|
if currentCookies := c.loadCookies(); currentCookies != nil {
|
||||||
currentCookiesByName := make(map[string]*http.Cookie)
|
currentCookiesByName := make(map[string]*http.Cookie)
|
||||||
for _, cookie := range currentCookies {
|
for _, cookie := range currentCookies {
|
||||||
currentCookiesByName[cookie.Name] = cookie
|
currentCookiesByName[cookie.Name+cookie.Domain] = cookie
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, cookie := range cookies {
|
for _, cookie := range cookies {
|
||||||
currentCookiesByName[cookie.Name] = cookie
|
currentCookiesByName[cookie.Name+cookie.Domain] = cookie
|
||||||
}
|
}
|
||||||
|
|
||||||
mergedCookies := make([]*http.Cookie, 0, len(currentCookiesByName))
|
mergedCookies := make([]*http.Cookie, 0, len(currentCookiesByName))
|
||||||
@@ -102,14 +121,17 @@ func (c *Cli) loadCookies() []*http.Cookie {
|
|||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Failed to open %s: %s", c.cookieFile, err)
|
log.Errorf("Failed to open %s: %s", c.cookieFile, err)
|
||||||
os.Exit(1)
|
panic(err)
|
||||||
}
|
}
|
||||||
cookies := make([]*http.Cookie, 0)
|
cookies := []*http.Cookie{}
|
||||||
err = json.Unmarshal(bytes, &cookies)
|
err = json.Unmarshal(bytes, &cookies)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Failed to parse json from file %s: %s", c.cookieFile, err)
|
log.Errorf("Failed to parse json from file %s: %s", c.cookieFile, err)
|
||||||
}
|
}
|
||||||
log.Debugf("Loading Cookies: %s", cookies)
|
|
||||||
|
if os.Getenv("LOG_TRACE") != "" && log.IsEnabledFor(logging.DEBUG) {
|
||||||
|
log.Debugf("Loading Cookies: %s", cookies)
|
||||||
|
}
|
||||||
return cookies
|
return cookies
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,53 +143,42 @@ func (c *Cli) put(uri string, content string) (*http.Response, error) {
|
|||||||
return c.makeRequestWithContent("PUT", uri, content)
|
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"
|
method := "DELETE"
|
||||||
req, _ := http.NewRequest(method, uri, nil)
|
req, _ := http.NewRequest(method, uri, nil)
|
||||||
log.Infof("%s %s", req.Method, req.URL.String())
|
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
|
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)
|
buffer := bytes.NewBufferString(content)
|
||||||
req, _ := http.NewRequest(method, uri, buffer)
|
req, _ := http.NewRequest(method, uri, buffer)
|
||||||
|
|
||||||
log.Infof("%s %s", req.Method, req.URL.String())
|
log.Infof("%s %s", req.Method, req.URL.String())
|
||||||
if log.IsEnabledFor(logging.DEBUG) {
|
if resp, err = c.makeRequest(req); err != nil {
|
||||||
logBuffer := bytes.NewBuffer(make([]byte, 0, len(content)))
|
|
||||||
req.Write(logBuffer)
|
|
||||||
log.Debugf("%s", logBuffer)
|
|
||||||
// need to recreate the buffer since the offset is now at the end
|
|
||||||
// need to be able to rewind the buffer offset, dont know how yet
|
|
||||||
req, _ = http.NewRequest(method, uri, bytes.NewBufferString(content))
|
|
||||||
}
|
|
||||||
|
|
||||||
if resp, err := c.makeRequest(req); err != nil {
|
|
||||||
return nil, err
|
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)
|
req, _ := http.NewRequest("GET", uri, nil)
|
||||||
log.Infof("%s %s", req.Method, req.URL.String())
|
log.Infof("%s %s", req.Method, req.URL.String())
|
||||||
if log.IsEnabledFor(logging.DEBUG) {
|
if log.IsEnabledFor(logging.DEBUG) {
|
||||||
@@ -176,40 +187,56 @@ func (c *Cli) get(uri string) (*http.Response, error) {
|
|||||||
log.Debugf("%s", logBuffer)
|
log.Debugf("%s", logBuffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
if resp, err := c.makeRequest(req); err != nil {
|
if resp, err = c.makeRequest(req); err != nil {
|
||||||
return nil, err
|
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) {
|
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")
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
req.AddCookie(cookie)
|
||||||
|
}
|
||||||
|
|
||||||
|
if log.IsEnabledFor(logging.DEBUG) {
|
||||||
|
out, _ := httputil.DumpRequest(req, true)
|
||||||
|
log.Debugf("Request: %s", out)
|
||||||
|
}
|
||||||
|
|
||||||
if resp, err = c.ua.Do(req); err != nil {
|
if resp, err = c.ua.Do(req); err != nil {
|
||||||
log.Errorf("Failed to %s %s: %s", req.Method, req.URL.String(), err)
|
log.Errorf("Failed to %s %s: %s", req.Method, req.URL.String(), err)
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
}
|
||||||
if resp.StatusCode < 200 || resp.StatusCode >= 300 && resp.StatusCode != 401 {
|
if resp.StatusCode < 200 || resp.StatusCode >= 300 && resp.StatusCode != 401 {
|
||||||
log.Errorf("response status: %s", resp.Status)
|
log.Errorf("response status: %s", resp.Status)
|
||||||
}
|
}
|
||||||
|
|
||||||
runtime.SetFinalizer(resp, func(r *http.Response) {
|
runtime.SetFinalizer(resp, func(r *http.Response) {
|
||||||
r.Body.Close()
|
r.Body.Close()
|
||||||
})
|
})
|
||||||
|
|
||||||
if _, ok := resp.Header["Set-Cookie"]; ok {
|
if _, ok := resp.Header["Set-Cookie"]; ok {
|
||||||
c.saveCookies(resp.Cookies())
|
c.saveCookies(resp)
|
||||||
}
|
}
|
||||||
|
if log.IsEnabledFor(logging.DEBUG) {
|
||||||
|
out, _ := httputil.DumpResponse(resp, true)
|
||||||
|
log.Debugf("Response: %s", out)
|
||||||
}
|
}
|
||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetTemplate will return the text/template for the given command name
|
||||||
func (c *Cli) GetTemplate(name string) string {
|
func (c *Cli) GetTemplate(name string) string {
|
||||||
return c.getTemplate(name)
|
return c.getTemplate(name)
|
||||||
}
|
}
|
||||||
@@ -229,10 +256,9 @@ func (c *Cli) getTemplate(name string) string {
|
|||||||
if override, ok := c.opts["template"].(string); ok {
|
if override, ok := c.opts["template"].(string); ok {
|
||||||
if _, err := os.Stat(override); err == nil {
|
if _, err := os.Stat(override); err == nil {
|
||||||
return readFile(override)
|
return readFile(override)
|
||||||
} else {
|
}
|
||||||
if t := getLookedUpTemplate(override, all_templates[override]); t != "" {
|
if t := getLookedUpTemplate(override, allTemplates[override]); t != "" {
|
||||||
return t
|
return t
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// create-bug etc are special, if we dont find it in the path
|
// create-bug etc are special, if we dont find it in the path
|
||||||
@@ -240,9 +266,11 @@ func (c *Cli) getTemplate(name string) string {
|
|||||||
if strings.HasPrefix(name, "create-") {
|
if strings.HasPrefix(name, "create-") {
|
||||||
return getLookedUpTemplate(name, c.getTemplate("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{}
|
type NoChangesFound struct{}
|
||||||
|
|
||||||
func (f NoChangesFound) Error() string {
|
func (f NoChangesFound) Error() string {
|
||||||
@@ -261,13 +289,25 @@ func (c *Cli) editTemplate(template string, tmpFilePrefix string, templateData m
|
|||||||
log.Errorf("Failed to make temp file in %s: %s", tmpdir, err)
|
log.Errorf("Failed to make temp file in %s: %s", tmpdir, err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer fh.Close()
|
|
||||||
|
|
||||||
tmpFileName := fmt.Sprintf("%s.yml", fh.Name())
|
oldFileName := fh.Name()
|
||||||
if err := os.Rename(fh.Name(), tmpFileName); err != nil {
|
tmpFileName := fmt.Sprintf("%s.yml", oldFileName)
|
||||||
log.Errorf("Failed to rename %s to %s: %s", fh.Name(), fmt.Sprintf("%s.yml", fh.Name()), err)
|
|
||||||
|
// close tmpfile so we can rename on windows
|
||||||
|
fh.Close()
|
||||||
|
|
||||||
|
if err := os.Rename(oldFileName, tmpFileName); err != nil {
|
||||||
|
log.Errorf("Failed to rename %s to %s: %s", oldFileName, tmpFileName, err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fh, err = os.OpenFile(tmpFileName, os.O_RDWR|os.O_EXCL, 0600)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Failed to reopen temp file file in %s: %s", tmpFileName, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer fh.Close()
|
||||||
defer func() {
|
defer func() {
|
||||||
os.Remove(tmpFileName)
|
os.Remove(tmpFileName)
|
||||||
}()
|
}()
|
||||||
@@ -321,27 +361,27 @@ func (c *Cli) editTemplate(template string, tmpFilePrefix string, templateData m
|
|||||||
}
|
}
|
||||||
|
|
||||||
edited := make(map[string]interface{})
|
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)
|
log.Errorf("Failed to read tmpfile %s: %s", tmpFileName, err)
|
||||||
if editing && promptYN("edit again?", true) {
|
if editing && promptYN("edit again?", true) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
} else {
|
}
|
||||||
if err := yaml.Unmarshal(fh, &edited); err != nil {
|
if err := yaml.Unmarshal(data, &edited); err != nil {
|
||||||
log.Errorf("Failed to parse YAML: %s", err)
|
log.Errorf("Failed to parse YAML: %s", err)
|
||||||
if editing && promptYN("edit again?", true) {
|
if editing && promptYN("edit again?", true) {
|
||||||
continue
|
continue
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if fixed, err := yamlFixup(edited); err != nil {
|
var fixed interface{}
|
||||||
|
if fixed, err = yamlFixup(edited); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
|
||||||
edited = fixed.(map[string]interface{})
|
|
||||||
}
|
}
|
||||||
|
edited = fixed.(map[string]interface{})
|
||||||
|
|
||||||
// if you want to abort editing a jira issue then
|
// if you want to abort editing a jira issue then
|
||||||
// you can add the "abort: true" flag to the document
|
// you can add the "abort: true" flag to the document
|
||||||
@@ -383,6 +423,7 @@ func (c *Cli) editTemplate(template string, tmpFilePrefix string, templateData m
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Browse will open up your default browser to the provided issue
|
||||||
func (c *Cli) Browse(issue string) error {
|
func (c *Cli) Browse(issue string) error {
|
||||||
if val, ok := c.opts["browse"].(bool); ok && val {
|
if val, ok := c.opts["browse"].(bool); ok && val {
|
||||||
if runtime.GOOS == "darwin" {
|
if runtime.GOOS == "darwin" {
|
||||||
@@ -394,6 +435,7 @@ func (c *Cli) Browse(issue string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SaveData will write out the yaml formated --saveFile file with provided data
|
||||||
func (c *Cli) SaveData(data interface{}) error {
|
func (c *Cli) SaveData(data interface{}) error {
|
||||||
if val, ok := c.opts["saveFile"].(string); ok && val != "" {
|
if val, ok := c.opts["saveFile"].(string); ok && val != "" {
|
||||||
yamlWrite(val, data)
|
yamlWrite(val, data)
|
||||||
@@ -401,33 +443,39 @@ func (c *Cli) SaveData(data interface{}) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ViewIssue will return the details for the given issue id
|
||||||
func (c *Cli) ViewIssue(issue string) (interface{}, error) {
|
func (c *Cli) ViewIssue(issue string) (interface{}, error) {
|
||||||
uri := fmt.Sprintf("%s/rest/api/2/issue/%s", c.endpoint, issue)
|
uri := fmt.Sprintf("%s/rest/api/2/issue/%s", c.endpoint, issue)
|
||||||
if x := c.expansions(); len(x) > 0 {
|
if x := c.expansions(); len(x) > 0 {
|
||||||
uri = fmt.Sprintf("%s?expand=%s", uri, strings.Join(x, ","))
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
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) {
|
func (c *Cli) FindIssues() (interface{}, error) {
|
||||||
var query string
|
var query string
|
||||||
var ok bool
|
var ok bool
|
||||||
// project = BAKERY and status not in (Resolved, Closed)
|
// project = BAKERY and status not in (Resolved, Closed)
|
||||||
if query, ok = c.opts["query"].(string); !ok {
|
if query, ok = c.opts["query"].(string); !ok {
|
||||||
qbuff := bytes.NewBufferString("resolution = unresolved")
|
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")
|
err := fmt.Errorf("Missing required arguments, either 'query' or 'project' are required")
|
||||||
log.Errorf("%s", err)
|
log.Errorf("%s", err)
|
||||||
return nil, 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 {
|
if component, ok := c.opts["component"]; ok {
|
||||||
qbuff.WriteString(fmt.Sprintf(" AND component = '%s'", component))
|
qbuff.WriteString(fmt.Sprintf(" AND component = '%s'", component))
|
||||||
@@ -456,11 +504,9 @@ func (c *Cli) FindIssues() (interface{}, error) {
|
|||||||
query = qbuff.String()
|
query = qbuff.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
fields := make([]string, 0)
|
fields := []string{"summary"}
|
||||||
if qf, ok := c.opts["queryfields"].(string); ok {
|
if qf, ok := c.opts["queryfields"].(string); ok {
|
||||||
fields = strings.Split(qf, ",")
|
fields = strings.Split(qf, ",")
|
||||||
} else {
|
|
||||||
fields = append(fields, "summary")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
json, err := jsonEncode(map[string]interface{}{
|
json, err := jsonEncode(map[string]interface{}{
|
||||||
@@ -475,13 +521,15 @@ func (c *Cli) FindIssues() (interface{}, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
uri := fmt.Sprintf("%s/rest/api/2/search", c.endpoint)
|
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
|
return nil, err
|
||||||
} else {
|
|
||||||
return data, nil
|
|
||||||
}
|
}
|
||||||
|
return data, 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 {
|
func (c *Cli) GetOptString(optName string, dflt string) string {
|
||||||
return c.getOptString(optName, dflt)
|
return c.getOptString(optName, dflt)
|
||||||
}
|
}
|
||||||
@@ -489,11 +537,12 @@ func (c *Cli) GetOptString(optName string, dflt string) string {
|
|||||||
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 {
|
if val, ok := c.opts[optName].(string); ok {
|
||||||
return val
|
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 {
|
func (c *Cli) GetOptBool(optName string, dflt bool) bool {
|
||||||
return c.getOptBool(optName, dflt)
|
return c.getOptBool(optName, dflt)
|
||||||
}
|
}
|
||||||
@@ -501,14 +550,13 @@ func (c *Cli) GetOptBool(optName string, dflt bool) bool {
|
|||||||
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 {
|
if val, ok := c.opts[optName].(bool); ok {
|
||||||
return val
|
return val
|
||||||
} else {
|
|
||||||
return dflt
|
|
||||||
}
|
}
|
||||||
|
return dflt
|
||||||
}
|
}
|
||||||
|
|
||||||
// expansions returns a comma-separated list of values for field expansion
|
// expansions returns a comma-separated list of values for field expansion
|
||||||
func (c *Cli) expansions() []string {
|
func (c *Cli) expansions() []string {
|
||||||
expansions := make([]string, 0)
|
var expansions []string
|
||||||
if x, ok := c.opts["expand"].(string); ok {
|
if x, ok := c.opts["expand"].(string); ok {
|
||||||
expansions = strings.Split(x, ",")
|
expansions = strings.Split(x, ",")
|
||||||
}
|
}
|
||||||
|
|||||||
+237
-128
@@ -2,67 +2,90 @@ package jira
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"golang.org/x/crypto/ssh/terminal"
|
"github.com/Netflix-Skunkworks/go-jira/data"
|
||||||
|
"github.com/howeyc/gopass"
|
||||||
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httputil"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
// "github.com/kr/pretty"
|
// "github.com/kr/pretty"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// CmdLogin will attempt to login into jira server
|
||||||
func (c *Cli) CmdLogin() error {
|
func (c *Cli) CmdLogin() error {
|
||||||
uri := fmt.Sprintf("%s/rest/auth/1/session", c.endpoint)
|
uri := fmt.Sprintf("%s/rest/auth/1/session", c.endpoint)
|
||||||
for true {
|
for {
|
||||||
req, _ := http.NewRequest("GET", uri, nil)
|
req, _ := http.NewRequest("GET", uri, nil)
|
||||||
user, _ := c.opts["user"].(string)
|
user, _ := c.opts["user"].(string)
|
||||||
|
|
||||||
fmt.Printf("Enter Password for %s: ", user)
|
fmt.Printf("Jira Password [%s]: ", user)
|
||||||
pwbytes, _ := terminal.ReadPassword(int(os.Stdin.Fd()))
|
pw, err := gopass.GetPasswdMasked()
|
||||||
passwd := string(pwbytes)
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
passwd := string(pw)
|
||||||
|
|
||||||
req.SetBasicAuth(user, passwd)
|
req.SetBasicAuth(user, passwd)
|
||||||
log.Infof("%s %s", req.Method, req.URL.String())
|
|
||||||
if resp, err := c.makeRequest(req); err != nil {
|
resp, err := c.makeRequest(req)
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
}
|
||||||
out, _ := httputil.DumpResponse(resp, true)
|
if resp.StatusCode == 403 {
|
||||||
log.Debugf("%s", out)
|
// probably got this, need to redirect the user to login manually
|
||||||
if resp.StatusCode == 403 {
|
// X-Authentication-Denied-Reason: CAPTCHA_CHALLENGE; login-url=https://jira/login.jsp
|
||||||
// probably got this, need to redirect the user to login manually
|
if reason := resp.Header.Get("X-Authentication-Denied-Reason"); reason != "" {
|
||||||
// X-Authentication-Denied-Reason: CAPTCHA_CHALLENGE; login-url=https://jira/login.jsp
|
err := fmt.Errorf("Authenticaion Failed: %s", reason)
|
||||||
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)
|
log.Errorf("%s", err)
|
||||||
return err
|
return err
|
||||||
|
}
|
||||||
|
err := fmt.Errorf("Authentication Failed: Unknown Reason")
|
||||||
|
log.Errorf("%s", err)
|
||||||
|
return err
|
||||||
|
|
||||||
} else if resp.StatusCode == 200 {
|
} else if resp.StatusCode == 200 {
|
||||||
// https://confluence.atlassian.com/display/JIRA043/JIRA+REST+API+%28Alpha%29+Tutorial#JIRARESTAPI%28Alpha%29Tutorial-CAPTCHAs
|
// https://confluence.atlassian.com/display/JIRA043/JIRA+REST+API+%28Alpha%29+Tutorial#JIRARESTAPI%28Alpha%29Tutorial-CAPTCHAs
|
||||||
// probably bad password, try again
|
// probably bad password, try again
|
||||||
if reason := resp.Header.Get("X-Seraph-Loginreason"); reason == "AUTHENTICATION_DENIED" {
|
if reason := resp.Header.Get("X-Seraph-Loginreason"); reason == "AUTHENTICATION_DENIED" {
|
||||||
log.Warning("Authentication Failed: %s", reason)
|
log.Warning("Authentication Failed: %s", reason)
|
||||||
continue
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log.Warning("Login failed")
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
log.Warning("Login failed")
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
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)
|
||||||
|
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 {
|
||||||
|
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 {
|
func (c *Cli) CmdFields() error {
|
||||||
log.Debugf("fields called")
|
log.Debugf("fields called")
|
||||||
uri := fmt.Sprintf("%s/rest/api/2/field", c.endpoint)
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -70,15 +93,17 @@ func (c *Cli) CmdFields() error {
|
|||||||
return runTemplate(c.getTemplate("fields"), data, nil)
|
return runTemplate(c.getTemplate("fields"), data, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CmdList will query jira and send data to "list" template
|
||||||
func (c *Cli) CmdList() error {
|
func (c *Cli) CmdList() error {
|
||||||
log.Debugf("list called")
|
log.Debugf("list called")
|
||||||
if data, err := c.FindIssues(); err != nil {
|
data, err := c.FindIssues()
|
||||||
|
if err != nil {
|
||||||
return err
|
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 {
|
func (c *Cli) CmdView(issue string) error {
|
||||||
log.Debugf("view called")
|
log.Debugf("view called")
|
||||||
c.Browse(issue)
|
c.Browse(issue)
|
||||||
@@ -89,23 +114,23 @@ func (c *Cli) CmdView(issue string) error {
|
|||||||
return runTemplate(c.getTemplate("view"), data, nil)
|
return runTemplate(c.getTemplate("view"), data, 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 {
|
func (c *Cli) CmdEdit(issue string) error {
|
||||||
log.Debugf("edit called")
|
log.Debugf("edit called")
|
||||||
|
|
||||||
uri := fmt.Sprintf("%s/rest/api/2/issue/%s/editmeta", c.endpoint, issue)
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
uri = fmt.Sprintf("%s/rest/api/2/issue/%s", c.endpoint, issue)
|
uri = fmt.Sprintf("%s/rest/api/2/issue/%s", c.endpoint, issue)
|
||||||
var issueData map[string]interface{}
|
data, err := responseToJSON(c.get(uri))
|
||||||
if data, err := responseToJson(c.get(uri)); err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
|
||||||
issueData = data.(map[string]interface{})
|
|
||||||
}
|
}
|
||||||
|
issueData := data.(map[string]interface{})
|
||||||
issueData["meta"] = editmeta.(map[string]interface{})
|
issueData["meta"] = editmeta.(map[string]interface{})
|
||||||
issueData["overrides"] = c.opts
|
issueData["overrides"] = c.opts
|
||||||
|
|
||||||
@@ -130,22 +155,22 @@ func (c *Cli) CmdEdit(issue string) error {
|
|||||||
fmt.Printf("OK %s %s/browse/%s\n", issueData["key"], c.endpoint, issueData["key"])
|
fmt.Printf("OK %s %s/browse/%s\n", issueData["key"], c.endpoint, issueData["key"])
|
||||||
}
|
}
|
||||||
return nil
|
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 {
|
func (c *Cli) CmdEditMeta(issue string) error {
|
||||||
log.Debugf("editMeta called")
|
log.Debugf("editMeta called")
|
||||||
c.Browse(issue)
|
c.Browse(issue)
|
||||||
uri := fmt.Sprintf("%s/rest/api/2/issue/%s/editmeta", c.endpoint, 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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -153,11 +178,12 @@ func (c *Cli) CmdEditMeta(issue string) error {
|
|||||||
return runTemplate(c.getTemplate("editmeta"), data, nil)
|
return runTemplate(c.getTemplate("editmeta"), data, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CmdTransitionMeta will send available transition metadata to the "transmeta" template
|
||||||
func (c *Cli) CmdTransitionMeta(issue string) error {
|
func (c *Cli) CmdTransitionMeta(issue string) error {
|
||||||
log.Debugf("tranisionMeta called")
|
log.Debugf("tranisionMeta called")
|
||||||
c.Browse(issue)
|
c.Browse(issue)
|
||||||
uri := fmt.Sprintf("%s/rest/api/2/issue/%s/transitions?expand=transitions.fields", c.endpoint, 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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -165,11 +191,12 @@ func (c *Cli) CmdTransitionMeta(issue string) error {
|
|||||||
return runTemplate(c.getTemplate("transmeta"), data, nil)
|
return runTemplate(c.getTemplate("transmeta"), data, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CmdIssueTypes will send issue 'create' metadata to the 'issuetypes'
|
||||||
func (c *Cli) CmdIssueTypes() error {
|
func (c *Cli) CmdIssueTypes() error {
|
||||||
project := c.opts["project"].(string)
|
project := c.opts["project"].(string)
|
||||||
log.Debugf("issueTypes called")
|
log.Debugf("issueTypes called")
|
||||||
uri := fmt.Sprintf("%s/rest/api/2/issue/createmeta?projectKeys=%s", c.endpoint, project)
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -177,13 +204,46 @@ func (c *Cli) CmdIssueTypes() error {
|
|||||||
return runTemplate(c.getTemplate("issuetypes"), data, nil)
|
return runTemplate(c.getTemplate("issuetypes"), data, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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))
|
||||||
|
issueTypeNames := make(map[string]bool)
|
||||||
|
|
||||||
|
if data, ok := data.(map[string]interface{}); ok {
|
||||||
|
if projects, ok := data["projects"].([]interface{}); ok {
|
||||||
|
for _, project := range projects {
|
||||||
|
if project, ok := project.(map[string]interface{}); ok {
|
||||||
|
if issuetypes, ok := project["issuetypes"].([]interface{}); ok {
|
||||||
|
if len(issuetypes) > 0 {
|
||||||
|
for _, issuetype := range issuetypes {
|
||||||
|
issueTypeNames[issuetype.(map[string]interface{})["name"].(string)] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if _, ok := issueTypeNames["Bug"]; ok {
|
||||||
|
return "Bug"
|
||||||
|
} else if _, ok := issueTypeNames["Task"]; ok {
|
||||||
|
return "Task"
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// CmdCreateMeta sends the 'create' metadata for the given project & issuetype and sends it to the 'createmeta' template
|
||||||
func (c *Cli) CmdCreateMeta() error {
|
func (c *Cli) CmdCreateMeta() error {
|
||||||
project := c.opts["project"].(string)
|
project := c.opts["project"].(string)
|
||||||
issuetype := c.getOptString("issuetype", "Bug")
|
issuetype := c.getOptString("issuetype", "")
|
||||||
|
if issuetype == "" {
|
||||||
|
issuetype = c.defaultIssueType()
|
||||||
|
}
|
||||||
|
|
||||||
log.Debugf("createMeta called")
|
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))
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -202,34 +262,63 @@ func (c *Cli) CmdCreateMeta() error {
|
|||||||
return runTemplate(c.getTemplate("createmeta"), data, nil)
|
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 {
|
func (c *Cli) CmdComponents(project string) error {
|
||||||
log.Debugf("Components called")
|
log.Debugf("Components called")
|
||||||
uri := fmt.Sprintf("%s/rest/api/2/project/%s/components", c.endpoint, project)
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return runTemplate(c.getTemplate("components"), data, nil)
|
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)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
transMeta := &jiradata.TransitionsMeta{}
|
||||||
|
content, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = json.Unmarshal(content, transMeta)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return transMeta.Transitions, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CmdTransitions sends valid transtions for given issue to the "transitions" template
|
||||||
func (c *Cli) CmdTransitions(issue string) error {
|
func (c *Cli) CmdTransitions(issue string) error {
|
||||||
log.Debugf("Transitions called")
|
log.Debugf("Transitions called")
|
||||||
|
// FIXME this should just call ValidTransitions then pass that data to templates
|
||||||
c.Browse(issue)
|
c.Browse(issue)
|
||||||
uri := fmt.Sprintf("%s/rest/api/2/issue/%s/transitions", c.endpoint, issue)
|
uri := fmt.Sprintf("%s/rest/api/2/issue/%s/transitions", c.endpoint, issue)
|
||||||
data, err := responseToJson(c.get(uri))
|
data, err := responseToJSON(c.get(uri))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return runTemplate(c.getTemplate("transitions"), data, nil)
|
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 {
|
func (c *Cli) CmdCreate() error {
|
||||||
project := c.opts["project"].(string)
|
|
||||||
issuetype := c.getOptString("issuetype", "Bug")
|
|
||||||
log.Debugf("create called")
|
log.Debugf("create called")
|
||||||
|
project := c.opts["project"].(string)
|
||||||
|
issuetype := c.getOptString("issuetype", "")
|
||||||
|
if issuetype == "" {
|
||||||
|
issuetype = c.defaultIssueType()
|
||||||
|
}
|
||||||
|
|
||||||
uri := fmt.Sprintf("%s/rest/api/2/issue/createmeta?projectKeys=%s&issuetypeNames=%s&expand=projects.issuetypes.fields", c.endpoint, project, url.QueryEscape(issuetype))
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -260,7 +349,6 @@ func (c *Cli) CmdCreate() error {
|
|||||||
fmt.Sprintf("create-%s-", sanitizedType),
|
fmt.Sprintf("create-%s-", sanitizedType),
|
||||||
issueData,
|
issueData,
|
||||||
func(json string) error {
|
func(json string) error {
|
||||||
log.Debugf("JSON: %s", json)
|
|
||||||
uri := fmt.Sprintf("%s/rest/api/2/issue", c.endpoint)
|
uri := fmt.Sprintf("%s/rest/api/2/issue", c.endpoint)
|
||||||
if c.getOptBool("dryrun", false) {
|
if c.getOptBool("dryrun", false) {
|
||||||
log.Debugf("POST: %s", json)
|
log.Debugf("POST: %s", json)
|
||||||
@@ -274,49 +362,49 @@ func (c *Cli) CmdCreate() error {
|
|||||||
|
|
||||||
if resp.StatusCode == 201 {
|
if resp.StatusCode == 201 {
|
||||||
// response: {"id":"410836","key":"PROJ-238","self":"https://jira/rest/api/2/issue/410836"}
|
// 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
|
return err
|
||||||
} else {
|
}
|
||||||
key := json.(map[string]interface{})["key"].(string)
|
key := json.(map[string]interface{})["key"].(string)
|
||||||
link := fmt.Sprintf("%s/browse/%s", c.endpoint, key)
|
link := fmt.Sprintf("%s/browse/%s", c.endpoint, key)
|
||||||
c.Browse(key)
|
c.Browse(key)
|
||||||
c.SaveData(map[string]string{
|
c.SaveData(map[string]string{
|
||||||
"issue": key,
|
"issue": key,
|
||||||
"link": link,
|
"link": link,
|
||||||
})
|
})
|
||||||
if !c.opts["quiet"].(bool) {
|
if !c.opts["quiet"].(bool) {
|
||||||
fmt.Printf("OK %s %s\n", key, link)
|
fmt.Printf("OK %s %s\n", key, link)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return nil
|
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 {
|
func (c *Cli) CmdIssueLinkTypes() error {
|
||||||
log.Debugf("Transitions called")
|
log.Debugf("Transitions called")
|
||||||
uri := fmt.Sprintf("%s/rest/api/2/issueLinkType", c.endpoint)
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return runTemplate(c.getTemplate("issuelinktypes"), data, nil)
|
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 {
|
func (c *Cli) CmdBlocks(blocker string, issue string) error {
|
||||||
log.Debugf("blocks called")
|
log.Debugf("blocks called")
|
||||||
|
|
||||||
json, err := jsonEncode(map[string]interface{}{
|
json, err := jsonEncode(map[string]interface{}{
|
||||||
"type": map[string]string{
|
"type": map[string]string{
|
||||||
"name": "Depends", // TODO This is probably not constant across Jira installs
|
"name": c.GetOptString("blockerType", "Blocks"),
|
||||||
},
|
},
|
||||||
"inwardIssue": map[string]string{
|
"inwardIssue": map[string]string{
|
||||||
"key": issue,
|
"key": issue,
|
||||||
@@ -354,6 +442,8 @@ func (c *Cli) CmdBlocks(blocker string, issue string) error {
|
|||||||
return nil
|
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 {
|
func (c *Cli) CmdDups(duplicate string, issue string) error {
|
||||||
log.Debugf("dups called")
|
log.Debugf("dups called")
|
||||||
|
|
||||||
@@ -397,6 +487,8 @@ func (c *Cli) CmdDups(duplicate string, issue string) error {
|
|||||||
return nil
|
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 {
|
func (c *Cli) CmdWatch(issue string, watcher string, remove bool) error {
|
||||||
log.Debugf("watch called: watcher: %q, remove: %n", watcher, remove)
|
log.Debugf("watch called: watcher: %q, remove: %n", watcher, remove)
|
||||||
|
|
||||||
@@ -447,6 +539,7 @@ func (c *Cli) CmdWatch(issue string, watcher string, remove bool) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CmdVote will add or remove a vote on an issue
|
||||||
func (c *Cli) CmdVote(issue string, up bool) error {
|
func (c *Cli) CmdVote(issue string, up bool) error {
|
||||||
log.Debugf("vote called, with up: %n", up)
|
log.Debugf("vote called, with up: %n", up)
|
||||||
|
|
||||||
@@ -490,16 +583,17 @@ func (c *Cli) CmdVote(issue string, up bool) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CmdTransition will move state of the given issue to the given transtion
|
||||||
func (c *Cli) CmdTransition(issue string, trans string) error {
|
func (c *Cli) CmdTransition(issue string, trans string) error {
|
||||||
log.Debugf("transition called")
|
log.Debugf("transition called")
|
||||||
uri := fmt.Sprintf("%s/rest/api/2/issue/%s/transitions?expand=transitions.fields", c.endpoint, 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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
transitions := data.(map[string]interface{})["transitions"].([]interface{})
|
transitions := data.(map[string]interface{})["transitions"].([]interface{})
|
||||||
var transId, transName string
|
var transID, transName string
|
||||||
var transMeta map[string]interface{}
|
var transMeta map[string]interface{}
|
||||||
found := make([]string, 0, len(transitions))
|
found := make([]string, 0, len(transitions))
|
||||||
for _, transition := range transitions {
|
for _, transition := range transitions {
|
||||||
@@ -508,19 +602,17 @@ func (c *Cli) CmdTransition(issue string, trans string) error {
|
|||||||
found = append(found, name)
|
found = append(found, name)
|
||||||
if strings.Contains(strings.ToLower(name), strings.ToLower(trans)) {
|
if strings.Contains(strings.ToLower(name), strings.ToLower(trans)) {
|
||||||
transName = name
|
transName = name
|
||||||
transId = id
|
transID = id
|
||||||
transMeta = transition.(map[string]interface{})
|
transMeta = transition.(map[string]interface{})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if transId == "" {
|
if transID == "" {
|
||||||
err := fmt.Errorf("Invalid Transition '%s', Available: %s", trans, strings.Join(found, ", "))
|
err := fmt.Errorf("Invalid Transition '%s', Available: %s", trans, strings.Join(found, ", "))
|
||||||
log.Errorf("%s", err)
|
log.Debugf("%s", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePost := func(json string) error {
|
handlePost := func(json string) error {
|
||||||
log.Debugf("POST: %s", json)
|
|
||||||
// os.Exit(0)
|
|
||||||
uri = fmt.Sprintf("%s/rest/api/2/issue/%s/transitions", c.endpoint, issue)
|
uri = fmt.Sprintf("%s/rest/api/2/issue/%s/transitions", c.endpoint, issue)
|
||||||
if c.getOptBool("dryrun", false) {
|
if c.getOptBool("dryrun", false) {
|
||||||
log.Debugf("POST: %s", json)
|
log.Debugf("POST: %s", json)
|
||||||
@@ -547,19 +639,35 @@ func (c *Cli) CmdTransition(issue string, trans string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
uri = fmt.Sprintf("%s/rest/api/2/issue/%s", c.endpoint, issue)
|
uri = fmt.Sprintf("%s/rest/api/2/issue/%s", c.endpoint, issue)
|
||||||
var issueData map[string]interface{}
|
data, err = responseToJSON(c.get(uri))
|
||||||
if data, err := responseToJson(c.get(uri)); err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
|
||||||
issueData = data.(map[string]interface{})
|
|
||||||
}
|
}
|
||||||
|
issueData := data.(map[string]interface{})
|
||||||
issueData["meta"] = transMeta
|
issueData["meta"] = transMeta
|
||||||
|
if c.GetOptString("defaultResolution", "") == "" {
|
||||||
|
// .meta.fields.resolution.allowedValues
|
||||||
|
if fields, ok := transMeta["fields"].(map[string]interface{}); ok {
|
||||||
|
if resolution, ok := fields["resolution"].(map[string]interface{}); ok {
|
||||||
|
if allowedValues, ok := resolution["allowedValues"].([]interface{}); ok {
|
||||||
|
for _, allowedValueRaw := range allowedValues {
|
||||||
|
if allowedValues, ok := allowedValueRaw.(map[string]interface{}); ok {
|
||||||
|
if allowedValues["name"] == "Fixed" {
|
||||||
|
c.opts["defaultResolution"] = "Fixed"
|
||||||
|
} else if allowedValues["name"] == "Done" {
|
||||||
|
c.opts["defaultResolution"] = "Done"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
issueData["overrides"] = c.opts
|
issueData["overrides"] = c.opts
|
||||||
issueData["transition"] = map[string]interface{}{
|
issueData["transition"] = map[string]interface{}{
|
||||||
"name": transName,
|
"name": transName,
|
||||||
"id": transId,
|
"id": transID,
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.editTemplate(
|
return c.editTemplate(
|
||||||
c.getTemplate("transition"),
|
c.getTemplate("transition"),
|
||||||
fmt.Sprintf("%s-trans-%s-", issue, trans),
|
fmt.Sprintf("%s-trans-%s-", issue, trans),
|
||||||
@@ -568,11 +676,12 @@ 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 {
|
func (c *Cli) CmdComment(issue string) error {
|
||||||
log.Debugf("comment called")
|
log.Debugf("comment called")
|
||||||
|
|
||||||
handlePost := func(json string) error {
|
handlePost := func(json string) error {
|
||||||
log.Debugf("JSON: %s", json)
|
|
||||||
uri := fmt.Sprintf("%s/rest/api/2/issue/%s/comment", c.endpoint, issue)
|
uri := fmt.Sprintf("%s/rest/api/2/issue/%s/comment", c.endpoint, issue)
|
||||||
if c.getOptBool("dryrun", false) {
|
if c.getOptBool("dryrun", false) {
|
||||||
log.Debugf("POST: %s", json)
|
log.Debugf("POST: %s", json)
|
||||||
@@ -590,13 +699,12 @@ func (c *Cli) CmdComment(issue string) error {
|
|||||||
fmt.Printf("OK %s %s/browse/%s\n", issue, c.endpoint, issue)
|
fmt.Printf("OK %s %s/browse/%s\n", issue, c.endpoint, issue)
|
||||||
}
|
}
|
||||||
return nil
|
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 != "" {
|
if comment, ok := c.opts["comment"]; ok && comment != "" {
|
||||||
@@ -607,24 +715,23 @@ func (c *Cli) CmdComment(issue string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return handlePost(json)
|
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 {
|
func (c *Cli) CmdComponent(action string, project string, name string, desc string, lead string) error {
|
||||||
log.Debugf("component called")
|
log.Debugf("component called")
|
||||||
|
|
||||||
switch action {
|
switch action {
|
||||||
case "add":
|
case "add":
|
||||||
default:
|
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{}{
|
json, err := jsonEncode(map[string]interface{}{
|
||||||
@@ -661,6 +768,7 @@ func (c *Cli) CmdComponent(action string, project string, name string, desc stri
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CmdLabels will add, remove or set labels on a given issue
|
||||||
func (c *Cli) CmdLabels(action string, issue string, labels []string) error {
|
func (c *Cli) CmdLabels(action string, issue string, labels []string) error {
|
||||||
log.Debugf("label called")
|
log.Debugf("label called")
|
||||||
|
|
||||||
@@ -669,7 +777,6 @@ func (c *Cli) CmdLabels(action string, issue string, labels []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handlePut := func(json string) error {
|
handlePut := func(json string) error {
|
||||||
log.Debugf("JSON: %s", json)
|
|
||||||
uri := fmt.Sprintf("%s/rest/api/2/issue/%s", c.endpoint, issue)
|
uri := fmt.Sprintf("%s/rest/api/2/issue/%s", c.endpoint, issue)
|
||||||
if c.getOptBool("dryrun", false) {
|
if c.getOptBool("dryrun", false) {
|
||||||
log.Debugf("PUT: %s", json)
|
log.Debugf("PUT: %s", json)
|
||||||
@@ -687,23 +794,22 @@ func (c *Cli) CmdLabels(action string, issue string, labels []string) error {
|
|||||||
fmt.Printf("OK %s %s/browse/%s\n", issue, c.endpoint, issue)
|
fmt.Printf("OK %s %s/browse/%s\n", issue, c.endpoint, issue)
|
||||||
}
|
}
|
||||||
return nil
|
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
|
var err error
|
||||||
if action == "set" {
|
if action == "set" {
|
||||||
labelsActions := make([]map[string][]string, 1)
|
labelsActions := make([]map[string][]string, 1)
|
||||||
labelsActions[0] = map[string][]string{
|
labelsActions[0] = map[string][]string{
|
||||||
"set": labels,
|
"set": labels,
|
||||||
}
|
}
|
||||||
labels_json, err = jsonEncode(map[string]interface{}{
|
labelsJSON, err = jsonEncode(map[string]interface{}{
|
||||||
"labels": labelsActions,
|
"labels": labelsActions,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
@@ -714,18 +820,19 @@ func (c *Cli) CmdLabels(action string, issue string, labels []string) error {
|
|||||||
}
|
}
|
||||||
labelsActions[i] = labelActionMap
|
labelsActions[i] = labelActionMap
|
||||||
}
|
}
|
||||||
labels_json, err = jsonEncode(map[string]interface{}{
|
labelsJSON, err = jsonEncode(map[string]interface{}{
|
||||||
"labels": labelsActions,
|
"labels": labelsActions,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
json := fmt.Sprintf("{ \"update\": %s }", labels_json)
|
json := fmt.Sprintf("{ \"update\": %s }", labelsJSON)
|
||||||
return handlePut(json)
|
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 {
|
func (c *Cli) CmdAssign(issue string, user string) error {
|
||||||
log.Debugf("assign called")
|
log.Debugf("assign called")
|
||||||
|
|
||||||
@@ -761,13 +868,14 @@ func (c *Cli) CmdAssign(issue string, user string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CmdExportTemplates will export the default templates to the template directory.
|
||||||
func (c *Cli) CmdExportTemplates() error {
|
func (c *Cli) CmdExportTemplates() error {
|
||||||
dir := c.opts["directory"].(string)
|
dir := c.opts["directory"].(string)
|
||||||
if err := mkdir(dir); err != nil {
|
if err := mkdir(dir); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
for name, template := range all_templates {
|
for name, template := range allTemplates {
|
||||||
if wanted, ok := c.opts["template"]; ok && wanted != name {
|
if wanted, ok := c.opts["template"]; ok && wanted != name {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -776,18 +884,19 @@ func (c *Cli) CmdExportTemplates() error {
|
|||||||
log.Warning("Skipping %s, already exists", templateFile)
|
log.Warning("Skipping %s, already exists", templateFile)
|
||||||
continue
|
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)
|
log.Errorf("Failed to open %s for writing: %s", templateFile, err)
|
||||||
return 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
|
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) {
|
func (c *Cli) CmdRequest(uri, content string) (err error) {
|
||||||
log.Debugf("request called")
|
log.Debugf("request called")
|
||||||
|
|
||||||
@@ -798,11 +907,11 @@ func (c *Cli) CmdRequest(uri, content string) (err error) {
|
|||||||
method := strings.ToUpper(c.opts["method"].(string))
|
method := strings.ToUpper(c.opts["method"].(string))
|
||||||
var data interface{}
|
var data interface{}
|
||||||
if method == "GET" {
|
if method == "GET" {
|
||||||
data, err = responseToJson(c.get(uri))
|
data, err = responseToJSON(c.get(uri))
|
||||||
} else if method == "POST" {
|
} else if method == "POST" {
|
||||||
data, err = responseToJson(c.post(uri, content))
|
data, err = responseToJSON(c.post(uri, content))
|
||||||
} else if method == "PUT" {
|
} else if method == "PUT" {
|
||||||
data, err = responseToJson(c.put(uri, content))
|
data, err = responseToJSON(c.put(uri, content))
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -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{}
|
||||||
@@ -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"`
|
||||||
|
}
|
||||||
@@ -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
|
||||||
@@ -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"`
|
||||||
|
}
|
||||||
@@ -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
|
||||||
@@ -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"`
|
||||||
|
}
|
||||||
@@ -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"`
|
||||||
|
}
|
||||||
@@ -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"`
|
||||||
|
}
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -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"`
|
||||||
|
}
|
||||||
+59
-7
@@ -14,12 +14,16 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
log = logging.MustGetLogger("jira")
|
log = logging.MustGetLogger("jira")
|
||||||
format = "%{color}%{time:2006-01-02T15:04:05.000Z07:00} %{level:-5s} [%{shortfile}]%{color:reset} %{message}"
|
defaultFormat = "%{color}%{time:2006-01-02T15:04:05.000Z07:00} %{level:-5s} [%{shortfile}]%{color:reset} %{message}"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
logBackend := logging.NewLogBackend(os.Stderr, "", 0)
|
logBackend := logging.NewLogBackend(os.Stderr, "", 0)
|
||||||
|
format := os.Getenv("JIRA_LOG_FORMAT")
|
||||||
|
if format == "" {
|
||||||
|
format = defaultFormat
|
||||||
|
}
|
||||||
logging.SetBackend(
|
logging.SetBackend(
|
||||||
logging.NewBackendFormatter(
|
logging.NewBackendFormatter(
|
||||||
logBackend,
|
logBackend,
|
||||||
@@ -31,7 +35,7 @@ func main() {
|
|||||||
user := os.Getenv("USER")
|
user := os.Getenv("USER")
|
||||||
home := os.Getenv("HOME")
|
home := os.Getenv("HOME")
|
||||||
defaultQueryFields := "summary,created,updated,priority,status,reporter,assignee"
|
defaultQueryFields := "summary,created,updated,priority,status,reporter,assignee"
|
||||||
defaultSort := "priority asc, created"
|
defaultSort := "priority asc, key"
|
||||||
defaultMaxResults := 500
|
defaultMaxResults := 500
|
||||||
|
|
||||||
usage := func(ok bool) {
|
usage := func(ok bool) {
|
||||||
@@ -65,6 +69,10 @@ Usage:
|
|||||||
jira reopen ISSUE [--edit] <Edit Options>
|
jira reopen ISSUE [--edit] <Edit Options>
|
||||||
jira start ISSUE [--edit] <Edit Options>
|
jira start ISSUE [--edit] <Edit Options>
|
||||||
jira stop ISSUE [--edit] <Edit Options>
|
jira stop ISSUE [--edit] <Edit Options>
|
||||||
|
jira todo ISSUE [--edit] <Edit Options>
|
||||||
|
jira backlog ISSUE [--edit] <Edit Options>
|
||||||
|
jira done ISSUE [--edit] <Edit Options>
|
||||||
|
jira prog|progress|in-progress [--edit] <Edit Options>
|
||||||
jira comment ISSUE [--noedit] <Edit Options>
|
jira comment ISSUE [--noedit] <Edit Options>
|
||||||
jira (set,add,remove) labels ISSUE [LABEL] ...
|
jira (set,add,remove) labels ISSUE [LABEL] ...
|
||||||
jira take ISSUE
|
jira take ISSUE
|
||||||
@@ -81,6 +89,7 @@ Usage:
|
|||||||
jira export-templates [-d DIR] [-t template]
|
jira export-templates [-d DIR] [-t template]
|
||||||
jira (b|browse) ISSUE
|
jira (b|browse) ISSUE
|
||||||
jira login
|
jira login
|
||||||
|
jira logout
|
||||||
jira request [-M METHOD] URI [DATA]
|
jira request [-M METHOD] URI [DATA]
|
||||||
jira ISSUE
|
jira ISSUE
|
||||||
|
|
||||||
@@ -140,6 +149,12 @@ Command Options:
|
|||||||
"reopen": "reopen",
|
"reopen": "reopen",
|
||||||
"start": "start",
|
"start": "start",
|
||||||
"stop": "stop",
|
"stop": "stop",
|
||||||
|
"todo": "todo",
|
||||||
|
"backlog": "backlog",
|
||||||
|
"done": "done",
|
||||||
|
"prog": "in-progress",
|
||||||
|
"progress": "in-progress",
|
||||||
|
"in-progress": "in-progress",
|
||||||
"comment": "comment",
|
"comment": "comment",
|
||||||
"label": "labels",
|
"label": "labels",
|
||||||
"labels": "labels",
|
"labels": "labels",
|
||||||
@@ -158,6 +173,7 @@ Command Options:
|
|||||||
"export-templates": "export-templates",
|
"export-templates": "export-templates",
|
||||||
"browse": "browse",
|
"browse": "browse",
|
||||||
"login": "login",
|
"login": "login",
|
||||||
|
"logout": "logout",
|
||||||
"req": "request",
|
"req": "request",
|
||||||
"request": "request",
|
"request": "request",
|
||||||
"vote": "vote",
|
"vote": "vote",
|
||||||
@@ -305,6 +321,8 @@ Command Options:
|
|||||||
switch command {
|
switch command {
|
||||||
case "login":
|
case "login":
|
||||||
err = c.CmdLogin()
|
err = c.CmdLogin()
|
||||||
|
case "logout":
|
||||||
|
err = c.CmdLogout()
|
||||||
case "fields":
|
case "fields":
|
||||||
err = c.CmdFields()
|
err = c.CmdFields()
|
||||||
case "list":
|
case "list":
|
||||||
@@ -352,10 +370,26 @@ Command Options:
|
|||||||
requireArgs(2)
|
requireArgs(2)
|
||||||
err = c.CmdBlocks(args[0], args[1])
|
err = c.CmdBlocks(args[0], args[1])
|
||||||
case "dups":
|
case "dups":
|
||||||
|
setEditing(true)
|
||||||
requireArgs(2)
|
requireArgs(2)
|
||||||
if err = c.CmdDups(args[0], args[1]); err == nil {
|
if err = c.CmdDups(args[0], args[1]); err == nil {
|
||||||
opts["resolution"] = "Duplicate"
|
opts["resolution"] = "Duplicate"
|
||||||
err = c.CmdTransition(args[0], "close")
|
trans, err := c.ValidTransitions(args[0])
|
||||||
|
if err == nil {
|
||||||
|
if trans.Find("close") != nil {
|
||||||
|
err = c.CmdTransition(args[0], "close")
|
||||||
|
} else if trans.Find("done") != nil {
|
||||||
|
// for now just assume if there is no "close", then
|
||||||
|
// there is a "done" state
|
||||||
|
err = c.CmdTransition(args[0], "done")
|
||||||
|
} else if trans.Find("start") != nil {
|
||||||
|
err = c.CmdTransition(args[0], "start")
|
||||||
|
if err == nil {
|
||||||
|
err = c.CmdTransition(args[0], "stop")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
case "watch":
|
case "watch":
|
||||||
requireArgs(1)
|
requireArgs(1)
|
||||||
@@ -390,6 +424,22 @@ Command Options:
|
|||||||
requireArgs(1)
|
requireArgs(1)
|
||||||
setEditing(false)
|
setEditing(false)
|
||||||
err = c.CmdTransition(args[0], "stop")
|
err = c.CmdTransition(args[0], "stop")
|
||||||
|
case "todo":
|
||||||
|
requireArgs(1)
|
||||||
|
setEditing(false)
|
||||||
|
err = c.CmdTransition(args[0], "To Do")
|
||||||
|
case "backlog":
|
||||||
|
requireArgs(1)
|
||||||
|
setEditing(false)
|
||||||
|
err = c.CmdTransition(args[0], "Backlog")
|
||||||
|
case "done":
|
||||||
|
requireArgs(1)
|
||||||
|
setEditing(false)
|
||||||
|
err = c.CmdTransition(args[0], "Done")
|
||||||
|
case "in-progress":
|
||||||
|
requireArgs(1)
|
||||||
|
setEditing(false)
|
||||||
|
err = c.CmdTransition(args[0], "Progress")
|
||||||
case "comment":
|
case "comment":
|
||||||
requireArgs(1)
|
requireArgs(1)
|
||||||
setEditing(true)
|
setEditing(true)
|
||||||
@@ -461,7 +511,9 @@ Command Options:
|
|||||||
func parseYaml(file string, opts map[string]interface{}) {
|
func parseYaml(file string, opts map[string]interface{}) {
|
||||||
if fh, err := ioutil.ReadFile(file); err == nil {
|
if fh, err := ioutil.ReadFile(file); err == nil {
|
||||||
log.Debugf("Found Config file: %s", file)
|
log.Debugf("Found Config file: %s", file)
|
||||||
yaml.Unmarshal(fh, &opts)
|
if err := yaml.Unmarshal(fh, &opts); err != nil {
|
||||||
|
log.Errorf("Unable to parse %s: %s", file, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -489,10 +541,10 @@ func loadConfigs(opts map[string]interface{}) {
|
|||||||
populateEnv(opts)
|
populateEnv(opts)
|
||||||
paths := jira.FindParentPaths(".jira.d/config.yml")
|
paths := jira.FindParentPaths(".jira.d/config.yml")
|
||||||
// prepend
|
// prepend
|
||||||
paths = append([]string{"/etc/go-jira.yml"}, paths...)
|
paths = append(paths, "/etc/go-jira.yml")
|
||||||
|
|
||||||
// iterate paths in reverse
|
// iterate paths in reverse
|
||||||
for i := len(paths) - 1; i >= 0; i-- {
|
for i := 0; i < len(paths); i++ {
|
||||||
file := paths[i]
|
file := paths[i]
|
||||||
if stat, err := os.Stat(file); err == nil {
|
if stat, err := os.Stat(file); err == nil {
|
||||||
tmp := make(map[string]interface{})
|
tmp := make(map[string]interface{})
|
||||||
|
|||||||
Executable
+21
@@ -0,0 +1,21 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
from lxml import html
|
||||||
|
import requests
|
||||||
|
import json
|
||||||
|
|
||||||
|
page = requests.get('https://docs.atlassian.com/jira/REST/cloud')
|
||||||
|
tree = html.fromstring(page.content)
|
||||||
|
|
||||||
|
schemas = tree.xpath("//div[@class='representation-doc-block']//code/text()")
|
||||||
|
|
||||||
|
for schema in schemas:
|
||||||
|
try:
|
||||||
|
data = json.loads(schema)
|
||||||
|
if "title" in data:
|
||||||
|
title = data["title"].replace(" ", "")
|
||||||
|
print "Writing {}.json".format(title)
|
||||||
|
with open("{}.json".format(title), 'w') as f:
|
||||||
|
f.write(schema)
|
||||||
|
except:
|
||||||
|
True
|
||||||
|
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
endpoint: http://localhost:8080
|
||||||
|
user: gojira
|
||||||
Executable
+51
@@ -0,0 +1,51 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
eval "$(curl -q -s https://raw.githubusercontent.com/coryb/osht/master/osht.sh)"
|
||||||
|
cd $(dirname $0)
|
||||||
|
jira="../jira --user admin"
|
||||||
|
|
||||||
|
PLAN 14
|
||||||
|
|
||||||
|
# clean out any old containers
|
||||||
|
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
|
||||||
|
|
||||||
|
# wait a few seconds for it to bind to port 8080
|
||||||
|
RUNS sleep 10
|
||||||
|
|
||||||
|
# wait for healthchecks to pass, curl will retry 60 times over 5 min waiting
|
||||||
|
RUNS curl -q -L --retry 360 --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
|
||||||
|
|
||||||
|
# create gojira user
|
||||||
|
RUNS $jira req -M POST /rest/api/2/user '{"name":"gojira","password":"gojira123","emailAddress":"gojira@example.com","displayName":"Go Jira"}'
|
||||||
|
|
||||||
|
# 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 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"}'
|
||||||
|
|
||||||
|
# create KANBAN software project
|
||||||
|
RUNS $jira req -M POST /rest/api/2/project '{"key":"KANBAN","name":"Kanban","projectTypeKey":"software","projectTemplateKey":"com.pyxis.greenhopper.jira:gh-kanban-template","lead":"gojira"}'
|
||||||
|
|
||||||
|
# create BAISC software project
|
||||||
|
RUNS $jira req -M POST /rest/api/2/project '{"key":"BASIC","name":"Basic","projectTypeKey":"software","projectTemplateKey":"com.pyxis.greenhopper.jira:basic-software-development-template","lead":"gojira"}'
|
||||||
|
|
||||||
|
# create PROJECT business project
|
||||||
|
RUNS $jira req -M POST /rest/api/2/project '{"key":"PROJECT","name":"Project","projectTypeKey":"business","projectTemplateKey":"com.atlassian.jira-core-project-templates:jira-core-project-management","lead":"gojira"}'
|
||||||
|
|
||||||
|
# create PROCESS business project
|
||||||
|
RUNS $jira req -M POST /rest/api/2/project '{"key":"PROCESS","name":"Process","projectTypeKey":"business","projectTemplateKey":"com.atlassian.jira-core-project-templates:jira-core-process-management","lead":"gojira"}'
|
||||||
|
|
||||||
|
# create TASK business project
|
||||||
|
RUNS $jira req -M POST /rest/api/2/project '{"key":"TASK","name":"Task","projectTypeKey":"business","projectTemplateKey":"com.atlassian.jira-core-project-templates:jira-core-task-management","lead":"gojira"}'
|
||||||
|
|
||||||
|
RUNS $jira logout
|
||||||
|
|
||||||
|
# export new templates so we are always using whatever is latest
|
||||||
|
# and not whatever is in the test-runners homedir
|
||||||
|
RUNS $jira export-templates -d .jira.d/templates
|
||||||
Executable
+29
@@ -0,0 +1,29 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
eval "$(curl -q -s https://raw.githubusercontent.com/coryb/osht/master/osht.sh)"
|
||||||
|
cd $(dirname $0)
|
||||||
|
jira=../jira
|
||||||
|
|
||||||
|
PLAN 7
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Verify logout works, we expect when we call the session api
|
||||||
|
## that we will get a 401 and prompt user for password
|
||||||
|
################################################################################
|
||||||
|
RUNS $jira logout
|
||||||
|
|
||||||
|
NRUNS $jira req /rest/auth/1/session </dev/null
|
||||||
|
ODIFF <<EOF
|
||||||
|
Jira Password [gojira]:
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Verify login works (password read from stdin) and verify that the
|
||||||
|
## sesion api no longer prompts
|
||||||
|
###############################################################################
|
||||||
|
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"'
|
||||||
|
|
||||||
|
|
||||||
Executable
+508
@@ -0,0 +1,508 @@
|
|||||||
|
#!/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}"
|
||||||
|
|
||||||
|
PLAN 84
|
||||||
|
|
||||||
|
# cleanup from previous failed test executions
|
||||||
|
($jira ls | awk -F: '{print $1}' | while read issue; do ../jira done $issue; done) | sed 's/^/# CLEANUP: /g'
|
||||||
|
|
||||||
|
# reset login
|
||||||
|
RUNS $jira logout
|
||||||
|
echo "gojira123" | RUNS $jira login
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## 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 http://localhost:8080/browse/$issue
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## View the issue we just created
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira view $issue
|
||||||
|
DIFF <<EOF
|
||||||
|
issue: $issue
|
||||||
|
created: a minute ago
|
||||||
|
status: To Do
|
||||||
|
summary: summary
|
||||||
|
project: BASIC
|
||||||
|
issuetype: Bug
|
||||||
|
assignee: gojira
|
||||||
|
reporter: gojira
|
||||||
|
priority: Medium
|
||||||
|
votes: 0
|
||||||
|
description: |
|
||||||
|
description
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## List all issues, should be just the one we created
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira ls
|
||||||
|
DIFF <<EOF
|
||||||
|
$(printf %-12s $issue:) summary
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Try to close the issue, bug Basic projects do not allow that state
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
NRUNS $jira close $issue
|
||||||
|
EDIFF <<EOF
|
||||||
|
ERROR Invalid Transition 'close', Available: To Do, In Progress, In Review, Done
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## put the issue into Done state, resolving it.
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira done $issue
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $issue http://localhost:8080/browse/$issue
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Verify there are no unresolved issues
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira ls
|
||||||
|
DIFF <<EOF
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Setup 2 more issues so we can test duping
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
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
|
||||||
|
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
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Mark issue as duplicate, expect both issues to be updated and when viewing
|
||||||
|
## the main issue there should be a "depends" line showing the dup'd issue, and
|
||||||
|
## that issue should be resolved
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira $dup dups $issue --noedit
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $issue http://localhost:8080/browse/$issue
|
||||||
|
OK $dup http://localhost:8080/browse/$dup
|
||||||
|
EOF
|
||||||
|
|
||||||
|
RUNS $jira $issue
|
||||||
|
DIFF <<EOF
|
||||||
|
issue: $issue
|
||||||
|
created: a minute ago
|
||||||
|
status: To Do
|
||||||
|
summary: summary
|
||||||
|
project: BASIC
|
||||||
|
issuetype: Bug
|
||||||
|
assignee: gojira
|
||||||
|
reporter: gojira
|
||||||
|
blockers:
|
||||||
|
depends: $dup[Done]
|
||||||
|
priority: Medium
|
||||||
|
votes: 0
|
||||||
|
description: |
|
||||||
|
description
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## We should see only one unresolved issue, the Dup should be resolved
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira ls
|
||||||
|
DIFF <<EOF
|
||||||
|
$(printf %-12s $issue:) summary
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Setup for testing blocking issues
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
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
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Set blocker and verify it shows up when viewing the main issue
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira $blocker blocks $issue
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $issue http://localhost:8080/browse/$issue
|
||||||
|
EOF
|
||||||
|
|
||||||
|
RUNS $jira $issue
|
||||||
|
DIFF <<EOF
|
||||||
|
issue: $issue
|
||||||
|
created: a minute ago
|
||||||
|
status: To Do
|
||||||
|
summary: summary
|
||||||
|
project: BASIC
|
||||||
|
issuetype: Bug
|
||||||
|
assignee: gojira
|
||||||
|
reporter: gojira
|
||||||
|
blockers: $blocker[To Do]
|
||||||
|
depends: $dup[Done]
|
||||||
|
priority: Medium
|
||||||
|
votes: 0
|
||||||
|
description: |
|
||||||
|
description
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Both issues are unresolved now
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira ls
|
||||||
|
DIFF <<EOF
|
||||||
|
$(printf %-12s $issue:) summary
|
||||||
|
$(printf %-12s $blocker:) blocks
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# reset login for mojira for voting
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
jira="$jira --user mojira"
|
||||||
|
|
||||||
|
RUNS $jira logout
|
||||||
|
echo "mojira123" | RUNS $jira login
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## vote for main issue, verify it shows when viewing the issue
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira vote $issue
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $issue http://localhost:8080/browse/$issue
|
||||||
|
EOF
|
||||||
|
|
||||||
|
RUNS $jira $issue
|
||||||
|
DIFF <<EOF
|
||||||
|
issue: $issue
|
||||||
|
created: a minute ago
|
||||||
|
status: To Do
|
||||||
|
summary: summary
|
||||||
|
project: BASIC
|
||||||
|
issuetype: Bug
|
||||||
|
assignee: gojira
|
||||||
|
reporter: gojira
|
||||||
|
blockers: $blocker[To Do]
|
||||||
|
depends: $dup[Done]
|
||||||
|
priority: Medium
|
||||||
|
votes: 1
|
||||||
|
description: |
|
||||||
|
description
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## downvote the main issue, verify the vote count goes back to 0
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira vote $issue --down
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $issue http://localhost:8080/browse/$issue
|
||||||
|
EOF
|
||||||
|
|
||||||
|
RUNS $jira $issue
|
||||||
|
DIFF <<EOF
|
||||||
|
issue: $issue
|
||||||
|
created: a minute ago
|
||||||
|
status: To Do
|
||||||
|
summary: summary
|
||||||
|
project: BASIC
|
||||||
|
issuetype: Bug
|
||||||
|
assignee: gojira
|
||||||
|
reporter: gojira
|
||||||
|
blockers: $blocker[To Do]
|
||||||
|
depends: $dup[Done]
|
||||||
|
priority: Medium
|
||||||
|
votes: 0
|
||||||
|
description: |
|
||||||
|
description
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## set mojira user as watcher to issue and verify from REST api
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira watch $issue
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $issue http://localhost:8080/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
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## set issue to In Progress state
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira trans "In Progress" $blocker --noedit
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $blocker http://localhost:8080/browse/$blocker
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## set it back to "To Do"
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira todo $blocker
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $blocker http://localhost:8080/browse/$blocker
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Set issue to "In Review" state
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira trans "review" $blocker --noedit
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $blocker http://localhost:8080/browse/$blocker
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Set it back to "To Do"
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira todo $blocker
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $blocker http://localhost:8080/browse/$blocker
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Set it to "In Progress"
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira prog $blocker
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $blocker http://localhost:8080/browse/$blocker
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Set it to "Done"
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira done $blocker
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $blocker http://localhost:8080/browse/$blocker
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Verify issue is now in Done state (the "blocker" issue is now Done)
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira $issue
|
||||||
|
DIFF <<EOF
|
||||||
|
issue: $issue
|
||||||
|
created: a minute ago
|
||||||
|
status: To Do
|
||||||
|
summary: summary
|
||||||
|
project: BASIC
|
||||||
|
issuetype: Bug
|
||||||
|
assignee: gojira
|
||||||
|
reporter: gojira
|
||||||
|
blockers: $blocker[Done]
|
||||||
|
depends: $dup[Done]
|
||||||
|
priority: Medium
|
||||||
|
votes: 0
|
||||||
|
description: |
|
||||||
|
description
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Verify we can add a comment
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira comment $issue --noedit -m "Yo, Comment"
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $issue http://localhost:8080/browse/$issue
|
||||||
|
EOF
|
||||||
|
|
||||||
|
RUNS $jira $issue
|
||||||
|
DIFF <<EOF
|
||||||
|
issue: $issue
|
||||||
|
created: a minute ago
|
||||||
|
status: To Do
|
||||||
|
summary: summary
|
||||||
|
project: BASIC
|
||||||
|
issuetype: Bug
|
||||||
|
assignee: gojira
|
||||||
|
reporter: gojira
|
||||||
|
blockers: $blocker[Done]
|
||||||
|
depends: $dup[Done]
|
||||||
|
priority: Medium
|
||||||
|
votes: 0
|
||||||
|
description: |
|
||||||
|
description
|
||||||
|
|
||||||
|
comments:
|
||||||
|
- | # mojira, a minute ago
|
||||||
|
Yo, Comment
|
||||||
|
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Verify we can add labels to an issue
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira add labels $blocker test-label another-label
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $blocker http://localhost:8080/browse/$blocker
|
||||||
|
EOF
|
||||||
|
|
||||||
|
RUNS $jira $blocker
|
||||||
|
DIFF <<EOF
|
||||||
|
issue: $blocker
|
||||||
|
created: a minute ago
|
||||||
|
status: Done
|
||||||
|
summary: blocks
|
||||||
|
project: BASIC
|
||||||
|
issuetype: Bug
|
||||||
|
assignee: gojira
|
||||||
|
reporter: gojira
|
||||||
|
blockers:
|
||||||
|
depends: $issue[To Do]
|
||||||
|
priority: Medium
|
||||||
|
votes: 0
|
||||||
|
labels: another-label, test-label
|
||||||
|
description: |
|
||||||
|
blocks
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Verify we can remove a label
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira remove labels $blocker another-label
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $blocker http://localhost:8080/browse/$blocker
|
||||||
|
EOF
|
||||||
|
|
||||||
|
RUNS $jira $blocker
|
||||||
|
DIFF <<EOF
|
||||||
|
issue: $blocker
|
||||||
|
created: a minute ago
|
||||||
|
status: Done
|
||||||
|
summary: blocks
|
||||||
|
project: BASIC
|
||||||
|
issuetype: Bug
|
||||||
|
assignee: gojira
|
||||||
|
reporter: gojira
|
||||||
|
blockers:
|
||||||
|
depends: $issue[To Do]
|
||||||
|
priority: Medium
|
||||||
|
votes: 0
|
||||||
|
labels: test-label
|
||||||
|
description: |
|
||||||
|
blocks
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Verify we can replace the labels with a new set
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira set labels $blocker more-label better-label
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $blocker http://localhost:8080/browse/$blocker
|
||||||
|
EOF
|
||||||
|
|
||||||
|
RUNS $jira $blocker
|
||||||
|
DIFF <<EOF
|
||||||
|
issue: $blocker
|
||||||
|
created: a minute ago
|
||||||
|
status: Done
|
||||||
|
summary: blocks
|
||||||
|
project: BASIC
|
||||||
|
issuetype: Bug
|
||||||
|
assignee: gojira
|
||||||
|
reporter: gojira
|
||||||
|
blockers:
|
||||||
|
depends: $issue[To Do]
|
||||||
|
priority: Medium
|
||||||
|
votes: 0
|
||||||
|
labels: better-label, more-label
|
||||||
|
description: |
|
||||||
|
blocks
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Verify that "mojira" user can take the issue (reassign to self)
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira take $blocker
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $blocker http://localhost:8080/browse/$blocker
|
||||||
|
EOF
|
||||||
|
|
||||||
|
RUNS $jira $blocker
|
||||||
|
DIFF <<EOF
|
||||||
|
issue: $blocker
|
||||||
|
created: a minute ago
|
||||||
|
status: Done
|
||||||
|
summary: blocks
|
||||||
|
project: BASIC
|
||||||
|
issuetype: Bug
|
||||||
|
assignee: mojira
|
||||||
|
reporter: gojira
|
||||||
|
blockers:
|
||||||
|
depends: $issue[To Do]
|
||||||
|
priority: Medium
|
||||||
|
votes: 0
|
||||||
|
labels: better-label, more-label
|
||||||
|
description: |
|
||||||
|
blocks
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Verify we can give the issue back go "gojira" user
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira give $blocker gojira
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $blocker http://localhost:8080/browse/$blocker
|
||||||
|
EOF
|
||||||
|
|
||||||
|
RUNS $jira $blocker
|
||||||
|
DIFF <<EOF
|
||||||
|
issue: $blocker
|
||||||
|
created: a minute ago
|
||||||
|
status: Done
|
||||||
|
summary: blocks
|
||||||
|
project: BASIC
|
||||||
|
issuetype: Bug
|
||||||
|
assignee: gojira
|
||||||
|
reporter: gojira
|
||||||
|
blockers:
|
||||||
|
depends: $issue[To Do]
|
||||||
|
priority: Medium
|
||||||
|
votes: 0
|
||||||
|
labels: better-label, more-label
|
||||||
|
description: |
|
||||||
|
blocks
|
||||||
|
EOF
|
||||||
Executable
+508
@@ -0,0 +1,508 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
eval "$(curl -q -s https://raw.githubusercontent.com/coryb/osht/master/osht.sh)"
|
||||||
|
cd $(dirname $0)
|
||||||
|
jira="../jira --project SCRUM"
|
||||||
|
export JIRA_LOG_FORMAT="%{level:-5s} %{message}"
|
||||||
|
|
||||||
|
PLAN 84
|
||||||
|
|
||||||
|
# cleanup from previous failed test executions
|
||||||
|
($jira ls | awk -F: '{print $1}' | while read issue; do ../jira done $issue; done) | sed 's/^/# CLEANUP: /g'
|
||||||
|
|
||||||
|
# reset login
|
||||||
|
RUNS $jira logout
|
||||||
|
echo "gojira123" | RUNS $jira login
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## 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 http://localhost:8080/browse/$issue
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## View the issue we just created
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira view $issue
|
||||||
|
DIFF <<EOF
|
||||||
|
issue: $issue
|
||||||
|
created: a minute ago
|
||||||
|
status: To Do
|
||||||
|
summary: summary
|
||||||
|
project: SCRUM
|
||||||
|
issuetype: Bug
|
||||||
|
assignee: gojira
|
||||||
|
reporter: gojira
|
||||||
|
priority: Medium
|
||||||
|
votes: 0
|
||||||
|
description: |
|
||||||
|
description
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## List all issues, should be just the one we created
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira ls
|
||||||
|
DIFF <<EOF
|
||||||
|
$(printf %-12s $issue:) summary
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Try to close the issue, bug Basic projects do not allow that state
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
NRUNS $jira close $issue
|
||||||
|
EDIFF <<EOF
|
||||||
|
ERROR Invalid Transition 'close', Available: To Do, In Progress, Done
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## put the issue into Done state, resolving it.
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira done $issue
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $issue http://localhost:8080/browse/$issue
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Verify there are no unresolved issues
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira ls
|
||||||
|
DIFF <<EOF
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Setup 2 more issues so we can test duping
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
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
|
||||||
|
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
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Mark issue as duplicate, expect both issues to be updated and when viewing
|
||||||
|
## the main issue there should be a "depends" line showing the dup'd issue, and
|
||||||
|
## that issue should be resolved
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira $dup dups $issue --noedit
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $issue http://localhost:8080/browse/$issue
|
||||||
|
OK $dup http://localhost:8080/browse/$dup
|
||||||
|
EOF
|
||||||
|
|
||||||
|
RUNS $jira $issue
|
||||||
|
DIFF <<EOF
|
||||||
|
issue: $issue
|
||||||
|
created: a minute ago
|
||||||
|
status: To Do
|
||||||
|
summary: summary
|
||||||
|
project: SCRUM
|
||||||
|
issuetype: Bug
|
||||||
|
assignee: gojira
|
||||||
|
reporter: gojira
|
||||||
|
blockers:
|
||||||
|
depends: $dup[Done]
|
||||||
|
priority: Medium
|
||||||
|
votes: 0
|
||||||
|
description: |
|
||||||
|
description
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## We should see only one unresolved issue, the Dup should be resolved
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira ls
|
||||||
|
DIFF <<EOF
|
||||||
|
$(printf %-12s $issue:) summary
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Setup for testing blocking issues
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
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
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Set blocker and verify it shows up when viewing the main issue
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira $blocker blocks $issue
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $issue http://localhost:8080/browse/$issue
|
||||||
|
EOF
|
||||||
|
|
||||||
|
RUNS $jira $issue
|
||||||
|
DIFF <<EOF
|
||||||
|
issue: $issue
|
||||||
|
created: a minute ago
|
||||||
|
status: To Do
|
||||||
|
summary: summary
|
||||||
|
project: SCRUM
|
||||||
|
issuetype: Bug
|
||||||
|
assignee: gojira
|
||||||
|
reporter: gojira
|
||||||
|
blockers: $blocker[To Do]
|
||||||
|
depends: $dup[Done]
|
||||||
|
priority: Medium
|
||||||
|
votes: 0
|
||||||
|
description: |
|
||||||
|
description
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Both issues are unresolved now
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira ls
|
||||||
|
DIFF <<EOF
|
||||||
|
$(printf %-12s $issue:) summary
|
||||||
|
$(printf %-12s $blocker:) blocks
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# reset login for mojira for voting
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
jira="$jira --user mojira"
|
||||||
|
|
||||||
|
RUNS $jira logout
|
||||||
|
echo "mojira123" | RUNS $jira login
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## vote for main issue, verify it shows when viewing the issue
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira vote $issue
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $issue http://localhost:8080/browse/$issue
|
||||||
|
EOF
|
||||||
|
|
||||||
|
RUNS $jira $issue
|
||||||
|
DIFF <<EOF
|
||||||
|
issue: $issue
|
||||||
|
created: a minute ago
|
||||||
|
status: To Do
|
||||||
|
summary: summary
|
||||||
|
project: SCRUM
|
||||||
|
issuetype: Bug
|
||||||
|
assignee: gojira
|
||||||
|
reporter: gojira
|
||||||
|
blockers: $blocker[To Do]
|
||||||
|
depends: $dup[Done]
|
||||||
|
priority: Medium
|
||||||
|
votes: 1
|
||||||
|
description: |
|
||||||
|
description
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## downvote the main issue, verify the vote count goes back to 0
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira vote $issue --down
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $issue http://localhost:8080/browse/$issue
|
||||||
|
EOF
|
||||||
|
|
||||||
|
RUNS $jira $issue
|
||||||
|
DIFF <<EOF
|
||||||
|
issue: $issue
|
||||||
|
created: a minute ago
|
||||||
|
status: To Do
|
||||||
|
summary: summary
|
||||||
|
project: SCRUM
|
||||||
|
issuetype: Bug
|
||||||
|
assignee: gojira
|
||||||
|
reporter: gojira
|
||||||
|
blockers: $blocker[To Do]
|
||||||
|
depends: $dup[Done]
|
||||||
|
priority: Medium
|
||||||
|
votes: 0
|
||||||
|
description: |
|
||||||
|
description
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## set mojira user as watcher to issue and verify from REST api
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira watch $issue
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $issue http://localhost:8080/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
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## set issue to In Progress state
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira trans "In Progress" $blocker --noedit
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $blocker http://localhost:8080/browse/$blocker
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## set it back to "To Do"
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira todo $blocker
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $blocker http://localhost:8080/browse/$blocker
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Set issue to "In Review" state, which is an invalid state for SCRUM
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
NRUNS $jira trans "review" $blocker --noedit
|
||||||
|
DIFF <<EOF
|
||||||
|
ERROR Invalid Transition 'review', Available: To Do, In Progress, Done
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Set it back to "To Do"
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira todo $blocker
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $blocker http://localhost:8080/browse/$blocker
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Set it to "In Progress"
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira prog $blocker
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $blocker http://localhost:8080/browse/$blocker
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Set it to "Done"
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira done $blocker
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $blocker http://localhost:8080/browse/$blocker
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Verify issue is now in Done state (the "blocker" issue is now Done)
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira $issue
|
||||||
|
DIFF <<EOF
|
||||||
|
issue: $issue
|
||||||
|
created: a minute ago
|
||||||
|
status: To Do
|
||||||
|
summary: summary
|
||||||
|
project: SCRUM
|
||||||
|
issuetype: Bug
|
||||||
|
assignee: gojira
|
||||||
|
reporter: gojira
|
||||||
|
blockers: $blocker[Done]
|
||||||
|
depends: $dup[Done]
|
||||||
|
priority: Medium
|
||||||
|
votes: 0
|
||||||
|
description: |
|
||||||
|
description
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Verify we can add a comment
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira comment $issue --noedit -m "Yo, Comment"
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $issue http://localhost:8080/browse/$issue
|
||||||
|
EOF
|
||||||
|
|
||||||
|
RUNS $jira $issue
|
||||||
|
DIFF <<EOF
|
||||||
|
issue: $issue
|
||||||
|
created: a minute ago
|
||||||
|
status: To Do
|
||||||
|
summary: summary
|
||||||
|
project: SCRUM
|
||||||
|
issuetype: Bug
|
||||||
|
assignee: gojira
|
||||||
|
reporter: gojira
|
||||||
|
blockers: $blocker[Done]
|
||||||
|
depends: $dup[Done]
|
||||||
|
priority: Medium
|
||||||
|
votes: 0
|
||||||
|
description: |
|
||||||
|
description
|
||||||
|
|
||||||
|
comments:
|
||||||
|
- | # mojira, a minute ago
|
||||||
|
Yo, Comment
|
||||||
|
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Verify we can add labels to an issue
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira add labels $blocker test-label another-label
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $blocker http://localhost:8080/browse/$blocker
|
||||||
|
EOF
|
||||||
|
|
||||||
|
RUNS $jira $blocker
|
||||||
|
DIFF <<EOF
|
||||||
|
issue: $blocker
|
||||||
|
created: a minute ago
|
||||||
|
status: Done
|
||||||
|
summary: blocks
|
||||||
|
project: SCRUM
|
||||||
|
issuetype: Bug
|
||||||
|
assignee: gojira
|
||||||
|
reporter: gojira
|
||||||
|
blockers:
|
||||||
|
depends: $issue[To Do]
|
||||||
|
priority: Medium
|
||||||
|
votes: 0
|
||||||
|
labels: another-label, test-label
|
||||||
|
description: |
|
||||||
|
blocks
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Verify we can remove a label
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira remove labels $blocker another-label
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $blocker http://localhost:8080/browse/$blocker
|
||||||
|
EOF
|
||||||
|
|
||||||
|
RUNS $jira $blocker
|
||||||
|
DIFF <<EOF
|
||||||
|
issue: $blocker
|
||||||
|
created: a minute ago
|
||||||
|
status: Done
|
||||||
|
summary: blocks
|
||||||
|
project: SCRUM
|
||||||
|
issuetype: Bug
|
||||||
|
assignee: gojira
|
||||||
|
reporter: gojira
|
||||||
|
blockers:
|
||||||
|
depends: $issue[To Do]
|
||||||
|
priority: Medium
|
||||||
|
votes: 0
|
||||||
|
labels: test-label
|
||||||
|
description: |
|
||||||
|
blocks
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Verify we can replace the labels with a new set
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira set labels $blocker more-label better-label
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $blocker http://localhost:8080/browse/$blocker
|
||||||
|
EOF
|
||||||
|
|
||||||
|
RUNS $jira $blocker
|
||||||
|
DIFF <<EOF
|
||||||
|
issue: $blocker
|
||||||
|
created: a minute ago
|
||||||
|
status: Done
|
||||||
|
summary: blocks
|
||||||
|
project: SCRUM
|
||||||
|
issuetype: Bug
|
||||||
|
assignee: gojira
|
||||||
|
reporter: gojira
|
||||||
|
blockers:
|
||||||
|
depends: $issue[To Do]
|
||||||
|
priority: Medium
|
||||||
|
votes: 0
|
||||||
|
labels: better-label, more-label
|
||||||
|
description: |
|
||||||
|
blocks
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Verify that "mojira" user can take the issue (reassign to self)
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira take $blocker
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $blocker http://localhost:8080/browse/$blocker
|
||||||
|
EOF
|
||||||
|
|
||||||
|
RUNS $jira $blocker
|
||||||
|
DIFF <<EOF
|
||||||
|
issue: $blocker
|
||||||
|
created: a minute ago
|
||||||
|
status: Done
|
||||||
|
summary: blocks
|
||||||
|
project: SCRUM
|
||||||
|
issuetype: Bug
|
||||||
|
assignee: mojira
|
||||||
|
reporter: gojira
|
||||||
|
blockers:
|
||||||
|
depends: $issue[To Do]
|
||||||
|
priority: Medium
|
||||||
|
votes: 0
|
||||||
|
labels: better-label, more-label
|
||||||
|
description: |
|
||||||
|
blocks
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Verify we can give the issue back go "gojira" user
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira give $blocker gojira
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $blocker http://localhost:8080/browse/$blocker
|
||||||
|
EOF
|
||||||
|
|
||||||
|
RUNS $jira $blocker
|
||||||
|
DIFF <<EOF
|
||||||
|
issue: $blocker
|
||||||
|
created: a minute ago
|
||||||
|
status: Done
|
||||||
|
summary: blocks
|
||||||
|
project: SCRUM
|
||||||
|
issuetype: Bug
|
||||||
|
assignee: gojira
|
||||||
|
reporter: gojira
|
||||||
|
blockers:
|
||||||
|
depends: $issue[To Do]
|
||||||
|
priority: Medium
|
||||||
|
votes: 0
|
||||||
|
labels: better-label, more-label
|
||||||
|
description: |
|
||||||
|
blocks
|
||||||
|
EOF
|
||||||
Executable
+517
@@ -0,0 +1,517 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
eval "$(curl -q -s https://raw.githubusercontent.com/coryb/osht/master/osht.sh)"
|
||||||
|
cd $(dirname $0)
|
||||||
|
jira="../jira --project KANBAN"
|
||||||
|
export JIRA_LOG_FORMAT="%{level:-5s} %{message}"
|
||||||
|
|
||||||
|
PLAN 86
|
||||||
|
|
||||||
|
# cleanup from previous failed test executions
|
||||||
|
($jira ls | awk -F: '{print $1}' | while read issue; do ../jira done $issue; done) | sed 's/^/# CLEANUP: /g'
|
||||||
|
|
||||||
|
# reset login
|
||||||
|
RUNS $jira logout
|
||||||
|
echo "gojira123" | RUNS $jira login
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## 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 http://localhost:8080/browse/$issue
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## View the issue we just created
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira view $issue
|
||||||
|
DIFF <<EOF
|
||||||
|
issue: $issue
|
||||||
|
created: a minute ago
|
||||||
|
status: Backlog
|
||||||
|
summary: summary
|
||||||
|
project: KANBAN
|
||||||
|
issuetype: Bug
|
||||||
|
assignee: gojira
|
||||||
|
reporter: gojira
|
||||||
|
priority: Medium
|
||||||
|
votes: 0
|
||||||
|
description: |
|
||||||
|
description
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## List all issues, should be just the one we created
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira ls
|
||||||
|
DIFF <<EOF
|
||||||
|
$(printf %-12s $issue:) summary
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Try to close the issue, bug Basic projects do not allow that state
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
NRUNS $jira close $issue
|
||||||
|
EDIFF <<EOF
|
||||||
|
ERROR Invalid Transition 'close', Available: Backlog, Selected for Development, In Progress, Done
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## put the issue into Done state, resolving it.
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira done $issue
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $issue http://localhost:8080/browse/$issue
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Verify there are no unresolved issues
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira ls
|
||||||
|
DIFF <<EOF
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Setup 2 more issues so we can test duping
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
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
|
||||||
|
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
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Mark issue as duplicate, expect both issues to be updated and when viewing
|
||||||
|
## the main issue there should be a "depends" line showing the dup'd issue, and
|
||||||
|
## that issue should be resolved
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira $dup dups $issue --noedit
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $issue http://localhost:8080/browse/$issue
|
||||||
|
OK $dup http://localhost:8080/browse/$dup
|
||||||
|
EOF
|
||||||
|
|
||||||
|
RUNS $jira $issue
|
||||||
|
DIFF <<EOF
|
||||||
|
issue: $issue
|
||||||
|
created: a minute ago
|
||||||
|
status: Backlog
|
||||||
|
summary: summary
|
||||||
|
project: KANBAN
|
||||||
|
issuetype: Bug
|
||||||
|
assignee: gojira
|
||||||
|
reporter: gojira
|
||||||
|
blockers:
|
||||||
|
depends: $dup[Done]
|
||||||
|
priority: Medium
|
||||||
|
votes: 0
|
||||||
|
description: |
|
||||||
|
description
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## We should see only one unresolved issue, the Dup should be resolved
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira ls
|
||||||
|
DIFF <<EOF
|
||||||
|
$(printf %-12s $issue:) summary
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Setup for testing blocking issues
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
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
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Set blocker and verify it shows up when viewing the main issue
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira $blocker blocks $issue
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $issue http://localhost:8080/browse/$issue
|
||||||
|
EOF
|
||||||
|
|
||||||
|
RUNS $jira $issue
|
||||||
|
DIFF <<EOF
|
||||||
|
issue: $issue
|
||||||
|
created: a minute ago
|
||||||
|
status: Backlog
|
||||||
|
summary: summary
|
||||||
|
project: KANBAN
|
||||||
|
issuetype: Bug
|
||||||
|
assignee: gojira
|
||||||
|
reporter: gojira
|
||||||
|
blockers: $blocker[Backlog]
|
||||||
|
depends: $dup[Done]
|
||||||
|
priority: Medium
|
||||||
|
votes: 0
|
||||||
|
description: |
|
||||||
|
description
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Both issues are unresolved now
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira ls
|
||||||
|
DIFF <<EOF
|
||||||
|
$(printf %-12s $issue:) summary
|
||||||
|
$(printf %-12s $blocker:) blocks
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# reset login for mojira for voting
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
jira="$jira --user mojira"
|
||||||
|
|
||||||
|
RUNS $jira logout
|
||||||
|
echo "mojira123" | RUNS $jira login
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## vote for main issue, verify it shows when viewing the issue
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira vote $issue
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $issue http://localhost:8080/browse/$issue
|
||||||
|
EOF
|
||||||
|
|
||||||
|
RUNS $jira $issue
|
||||||
|
DIFF <<EOF
|
||||||
|
issue: $issue
|
||||||
|
created: a minute ago
|
||||||
|
status: Backlog
|
||||||
|
summary: summary
|
||||||
|
project: KANBAN
|
||||||
|
issuetype: Bug
|
||||||
|
assignee: gojira
|
||||||
|
reporter: gojira
|
||||||
|
blockers: $blocker[Backlog]
|
||||||
|
depends: $dup[Done]
|
||||||
|
priority: Medium
|
||||||
|
votes: 1
|
||||||
|
description: |
|
||||||
|
description
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## downvote the main issue, verify the vote count goes back to 0
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira vote $issue --down
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $issue http://localhost:8080/browse/$issue
|
||||||
|
EOF
|
||||||
|
|
||||||
|
RUNS $jira $issue
|
||||||
|
DIFF <<EOF
|
||||||
|
issue: $issue
|
||||||
|
created: a minute ago
|
||||||
|
status: Backlog
|
||||||
|
summary: summary
|
||||||
|
project: KANBAN
|
||||||
|
issuetype: Bug
|
||||||
|
assignee: gojira
|
||||||
|
reporter: gojira
|
||||||
|
blockers: $blocker[Backlog]
|
||||||
|
depends: $dup[Done]
|
||||||
|
priority: Medium
|
||||||
|
votes: 0
|
||||||
|
description: |
|
||||||
|
description
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## set mojira user as watcher to issue and verify from REST api
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira watch $issue
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $issue http://localhost:8080/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
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## set issue to In Progress state
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira trans "In Progress" $blocker --noedit
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $blocker http://localhost:8080/browse/$blocker
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## set it to "To Do", which is not a valid state for KANBAN issues
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
NRUNS $jira todo $blocker
|
||||||
|
DIFF <<EOF
|
||||||
|
ERROR Invalid Transition 'To Do', Available: Backlog, Selected for Development, In Progress, Done
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## set issue back to backlog state
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira backlog $blocker --noedit
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $blocker http://localhost:8080/browse/$blocker
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Set issue to "In Review" state, which is an invalid state for KANBAN
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
NRUNS $jira trans "review" $blocker --noedit
|
||||||
|
DIFF <<EOF
|
||||||
|
ERROR Invalid Transition 'review', Available: Backlog, Selected for Development, In Progress, Done
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Set it back to "Backlog"
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira backlog $blocker
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $blocker http://localhost:8080/browse/$blocker
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Set it to "In Progress"
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira prog $blocker
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $blocker http://localhost:8080/browse/$blocker
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Set it to "Done"
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira done $blocker
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $blocker http://localhost:8080/browse/$blocker
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Verify issue is now in Done state (the "blocker" issue is now Done)
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira $issue
|
||||||
|
DIFF <<EOF
|
||||||
|
issue: $issue
|
||||||
|
created: a minute ago
|
||||||
|
status: Backlog
|
||||||
|
summary: summary
|
||||||
|
project: KANBAN
|
||||||
|
issuetype: Bug
|
||||||
|
assignee: gojira
|
||||||
|
reporter: gojira
|
||||||
|
blockers: $blocker[Done]
|
||||||
|
depends: $dup[Done]
|
||||||
|
priority: Medium
|
||||||
|
votes: 0
|
||||||
|
description: |
|
||||||
|
description
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Verify we can add a comment
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira comment $issue --noedit -m "Yo, Comment"
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $issue http://localhost:8080/browse/$issue
|
||||||
|
EOF
|
||||||
|
|
||||||
|
RUNS $jira $issue
|
||||||
|
DIFF <<EOF
|
||||||
|
issue: $issue
|
||||||
|
created: a minute ago
|
||||||
|
status: Backlog
|
||||||
|
summary: summary
|
||||||
|
project: KANBAN
|
||||||
|
issuetype: Bug
|
||||||
|
assignee: gojira
|
||||||
|
reporter: gojira
|
||||||
|
blockers: $blocker[Done]
|
||||||
|
depends: $dup[Done]
|
||||||
|
priority: Medium
|
||||||
|
votes: 0
|
||||||
|
description: |
|
||||||
|
description
|
||||||
|
|
||||||
|
comments:
|
||||||
|
- | # mojira, a minute ago
|
||||||
|
Yo, Comment
|
||||||
|
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Verify we can add labels to an issue
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira add labels $blocker test-label another-label
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $blocker http://localhost:8080/browse/$blocker
|
||||||
|
EOF
|
||||||
|
|
||||||
|
RUNS $jira $blocker
|
||||||
|
DIFF <<EOF
|
||||||
|
issue: $blocker
|
||||||
|
created: a minute ago
|
||||||
|
status: Done
|
||||||
|
summary: blocks
|
||||||
|
project: KANBAN
|
||||||
|
issuetype: Bug
|
||||||
|
assignee: gojira
|
||||||
|
reporter: gojira
|
||||||
|
blockers:
|
||||||
|
depends: $issue[Backlog]
|
||||||
|
priority: Medium
|
||||||
|
votes: 0
|
||||||
|
labels: another-label, test-label
|
||||||
|
description: |
|
||||||
|
blocks
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Verify we can remove a label
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira remove labels $blocker another-label
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $blocker http://localhost:8080/browse/$blocker
|
||||||
|
EOF
|
||||||
|
|
||||||
|
RUNS $jira $blocker
|
||||||
|
DIFF <<EOF
|
||||||
|
issue: $blocker
|
||||||
|
created: a minute ago
|
||||||
|
status: Done
|
||||||
|
summary: blocks
|
||||||
|
project: KANBAN
|
||||||
|
issuetype: Bug
|
||||||
|
assignee: gojira
|
||||||
|
reporter: gojira
|
||||||
|
blockers:
|
||||||
|
depends: $issue[Backlog]
|
||||||
|
priority: Medium
|
||||||
|
votes: 0
|
||||||
|
labels: test-label
|
||||||
|
description: |
|
||||||
|
blocks
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Verify we can replace the labels with a new set
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira set labels $blocker more-label better-label
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $blocker http://localhost:8080/browse/$blocker
|
||||||
|
EOF
|
||||||
|
|
||||||
|
RUNS $jira $blocker
|
||||||
|
DIFF <<EOF
|
||||||
|
issue: $blocker
|
||||||
|
created: a minute ago
|
||||||
|
status: Done
|
||||||
|
summary: blocks
|
||||||
|
project: KANBAN
|
||||||
|
issuetype: Bug
|
||||||
|
assignee: gojira
|
||||||
|
reporter: gojira
|
||||||
|
blockers:
|
||||||
|
depends: $issue[Backlog]
|
||||||
|
priority: Medium
|
||||||
|
votes: 0
|
||||||
|
labels: better-label, more-label
|
||||||
|
description: |
|
||||||
|
blocks
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Verify that "mojira" user can take the issue (reassign to self)
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira take $blocker
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $blocker http://localhost:8080/browse/$blocker
|
||||||
|
EOF
|
||||||
|
|
||||||
|
RUNS $jira $blocker
|
||||||
|
DIFF <<EOF
|
||||||
|
issue: $blocker
|
||||||
|
created: a minute ago
|
||||||
|
status: Done
|
||||||
|
summary: blocks
|
||||||
|
project: KANBAN
|
||||||
|
issuetype: Bug
|
||||||
|
assignee: mojira
|
||||||
|
reporter: gojira
|
||||||
|
blockers:
|
||||||
|
depends: $issue[Backlog]
|
||||||
|
priority: Medium
|
||||||
|
votes: 0
|
||||||
|
labels: better-label, more-label
|
||||||
|
description: |
|
||||||
|
blocks
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Verify we can give the issue back go "gojira" user
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira give $blocker gojira
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $blocker http://localhost:8080/browse/$blocker
|
||||||
|
EOF
|
||||||
|
|
||||||
|
RUNS $jira $blocker
|
||||||
|
DIFF <<EOF
|
||||||
|
issue: $blocker
|
||||||
|
created: a minute ago
|
||||||
|
status: Done
|
||||||
|
summary: blocks
|
||||||
|
project: KANBAN
|
||||||
|
issuetype: Bug
|
||||||
|
assignee: gojira
|
||||||
|
reporter: gojira
|
||||||
|
blockers:
|
||||||
|
depends: $issue[Backlog]
|
||||||
|
priority: Medium
|
||||||
|
votes: 0
|
||||||
|
labels: better-label, more-label
|
||||||
|
description: |
|
||||||
|
blocks
|
||||||
|
EOF
|
||||||
Executable
+520
@@ -0,0 +1,520 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
eval "$(curl -q -s https://raw.githubusercontent.com/coryb/osht/master/osht.sh)"
|
||||||
|
cd $(dirname $0)
|
||||||
|
jira="../jira --project PROJECT"
|
||||||
|
export JIRA_LOG_FORMAT="%{level:-5s} %{message}"
|
||||||
|
|
||||||
|
PLAN 84
|
||||||
|
|
||||||
|
# cleanup from previous failed test executions
|
||||||
|
($jira ls | awk -F: '{print $1}' | while read issue; do ../jira done $issue; done) | sed 's/^/# CLEANUP: /g'
|
||||||
|
|
||||||
|
# reset login
|
||||||
|
RUNS $jira logout
|
||||||
|
echo "gojira123" | RUNS $jira login
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## 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 http://localhost:8080/browse/$issue
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## View the issue we just created
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira view $issue
|
||||||
|
DIFF <<EOF
|
||||||
|
issue: $issue
|
||||||
|
created: a minute ago
|
||||||
|
status: To Do
|
||||||
|
summary: summary
|
||||||
|
project: PROJECT
|
||||||
|
issuetype: Task
|
||||||
|
assignee: gojira
|
||||||
|
reporter: gojira
|
||||||
|
priority: Medium
|
||||||
|
votes: 0
|
||||||
|
description: |
|
||||||
|
description
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## List all issues, should be just the one we created
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira ls
|
||||||
|
DIFF <<EOF
|
||||||
|
$(printf %-12s $issue:) summary
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Try to close the issue, bug Basic projects do not allow that state
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
NRUNS $jira close $issue
|
||||||
|
EDIFF <<EOF
|
||||||
|
ERROR Invalid Transition 'close', Available: Start Progress, Done
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## put the issue into Done state, resolving it.
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira done $issue
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $issue http://localhost:8080/browse/$issue
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Verify there are no unresolved issues
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira ls
|
||||||
|
DIFF <<EOF
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Setup 2 more issues so we can test duping
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
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
|
||||||
|
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
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Mark issue as duplicate, expect both issues to be updated and when viewing
|
||||||
|
## the main issue there should be a "depends" line showing the dup'd issue, and
|
||||||
|
## that issue should be resolved
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira $dup dups $issue --noedit
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $issue http://localhost:8080/browse/$issue
|
||||||
|
OK $dup http://localhost:8080/browse/$dup
|
||||||
|
EOF
|
||||||
|
|
||||||
|
RUNS $jira $issue
|
||||||
|
DIFF <<EOF
|
||||||
|
issue: $issue
|
||||||
|
created: a minute ago
|
||||||
|
status: To Do
|
||||||
|
summary: summary
|
||||||
|
project: PROJECT
|
||||||
|
issuetype: Task
|
||||||
|
assignee: gojira
|
||||||
|
reporter: gojira
|
||||||
|
blockers:
|
||||||
|
depends: $dup[Done]
|
||||||
|
priority: Medium
|
||||||
|
votes: 0
|
||||||
|
description: |
|
||||||
|
description
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## We should see only one unresolved issue, the Dup should be resolved
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira ls
|
||||||
|
DIFF <<EOF
|
||||||
|
$(printf %-12s $issue:) summary
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Setup for testing blocking issues
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
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
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Set blocker and verify it shows up when viewing the main issue
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira $blocker blocks $issue
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $issue http://localhost:8080/browse/$issue
|
||||||
|
EOF
|
||||||
|
|
||||||
|
RUNS $jira $issue
|
||||||
|
DIFF <<EOF
|
||||||
|
issue: $issue
|
||||||
|
created: a minute ago
|
||||||
|
status: To Do
|
||||||
|
summary: summary
|
||||||
|
project: PROJECT
|
||||||
|
issuetype: Task
|
||||||
|
assignee: gojira
|
||||||
|
reporter: gojira
|
||||||
|
blockers: $blocker[To Do]
|
||||||
|
depends: $dup[Done]
|
||||||
|
priority: Medium
|
||||||
|
votes: 0
|
||||||
|
description: |
|
||||||
|
description
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Both issues are unresolved now
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira ls
|
||||||
|
DIFF <<EOF
|
||||||
|
$(printf %-12s $issue:) summary
|
||||||
|
$(printf %-12s $blocker:) blocks
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# reset login for mojira for voting
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
jira="$jira --user mojira"
|
||||||
|
|
||||||
|
RUNS $jira logout
|
||||||
|
echo "mojira123" | RUNS $jira login
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## vote for main issue, verify it shows when viewing the issue
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira vote $issue
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $issue http://localhost:8080/browse/$issue
|
||||||
|
EOF
|
||||||
|
|
||||||
|
RUNS $jira $issue
|
||||||
|
DIFF <<EOF
|
||||||
|
issue: $issue
|
||||||
|
created: a minute ago
|
||||||
|
status: To Do
|
||||||
|
summary: summary
|
||||||
|
project: PROJECT
|
||||||
|
issuetype: Task
|
||||||
|
assignee: gojira
|
||||||
|
reporter: gojira
|
||||||
|
blockers: $blocker[To Do]
|
||||||
|
depends: $dup[Done]
|
||||||
|
priority: Medium
|
||||||
|
votes: 1
|
||||||
|
description: |
|
||||||
|
description
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## downvote the main issue, verify the vote count goes back to 0
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira vote $issue --down
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $issue http://localhost:8080/browse/$issue
|
||||||
|
EOF
|
||||||
|
|
||||||
|
RUNS $jira $issue
|
||||||
|
DIFF <<EOF
|
||||||
|
issue: $issue
|
||||||
|
created: a minute ago
|
||||||
|
status: To Do
|
||||||
|
summary: summary
|
||||||
|
project: PROJECT
|
||||||
|
issuetype: Task
|
||||||
|
assignee: gojira
|
||||||
|
reporter: gojira
|
||||||
|
blockers: $blocker[To Do]
|
||||||
|
depends: $dup[Done]
|
||||||
|
priority: Medium
|
||||||
|
votes: 0
|
||||||
|
description: |
|
||||||
|
description
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## set mojira user as watcher to issue and verify from REST api
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira watch $issue
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $issue http://localhost:8080/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
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## set issue to In Progress state, which is an invalid state for PROJECT
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
NRUNS $jira trans "In Progress" $blocker --noedit
|
||||||
|
DIFF <<EOF
|
||||||
|
ERROR Invalid Transition 'In Progress', Available: Start Progress, Done
|
||||||
|
EOF
|
||||||
|
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Set issue to "In Review" state, which is an invalid state for PROJECT
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
NRUNS $jira trans "review" $blocker --noedit
|
||||||
|
DIFF <<EOF
|
||||||
|
ERROR Invalid Transition 'review', Available: Start Progress, Done
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Set it to "Start Progress" and verify that assignee is set to mojira
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira start $blocker
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $blocker http://localhost:8080/browse/$blocker
|
||||||
|
EOF
|
||||||
|
|
||||||
|
RUNS $jira $blocker
|
||||||
|
DIFF <<EOF
|
||||||
|
issue: $blocker
|
||||||
|
created: a minute ago
|
||||||
|
status: In Progress
|
||||||
|
summary: blocks
|
||||||
|
project: PROJECT
|
||||||
|
issuetype: Task
|
||||||
|
assignee: mojira
|
||||||
|
reporter: gojira
|
||||||
|
blockers:
|
||||||
|
depends: $issue[To Do]
|
||||||
|
priority: Medium
|
||||||
|
votes: 0
|
||||||
|
description: |
|
||||||
|
blocks
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Set it back to "Stop Progress"
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira stop $blocker
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $blocker http://localhost:8080/browse/$blocker
|
||||||
|
EOF
|
||||||
|
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Set it to "Done"
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira done $blocker
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $blocker http://localhost:8080/browse/$blocker
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Verify issue is now in Done state (the "blocker" issue is now Done)
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira $issue
|
||||||
|
DIFF <<EOF
|
||||||
|
issue: $issue
|
||||||
|
created: a minute ago
|
||||||
|
status: To Do
|
||||||
|
summary: summary
|
||||||
|
project: PROJECT
|
||||||
|
issuetype: Task
|
||||||
|
assignee: gojira
|
||||||
|
reporter: gojira
|
||||||
|
blockers: $blocker[Done]
|
||||||
|
depends: $dup[Done]
|
||||||
|
priority: Medium
|
||||||
|
votes: 0
|
||||||
|
description: |
|
||||||
|
description
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Verify we can add a comment
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira comment $issue --noedit -m "Yo, Comment"
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $issue http://localhost:8080/browse/$issue
|
||||||
|
EOF
|
||||||
|
|
||||||
|
RUNS $jira $issue
|
||||||
|
DIFF <<EOF
|
||||||
|
issue: $issue
|
||||||
|
created: a minute ago
|
||||||
|
status: To Do
|
||||||
|
summary: summary
|
||||||
|
project: PROJECT
|
||||||
|
issuetype: Task
|
||||||
|
assignee: gojira
|
||||||
|
reporter: gojira
|
||||||
|
blockers: $blocker[Done]
|
||||||
|
depends: $dup[Done]
|
||||||
|
priority: Medium
|
||||||
|
votes: 0
|
||||||
|
description: |
|
||||||
|
description
|
||||||
|
|
||||||
|
comments:
|
||||||
|
- | # mojira, a minute ago
|
||||||
|
Yo, Comment
|
||||||
|
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Verify we can add labels to an issue
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira add labels $blocker test-label another-label
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $blocker http://localhost:8080/browse/$blocker
|
||||||
|
EOF
|
||||||
|
|
||||||
|
RUNS $jira $blocker
|
||||||
|
DIFF <<EOF
|
||||||
|
issue: $blocker
|
||||||
|
created: a minute ago
|
||||||
|
status: Done
|
||||||
|
summary: blocks
|
||||||
|
project: PROJECT
|
||||||
|
issuetype: Task
|
||||||
|
assignee: mojira
|
||||||
|
reporter: gojira
|
||||||
|
blockers:
|
||||||
|
depends: $issue[To Do]
|
||||||
|
priority: Medium
|
||||||
|
votes: 0
|
||||||
|
labels: another-label, test-label
|
||||||
|
description: |
|
||||||
|
blocks
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Verify we can remove a label
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira remove labels $blocker another-label
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $blocker http://localhost:8080/browse/$blocker
|
||||||
|
EOF
|
||||||
|
|
||||||
|
RUNS $jira $blocker
|
||||||
|
DIFF <<EOF
|
||||||
|
issue: $blocker
|
||||||
|
created: a minute ago
|
||||||
|
status: Done
|
||||||
|
summary: blocks
|
||||||
|
project: PROJECT
|
||||||
|
issuetype: Task
|
||||||
|
assignee: mojira
|
||||||
|
reporter: gojira
|
||||||
|
blockers:
|
||||||
|
depends: $issue[To Do]
|
||||||
|
priority: Medium
|
||||||
|
votes: 0
|
||||||
|
labels: test-label
|
||||||
|
description: |
|
||||||
|
blocks
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Verify we can replace the labels with a new set
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira set labels $blocker more-label better-label
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $blocker http://localhost:8080/browse/$blocker
|
||||||
|
EOF
|
||||||
|
|
||||||
|
RUNS $jira $blocker
|
||||||
|
DIFF <<EOF
|
||||||
|
issue: $blocker
|
||||||
|
created: a minute ago
|
||||||
|
status: Done
|
||||||
|
summary: blocks
|
||||||
|
project: PROJECT
|
||||||
|
issuetype: Task
|
||||||
|
assignee: mojira
|
||||||
|
reporter: gojira
|
||||||
|
blockers:
|
||||||
|
depends: $issue[To Do]
|
||||||
|
priority: Medium
|
||||||
|
votes: 0
|
||||||
|
labels: better-label, more-label
|
||||||
|
description: |
|
||||||
|
blocks
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Verify we can give the issue back go "gojira" user
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira give $blocker gojira
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $blocker http://localhost:8080/browse/$blocker
|
||||||
|
EOF
|
||||||
|
|
||||||
|
RUNS $jira $blocker
|
||||||
|
DIFF <<EOF
|
||||||
|
issue: $blocker
|
||||||
|
created: a minute ago
|
||||||
|
status: Done
|
||||||
|
summary: blocks
|
||||||
|
project: PROJECT
|
||||||
|
issuetype: Task
|
||||||
|
assignee: gojira
|
||||||
|
reporter: gojira
|
||||||
|
blockers:
|
||||||
|
depends: $issue[To Do]
|
||||||
|
priority: Medium
|
||||||
|
votes: 0
|
||||||
|
labels: better-label, more-label
|
||||||
|
description: |
|
||||||
|
blocks
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Verify that "mojira" user can take the issue (reassign to self)
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira take $blocker
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $blocker http://localhost:8080/browse/$blocker
|
||||||
|
EOF
|
||||||
|
|
||||||
|
RUNS $jira $blocker
|
||||||
|
DIFF <<EOF
|
||||||
|
issue: $blocker
|
||||||
|
created: a minute ago
|
||||||
|
status: Done
|
||||||
|
summary: blocks
|
||||||
|
project: PROJECT
|
||||||
|
issuetype: Task
|
||||||
|
assignee: mojira
|
||||||
|
reporter: gojira
|
||||||
|
blockers:
|
||||||
|
depends: $issue[To Do]
|
||||||
|
priority: Medium
|
||||||
|
votes: 0
|
||||||
|
labels: better-label, more-label
|
||||||
|
description: |
|
||||||
|
blocks
|
||||||
|
EOF
|
||||||
|
|
||||||
Executable
+513
@@ -0,0 +1,513 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
eval "$(curl -q -s https://raw.githubusercontent.com/coryb/osht/master/osht.sh)"
|
||||||
|
cd $(dirname $0)
|
||||||
|
jira="../jira --project PROCESS"
|
||||||
|
export JIRA_LOG_FORMAT="%{level:-5s} %{message}"
|
||||||
|
|
||||||
|
PLAN 84
|
||||||
|
|
||||||
|
# cleanup from previous failed test executions
|
||||||
|
($jira ls | awk -F: '{print $1}' | while read issue; do ../jira start $issue; done) | sed 's/^/# CLEANUP: /g'
|
||||||
|
($jira ls | awk -F: '{print $1}' | while read issue; do ../jira stop $issue; done) | sed 's/^/# CLEANUP: /g'
|
||||||
|
|
||||||
|
# reset login
|
||||||
|
RUNS $jira logout
|
||||||
|
echo "gojira123" | RUNS $jira login
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## 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 http://localhost:8080/browse/$issue
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## View the issue we just created
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira view $issue
|
||||||
|
DIFF <<EOF
|
||||||
|
issue: $issue
|
||||||
|
created: a minute ago
|
||||||
|
status: Open
|
||||||
|
summary: summary
|
||||||
|
project: PROCESS
|
||||||
|
issuetype: Task
|
||||||
|
assignee: gojira
|
||||||
|
reporter: gojira
|
||||||
|
priority: Medium
|
||||||
|
votes: 0
|
||||||
|
description: |
|
||||||
|
description
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## List all issues, should be just the one we created
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira ls
|
||||||
|
DIFF <<EOF
|
||||||
|
$(printf %-12s $issue:) summary
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Try to close the issue, but PROCESS projects do not allow that state
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
NRUNS $jira close $issue
|
||||||
|
EDIFF <<EOF
|
||||||
|
ERROR Invalid Transition 'close', Available: Start Progress
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## put the issue into Start Progress state, then Stop Progress state
|
||||||
|
## which will resolve the issue
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira start $issue
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $issue http://localhost:8080/browse/$issue
|
||||||
|
EOF
|
||||||
|
|
||||||
|
|
||||||
|
RUNS $jira stop $issue
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $issue http://localhost:8080/browse/$issue
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Verify there are no unresolved issues
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira ls
|
||||||
|
DIFF <<EOF
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Setup 2 more issues so we can test duping
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
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
|
||||||
|
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
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Mark issue as duplicate, expect both issues to be updated and when viewing
|
||||||
|
## the main issue there should be a "depends" line showing the dup'd issue, and
|
||||||
|
## that issue should be resolved. For PROCESSS projects it has to go through
|
||||||
|
## 2 steps to resolve, one is "Start Progress" then resolved with "Stop
|
||||||
|
## Progress", so we see 3 updates in total
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
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
|
||||||
|
EOF
|
||||||
|
|
||||||
|
RUNS $jira $issue
|
||||||
|
DIFF <<EOF
|
||||||
|
issue: $issue
|
||||||
|
created: a minute ago
|
||||||
|
status: Open
|
||||||
|
summary: summary
|
||||||
|
project: PROCESS
|
||||||
|
issuetype: Task
|
||||||
|
assignee: gojira
|
||||||
|
reporter: gojira
|
||||||
|
blockers:
|
||||||
|
depends: $dup[Cancelled]
|
||||||
|
priority: Medium
|
||||||
|
votes: 0
|
||||||
|
description: |
|
||||||
|
description
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## We should see only one unresolved issue, the Dup should be resolved
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira ls
|
||||||
|
DIFF <<EOF
|
||||||
|
$(printf %-12s $issue:) summary
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Setup for testing blocking issues
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
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
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Set blocker and verify it shows up when viewing the main issue
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira $blocker blocks $issue
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $issue http://localhost:8080/browse/$issue
|
||||||
|
EOF
|
||||||
|
|
||||||
|
RUNS $jira $issue
|
||||||
|
DIFF <<EOF
|
||||||
|
issue: $issue
|
||||||
|
created: a minute ago
|
||||||
|
status: Open
|
||||||
|
summary: summary
|
||||||
|
project: PROCESS
|
||||||
|
issuetype: Task
|
||||||
|
assignee: gojira
|
||||||
|
reporter: gojira
|
||||||
|
blockers: $blocker[Open]
|
||||||
|
depends: $dup[Cancelled]
|
||||||
|
priority: Medium
|
||||||
|
votes: 0
|
||||||
|
description: |
|
||||||
|
description
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Both issues are unresolved now
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira ls
|
||||||
|
DIFF <<EOF
|
||||||
|
$(printf %-12s $issue:) summary
|
||||||
|
$(printf %-12s $blocker:) blocks
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# reset login for mojira for voting
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
jira="$jira --user mojira"
|
||||||
|
|
||||||
|
RUNS $jira logout
|
||||||
|
echo "mojira123" | RUNS $jira login
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## vote for main issue, verify it shows when viewing the issue
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira vote $issue
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $issue http://localhost:8080/browse/$issue
|
||||||
|
EOF
|
||||||
|
|
||||||
|
RUNS $jira $issue
|
||||||
|
DIFF <<EOF
|
||||||
|
issue: $issue
|
||||||
|
created: a minute ago
|
||||||
|
status: Open
|
||||||
|
summary: summary
|
||||||
|
project: PROCESS
|
||||||
|
issuetype: Task
|
||||||
|
assignee: gojira
|
||||||
|
reporter: gojira
|
||||||
|
blockers: $blocker[Open]
|
||||||
|
depends: $dup[Cancelled]
|
||||||
|
priority: Medium
|
||||||
|
votes: 1
|
||||||
|
description: |
|
||||||
|
description
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## downvote the main issue, verify the vote count goes back to 0
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira vote $issue --down
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $issue http://localhost:8080/browse/$issue
|
||||||
|
EOF
|
||||||
|
|
||||||
|
RUNS $jira $issue
|
||||||
|
DIFF <<EOF
|
||||||
|
issue: $issue
|
||||||
|
created: a minute ago
|
||||||
|
status: Open
|
||||||
|
summary: summary
|
||||||
|
project: PROCESS
|
||||||
|
issuetype: Task
|
||||||
|
assignee: gojira
|
||||||
|
reporter: gojira
|
||||||
|
blockers: $blocker[Open]
|
||||||
|
depends: $dup[Cancelled]
|
||||||
|
priority: Medium
|
||||||
|
votes: 0
|
||||||
|
description: |
|
||||||
|
description
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## set mojira user as watcher to issue and verify from REST api
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira watch $issue
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $issue http://localhost:8080/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
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## set issue to In Progress state, which is an invalid state for PROCESS
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
NRUNS $jira trans "In Progress" $blocker --noedit
|
||||||
|
DIFF <<EOF
|
||||||
|
ERROR Invalid Transition 'In Progress', Available: Start Progress
|
||||||
|
EOF
|
||||||
|
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Set issue to "In Review" state, which is an invalid state for PROCESS
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
NRUNS $jira trans "review" $blocker --noedit
|
||||||
|
DIFF <<EOF
|
||||||
|
ERROR Invalid Transition 'review', Available: Start Progress
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Set it to "Start Progress"
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira start $blocker
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $blocker http://localhost:8080/browse/$blocker
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Set it back to "Stop Progress"
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira stop $blocker
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $blocker http://localhost:8080/browse/$blocker
|
||||||
|
EOF
|
||||||
|
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Set it to "Done"
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira reopen $blocker
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $blocker http://localhost:8080/browse/$blocker
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Verify issue is now in Done state (the "blocker" issue is now Done)
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira $issue
|
||||||
|
DIFF <<EOF
|
||||||
|
issue: $issue
|
||||||
|
created: a minute ago
|
||||||
|
status: Open
|
||||||
|
summary: summary
|
||||||
|
project: PROCESS
|
||||||
|
issuetype: Task
|
||||||
|
assignee: gojira
|
||||||
|
reporter: gojira
|
||||||
|
blockers: $blocker[Open]
|
||||||
|
depends: $dup[Cancelled]
|
||||||
|
priority: Medium
|
||||||
|
votes: 0
|
||||||
|
description: |
|
||||||
|
description
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Verify we can add a comment
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira comment $issue --noedit -m "Yo, Comment"
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $issue http://localhost:8080/browse/$issue
|
||||||
|
EOF
|
||||||
|
|
||||||
|
RUNS $jira $issue
|
||||||
|
DIFF <<EOF
|
||||||
|
issue: $issue
|
||||||
|
created: a minute ago
|
||||||
|
status: Open
|
||||||
|
summary: summary
|
||||||
|
project: PROCESS
|
||||||
|
issuetype: Task
|
||||||
|
assignee: gojira
|
||||||
|
reporter: gojira
|
||||||
|
blockers: $blocker[Open]
|
||||||
|
depends: $dup[Cancelled]
|
||||||
|
priority: Medium
|
||||||
|
votes: 0
|
||||||
|
description: |
|
||||||
|
description
|
||||||
|
|
||||||
|
comments:
|
||||||
|
- | # mojira, a minute ago
|
||||||
|
Yo, Comment
|
||||||
|
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Verify we can add labels to an issue
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira add labels $blocker test-label another-label
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $blocker http://localhost:8080/browse/$blocker
|
||||||
|
EOF
|
||||||
|
|
||||||
|
RUNS $jira $blocker
|
||||||
|
DIFF <<EOF
|
||||||
|
issue: $blocker
|
||||||
|
created: a minute ago
|
||||||
|
status: Open
|
||||||
|
summary: blocks
|
||||||
|
project: PROCESS
|
||||||
|
issuetype: Task
|
||||||
|
assignee: gojira
|
||||||
|
reporter: gojira
|
||||||
|
blockers:
|
||||||
|
depends: $issue[Open]
|
||||||
|
priority: Medium
|
||||||
|
votes: 0
|
||||||
|
labels: another-label, test-label
|
||||||
|
description: |
|
||||||
|
blocks
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Verify we can remove a label
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira remove labels $blocker another-label
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $blocker http://localhost:8080/browse/$blocker
|
||||||
|
EOF
|
||||||
|
|
||||||
|
RUNS $jira $blocker
|
||||||
|
DIFF <<EOF
|
||||||
|
issue: $blocker
|
||||||
|
created: a minute ago
|
||||||
|
status: Open
|
||||||
|
summary: blocks
|
||||||
|
project: PROCESS
|
||||||
|
issuetype: Task
|
||||||
|
assignee: gojira
|
||||||
|
reporter: gojira
|
||||||
|
blockers:
|
||||||
|
depends: $issue[Open]
|
||||||
|
priority: Medium
|
||||||
|
votes: 0
|
||||||
|
labels: test-label
|
||||||
|
description: |
|
||||||
|
blocks
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Verify we can replace the labels with a new set
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira set labels $blocker more-label better-label
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $blocker http://localhost:8080/browse/$blocker
|
||||||
|
EOF
|
||||||
|
|
||||||
|
RUNS $jira $blocker
|
||||||
|
DIFF <<EOF
|
||||||
|
issue: $blocker
|
||||||
|
created: a minute ago
|
||||||
|
status: Open
|
||||||
|
summary: blocks
|
||||||
|
project: PROCESS
|
||||||
|
issuetype: Task
|
||||||
|
assignee: gojira
|
||||||
|
reporter: gojira
|
||||||
|
blockers:
|
||||||
|
depends: $issue[Open]
|
||||||
|
priority: Medium
|
||||||
|
votes: 0
|
||||||
|
labels: better-label, more-label
|
||||||
|
description: |
|
||||||
|
blocks
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Verify that "mojira" user can take the issue (reassign to self)
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira take $blocker
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $blocker http://localhost:8080/browse/$blocker
|
||||||
|
EOF
|
||||||
|
|
||||||
|
RUNS $jira $blocker
|
||||||
|
DIFF <<EOF
|
||||||
|
issue: $blocker
|
||||||
|
created: a minute ago
|
||||||
|
status: Open
|
||||||
|
summary: blocks
|
||||||
|
project: PROCESS
|
||||||
|
issuetype: Task
|
||||||
|
assignee: mojira
|
||||||
|
reporter: gojira
|
||||||
|
blockers:
|
||||||
|
depends: $issue[Open]
|
||||||
|
priority: Medium
|
||||||
|
votes: 0
|
||||||
|
labels: better-label, more-label
|
||||||
|
description: |
|
||||||
|
blocks
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Verify we can give the issue back go "gojira" user
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira give $blocker gojira
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $blocker http://localhost:8080/browse/$blocker
|
||||||
|
EOF
|
||||||
|
|
||||||
|
RUNS $jira $blocker
|
||||||
|
DIFF <<EOF
|
||||||
|
issue: $blocker
|
||||||
|
created: a minute ago
|
||||||
|
status: Open
|
||||||
|
summary: blocks
|
||||||
|
project: PROCESS
|
||||||
|
issuetype: Task
|
||||||
|
assignee: gojira
|
||||||
|
reporter: gojira
|
||||||
|
blockers:
|
||||||
|
depends: $issue[Open]
|
||||||
|
priority: Medium
|
||||||
|
votes: 0
|
||||||
|
labels: better-label, more-label
|
||||||
|
description: |
|
||||||
|
blocks
|
||||||
|
EOF
|
||||||
|
|
||||||
Executable
+504
@@ -0,0 +1,504 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
eval "$(curl -q -s https://raw.githubusercontent.com/coryb/osht/master/osht.sh)"
|
||||||
|
cd $(dirname $0)
|
||||||
|
jira="../jira --project TASK"
|
||||||
|
export JIRA_LOG_FORMAT="%{level:-5s} %{message}"
|
||||||
|
|
||||||
|
PLAN 82
|
||||||
|
|
||||||
|
# cleanup from previous failed test executions
|
||||||
|
($jira ls | awk -F: '{print $1}' | while read issue; do ../jira done $issue; done) | sed 's/^/# CLEANUP: /g'
|
||||||
|
|
||||||
|
# reset login
|
||||||
|
RUNS $jira logout
|
||||||
|
echo "gojira123" | RUNS $jira login
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## 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 http://localhost:8080/browse/$issue
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## View the issue we just created
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira view $issue
|
||||||
|
DIFF <<EOF
|
||||||
|
issue: $issue
|
||||||
|
created: a minute ago
|
||||||
|
status: To Do
|
||||||
|
summary: summary
|
||||||
|
project: TASK
|
||||||
|
issuetype: Task
|
||||||
|
assignee: gojira
|
||||||
|
reporter: gojira
|
||||||
|
priority: Medium
|
||||||
|
votes: 0
|
||||||
|
description: |
|
||||||
|
description
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## List all issues, should be just the one we created
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira ls
|
||||||
|
DIFF <<EOF
|
||||||
|
$(printf %-12s $issue:) summary
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Try to close the issue, but TASK projects do not allow that state
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
NRUNS $jira close $issue
|
||||||
|
EDIFF <<EOF
|
||||||
|
ERROR Invalid Transition 'close', Available: Done
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## put the issue into Done state, which will resolve the issue
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira done $issue
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $issue http://localhost:8080/browse/$issue
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Verify there are no unresolved issues
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira ls
|
||||||
|
DIFF <<EOF
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Setup 2 more issues so we can test duping
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
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
|
||||||
|
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
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Mark issue as duplicate, expect both issues to be updated and when viewing
|
||||||
|
## the main issue there should be a "depends" line showing the dup'd issue, and
|
||||||
|
## that issue should be resolved. For TASKS projects it has to go through
|
||||||
|
## 2 steps to resolve, one is "Start Progress" then resolved with "Stop
|
||||||
|
## Progress", so we see 3 updates in total
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira $dup dups $issue --noedit
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $issue http://localhost:8080/browse/$issue
|
||||||
|
OK $dup http://localhost:8080/browse/$dup
|
||||||
|
EOF
|
||||||
|
|
||||||
|
RUNS $jira $issue
|
||||||
|
DIFF <<EOF
|
||||||
|
issue: $issue
|
||||||
|
created: a minute ago
|
||||||
|
status: To Do
|
||||||
|
summary: summary
|
||||||
|
project: TASK
|
||||||
|
issuetype: Task
|
||||||
|
assignee: gojira
|
||||||
|
reporter: gojira
|
||||||
|
blockers:
|
||||||
|
depends: $dup[Done]
|
||||||
|
priority: Medium
|
||||||
|
votes: 0
|
||||||
|
description: |
|
||||||
|
description
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## We should see only one unresolved issue, the Dup should be resolved
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira ls
|
||||||
|
DIFF <<EOF
|
||||||
|
$(printf %-12s $issue:) summary
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Setup for testing blocking issues
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
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
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Set blocker and verify it shows up when viewing the main issue
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira $blocker blocks $issue
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $issue http://localhost:8080/browse/$issue
|
||||||
|
EOF
|
||||||
|
|
||||||
|
RUNS $jira $issue
|
||||||
|
DIFF <<EOF
|
||||||
|
issue: $issue
|
||||||
|
created: a minute ago
|
||||||
|
status: To Do
|
||||||
|
summary: summary
|
||||||
|
project: TASK
|
||||||
|
issuetype: Task
|
||||||
|
assignee: gojira
|
||||||
|
reporter: gojira
|
||||||
|
blockers: $blocker[To Do]
|
||||||
|
depends: $dup[Done]
|
||||||
|
priority: Medium
|
||||||
|
votes: 0
|
||||||
|
description: |
|
||||||
|
description
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Both issues are unresolved now
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira ls
|
||||||
|
DIFF <<EOF
|
||||||
|
$(printf %-12s $issue:) summary
|
||||||
|
$(printf %-12s $blocker:) blocks
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# reset login for mojira for voting
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
jira="$jira --user mojira"
|
||||||
|
|
||||||
|
RUNS $jira logout
|
||||||
|
echo "mojira123" | RUNS $jira login
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## vote for main issue, verify it shows when viewing the issue
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira vote $issue
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $issue http://localhost:8080/browse/$issue
|
||||||
|
EOF
|
||||||
|
|
||||||
|
RUNS $jira $issue
|
||||||
|
DIFF <<EOF
|
||||||
|
issue: $issue
|
||||||
|
created: a minute ago
|
||||||
|
status: To Do
|
||||||
|
summary: summary
|
||||||
|
project: TASK
|
||||||
|
issuetype: Task
|
||||||
|
assignee: gojira
|
||||||
|
reporter: gojira
|
||||||
|
blockers: $blocker[To Do]
|
||||||
|
depends: $dup[Done]
|
||||||
|
priority: Medium
|
||||||
|
votes: 1
|
||||||
|
description: |
|
||||||
|
description
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## downvote the main issue, verify the vote count goes back to 0
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira vote $issue --down
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $issue http://localhost:8080/browse/$issue
|
||||||
|
EOF
|
||||||
|
|
||||||
|
RUNS $jira $issue
|
||||||
|
DIFF <<EOF
|
||||||
|
issue: $issue
|
||||||
|
created: a minute ago
|
||||||
|
status: To Do
|
||||||
|
summary: summary
|
||||||
|
project: TASK
|
||||||
|
issuetype: Task
|
||||||
|
assignee: gojira
|
||||||
|
reporter: gojira
|
||||||
|
blockers: $blocker[To Do]
|
||||||
|
depends: $dup[Done]
|
||||||
|
priority: Medium
|
||||||
|
votes: 0
|
||||||
|
description: |
|
||||||
|
description
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## set mojira user as watcher to issue and verify from REST api
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira watch $issue
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $issue http://localhost:8080/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
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## set issue to In Progress state, which is an invalid state for TASK
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
NRUNS $jira trans "In Progress" $blocker --noedit
|
||||||
|
DIFF <<EOF
|
||||||
|
ERROR Invalid Transition 'In Progress', Available: Done
|
||||||
|
EOF
|
||||||
|
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Set issue to "In Review" state, which is an invalid state for TASK
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
NRUNS $jira trans "review" $blocker --noedit
|
||||||
|
DIFF <<EOF
|
||||||
|
ERROR Invalid Transition 'review', Available: Done
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Set it to "Start Progress", which is an invalid state for TASK
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
NRUNS $jira start $blocker
|
||||||
|
DIFF <<EOF
|
||||||
|
ERROR Invalid Transition 'start', Available: Done
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Set it back to "Stop Progress", which is an invalid state for TASK
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
NRUNS $jira stop $blocker
|
||||||
|
DIFF <<EOF
|
||||||
|
ERROR Invalid Transition 'stop', Available: Done
|
||||||
|
EOF
|
||||||
|
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Set it to "Done"
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira done $blocker
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $blocker http://localhost:8080/browse/$blocker
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Verify issue is now in Done state (the "blocker" issue is now Done)
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira $issue
|
||||||
|
DIFF <<EOF
|
||||||
|
issue: $issue
|
||||||
|
created: a minute ago
|
||||||
|
status: To Do
|
||||||
|
summary: summary
|
||||||
|
project: TASK
|
||||||
|
issuetype: Task
|
||||||
|
assignee: gojira
|
||||||
|
reporter: gojira
|
||||||
|
blockers: $blocker[Done]
|
||||||
|
depends: $dup[Done]
|
||||||
|
priority: Medium
|
||||||
|
votes: 0
|
||||||
|
description: |
|
||||||
|
description
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Verify we can add a comment
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira comment $issue --noedit -m "Yo, Comment"
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $issue http://localhost:8080/browse/$issue
|
||||||
|
EOF
|
||||||
|
|
||||||
|
RUNS $jira $issue
|
||||||
|
DIFF <<EOF
|
||||||
|
issue: $issue
|
||||||
|
created: a minute ago
|
||||||
|
status: To Do
|
||||||
|
summary: summary
|
||||||
|
project: TASK
|
||||||
|
issuetype: Task
|
||||||
|
assignee: gojira
|
||||||
|
reporter: gojira
|
||||||
|
blockers: $blocker[Done]
|
||||||
|
depends: $dup[Done]
|
||||||
|
priority: Medium
|
||||||
|
votes: 0
|
||||||
|
description: |
|
||||||
|
description
|
||||||
|
|
||||||
|
comments:
|
||||||
|
- | # mojira, a minute ago
|
||||||
|
Yo, Comment
|
||||||
|
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Verify we can add labels to an issue
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira add labels $blocker test-label another-label
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $blocker http://localhost:8080/browse/$blocker
|
||||||
|
EOF
|
||||||
|
|
||||||
|
RUNS $jira $blocker
|
||||||
|
DIFF <<EOF
|
||||||
|
issue: $blocker
|
||||||
|
created: a minute ago
|
||||||
|
status: Done
|
||||||
|
summary: blocks
|
||||||
|
project: TASK
|
||||||
|
issuetype: Task
|
||||||
|
assignee: gojira
|
||||||
|
reporter: gojira
|
||||||
|
blockers:
|
||||||
|
depends: $issue[To Do]
|
||||||
|
priority: Medium
|
||||||
|
votes: 0
|
||||||
|
labels: another-label, test-label
|
||||||
|
description: |
|
||||||
|
blocks
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Verify we can remove a label
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira remove labels $blocker another-label
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $blocker http://localhost:8080/browse/$blocker
|
||||||
|
EOF
|
||||||
|
|
||||||
|
RUNS $jira $blocker
|
||||||
|
DIFF <<EOF
|
||||||
|
issue: $blocker
|
||||||
|
created: a minute ago
|
||||||
|
status: Done
|
||||||
|
summary: blocks
|
||||||
|
project: TASK
|
||||||
|
issuetype: Task
|
||||||
|
assignee: gojira
|
||||||
|
reporter: gojira
|
||||||
|
blockers:
|
||||||
|
depends: $issue[To Do]
|
||||||
|
priority: Medium
|
||||||
|
votes: 0
|
||||||
|
labels: test-label
|
||||||
|
description: |
|
||||||
|
blocks
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Verify we can replace the labels with a new set
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira set labels $blocker more-label better-label
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $blocker http://localhost:8080/browse/$blocker
|
||||||
|
EOF
|
||||||
|
|
||||||
|
RUNS $jira $blocker
|
||||||
|
DIFF <<EOF
|
||||||
|
issue: $blocker
|
||||||
|
created: a minute ago
|
||||||
|
status: Done
|
||||||
|
summary: blocks
|
||||||
|
project: TASK
|
||||||
|
issuetype: Task
|
||||||
|
assignee: gojira
|
||||||
|
reporter: gojira
|
||||||
|
blockers:
|
||||||
|
depends: $issue[To Do]
|
||||||
|
priority: Medium
|
||||||
|
votes: 0
|
||||||
|
labels: better-label, more-label
|
||||||
|
description: |
|
||||||
|
blocks
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Verify that "mojira" user can take the issue (reassign to self)
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira take $blocker
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $blocker http://localhost:8080/browse/$blocker
|
||||||
|
EOF
|
||||||
|
|
||||||
|
RUNS $jira $blocker
|
||||||
|
DIFF <<EOF
|
||||||
|
issue: $blocker
|
||||||
|
created: a minute ago
|
||||||
|
status: Done
|
||||||
|
summary: blocks
|
||||||
|
project: TASK
|
||||||
|
issuetype: Task
|
||||||
|
assignee: mojira
|
||||||
|
reporter: gojira
|
||||||
|
blockers:
|
||||||
|
depends: $issue[To Do]
|
||||||
|
priority: Medium
|
||||||
|
votes: 0
|
||||||
|
labels: better-label, more-label
|
||||||
|
description: |
|
||||||
|
blocks
|
||||||
|
EOF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
## Verify we can give the issue back go "gojira" user
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
RUNS $jira give $blocker gojira
|
||||||
|
DIFF <<EOF
|
||||||
|
OK $blocker http://localhost:8080/browse/$blocker
|
||||||
|
EOF
|
||||||
|
|
||||||
|
RUNS $jira $blocker
|
||||||
|
DIFF <<EOF
|
||||||
|
issue: $blocker
|
||||||
|
created: a minute ago
|
||||||
|
status: Done
|
||||||
|
summary: blocks
|
||||||
|
project: TASK
|
||||||
|
issuetype: Task
|
||||||
|
assignee: gojira
|
||||||
|
reporter: gojira
|
||||||
|
blockers:
|
||||||
|
depends: $issue[To Do]
|
||||||
|
priority: Medium
|
||||||
|
votes: 0
|
||||||
|
labels: better-label, more-label
|
||||||
|
description: |
|
||||||
|
blocks
|
||||||
|
EOF
|
||||||
|
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
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
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
INCLUDE MERGE -VOLUME -COPY \
|
||||||
|
https://raw.githubusercontent.com/cptactionhank/docker-atlassian-jira-software/master/Dockerfile \
|
||||||
|
Dockerfile.inc
|
||||||
+54
@@ -0,0 +1,54 @@
|
|||||||
|
## Tests
|
||||||
|
|
||||||
|
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:
|
||||||
|
```
|
||||||
|
# this creates a local "jira" binary
|
||||||
|
make
|
||||||
|
|
||||||
|
# this runs the integration tests in the "t" directory
|
||||||
|
prove
|
||||||
|
```
|
||||||
|
|
||||||
|
## API Documentation:
|
||||||
|
https://docs.atlassian.com/jira/REST/cloud/
|
||||||
|
|
||||||
|
## projectTempalteKey missing documentation
|
||||||
|
https://answers.atlassian.com/questions/36176301/jira-api-7.1.0-create-project
|
||||||
|
|
||||||
+57
-56
@@ -1,58 +1,60 @@
|
|||||||
package jira
|
package jira
|
||||||
|
|
||||||
var all_templates = map[string]string{
|
var allTemplates = map[string]string{
|
||||||
"debug": default_debug_template,
|
"debug": defaultDebugTemplate,
|
||||||
"fields": default_debug_template,
|
"fields": defaultDebugTemplate,
|
||||||
"editmeta": default_debug_template,
|
"editmeta": defaultDebugTemplate,
|
||||||
"transmeta": default_debug_template,
|
"transmeta": defaultDebugTemplate,
|
||||||
"createmeta": default_debug_template,
|
"createmeta": defaultDebugTemplate,
|
||||||
"issuelinktypes": default_debug_template,
|
"issuelinktypes": defaultDebugTemplate,
|
||||||
"list": default_list_template,
|
"list": defaultListTemplate,
|
||||||
"table": default_table_template,
|
"table": defaultTableTemplate,
|
||||||
"view": default_view_template,
|
"view": defaultViewTemplate,
|
||||||
"edit": default_edit_template,
|
"edit": defaultEditTemplate,
|
||||||
"transitions": default_transitions_template,
|
"transitions": defaultTransitionsTemplate,
|
||||||
"components": default_components_template,
|
"components": defaultComponentsTemplate,
|
||||||
"issuetypes": default_issuetypes_template,
|
"issuetypes": defaultIssuetypesTemplate,
|
||||||
"create": default_create_template,
|
"create": defaultCreateTemplate,
|
||||||
"comment": default_comment_template,
|
"comment": defaultCommentTemplate,
|
||||||
"transition": default_transition_template,
|
"transition": defaultTransitionTemplate,
|
||||||
"request": default_debug_template,
|
"request": defaultDebugTemplate,
|
||||||
}
|
}
|
||||||
|
|
||||||
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" }} |
|
| {{ "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 }}+
|
+{{ "-" | 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}} |
|
{{ 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 }}+
|
{{ end }}+{{ "-" | rep 16 }}+{{ "-" | rep 57 }}+{{ "-" | rep 14 }}+{{ "-" | rep 14 }}+{{ "-" | rep 12 }}+{{ "-" | rep 14 }}+{{ "-" | rep 14 }}+
|
||||||
`
|
`
|
||||||
|
|
||||||
const default_view_template = `issue: {{ .key }}
|
const defaultViewTemplate = `issue: {{ .key }}{{if .fields.created}}
|
||||||
created: {{ .fields.created }}
|
created: {{ .fields.created | age }} ago{{end}}{{if .fields.status}}
|
||||||
status: {{ .fields.status.name }}
|
status: {{ .fields.status.name }}{{end}}
|
||||||
summary: {{ .fields.summary }}
|
summary: {{ .fields.summary }}
|
||||||
project: {{ .fields.project.key }}
|
project: {{ .fields.project.key }}{{if .fields.components}}
|
||||||
components: {{ range .fields.components }}{{ .name }} {{end}}
|
components: {{ range .fields.components }}{{ .name }} {{end}}{{end}}{{if .fields.issuetype}}
|
||||||
issuetype: {{ .fields.issuetype.name }}
|
issuetype: {{ .fields.issuetype.name }}{{end}}{{if .fields.assignee}}
|
||||||
assignee: {{ if .fields.assignee }}{{ .fields.assignee.name }}{{end}}
|
assignee: {{ .fields.assignee.name }}{{end}}
|
||||||
reporter: {{ if .fields.reporter }}{{ .fields.reporter.name }}{{end}}
|
reporter: {{ if .fields.reporter }}{{ .fields.reporter.name }}{{end}}{{if .fields.customfield_10110}}
|
||||||
watchers: {{ range .fields.customfield_10110 }}{{ .name }} {{end}}
|
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}}
|
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}}
|
depends: {{ range .fields.issuelinks }}{{if .inwardIssue}}{{ .inwardIssue.key }}[{{.inwardIssue.fields.status.name}}]{{end}}{{end}}{{end}}{{if .fields.priority}}
|
||||||
priority: {{ .fields.priority.name }}
|
priority: {{ .fields.priority.name }}{{end}}{{if .fields.votes}}
|
||||||
|
votes: {{ .fields.votes.votes}}{{end}}{{if .fields.labels}}
|
||||||
|
labels: {{ join ", " .fields.labels }}{{end}}
|
||||||
description: |
|
description: |
|
||||||
{{ or .fields.description "" | indent 2 }}
|
{{ or .fields.description "" | indent 2 }}{{if .fields.comment.comments}}
|
||||||
|
|
||||||
comments:
|
comments:
|
||||||
{{ range .fields.comment.comments }} - | # {{.author.name}} at {{.created}}
|
{{ range .fields.comment.comments }} - | # {{.author.name}}, {{.created | age}} ago
|
||||||
{{ or .body "" | indent 4}}
|
{{ or .body "" | indent 4}}
|
||||||
{{end}}
|
{{end}}{{end}}
|
||||||
`
|
`
|
||||||
const default_edit_template = `# issue: {{ .key }}
|
const defaultEditTemplate = `# issue: {{ .key }}
|
||||||
update:
|
update:
|
||||||
comment:
|
comment:
|
||||||
- add:
|
- add:
|
||||||
@@ -65,8 +67,8 @@ fields:
|
|||||||
- name: {{ .name }}{{end}}{{end}}
|
- name: {{ .name }}{{end}}{{end}}
|
||||||
assignee:
|
assignee:
|
||||||
name: {{ if .overrides.assignee }}{{.overrides.assignee}}{{else}}{{if .fields.assignee }}{{ .fields.assignee.name }}{{end}}{{end}}
|
name: {{ if .overrides.assignee }}{{.overrides.assignee}}{{else}}{{if .fields.assignee }}{{ .fields.assignee.name }}{{end}}{{end}}
|
||||||
reporter:
|
# reporter:
|
||||||
name: {{ if .overrides.reporter }}{{ .overrides.reporter }}{{else if .fields.reporter}}{{ .fields.reporter.name }}{{end}}
|
# name: {{ if .overrides.reporter }}{{ .overrides.reporter }}{{else if .fields.reporter}}{{ .fields.reporter.name }}{{end}}
|
||||||
# watchers
|
# watchers
|
||||||
customfield_10110: {{ range .fields.customfield_10110 }}
|
customfield_10110: {{ range .fields.customfield_10110 }}
|
||||||
- name: {{ .name }}{{end}}{{if .overrides.watcher}}
|
- name: {{ .name }}{{end}}{{if .overrides.watcher}}
|
||||||
@@ -76,46 +78,45 @@ fields:
|
|||||||
description: |~
|
description: |~
|
||||||
{{ or .overrides.description (or .fields.description "") | indent 4 }}
|
{{ or .overrides.description (or .fields.description "") | indent 4 }}
|
||||||
# comments:
|
# comments:
|
||||||
# {{ range .fields.comment.comments }} - | # {{.author.name}} at {{.created}}
|
# {{ range .fields.comment.comments }} - | # {{.author.name}}, {{.created | age}} ago
|
||||||
# {{ or .body "" | indent 4 | comment}}
|
# {{ or .body "" | indent 4 | comment}}
|
||||||
# {{end}}
|
# {{end}}
|
||||||
`
|
`
|
||||||
const default_transitions_template = `{{ range .transitions }}{{.id }}: {{.name}}
|
const defaultTransitionsTemplate = `{{ range .transitions }}{{.id }}: {{.name}}
|
||||||
{{end}}`
|
{{end}}`
|
||||||
|
|
||||||
const default_components_template = `{{ range . }}{{.id }}: {{.name}}
|
const defaultComponentsTemplate = `{{ range . }}{{.id }}: {{.name}}
|
||||||
{{end}}`
|
{{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}}`
|
{{end}}{{end}}`
|
||||||
|
|
||||||
const default_create_template = `fields:
|
const defaultCreateTemplate = `fields:
|
||||||
project:
|
project:
|
||||||
key: {{ .overrides.project }}
|
key: {{ or .overrides.project "" }}
|
||||||
issuetype:
|
issuetype:
|
||||||
name: {{ .overrides.issuetype }}
|
name: {{ or .overrides.issuetype "" }}
|
||||||
summary: {{ or .overrides.summary "" }}
|
summary: {{ or .overrides.summary "" }}{{if .meta.fields.priority.allowedValues}}
|
||||||
priority: # Values: {{ range .meta.fields.priority.allowedValues }}{{.name}}, {{end}}
|
priority: # Values: {{ range .meta.fields.priority.allowedValues }}{{.name}}, {{end}}
|
||||||
name: {{ or .overrides.priority "unassigned" }}
|
name: {{ or .overrides.priority ""}}{{end}}{{if .meta.fields.components.allowedValues}}
|
||||||
components: # Values: {{ range .meta.fields.components.allowedValues }}{{.name}}, {{end}}{{ range split "," (or .overrides.components "")}}
|
components: # Values: {{ range .meta.fields.components.allowedValues }}{{.name}}, {{end}}{{ range split "," (or .overrides.components "")}}
|
||||||
- name: {{ . }}{{end}}
|
- name: {{ . }}{{end}}{{end}}
|
||||||
description: |~
|
description: |~
|
||||||
{{ or .overrides.description "" | indent 4 }}
|
{{ or .overrides.description "" | indent 4 }}{{if .meta.fields.assignee}}
|
||||||
assignee:
|
assignee:
|
||||||
name: {{ or .overrides.assignee "" }}
|
name: {{ or .overrides.assignee "" }}{{end}}{{if .meta.fields.reporter}}
|
||||||
reporter:
|
reporter:
|
||||||
name: {{ or .overrides.reporter .overrides.user }}
|
name: {{ or .overrides.reporter .overrides.user }}{{end}}{{if .meta.fields.customfield_10110}}
|
||||||
# watchers
|
# watchers
|
||||||
customfield_10110: {{ range split "," (or .overrides.watchers "")}}
|
customfield_10110: {{ range split "," (or .overrides.watchers "")}}
|
||||||
- name: {{.}}{{end}}
|
- name: {{.}}{{end}}
|
||||||
- name:
|
- name:{{end}}`
|
||||||
`
|
|
||||||
|
|
||||||
const default_comment_template = `body: |~
|
const defaultCommentTemplate = `body: |~
|
||||||
{{ or .overrides.comment "" | indent 2 }}
|
{{ or .overrides.comment "" | indent 2 }}
|
||||||
`
|
`
|
||||||
|
|
||||||
const default_transition_template = `update:
|
const defaultTransitionTemplate = `update:
|
||||||
comment:
|
comment:
|
||||||
- add:
|
- add:
|
||||||
body: |~
|
body: |~
|
||||||
@@ -140,7 +141,7 @@ fields:{{if .meta.fields.assignee}}
|
|||||||
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}}
|
resolution: # Values: {{ range .meta.fields.resolution.allowedValues }}{{.name}}, {{end}}
|
||||||
name: {{if .overrides.resolution}}{{.overrides.resolution}}{{else if .fields.resolution}}{{.fields.resolution.name}}{{else}}Fixed{{end}}{{end}}{{if .meta.fields.summary}}
|
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}}
|
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)}}
|
versions: # Values: {{ range .meta.fields.versions.allowedValues }}{{.name}}, {{end}}{{if .overrides.versions}}{{ range (split "," .overrides.versions)}}
|
||||||
- name: {{.}}{{end}}{{else}}{{range .fields.versions}}
|
- name: {{.}}{{end}}{{else}}{{range .fields.versions}}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import (
|
|||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/mgutz/ansi"
|
"github.com/mgutz/ansi"
|
||||||
"gopkg.in/coryb/yaml.v2"
|
"gopkg.in/coryb/yaml.v2"
|
||||||
@@ -26,10 +25,12 @@ func homedir() string {
|
|||||||
return os.Getenv("HOME")
|
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 {
|
func FindParentPaths(fileName string) []string {
|
||||||
cwd, _ := os.Getwd()
|
cwd, _ := os.Getwd()
|
||||||
|
|
||||||
paths := make([]string, 0)
|
paths := []string{}
|
||||||
|
|
||||||
// special case if homedir is not in current path then check there anyway
|
// special case if homedir is not in current path then check there anyway
|
||||||
homedir := homedir()
|
homedir := homedir()
|
||||||
@@ -57,12 +58,14 @@ func FindParentPaths(fileName string) []string {
|
|||||||
return paths
|
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) {
|
func FindClosestParentPath(fileName string) (string, error) {
|
||||||
paths := FindParentPaths(fileName)
|
paths := FindParentPaths(fileName)
|
||||||
if len(paths) > 0 {
|
if len(paths) > 0 {
|
||||||
return paths[len(paths)-1], nil
|
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 {
|
func readFile(file string) string {
|
||||||
@@ -92,35 +95,36 @@ func copyFile(src, dst string) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func fuzzyAge(start string) (string, 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
|
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) {
|
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
|
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 {
|
func RunTemplate(templateContent string, data interface{}, out io.Writer) error {
|
||||||
return runTemplate(templateContent, data, out)
|
return runTemplate(templateContent, data, out)
|
||||||
}
|
}
|
||||||
@@ -132,11 +136,11 @@ func runTemplate(templateContent string, data interface{}, out io.Writer) error
|
|||||||
|
|
||||||
funcs := map[string]interface{}{
|
funcs := map[string]interface{}{
|
||||||
"toJson": func(content interface{}) (string, error) {
|
"toJson": func(content interface{}) (string, error) {
|
||||||
if bytes, err := json.MarshalIndent(content, "", " "); err != nil {
|
bytes, err := json.MarshalIndent(content, "", " ")
|
||||||
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
} else {
|
|
||||||
return string(bytes), nil
|
|
||||||
}
|
}
|
||||||
|
return string(bytes), nil
|
||||||
},
|
},
|
||||||
"append": func(more string, content interface{}) (string, error) {
|
"append": func(more string, content interface{}) (string, error) {
|
||||||
switch value := content.(type) {
|
switch value := content.(type) {
|
||||||
@@ -145,13 +149,13 @@ func runTemplate(templateContent string, data interface{}, out io.Writer) error
|
|||||||
case []byte:
|
case []byte:
|
||||||
return string(append(content.([]byte), []byte(more)...)), nil
|
return string(append(content.([]byte), []byte(more)...)), nil
|
||||||
default:
|
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": func(spaces int, content string) string {
|
||||||
indent := make([]rune, spaces+1, spaces+1)
|
indent := make([]rune, spaces+1, spaces+1)
|
||||||
indent[0] = '\n'
|
indent[0] = '\n'
|
||||||
for i := 1; i < spaces+1; i += 1 {
|
for i := 1; i < spaces+1; i++ {
|
||||||
indent[i] = ' '
|
indent[i] = ' '
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -194,7 +198,7 @@ func runTemplate(templateContent string, data interface{}, out io.Writer) error
|
|||||||
},
|
},
|
||||||
"rep": func(count int, content string) string {
|
"rep": func(count int, content string) string {
|
||||||
var buffer bytes.Buffer
|
var buffer bytes.Buffer
|
||||||
for i := 0; i < count; i += 1 {
|
for i := 0; i < count; i++ {
|
||||||
buffer.WriteString(content)
|
buffer.WriteString(content)
|
||||||
}
|
}
|
||||||
return buffer.String()
|
return buffer.String()
|
||||||
@@ -206,19 +210,19 @@ func runTemplate(templateContent string, data interface{}, out io.Writer) error
|
|||||||
return dateFormat(format, content)
|
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)
|
log.Errorf("Failed to parse template: %s", err)
|
||||||
return err
|
return err
|
||||||
} else {
|
}
|
||||||
if err := tmpl.Execute(out, data); err != nil {
|
if err := tmpl.Execute(out, data); err != nil {
|
||||||
log.Errorf("Failed to execute template: %s", err)
|
log.Errorf("Failed to execute template: %s", err)
|
||||||
return err
|
return err
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func responseToJson(resp *http.Response, err error) (interface{}, error) {
|
func responseToJSON(resp *http.Response, err error) (interface{}, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -322,6 +326,9 @@ func yamlFixup(data interface{}) (interface{}, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if len(copy) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
return copy, nil
|
return copy, nil
|
||||||
case map[string]interface{}:
|
case map[string]interface{}:
|
||||||
copy := make(map[string]interface{})
|
copy := make(map[string]interface{})
|
||||||
@@ -332,6 +339,9 @@ func yamlFixup(data interface{}) (interface{}, error) {
|
|||||||
copy[k] = fixed
|
copy[k] = fixed
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if len(copy) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
return copy, nil
|
return copy, nil
|
||||||
case []interface{}:
|
case []interface{}:
|
||||||
copy := make([]interface{}, 0, len(d))
|
copy := make([]interface{}, 0, len(d))
|
||||||
@@ -342,6 +352,9 @@ func yamlFixup(data interface{}) (interface{}, error) {
|
|||||||
copy = append(copy, fixed)
|
copy = append(copy, fixed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if len(copy) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
return copy, nil
|
return copy, nil
|
||||||
case string:
|
case string:
|
||||||
if d == "" || d == "\n" {
|
if d == "" || d == "\n" {
|
||||||
@@ -358,7 +371,7 @@ func mkdir(dir string) error {
|
|||||||
log.Errorf("Failed to stat %s: %s", dir, err)
|
log.Errorf("Failed to stat %s: %s", dir, err)
|
||||||
return err
|
return err
|
||||||
} else if err == nil && !stat.IsDir() {
|
} 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)
|
log.Errorf("%s", err)
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Reference in New Issue
Block a user