mirror of
https://github.com/Threnklyn/jira.git
synced 2026-05-19 20:53:27 +02:00
Compare commits
94 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a3633aa537 | |||
| 2a8b6521dc | |||
| 4cc172de6b | |||
| 0dd6061992 | |||
| 9d12f56332 | |||
| 824dd2f725 | |||
| 657bc59c8f | |||
| ec1914dfde | |||
| a22911a3f9 | |||
| 1f6191425f | |||
| 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 | |||
| de4fe76fec | |||
| 5b870cb7a2 | |||
| 89bb82b3f2 | |||
| dd0f5efd32 | |||
| 68b5e60dd9 | |||
| 71acc5d7fc | |||
| 4f91cecf25 | |||
| 688b987895 | |||
| 71bb04fabb | |||
| 3a9f763f9d | |||
| d86d85f7b2 | |||
| 4b798cbfb4 | |||
| 598924b51d | |||
| 674957af5d | |||
| c568d7e921 | |||
| 6eb3567ca5 | |||
| 87ec73c5c3 | |||
| 23551abb11 | |||
| 693e1441f7 | |||
| 6e5cc9821e | |||
| 9e90376816 | |||
| 20b32c2ed6 | |||
| ac170e9ab1 | |||
| d8bce08d3a | |||
| 382bf4faeb | |||
| 595a5212b4 | |||
| f595801202 | |||
| 404caf6400 | |||
| f7eb04e36d | |||
| b0d4f7273d | |||
| a927181db1 | |||
| ff56136937 | |||
| e58625b00c |
@@ -5,3 +5,10 @@ src/github.com/docopt/
|
||||
src/github.com/mgutz/
|
||||
src/github.com/op/
|
||||
src/gopkg.in/
|
||||
jira
|
||||
jira.exe
|
||||
schemas/*.json
|
||||
t/issue.props
|
||||
t/.jira.d/templates
|
||||
dist/
|
||||
src/
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.6
|
||||
|
||||
matrix:
|
||||
fast_finish: true
|
||||
|
||||
script:
|
||||
- make vet
|
||||
- make lint
|
||||
- make
|
||||
@@ -1,5 +1,65 @@
|
||||
# Changelog
|
||||
|
||||
## 0.1.7 - 2016-08-24
|
||||
|
||||
* Prefer transition names which match exactly [Don Brower] [[e40f9c1](https://github.com/Netflix-Skunkworks/go-jira/commit/e40f9c1)]
|
||||
* update tempates to make them more readable with space trimming added to go-1.6 [Cory Bennett] [[693b3e4](https://github.com/Netflix-Skunkworks/go-jira/commit/693b3e4)]
|
||||
|
||||
## 0.1.6 - 2016-08-21
|
||||
|
||||
* make "worklogs" command print output through template allow "add worklog" command to open edit template [Cory Bennett] [[cc3fbee](https://github.com/Netflix-Skunkworks/go-jira/commit/cc3fbee)]
|
||||
* remove extra newline at end of worklogs template [Cory Bennett] [[d08ef15](https://github.com/Netflix-Skunkworks/go-jira/commit/d08ef15)]
|
||||
* adding worklog related templates [Cory Bennett] [[ab1cd27](https://github.com/Netflix-Skunkworks/go-jira/commit/ab1cd27)]
|
||||
|
||||
## 0.1.5 - 2016-08-21
|
||||
|
||||
* update for golint [Cory Bennett] [[5a4e17c](https://github.com/Netflix-Skunkworks/go-jira/commit/5a4e17c)]
|
||||
* fix for go vet [Cory Bennett] [[355fb42](https://github.com/Netflix-Skunkworks/go-jira/commit/355fb42)]
|
||||
|
||||
## 0.1.4 - 2016-08-12
|
||||
|
||||
* when running "dups" on a Process Management Project type, you have to start/stop the task to resolve it [Cory Bennett] [[2c91905](https://github.com/Netflix-Skunkworks/go-jira/commit/2c91905)]
|
||||
* 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
|
||||
|
||||
* 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)]
|
||||
* Include templates from a system path [Mike Pountney] [[cf10f53](https://github.com/Netflix-Skunkworks/go-jira/commit/cf10f53)]
|
||||
* Added support for the ```expand``` option for Issues [tobyjoe] [[fb4afc9](https://github.com/Netflix-Skunkworks/go-jira/commit/fb4afc9)]
|
||||
* change for api changes to go-logging [Cory Bennett] [[7bfc6e8](https://github.com/Netflix-Skunkworks/go-jira/commit/7bfc6e8)]
|
||||
* Fix issuetype calls adding URL escaping [Jonathan Wright] [[e4a25e2](https://github.com/Netflix-Skunkworks/go-jira/commit/e4a25e2)]
|
||||
|
||||
## 0.1.0 - 2016-01-29
|
||||
|
||||
* Fixes [#32](https://github.com/Netflix-Skunkworks/go-jira/issues/32) - make path to cookieFile if it's not present [Mike Pountney] [[6644579](https://github.com/Netflix-Skunkworks/go-jira/commit/6644579)]
|
||||
* Add component/components support: add and list for now. [Mike Pountney] [[d7b3226](https://github.com/Netflix-Skunkworks/go-jira/commit/d7b3226)]
|
||||
* Tweak the CmdWatch contract and add watcher remove support [Mike Pountney] [[383847a](https://github.com/Netflix-Skunkworks/go-jira/commit/383847a)]
|
||||
* Amend vote/unvote to be vote/vote --down [Mike Pountney] [[797edef](https://github.com/Netflix-Skunkworks/go-jira/commit/797edef)]
|
||||
* Add 'vote' and 'unvote' [Mike Pountney] [[c95e66e](https://github.com/Netflix-Skunkworks/go-jira/commit/c95e66e)]
|
||||
|
||||
## 0.0.20 - 2016-01-21
|
||||
|
||||
* [issue [#28](https://github.com/Netflix-Skunkworks/go-jira/issues/28)] check to make sure we got back issuetypes for create metadata [Cory Bennett] [[ee0e780](https://github.com/Netflix-Skunkworks/go-jira/commit/ee0e780)]
|
||||
|
||||
@@ -1,29 +1,41 @@
|
||||
PLATFORMS= \
|
||||
freebsd-386 \
|
||||
freebsd-amd64 \
|
||||
freebsd-arm \
|
||||
linux-386 \
|
||||
linux-amd64 \
|
||||
linux-arm \
|
||||
openbsd-386 \
|
||||
openbsd-amd64 \
|
||||
windows-386 \
|
||||
windows-amd64 \
|
||||
darwin-386 \
|
||||
darwin-amd64 \
|
||||
$(NULL)
|
||||
|
||||
DIST=$(shell pwd)/dist
|
||||
# freebsd-386 \
|
||||
# freebsd-arm \
|
||||
# linux-arm \
|
||||
# openbsd-386 \
|
||||
# openbsd-amd64 \
|
||||
# darwin-386
|
||||
|
||||
export GOPATH=$(shell pwd)
|
||||
|
||||
GOBIN ?= $(shell pwd)
|
||||
NAME=jira
|
||||
|
||||
BIN ?= $(GOBIN)/$(NAME)
|
||||
OS=$(shell uname -s)
|
||||
ifeq ($(filter CYGWIN%,$(OS)),$(OS))
|
||||
export CWD=$(shell cygpath -wa .)
|
||||
export SEP=\\
|
||||
export CYGWIN=winsymlinks:native
|
||||
BIN ?= $(GOBIN)$(SEP)$(NAME).exe
|
||||
else
|
||||
export CWD=$(shell pwd)
|
||||
export SEP=/
|
||||
BIN ?= $(GOBIN)$(SEP)$(NAME)
|
||||
endif
|
||||
|
||||
CURVER ?= $(shell [ -d .git ] && git describe --abbrev=0 --tags || grep ^\#\# CHANGELOG.md | awk '{print $$2; exit}')
|
||||
LDFLAGS:=-X jira.VERSION=$(patsubst v%,%,$(CURVER)) -w
|
||||
export GOPATH=$(CWD)
|
||||
|
||||
DIST=$(CWD)$(SEP)dist
|
||||
|
||||
GOBIN ?= $(CWD)
|
||||
|
||||
CURVER ?= $(patsubst v%,%,$(shell [ -d .git ] && git describe --abbrev=0 --tags || grep ^\#\# CHANGELOG.md | awk '{print $$2; exit}'))
|
||||
LDFLAGS:=-X jira.VERSION=$(CURVER) -w
|
||||
|
||||
# use make DEBUG=1 and you can get a debuggable golang binary
|
||||
# see https://github.com/mailgun/godebug
|
||||
@@ -34,16 +46,30 @@ else
|
||||
endif
|
||||
|
||||
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/%:
|
||||
mkdir -p $(@D)
|
||||
test -L $@ || ln -sf ../../.. $@
|
||||
test -L $@ || ln -sf '$(GOPATH)' $@
|
||||
go get -v $* $*/main
|
||||
|
||||
vet:
|
||||
@go vet .
|
||||
@go vet ./data
|
||||
@go vet ./main
|
||||
|
||||
lint:
|
||||
@go get github.com/golang/lint/golint
|
||||
@./bin/golint .
|
||||
@./bin/golint ./data
|
||||
@./bin/golint ./main
|
||||
|
||||
cross-setup:
|
||||
for p in $(PLATFORMS); do \
|
||||
echo "Building for $$p"; \
|
||||
echo Building for $$p"; \
|
||||
cd $(GOROOT)/src && sudo GOROOT_BOOTSTRAP=$(GOROOT) GOOS=$${p/-*/} GOARCH=$${p/*-/} bash ./make.bash --no-clean; \
|
||||
done
|
||||
|
||||
@@ -54,18 +80,19 @@ all:
|
||||
echo "Building for $$p"; \
|
||||
${MAKE} build GOOS=$${p/-*/} GOARCH=$${p/*-/} BIN=$(DIST)/$(NAME)-$$p; \
|
||||
done
|
||||
for x in $(DIST)/jira-windows-*; do mv $$x $$x.exe; done
|
||||
|
||||
fmt:
|
||||
gofmt -s -w main/*.go *.go
|
||||
|
||||
install:
|
||||
${MAKE} GOBIN=~/bin build
|
||||
${MAKE} GOBIN=$$HOME/bin build
|
||||
|
||||
NEWVER ?= $(shell echo $(CURVER) | awk -F. '{print $$1"."$$2"."$$3+1}')
|
||||
TODAY := $(shell date +%Y-%m-%d)
|
||||
|
||||
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:
|
||||
@echo "# Changelog" > CHANGELOG.md.new; \
|
||||
@@ -81,7 +108,7 @@ update-changelog:
|
||||
git tag v$(NEWVER)
|
||||
|
||||
version:
|
||||
@echo $(patsubst v%,%,$(CURVER))
|
||||
@echo $(CURVER)
|
||||
|
||||
clean:
|
||||
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
|
||||
simple command line client for Atlassian's Jira service written in Go
|
||||
|
||||
@@ -69,25 +74,13 @@ You can download one of the pre-built binaries for **go-jira** [here](https://gi
|
||||
|
||||
* **NOTE** You will need **`go-1.4.1`** minimum
|
||||
|
||||
* If you do not have a **GOPATH** setup, these are simple build steps:
|
||||
|
||||
* To build the `jira` binary the current directory just run:
|
||||
```bash
|
||||
git clone git@github.com:Netflix-Skunkworks/go-jira.git
|
||||
cd go-jira
|
||||
export GOPATH=$(pwd)
|
||||
export GOBIN=$GOPATH/bin
|
||||
export PATH=$GOBIN:$PATH
|
||||
cd src/github.com/Netflix-Skunkworks/go-jira/jira
|
||||
go get -v
|
||||
make
|
||||
```
|
||||
|
||||
* If you do have a **GOPATH** setup, these are the standard steps to build:
|
||||
|
||||
```
|
||||
cd $GOPATH
|
||||
git clone git@github.com:Netflix-Skunkworks/go-jira.git src/github.com/Netflix-Skunkworks/go-jira
|
||||
cd src/github.com/Netflix-Skunkworks/go-jira/jira
|
||||
go get -v
|
||||
* To install the binary to you ~/bin directory you can run:
|
||||
```bash
|
||||
make install
|
||||
```
|
||||
|
||||
## Configuration
|
||||
@@ -117,6 +110,8 @@ endpoint: https://jira.mycompany.com
|
||||
EOM
|
||||
```
|
||||
|
||||
Then use `jira login` to authenticate yourself.
|
||||
|
||||
### Dynamic Configuration
|
||||
|
||||
If the **.jira.d/config.yml** file is executable, then **go-jira** will attempt to execute the file and use the stdout for configuration. You can use this to customize templates or other overrides depending on what type of operation you are running. For example if you would like to use the "table" template when ever you run `jira ls`, then you can create a template like this:
|
||||
@@ -167,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
|
||||
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
|
||||
|
||||
```
|
||||
@@ -205,7 +220,7 @@ General Options:
|
||||
-e --endpoint=URI URI to use for jira
|
||||
-h --help Show this usage
|
||||
-t --template=FILE Template file to use for output/editing
|
||||
-u --user=USER Username to use for authenticaion (default: $USER)
|
||||
-u --user=USER Username to use for authentication (default: $USER)
|
||||
-v --verbose Increase output logging
|
||||
|
||||
Query Options:
|
||||
|
||||
@@ -6,24 +6,29 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/kballard/go-shellquote"
|
||||
"github.com/op/go-logging"
|
||||
"gopkg.in/coryb/yaml.v2"
|
||||
"gopkg.in/op/go-logging.v1"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/cookiejar"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
log = logging.MustGetLogger("jira")
|
||||
log = logging.MustGetLogger("jira")
|
||||
// VERSION is the go-jira library version
|
||||
VERSION string
|
||||
)
|
||||
|
||||
// Cli is go-jira client object
|
||||
type Cli struct {
|
||||
endpoint *url.URL
|
||||
opts map[string]interface{}
|
||||
@@ -31,8 +36,9 @@ type Cli struct {
|
||||
ua *http.Client
|
||||
}
|
||||
|
||||
// New creates go-jira client object
|
||||
func New(opts map[string]interface{}) *Cli {
|
||||
homedir := os.Getenv("HOME")
|
||||
homedir := homedir()
|
||||
cookieJar, _ := cookiejar.New(nil)
|
||||
endpoint, _ := opts["endpoint"].(string)
|
||||
url, _ := url.Parse(strings.TrimRight(endpoint, "/"))
|
||||
@@ -52,7 +58,7 @@ func New(opts map[string]interface{}) *Cli {
|
||||
cli := &Cli{
|
||||
endpoint: url,
|
||||
opts: opts,
|
||||
cookieFile: fmt.Sprintf("%s/.jira.d/cookies.js", homedir),
|
||||
cookieFile: filepath.Join(homedir, ".jira.d", "cookies.js"),
|
||||
ua: &http.Client{
|
||||
Jar: cookieJar,
|
||||
Transport: transport,
|
||||
@@ -64,7 +70,22 @@ func New(opts map[string]interface{}) *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 := time.Now().Add(24 * 7 * time.Hour)
|
||||
for _, cookie := range cookies {
|
||||
@@ -74,11 +95,11 @@ func (c *Cli) saveCookies(cookies []*http.Cookie) {
|
||||
if currentCookies := c.loadCookies(); currentCookies != nil {
|
||||
currentCookiesByName := make(map[string]*http.Cookie)
|
||||
for _, cookie := range currentCookies {
|
||||
currentCookiesByName[cookie.Name] = cookie
|
||||
currentCookiesByName[cookie.Name+cookie.Domain] = cookie
|
||||
}
|
||||
|
||||
for _, cookie := range cookies {
|
||||
currentCookiesByName[cookie.Name] = cookie
|
||||
currentCookiesByName[cookie.Name+cookie.Domain] = cookie
|
||||
}
|
||||
|
||||
mergedCookies := make([]*http.Cookie, 0, len(currentCookiesByName))
|
||||
@@ -87,6 +108,7 @@ func (c *Cli) saveCookies(cookies []*http.Cookie) {
|
||||
}
|
||||
jsonWrite(c.cookieFile, mergedCookies)
|
||||
} else {
|
||||
mkdir(path.Dir(c.cookieFile))
|
||||
jsonWrite(c.cookieFile, cookies)
|
||||
}
|
||||
}
|
||||
@@ -98,15 +120,18 @@ func (c *Cli) loadCookies() []*http.Cookie {
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
log.Error("Failed to open %s: %s", c.cookieFile, err)
|
||||
os.Exit(1)
|
||||
log.Errorf("Failed to open %s: %s", c.cookieFile, err)
|
||||
panic(err)
|
||||
}
|
||||
cookies := make([]*http.Cookie, 0)
|
||||
cookies := []*http.Cookie{}
|
||||
err = json.Unmarshal(bytes, &cookies)
|
||||
if err != nil {
|
||||
log.Error("Failed to parse json from file %s: %s", c.cookieFile, err)
|
||||
log.Errorf("Failed to parse json from file %s: %s", c.cookieFile, err)
|
||||
}
|
||||
|
||||
if os.Getenv("LOG_TRACE") != "" && log.IsEnabledFor(logging.DEBUG) {
|
||||
log.Debugf("Loading Cookies: %s", cookies)
|
||||
}
|
||||
log.Debug("Loading Cookies: %s", cookies)
|
||||
return cookies
|
||||
}
|
||||
|
||||
@@ -118,110 +143,134 @@ func (c *Cli) put(uri string, content string) (*http.Response, error) {
|
||||
return c.makeRequestWithContent("PUT", uri, content)
|
||||
}
|
||||
|
||||
func (c *Cli) makeRequestWithContent(method string, uri string, content string) (*http.Response, error) {
|
||||
func (c *Cli) delete(uri string) (resp *http.Response, err error) {
|
||||
method := "DELETE"
|
||||
req, _ := http.NewRequest(method, uri, nil)
|
||||
log.Infof("%s %s", req.Method, req.URL.String())
|
||||
if resp, err = c.makeRequest(req); err != nil {
|
||||
return nil, 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) (resp *http.Response, err error) {
|
||||
buffer := bytes.NewBufferString(content)
|
||||
req, _ := http.NewRequest(method, uri, buffer)
|
||||
|
||||
log.Info("%s %s", req.Method, req.URL.String())
|
||||
if log.IsEnabledFor(logging.DEBUG) {
|
||||
logBuffer := bytes.NewBuffer(make([]byte, 0, len(content)))
|
||||
req.Write(logBuffer)
|
||||
log.Debug("%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 {
|
||||
log.Infof("%s %s", req.Method, req.URL.String())
|
||||
if resp, err = c.makeRequest(req); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
if resp.StatusCode == 401 {
|
||||
if err := c.CmdLogin(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req, _ = http.NewRequest(method, uri, bytes.NewBufferString(content))
|
||||
return c.makeRequest(req)
|
||||
}
|
||||
return resp, err
|
||||
}
|
||||
if resp.StatusCode == 401 {
|
||||
if err = c.CmdLogin(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req, _ = http.NewRequest(method, uri, bytes.NewBufferString(content))
|
||||
return c.makeRequest(req)
|
||||
}
|
||||
return resp, err
|
||||
}
|
||||
|
||||
func (c *Cli) get(uri string) (*http.Response, error) {
|
||||
func (c *Cli) get(uri string) (resp *http.Response, err error) {
|
||||
req, _ := http.NewRequest("GET", uri, nil)
|
||||
log.Info("%s %s", req.Method, req.URL.String())
|
||||
log.Infof("%s %s", req.Method, req.URL.String())
|
||||
if log.IsEnabledFor(logging.DEBUG) {
|
||||
logBuffer := bytes.NewBuffer(make([]byte, 0))
|
||||
req.Write(logBuffer)
|
||||
log.Debug("%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
|
||||
} else {
|
||||
if resp.StatusCode == 401 {
|
||||
if err := c.CmdLogin(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.makeRequest(req)
|
||||
}
|
||||
return resp, err
|
||||
}
|
||||
if resp.StatusCode == 401 {
|
||||
if err := c.CmdLogin(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.makeRequest(req)
|
||||
}
|
||||
return resp, err
|
||||
}
|
||||
|
||||
func (c *Cli) makeRequest(req *http.Request) (resp *http.Response, err error) {
|
||||
req.Header.Set("Accept", "application/json")
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
// 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 {
|
||||
log.Error("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
|
||||
} else {
|
||||
if resp.StatusCode < 200 || resp.StatusCode >= 300 && resp.StatusCode != 401 {
|
||||
log.Error("response status: %s", resp.Status)
|
||||
}
|
||||
}
|
||||
if resp.StatusCode < 200 || resp.StatusCode >= 300 && resp.StatusCode != 401 {
|
||||
log.Errorf("response status: %s", resp.Status)
|
||||
}
|
||||
|
||||
runtime.SetFinalizer(resp, func(r *http.Response) {
|
||||
r.Body.Close()
|
||||
})
|
||||
runtime.SetFinalizer(resp, func(r *http.Response) {
|
||||
r.Body.Close()
|
||||
})
|
||||
|
||||
if _, ok := resp.Header["Set-Cookie"]; ok {
|
||||
c.saveCookies(resp.Cookies())
|
||||
}
|
||||
if _, ok := resp.Header["Set-Cookie"]; ok {
|
||||
c.saveCookies(resp)
|
||||
}
|
||||
if log.IsEnabledFor(logging.DEBUG) {
|
||||
out, _ := httputil.DumpResponse(resp, true)
|
||||
log.Debugf("Response: %s", out)
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// GetTemplate will return the text/template for the given command name
|
||||
func (c *Cli) GetTemplate(name string) string {
|
||||
return c.getTemplate(name)
|
||||
}
|
||||
|
||||
func getLookedUpTemplate(name string, dflt string) string {
|
||||
if file, err := FindClosestParentPath(filepath.Join(".jira.d", "templates", name)); err == nil {
|
||||
return readFile(file)
|
||||
}
|
||||
if _, err := os.Stat(fmt.Sprintf("/etc/go-jira/templates/%s", name)); err == nil {
|
||||
file := fmt.Sprintf("/etc/go-jira/templates/%s", name)
|
||||
return readFile(file)
|
||||
}
|
||||
return dflt
|
||||
}
|
||||
|
||||
func (c *Cli) getTemplate(name string) string {
|
||||
if override, ok := c.opts["template"].(string); ok {
|
||||
if _, err := os.Stat(override); err == nil {
|
||||
return readFile(override)
|
||||
} else {
|
||||
if file, err := FindClosestParentPath(fmt.Sprintf(".jira.d/templates/%s", override)); err == nil {
|
||||
return readFile(file)
|
||||
}
|
||||
if dflt, ok := all_templates[override]; ok {
|
||||
return dflt
|
||||
}
|
||||
}
|
||||
if t := getLookedUpTemplate(override, allTemplates[override]); t != "" {
|
||||
return t
|
||||
}
|
||||
}
|
||||
if file, err := FindClosestParentPath(fmt.Sprintf(".jira.d/templates/%s", name)); err != nil {
|
||||
// create-bug etc are special, if we dont find it in the path
|
||||
// then just return a generic create template
|
||||
if strings.HasPrefix(name, "create-") {
|
||||
if file, err := FindClosestParentPath(".jira.d/templates/create"); err != nil {
|
||||
return all_templates["create"]
|
||||
} else {
|
||||
return readFile(file)
|
||||
}
|
||||
}
|
||||
return all_templates[name]
|
||||
} else {
|
||||
return readFile(file)
|
||||
// create-bug etc are special, if we dont find it in the path
|
||||
// then just return the create template
|
||||
if strings.HasPrefix(name, "create-") {
|
||||
return getLookedUpTemplate(name, c.getTemplate("create"))
|
||||
}
|
||||
return getLookedUpTemplate(name, allTemplates[name])
|
||||
}
|
||||
|
||||
// NoChangesFound is an error returned from when editing templates
|
||||
// and no modifications were made while editing
|
||||
type NoChangesFound struct{}
|
||||
|
||||
func (f NoChangesFound) Error() string {
|
||||
@@ -230,23 +279,35 @@ func (f NoChangesFound) Error() string {
|
||||
|
||||
func (c *Cli) editTemplate(template string, tmpFilePrefix string, templateData map[string]interface{}, templateProcessor func(string) error) error {
|
||||
|
||||
tmpdir := fmt.Sprintf("%s/.jira.d/tmp", os.Getenv("HOME"))
|
||||
tmpdir := filepath.Join(homedir(), ".jira.d", "tmp")
|
||||
if err := mkdir(tmpdir); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fh, err := ioutil.TempFile(tmpdir, tmpFilePrefix)
|
||||
if err != nil {
|
||||
log.Error("Failed to make temp file in %s: %s", tmpdir, err)
|
||||
log.Errorf("Failed to make temp file in %s: %s", tmpdir, err)
|
||||
return err
|
||||
}
|
||||
defer fh.Close()
|
||||
|
||||
tmpFileName := fmt.Sprintf("%s.yml", fh.Name())
|
||||
if err := os.Rename(fh.Name(), tmpFileName); err != nil {
|
||||
log.Error("Failed to rename %s to %s: %s", fh.Name(), fmt.Sprintf("%s.yml", fh.Name()), err)
|
||||
oldFileName := fh.Name()
|
||||
tmpFileName := fmt.Sprintf("%s.yml", oldFileName)
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
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() {
|
||||
os.Remove(tmpFileName)
|
||||
}()
|
||||
@@ -281,11 +342,11 @@ func (c *Cli) editTemplate(template string, tmpFilePrefix string, templateData m
|
||||
if editing {
|
||||
shell, _ := shellquote.Split(editor)
|
||||
shell = append(shell, tmpFileName)
|
||||
log.Debug("Running: %#v", shell)
|
||||
log.Debugf("Running: %#v", shell)
|
||||
cmd := exec.Command(shell[0], shell[1:]...)
|
||||
cmd.Stdout, cmd.Stderr, cmd.Stdin = os.Stdout, os.Stderr, os.Stdin
|
||||
if err := cmd.Run(); err != nil {
|
||||
log.Error("Failed to edit template with %s: %s", editor, err)
|
||||
log.Errorf("Failed to edit template with %s: %s", editor, err)
|
||||
if promptYN("edit again?", true) {
|
||||
continue
|
||||
}
|
||||
@@ -300,33 +361,33 @@ func (c *Cli) editTemplate(template string, tmpFilePrefix string, templateData m
|
||||
}
|
||||
|
||||
edited := make(map[string]interface{})
|
||||
if fh, err := ioutil.ReadFile(tmpFileName); err != nil {
|
||||
log.Error("Failed to read tmpfile %s: %s", tmpFileName, err)
|
||||
var data []byte
|
||||
if data, err = ioutil.ReadFile(tmpFileName); err != nil {
|
||||
log.Errorf("Failed to read tmpfile %s: %s", tmpFileName, err)
|
||||
if editing && promptYN("edit again?", true) {
|
||||
continue
|
||||
}
|
||||
return err
|
||||
} else {
|
||||
if err := yaml.Unmarshal(fh, &edited); err != nil {
|
||||
log.Error("Failed to parse YAML: %s", err)
|
||||
if editing && promptYN("edit again?", true) {
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
if err := yaml.Unmarshal(data, &edited); err != nil {
|
||||
log.Errorf("Failed to parse YAML: %s", err)
|
||||
if editing && promptYN("edit again?", true) {
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
if fixed, err := yamlFixup(edited); err != nil {
|
||||
var fixed interface{}
|
||||
if fixed, err = yamlFixup(edited); err != nil {
|
||||
return err
|
||||
} else {
|
||||
edited = fixed.(map[string]interface{})
|
||||
}
|
||||
edited = fixed.(map[string]interface{})
|
||||
|
||||
// if you want to abort editing a jira issue then
|
||||
// you can add the "abort: true" flag to the document
|
||||
// and we will abort now
|
||||
if val, ok := edited["abort"].(bool); ok && val {
|
||||
log.Info("abort flag found in template, quiting")
|
||||
log.Infof("abort flag found in template, quiting")
|
||||
return fmt.Errorf("abort flag found in template, quiting")
|
||||
}
|
||||
|
||||
@@ -336,7 +397,7 @@ func (c *Cli) editTemplate(template string, tmpFilePrefix string, templateData m
|
||||
for k := range f {
|
||||
if _, ok := mf.(map[string]interface{})[k]; !ok {
|
||||
err := fmt.Errorf("Field %s is not editable", k)
|
||||
log.Error("%s", err)
|
||||
log.Errorf("%s", err)
|
||||
if editing && promptYN("edit again?", true) {
|
||||
continue
|
||||
}
|
||||
@@ -352,7 +413,7 @@ func (c *Cli) editTemplate(template string, tmpFilePrefix string, templateData m
|
||||
}
|
||||
|
||||
if err := templateProcessor(json); err != nil {
|
||||
log.Error("%s", err)
|
||||
log.Errorf("%s", err)
|
||||
if editing && promptYN("edit again?", true) {
|
||||
continue
|
||||
}
|
||||
@@ -362,6 +423,7 @@ func (c *Cli) editTemplate(template string, tmpFilePrefix string, templateData m
|
||||
return nil
|
||||
}
|
||||
|
||||
// Browse will open up your default browser to the provided issue
|
||||
func (c *Cli) Browse(issue string) error {
|
||||
if val, ok := c.opts["browse"].(bool); ok && val {
|
||||
if runtime.GOOS == "darwin" {
|
||||
@@ -373,6 +435,7 @@ func (c *Cli) Browse(issue string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SaveData will write out the yaml formated --saveFile file with provided data
|
||||
func (c *Cli) SaveData(data interface{}) error {
|
||||
if val, ok := c.opts["saveFile"].(string); ok && val != "" {
|
||||
yamlWrite(val, data)
|
||||
@@ -380,29 +443,49 @@ func (c *Cli) SaveData(data interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Cli) ViewIssue(issue string) (interface{}, error) {
|
||||
uri := fmt.Sprintf("%s/rest/api/2/issue/%s", c.endpoint, issue)
|
||||
data, err := responseToJson(c.get(uri))
|
||||
// ViewIssueWorkLogs gets the worklog data for the given issue
|
||||
func (c *Cli) ViewIssueWorkLogs(issue string) (interface{}, error) {
|
||||
uri := fmt.Sprintf("%s/rest/api/2/issue/%s/worklog", c.endpoint, issue)
|
||||
data, err := responseToJSON(c.get(uri))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return data, nil
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// ViewIssue will return the details for the given issue id
|
||||
func (c *Cli) ViewIssue(issue string) (interface{}, error) {
|
||||
uri := fmt.Sprintf("%s/rest/api/2/issue/%s", c.endpoint, issue)
|
||||
if x := c.expansions(); len(x) > 0 {
|
||||
uri = fmt.Sprintf("%s?expand=%s", uri, strings.Join(x, ","))
|
||||
}
|
||||
|
||||
data, err := responseToJSON(c.get(uri))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// FindIssues will return a list of issues that match the given options.
|
||||
// If the "query" option is undefined it will generate a JQL query
|
||||
// using any/all of the provide options: project, component, assignee,
|
||||
// issuetype, watcher, reporter, sort
|
||||
// Further it will restrict the fields being extracted from the jira
|
||||
// response with the 'queryfields' option
|
||||
func (c *Cli) FindIssues() (interface{}, error) {
|
||||
var query string
|
||||
var ok bool
|
||||
// project = BAKERY and status not in (Resolved, Closed)
|
||||
if query, ok = c.opts["query"].(string); !ok {
|
||||
qbuff := bytes.NewBufferString("resolution = unresolved")
|
||||
if project, ok := c.opts["project"]; !ok {
|
||||
var project string
|
||||
if project, ok = c.opts["project"].(string); !ok {
|
||||
err := fmt.Errorf("Missing required arguments, either 'query' or 'project' are required")
|
||||
log.Error("%s", err)
|
||||
log.Errorf("%s", err)
|
||||
return nil, err
|
||||
} else {
|
||||
qbuff.WriteString(fmt.Sprintf(" AND project = '%s'", project))
|
||||
}
|
||||
qbuff.WriteString(fmt.Sprintf(" AND project = '%s'", project))
|
||||
|
||||
if component, ok := c.opts["component"]; ok {
|
||||
qbuff.WriteString(fmt.Sprintf(" AND component = '%s'", component))
|
||||
@@ -431,11 +514,9 @@ func (c *Cli) FindIssues() (interface{}, error) {
|
||||
query = qbuff.String()
|
||||
}
|
||||
|
||||
fields := make([]string, 0)
|
||||
fields := []string{"summary"}
|
||||
if qf, ok := c.opts["queryfields"].(string); ok {
|
||||
fields = strings.Split(qf, ",")
|
||||
} else {
|
||||
fields = append(fields, "summary")
|
||||
}
|
||||
|
||||
json, err := jsonEncode(map[string]interface{}{
|
||||
@@ -443,31 +524,51 @@ func (c *Cli) FindIssues() (interface{}, error) {
|
||||
"startAt": "0",
|
||||
"maxResults": c.opts["max_results"],
|
||||
"fields": fields,
|
||||
"expand": c.expansions(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
uri := fmt.Sprintf("%s/rest/api/2/search", c.endpoint)
|
||||
if data, err := responseToJson(c.post(uri, json)); err != nil {
|
||||
var data interface{}
|
||||
if data, err = responseToJSON(c.post(uri, json)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return data, nil
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// GetOptString will extract the string from the Cli object options
|
||||
// otherwise return the provided default
|
||||
func (c *Cli) GetOptString(optName string, dflt string) string {
|
||||
return c.getOptString(optName, dflt)
|
||||
}
|
||||
|
||||
func (c *Cli) getOptString(optName string, dflt string) string {
|
||||
if val, ok := c.opts[optName].(string); ok {
|
||||
return val
|
||||
} else {
|
||||
return dflt
|
||||
}
|
||||
return dflt
|
||||
}
|
||||
|
||||
// GetOptBool will extract the boolean value from the Client object options
|
||||
// otherwise return the provided default\
|
||||
func (c *Cli) GetOptBool(optName string, dflt bool) bool {
|
||||
return c.getOptBool(optName, dflt)
|
||||
}
|
||||
|
||||
func (c *Cli) getOptBool(optName string, dflt bool) bool {
|
||||
if val, ok := c.opts[optName].(bool); ok {
|
||||
return val
|
||||
} else {
|
||||
return dflt
|
||||
}
|
||||
return dflt
|
||||
}
|
||||
|
||||
// expansions returns a comma-separated list of values for field expansion
|
||||
func (c *Cli) expansions() []string {
|
||||
var expansions []string
|
||||
if x, ok := c.opts["expand"].(string); ok {
|
||||
expansions = strings.Split(x, ",")
|
||||
}
|
||||
return expansions
|
||||
}
|
||||
|
||||
+466
-178
File diff suppressed because it is too large
Load Diff
@@ -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"`
|
||||
}
|
||||
+132
-27
@@ -5,8 +5,8 @@ import (
|
||||
"fmt"
|
||||
"github.com/Netflix-Skunkworks/go-jira"
|
||||
"github.com/coryb/optigo"
|
||||
"github.com/op/go-logging"
|
||||
"gopkg.in/coryb/yaml.v2"
|
||||
"gopkg.in/op/go-logging.v1"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
@@ -14,12 +14,16 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
log = logging.MustGetLogger("jira")
|
||||
format = "%{color}%{time:2006-01-02T15:04:05.000Z07:00} %{level:-5s} [%{shortfile}]%{color:reset} %{message}"
|
||||
log = logging.MustGetLogger("jira")
|
||||
defaultFormat = "%{color}%{time:2006-01-02T15:04:05.000Z07:00} %{level:-5s} [%{shortfile}]%{color:reset} %{message}"
|
||||
)
|
||||
|
||||
func main() {
|
||||
logBackend := logging.NewLogBackend(os.Stderr, "", 0)
|
||||
format := os.Getenv("JIRA_LOG_FORMAT")
|
||||
if format == "" {
|
||||
format = defaultFormat
|
||||
}
|
||||
logging.SetBackend(
|
||||
logging.NewBackendFormatter(
|
||||
logBackend,
|
||||
@@ -31,7 +35,7 @@ func main() {
|
||||
user := os.Getenv("USER")
|
||||
home := os.Getenv("HOME")
|
||||
defaultQueryFields := "summary,created,updated,priority,status,reporter,assignee"
|
||||
defaultSort := "priority asc, created"
|
||||
defaultSort := "priority asc, key"
|
||||
defaultMaxResults := 500
|
||||
|
||||
usage := func(ok bool) {
|
||||
@@ -52,11 +56,14 @@ func main() {
|
||||
Usage:
|
||||
jira (ls|list) <Query Options>
|
||||
jira view ISSUE
|
||||
jira worklog ISSUE
|
||||
jira add worklog ISSUE <Worklog Options>
|
||||
jira edit [--noedit] <Edit Options> [ISSUE | <Query Options>]
|
||||
jira create [--noedit] [-p PROJECT] <Create Options>
|
||||
jira DUPLICATE dups ISSUE
|
||||
jira BLOCKER blocks ISSUE
|
||||
jira watch ISSUE [-w WATCHER]
|
||||
jira vote ISSUE [--down]
|
||||
jira watch ISSUE [-w WATCHER] [--remove]
|
||||
jira (trans|transition) TRANSITION ISSUE [--noedit] <Edit Options>
|
||||
jira ack ISSUE [--edit] <Edit Options>
|
||||
jira close ISSUE [--edit] <Edit Options>
|
||||
@@ -64,6 +71,10 @@ Usage:
|
||||
jira reopen ISSUE [--edit] <Edit Options>
|
||||
jira start 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 (set,add,remove) labels ISSUE [LABEL] ...
|
||||
jira take ISSUE
|
||||
@@ -72,12 +83,15 @@ Usage:
|
||||
jira issuelinktypes
|
||||
jira transmeta ISSUE
|
||||
jira editmeta ISSUE
|
||||
jira add component [-p PROJECT] NAME DESCRIPTION LEAD
|
||||
jira components [-p PROJECT]
|
||||
jira issuetypes [-p PROJECT]
|
||||
jira createmeta [-p PROJECT] [-i ISSUETYPE]
|
||||
jira transitions ISSUE
|
||||
jira export-templates [-d DIR] [-t template]
|
||||
jira (b|browse) ISSUE
|
||||
jira login
|
||||
jira logout
|
||||
jira request [-M METHOD] URI [DATA]
|
||||
jira ISSUE
|
||||
|
||||
@@ -113,6 +127,10 @@ Create Options:
|
||||
-m --comment=COMMENT Comment message for transition
|
||||
-o --override=KEY=VAL Set custom key/value pairs
|
||||
|
||||
Worklog Options:
|
||||
-T --time-spent=TIMESPENT Time spent working on issue
|
||||
-m --comment=COMMENT Comment message for worklog
|
||||
|
||||
Command Options:
|
||||
-d --directory=DIR Directory to export templates to (default: %s)
|
||||
`, user, defaultQueryFields, defaultMaxResults, defaultSort, user, fmt.Sprintf("%s/.jira.d/templates", home))
|
||||
@@ -137,9 +155,17 @@ Command Options:
|
||||
"reopen": "reopen",
|
||||
"start": "start",
|
||||
"stop": "stop",
|
||||
"todo": "todo",
|
||||
"backlog": "backlog",
|
||||
"done": "done",
|
||||
"prog": "in-progress",
|
||||
"progress": "in-progress",
|
||||
"in-progress": "in-progress",
|
||||
"comment": "comment",
|
||||
"label": "labels",
|
||||
"labels": "labels",
|
||||
"component": "component",
|
||||
"components": "components",
|
||||
"take": "take",
|
||||
"assign": "assign",
|
||||
"give": "assign",
|
||||
@@ -153,8 +179,12 @@ Command Options:
|
||||
"export-templates": "export-templates",
|
||||
"browse": "browse",
|
||||
"login": "login",
|
||||
"logout": "logout",
|
||||
"req": "request",
|
||||
"request": "request",
|
||||
"vote": "vote",
|
||||
"worklog": "worklog",
|
||||
"addworklog": "addworklog",
|
||||
}
|
||||
|
||||
defaults := map[string]interface{}{
|
||||
@@ -194,8 +224,10 @@ Command Options:
|
||||
"a|assignee=s": setopt,
|
||||
"i|issuetype=s": setopt,
|
||||
"w|watcher=s": setopt,
|
||||
"remove": setopt,
|
||||
"r|reporter=s": setopt,
|
||||
"f|queryfields=s": setopt,
|
||||
"x|expand=s": setopt,
|
||||
"s|sort=s": setopt,
|
||||
"l|limit|max_results=i": setopt,
|
||||
"o|override=s%": &opts,
|
||||
@@ -205,11 +237,13 @@ Command Options:
|
||||
"d|dir|directory=s": setopt,
|
||||
"M|method=s": setopt,
|
||||
"S|saveFile=s": setopt,
|
||||
"T|time-spent=s": setopt,
|
||||
"Q|quiet": setopt,
|
||||
"down": setopt,
|
||||
})
|
||||
|
||||
if err := op.ProcessAll(os.Args[1:]); err != nil {
|
||||
log.Error("%s", err)
|
||||
log.Errorf("%s", err)
|
||||
usage(false)
|
||||
}
|
||||
args := op.Args
|
||||
@@ -250,36 +284,36 @@ Command Options:
|
||||
// apply defaults
|
||||
for k, v := range defaults {
|
||||
if _, ok := opts[k]; !ok {
|
||||
log.Debug("Setting %q to %#v from defaults", k, v)
|
||||
log.Debugf("Setting %q to %#v from defaults", k, v)
|
||||
opts[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
log.Debug("opts: %v", opts)
|
||||
log.Debug("args: %v", args)
|
||||
log.Debugf("opts: %v", opts)
|
||||
log.Debugf("args: %v", args)
|
||||
|
||||
if _, ok := opts["endpoint"]; !ok {
|
||||
log.Error("endpoint option required. Either use --endpoint or set a endpoint option in your ~/.jira.d/config.yml file")
|
||||
log.Errorf("endpoint option required. Either use --endpoint or set a endpoint option in your ~/.jira.d/config.yml file")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
c := jira.New(opts)
|
||||
|
||||
log.Debug("opts: %s", opts)
|
||||
log.Debugf("opts: %s", opts)
|
||||
|
||||
setEditing := func(dflt bool) {
|
||||
log.Debug("Default Editing: %t", dflt)
|
||||
log.Debugf("Default Editing: %t", dflt)
|
||||
if dflt {
|
||||
if val, ok := opts["noedit"].(bool); ok && val {
|
||||
log.Debug("Setting edit = false")
|
||||
log.Debugf("Setting edit = false")
|
||||
opts["edit"] = false
|
||||
} else {
|
||||
log.Debug("Setting edit = true")
|
||||
log.Debugf("Setting edit = true")
|
||||
opts["edit"] = true
|
||||
}
|
||||
} else {
|
||||
if _, ok := opts["edit"].(bool); !ok {
|
||||
log.Debug("Setting edit = %t", dflt)
|
||||
log.Debugf("Setting edit = %t", dflt)
|
||||
opts["edit"] = dflt
|
||||
}
|
||||
}
|
||||
@@ -287,7 +321,7 @@ Command Options:
|
||||
|
||||
requireArgs := func(count int) {
|
||||
if len(args) < count {
|
||||
log.Error("Not enough arguments. %d required, %d provided", count, len(args))
|
||||
log.Errorf("Not enough arguments. %d required, %d provided", count, len(args))
|
||||
usage(false)
|
||||
}
|
||||
}
|
||||
@@ -296,6 +330,8 @@ Command Options:
|
||||
switch command {
|
||||
case "login":
|
||||
err = c.CmdLogin()
|
||||
case "logout":
|
||||
err = c.CmdLogout()
|
||||
case "fields":
|
||||
err = c.CmdFields()
|
||||
case "list":
|
||||
@@ -343,14 +379,32 @@ Command Options:
|
||||
requireArgs(2)
|
||||
err = c.CmdBlocks(args[0], args[1])
|
||||
case "dups":
|
||||
setEditing(true)
|
||||
requireArgs(2)
|
||||
if err = c.CmdDups(args[0], args[1]); err == nil {
|
||||
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":
|
||||
requireArgs(1)
|
||||
err = c.CmdWatch(args[0])
|
||||
watcher := c.GetOptString("watcher", opts["user"].(string))
|
||||
remove := c.GetOptBool("remove", false)
|
||||
err = c.CmdWatch(args[0], watcher, remove)
|
||||
case "transition":
|
||||
requireArgs(2)
|
||||
setEditing(true)
|
||||
@@ -379,6 +433,22 @@ Command Options:
|
||||
requireArgs(1)
|
||||
setEditing(false)
|
||||
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":
|
||||
requireArgs(1)
|
||||
setEditing(true)
|
||||
@@ -389,6 +459,23 @@ Command Options:
|
||||
issue := args[1]
|
||||
labels := args[2:]
|
||||
err = c.CmdLabels(action, issue, labels)
|
||||
case "component":
|
||||
requireArgs(2)
|
||||
action := args[0]
|
||||
project := opts["project"].(string)
|
||||
name := args[1]
|
||||
var lead string
|
||||
var description string
|
||||
if len(args) > 2 {
|
||||
description = args[2]
|
||||
}
|
||||
if len(args) > 3 {
|
||||
lead = args[2]
|
||||
}
|
||||
err = c.CmdComponent(action, project, name, description, lead)
|
||||
case "components":
|
||||
project := opts["project"].(string)
|
||||
err = c.CmdComponents(project)
|
||||
case "take":
|
||||
requireArgs(1)
|
||||
err = c.CmdAssign(args[0], opts["user"].(string))
|
||||
@@ -404,6 +491,22 @@ Command Options:
|
||||
case "view":
|
||||
requireArgs(1)
|
||||
err = c.CmdView(args[0])
|
||||
case "worklog":
|
||||
if len(args) > 0 && args[0] == "add" {
|
||||
setEditing(true)
|
||||
requireArgs(2)
|
||||
err = c.CmdWorklog(args[0], args[1])
|
||||
} else {
|
||||
requireArgs(1)
|
||||
err = c.CmdWorklogs(args[0])
|
||||
}
|
||||
case "vote":
|
||||
requireArgs(1)
|
||||
if val, ok := opts["down"]; ok {
|
||||
err = c.CmdVote(args[0], !val.(bool))
|
||||
} else {
|
||||
err = c.CmdVote(args[0], true)
|
||||
}
|
||||
case "request":
|
||||
requireArgs(1)
|
||||
data := ""
|
||||
@@ -412,12 +515,12 @@ Command Options:
|
||||
}
|
||||
err = c.CmdRequest(args[0], data)
|
||||
default:
|
||||
log.Error("Unknown command %s", command)
|
||||
log.Errorf("Unknown command %s", command)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Error("%s", err)
|
||||
log.Errorf("%s", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
os.Exit(0)
|
||||
@@ -425,8 +528,10 @@ Command Options:
|
||||
|
||||
func parseYaml(file string, opts map[string]interface{}) {
|
||||
if fh, err := ioutil.ReadFile(file); err == nil {
|
||||
log.Debug("Found Config file: %s", file)
|
||||
yaml.Unmarshal(fh, &opts)
|
||||
log.Debugf("Found Config file: %s", file)
|
||||
if err := yaml.Unmarshal(fh, &opts); err != nil {
|
||||
log.Errorf("Unable to parse %s: %s", file, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -454,10 +559,10 @@ func loadConfigs(opts map[string]interface{}) {
|
||||
populateEnv(opts)
|
||||
paths := jira.FindParentPaths(".jira.d/config.yml")
|
||||
// prepend
|
||||
paths = append([]string{"/etc/go-jira.yml"}, paths...)
|
||||
paths = append(paths, "/etc/go-jira.yml")
|
||||
|
||||
// iterate paths in reverse
|
||||
for i := len(paths) - 1; i >= 0; i-- {
|
||||
for i := 0; i < len(paths); i++ {
|
||||
file := paths[i]
|
||||
if stat, err := os.Stat(file); err == nil {
|
||||
tmp := make(map[string]interface{})
|
||||
@@ -465,21 +570,21 @@ func loadConfigs(opts map[string]interface{}) {
|
||||
if stat.Mode()&0111 == 0 {
|
||||
parseYaml(file, tmp)
|
||||
} else {
|
||||
log.Debug("Found Executable Config file: %s", file)
|
||||
log.Debugf("Found Executable Config file: %s", file)
|
||||
// it is executable, so run it and try to parse the output
|
||||
cmd := exec.Command(file)
|
||||
stdout := bytes.NewBufferString("")
|
||||
cmd.Stdout = stdout
|
||||
cmd.Stderr = bytes.NewBufferString("")
|
||||
if err := cmd.Run(); err != nil {
|
||||
log.Error("%s is exectuable, but it failed to execute: %s\n%s", file, err, cmd.Stderr)
|
||||
log.Errorf("%s is exectuable, but it failed to execute: %s\n%s", file, err, cmd.Stderr)
|
||||
os.Exit(1)
|
||||
}
|
||||
yaml.Unmarshal(stdout.Bytes(), &tmp)
|
||||
}
|
||||
for k, v := range tmp {
|
||||
if _, ok := opts[k]; !ok {
|
||||
log.Debug("Setting %q to %#v from %s", k, v, file)
|
||||
log.Debugf("Setting %q to %#v from %s", k, v, file)
|
||||
opts[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
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
+521
@@ -0,0 +1,521 @@
|
||||
#!/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 86
|
||||
|
||||
# reset login
|
||||
RUNS $jira logout
|
||||
echo "gojira123" | RUNS $jira login
|
||||
|
||||
# cleanup from previous failed test executions
|
||||
($jira ls | awk -F: '{print $1}' | while read issue; do ../jira done $issue; done) | sed 's/^/# CLEANUP: /g'
|
||||
|
||||
###############################################################################
|
||||
## Create an issue
|
||||
###############################################################################
|
||||
RUNS $jira create -o summary=summary -o description=description --noedit --saveFile issue.props
|
||||
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
|
||||
|
||||
###############################################################################
|
||||
## List all issues, using the table template
|
||||
###############################################################################
|
||||
|
||||
RUNS $jira ls --template table
|
||||
DIFF <<EOF
|
||||
+----------------+---------------------------------------------------------+--------------+--------------+------------+--------------+--------------+
|
||||
| Issue | Summary | Priority | Status | Age | Reporter | Assignee |
|
||||
+----------------+---------------------------------------------------------+--------------+--------------+------------+--------------+--------------+
|
||||
| $(printf %-14s $issue) | summary | Medium | To Do | a minute | gojira | gojira |
|
||||
+----------------+---------------------------------------------------------+--------------+--------------+------------+--------------+--------------+
|
||||
EOF
|
||||
|
||||
###############################################################################
|
||||
## Try to close the issue, bug Basic projects do not allow that state
|
||||
###############################################################################
|
||||
|
||||
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
+43
@@ -0,0 +1,43 @@
|
||||
#!/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 8
|
||||
|
||||
# reset login
|
||||
RUNS $jira logout
|
||||
echo "gojira123" | RUNS $jira login
|
||||
|
||||
# cleanup from previous failed test executions
|
||||
($jira ls | awk -F: '{print $1}' | while read issue; do ../jira done $issue; done) | sed 's/^/# CLEANUP: /g'
|
||||
|
||||
###############################################################################
|
||||
## Create an issue
|
||||
###############################################################################
|
||||
RUNS $jira create -o summary=summary -o description=description --noedit --saveFile issue.props
|
||||
issue=$(awk '/issue/{print $2}' issue.props)
|
||||
|
||||
DIFF <<EOF
|
||||
OK $issue http://localhost:8080/browse/$issue
|
||||
EOF
|
||||
|
||||
###############################################################################
|
||||
## Add a worklog to an issue
|
||||
###############################################################################
|
||||
RUNS $jira add worklog $issue --comment "work is hard" --time-spent "1h 12m" --noedit
|
||||
DIFF <<EOF
|
||||
OK $issue http://localhost:8080/browse/$issue
|
||||
EOF
|
||||
|
||||
###############################################################################
|
||||
## Verify worklog got added to issue
|
||||
###############################################################################
|
||||
RUNS $jira worklog $issue
|
||||
DIFF <<EOF
|
||||
- # gojira, a minute ago
|
||||
comment: work is hard
|
||||
timeSpent: 1h 12m
|
||||
|
||||
EOF
|
||||
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
|
||||
|
||||
+129
-56
@@ -1,57 +1,87 @@
|
||||
package jira
|
||||
|
||||
var all_templates = map[string]string{
|
||||
"debug": default_debug_template,
|
||||
"fields": default_debug_template,
|
||||
"editmeta": default_debug_template,
|
||||
"transmeta": default_debug_template,
|
||||
"createmeta": default_debug_template,
|
||||
"issuelinktypes": default_debug_template,
|
||||
"list": default_list_template,
|
||||
"table": default_table_template,
|
||||
"view": default_view_template,
|
||||
"edit": default_edit_template,
|
||||
"transitions": default_transitions_template,
|
||||
"issuetypes": default_issuetypes_template,
|
||||
"create": default_create_template,
|
||||
"comment": default_comment_template,
|
||||
"transition": default_transition_template,
|
||||
"request": default_debug_template,
|
||||
var allTemplates = map[string]string{
|
||||
"debug": defaultDebugTemplate,
|
||||
"fields": defaultDebugTemplate,
|
||||
"editmeta": defaultDebugTemplate,
|
||||
"transmeta": defaultDebugTemplate,
|
||||
"createmeta": defaultDebugTemplate,
|
||||
"issuelinktypes": defaultDebugTemplate,
|
||||
"list": defaultListTemplate,
|
||||
"table": defaultTableTemplate,
|
||||
"view": defaultViewTemplate,
|
||||
"edit": defaultEditTemplate,
|
||||
"transitions": defaultTransitionsTemplate,
|
||||
"components": defaultComponentsTemplate,
|
||||
"issuetypes": defaultIssuetypesTemplate,
|
||||
"create": defaultCreateTemplate,
|
||||
"comment": defaultCommentTemplate,
|
||||
"transition": defaultTransitionTemplate,
|
||||
"request": defaultDebugTemplate,
|
||||
"worklog": defaultWorklogTemplate,
|
||||
"worklogs": defaultWorklogsTemplate,
|
||||
}
|
||||
|
||||
const default_debug_template = "{{ . | toJson}}\n"
|
||||
const defaultDebugTemplate = "{{ . | toJson}}\n"
|
||||
|
||||
const default_list_template = "{{ range .issues }}{{ .key | append \":\" | printf \"%-12s\"}} {{ .fields.summary }}\n{{ end }}"
|
||||
const defaultListTemplate = "{{ range .issues }}{{ .key | append \":\" | printf \"%-12s\"}} {{ .fields.summary }}\n{{ end }}"
|
||||
|
||||
const default_table_template = `+{{ "-" | rep 16 }}+{{ "-" | rep 57 }}+{{ "-" | rep 14 }}+{{ "-" | rep 14 }}+{{ "-" | rep 12 }}+{{ "-" | rep 14 }}+{{ "-" | rep 14 }}+
|
||||
const defaultTableTemplate = `+{{ "-" | rep 16 }}+{{ "-" | rep 57 }}+{{ "-" | rep 14 }}+{{ "-" | rep 14 }}+{{ "-" | rep 12 }}+{{ "-" | rep 14 }}+{{ "-" | rep 14 }}+
|
||||
| {{ "Issue" | printf "%-14s" }} | {{ "Summary" | printf "%-55s" }} | {{ "Priority" | printf "%-12s" }} | {{ "Status" | printf "%-12s" }} | {{ "Age" | printf "%-10s" }} | {{ "Reporter" | printf "%-12s" }} | {{ "Assignee" | printf "%-12s" }} |
|
||||
+{{ "-" | rep 16 }}+{{ "-" | rep 57 }}+{{ "-" | rep 14 }}+{{ "-" | rep 14 }}+{{ "-" | rep 12 }}+{{ "-" | rep 14 }}+{{ "-" | rep 14 }}+
|
||||
{{ range .issues }}| {{ .key | printf "%-14s"}} | {{ .fields.summary | abbrev 55 | printf "%-55s" }} | {{.fields.priority.name | printf "%-12s" }} | {{.fields.status.name | printf "%-12s" }} | {{.fields.created | age | printf "%-10s" }} | {{if .fields.reporter}}{{ .fields.reporter.name | printf "%-12s"}}{{else}}<unassigned>{{end}} | {{if .fields.assignee }}{{.fields.assignee.name | printf "%-12s" }}{{else}}<unassigned>{{end}} |
|
||||
{{ end }}+{{ "-" | rep 16 }}+{{ "-" | rep 57 }}+{{ "-" | rep 14 }}+{{ "-" | rep 14 }}+{{ "-" | rep 12 }}+{{ "-" | rep 14 }}+{{ "-" | rep 14 }}+
|
||||
{{ range .issues -}}
|
||||
| {{ .key | printf "%-14s"}} | {{ .fields.summary | abbrev 55 | printf "%-55s" }} | {{.fields.priority.name | printf "%-12s" }} | {{.fields.status.name | printf "%-12s" }} | {{.fields.created | age | printf "%-10s" }} | {{if .fields.reporter}}{{ .fields.reporter.name | printf "%-12s"}}{{else}}<unassigned>{{end}} | {{if .fields.assignee }}{{.fields.assignee.name | printf "%-12s" }}{{else}}<unassigned> {{end}} |
|
||||
{{ end -}}
|
||||
+{{ "-" | rep 16 }}+{{ "-" | rep 57 }}+{{ "-" | rep 14 }}+{{ "-" | rep 14 }}+{{ "-" | rep 12 }}+{{ "-" | rep 14 }}+{{ "-" | rep 14 }}+
|
||||
`
|
||||
|
||||
const default_view_template = `issue: {{ .key }}
|
||||
created: {{ .fields.created }}
|
||||
const defaultViewTemplate = `{{/* view template */ -}}
|
||||
issue: {{ .key }}
|
||||
{{if .fields.created -}}
|
||||
created: {{ .fields.created | age }} ago
|
||||
{{end -}}
|
||||
{{if .fields.status -}}
|
||||
status: {{ .fields.status.name }}
|
||||
{{end -}}
|
||||
summary: {{ .fields.summary }}
|
||||
project: {{ .fields.project.key }}
|
||||
{{if .fields.components -}}
|
||||
components: {{ range .fields.components }}{{ .name }} {{end}}
|
||||
{{end -}}
|
||||
{{if .fields.issuetype -}}
|
||||
issuetype: {{ .fields.issuetype.name }}
|
||||
assignee: {{ if .fields.assignee }}{{ .fields.assignee.name }}{{end}}
|
||||
{{end -}}
|
||||
{{if .fields.assignee -}}
|
||||
assignee: {{ .fields.assignee.name }}
|
||||
{{end -}}
|
||||
reporter: {{ if .fields.reporter }}{{ .fields.reporter.name }}{{end}}
|
||||
{{if .fields.customfield_10110 -}}
|
||||
watchers: {{ range .fields.customfield_10110 }}{{ .name }} {{end}}
|
||||
{{end -}}
|
||||
{{if .fields.issuelinks -}}
|
||||
blockers: {{ range .fields.issuelinks }}{{if .outwardIssue}}{{ .outwardIssue.key }}[{{.outwardIssue.fields.status.name}}]{{end}}{{end}}
|
||||
depends: {{ range .fields.issuelinks }}{{if .inwardIssue}}{{ .inwardIssue.key }}[{{.inwardIssue.fields.status.name}}]{{end}}{{end}}
|
||||
{{end -}}
|
||||
{{if .fields.priority -}}
|
||||
priority: {{ .fields.priority.name }}
|
||||
{{end -}}
|
||||
{{if .fields.votes -}}
|
||||
votes: {{ .fields.votes.votes}}
|
||||
{{end -}}
|
||||
{{if .fields.labels -}}
|
||||
labels: {{ join ", " .fields.labels }}
|
||||
{{end -}}
|
||||
description: |
|
||||
{{ or .fields.description "" | indent 2 }}
|
||||
|
||||
{{if .fields.comment.comments}}
|
||||
comments:
|
||||
{{ range .fields.comment.comments }} - | # {{.author.name}} at {{.created}}
|
||||
{{ range .fields.comment.comments }} - | # {{.author.name}}, {{.created | age}} ago
|
||||
{{ or .body "" | indent 4}}
|
||||
{{end}}
|
||||
{{end -}}
|
||||
`
|
||||
const default_edit_template = `# issue: {{ .key }}
|
||||
const defaultEditTemplate = `{{/* edit template */ -}}
|
||||
# issue: {{ .key }}
|
||||
update:
|
||||
comment:
|
||||
- add:
|
||||
@@ -75,73 +105,116 @@ fields:
|
||||
description: |~
|
||||
{{ or .overrides.description (or .fields.description "") | indent 4 }}
|
||||
# comments:
|
||||
# {{ range .fields.comment.comments }} - | # {{.author.name}} at {{.created}}
|
||||
# {{ range .fields.comment.comments }} - | # {{.author.name}}, {{.created | age}} ago
|
||||
# {{ or .body "" | indent 4 | comment}}
|
||||
# {{end}}
|
||||
`
|
||||
const default_transitions_template = `{{ range .transitions }}{{.id }}: {{.name}}
|
||||
const defaultTransitionsTemplate = `{{ range .transitions }}{{.id }}: {{.name}}
|
||||
{{end}}`
|
||||
|
||||
const default_issuetypes_template = `{{ range .projects }}{{ range .issuetypes }}{{color "+bh"}}{{.name | append ":" | printf "%-13s" }}{{color "reset"}} {{.description}}
|
||||
const defaultComponentsTemplate = `{{ range . }}{{.id }}: {{.name}}
|
||||
{{end}}`
|
||||
|
||||
const defaultIssuetypesTemplate = `{{ range .projects }}{{ range .issuetypes }}{{color "+bh"}}{{.name | append ":" | printf "%-13s" }}{{color "reset"}} {{.description}}
|
||||
{{end}}{{end}}`
|
||||
|
||||
const default_create_template = `fields:
|
||||
const defaultCreateTemplate = `{{/* create template */ -}}
|
||||
fields:
|
||||
project:
|
||||
key: {{ .overrides.project }}
|
||||
key: {{ or .overrides.project "" }}
|
||||
issuetype:
|
||||
name: {{ .overrides.issuetype }}
|
||||
summary: {{ or .overrides.summary "" }}
|
||||
name: {{ or .overrides.issuetype "" }}
|
||||
summary: {{ or .overrides.summary "" }}{{if .meta.fields.priority.allowedValues}}
|
||||
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 "")}}
|
||||
- name: {{ . }}{{end}}
|
||||
- name: {{ . }}{{end}}{{end}}
|
||||
description: |~
|
||||
{{ or .overrides.description "" | indent 4 }}
|
||||
{{ or .overrides.description "" | indent 4 }}{{if .meta.fields.assignee}}
|
||||
assignee:
|
||||
name: {{ or .overrides.assignee "" }}
|
||||
name: {{ or .overrides.assignee "" }}{{end}}{{if .meta.fields.reporter}}
|
||||
reporter:
|
||||
name: {{ or .overrides.reporter .overrides.user }}
|
||||
name: {{ or .overrides.reporter .overrides.user }}{{end}}{{if .meta.fields.customfield_10110}}
|
||||
# watchers
|
||||
customfield_10110: {{ range split "," (or .overrides.watchers "")}}
|
||||
- name: {{.}}{{end}}
|
||||
- name:
|
||||
`
|
||||
- name:{{end}}`
|
||||
|
||||
const default_comment_template = `body: |~
|
||||
const defaultCommentTemplate = `body: |~
|
||||
{{ or .overrides.comment "" | indent 2 }}
|
||||
`
|
||||
|
||||
const default_transition_template = `update:
|
||||
const defaultTransitionTemplate = `{{/* transition template */ -}}
|
||||
update:
|
||||
comment:
|
||||
- add:
|
||||
body: |~
|
||||
{{ or .overrides.comment "" | indent 10 }}
|
||||
fields:{{if .meta.fields.assignee}}
|
||||
fields:
|
||||
{{- if .meta.fields.assignee}}
|
||||
assignee:
|
||||
name: {{if .overrides.assignee}}{{.overrides.assignee}}{{else}}{{if .fields.assignee}}{{.fields.assignee.name}}{{end}}{{end}}{{end}}{{if .meta.fields.components}}
|
||||
name: {{if .overrides.assignee}}{{.overrides.assignee}}{{else}}{{if .fields.assignee}}{{.fields.assignee.name}}{{end}}{{end}}
|
||||
{{- end -}}
|
||||
{{if .meta.fields.components}}
|
||||
components: # Values: {{ range .meta.fields.components.allowedValues }}{{.name}}, {{end}}{{if .overrides.components }}{{ range (split "," .overrides.components)}}
|
||||
- name: {{.}}{{end}}{{else}}{{ range .fields.components }}
|
||||
- name: {{ .name }}{{end}}{{end}}{{end}}{{if .meta.fields.description}}
|
||||
description: {{or .overrides.description .fields.description }}{{end}}{{if .meta.fields.fixVersions}}{{if .meta.fields.fixVersions.allowedValues}}
|
||||
- name: {{ .name }}{{end}}{{end}}
|
||||
{{- end -}}
|
||||
{{if .meta.fields.description}}
|
||||
description: {{or .overrides.description .fields.description }}
|
||||
{{- end -}}
|
||||
{{if .meta.fields.fixVersions -}}
|
||||
{{if .meta.fields.fixVersions.allowedValues}}
|
||||
fixVersions: # Values: {{ range .meta.fields.fixVersions.allowedValues }}{{.name}}, {{end}}{{if .overrides.fixVersions}}{{ range (split "," .overrides.fixVersions)}}
|
||||
- name: {{.}}{{end}}{{else}}{{range .fields.fixVersions}}
|
||||
- name: {{.}}{{end}}{{end}}{{end}}{{end}}{{if .meta.fields.issuetype}}
|
||||
- name: {{.}}{{end}}{{end}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{if .meta.fields.issuetype}}
|
||||
issuetype: # Values: {{ range .meta.fields.issuetype.allowedValues }}{{.name}}, {{end}}
|
||||
name: {{if .overrides.issuetype}}{{.overrides.issuetype}}{{else}}{{if .fields.issuetype}}{{.fields.issuetype.name}}{{end}}{{end}}{{end}}{{if .meta.fields.labels}}
|
||||
name: {{if .overrides.issuetype}}{{.overrides.issuetype}}{{else}}{{if .fields.issuetype}}{{.fields.issuetype.name}}{{end}}{{end}}
|
||||
{{- end -}}
|
||||
{{if .meta.fields.labels}}
|
||||
labels: {{range .fields.labels}}
|
||||
- {{.}}{{end}}{{if .overrides.labels}}{{range (split "," .overrides.labels)}}
|
||||
- {{.}}{{end}}{{end}}{{end}}{{if .meta.fields.priority}}
|
||||
- {{.}}{{end}}{{end}}
|
||||
{{- end -}}
|
||||
{{if .meta.fields.priority}}
|
||||
priority: # Values: {{ range .meta.fields.priority.allowedValues }}{{.name}}, {{end}}
|
||||
name: {{ or .overrides.priority "unassigned" }}{{end}}{{if .meta.fields.reporter}}
|
||||
name: {{ or .overrides.priority "unassigned" }}
|
||||
{{- end -}}
|
||||
{{if .meta.fields.reporter}}
|
||||
reporter:
|
||||
name: {{if .overrides.reporter}}{{.overrides.reporter}}{{else}}{{if .fields.reporter}}{{.fields.reporter.name}}{{end}}{{end}}{{end}}{{if .meta.fields.resolution}}
|
||||
name: {{if .overrides.reporter}}{{.overrides.reporter}}{{else}}{{if .fields.reporter}}{{.fields.reporter.name}}{{end}}{{end}}
|
||||
{{- end -}}
|
||||
{{if .meta.fields.resolution}}
|
||||
resolution: # Values: {{ range .meta.fields.resolution.allowedValues }}{{.name}}, {{end}}
|
||||
name: {{if .overrides.resolution}}{{.overrides.resolution}}{{else if .fields.resolution}}{{.fields.resolution.name}}{{else}}Fixed{{end}}{{end}}{{if .meta.fields.summary}}
|
||||
summary: {{or .overrides.summary .fields.summary}}{{end}}{{if .meta.fields.versions.allowedValues}}
|
||||
name: {{if .overrides.resolution}}{{.overrides.resolution}}{{else if .fields.resolution}}{{.fields.resolution.name}}{{else}}{{or .overrides.defaultResolution "Fixed"}}{{end}}
|
||||
{{- end -}}
|
||||
{{if .meta.fields.summary}}
|
||||
summary: {{or .overrides.summary .fields.summary}}
|
||||
{{- end -}}
|
||||
{{if .meta.fields.versions.allowedValues}}
|
||||
versions: # Values: {{ range .meta.fields.versions.allowedValues }}{{.name}}, {{end}}{{if .overrides.versions}}{{ range (split "," .overrides.versions)}}
|
||||
- name: {{.}}{{end}}{{else}}{{range .fields.versions}}
|
||||
- name: {{.}}{{end}}{{end}}{{end}}
|
||||
- name: {{.}}{{end}}{{end}}
|
||||
{{- end}}
|
||||
transition:
|
||||
id: {{ .transition.id }}
|
||||
name: {{ .transition.name }}
|
||||
`
|
||||
|
||||
const defaultWorklogTemplate = `{{/* worklog template */ -}}
|
||||
# issue: {{ .issue }}
|
||||
comment: |~
|
||||
{{ or .comment "" }}
|
||||
timeSpent: {{ or .timeSpent "" }}
|
||||
started:
|
||||
`
|
||||
|
||||
const defaultWorklogsTemplate = `{{/* worklogs template */ -}}
|
||||
{{ range .worklogs }}- # {{.author.name}}, {{.created | age}} ago
|
||||
comment: {{ or .comment "" }}
|
||||
timeSpent: {{ .timeSpent }}
|
||||
|
||||
{{end}}`
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/mgutz/ansi"
|
||||
"gopkg.in/coryb/yaml.v2"
|
||||
@@ -12,53 +11,69 @@ import (
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"text/template"
|
||||
"time"
|
||||
)
|
||||
|
||||
func homedir() string {
|
||||
if runtime.GOOS == "windows" {
|
||||
return os.Getenv("USERPROFILE")
|
||||
}
|
||||
return os.Getenv("HOME")
|
||||
}
|
||||
|
||||
// FindParentPaths will find all available paths from the current path up to the root
|
||||
// that matches the given fileName path
|
||||
func FindParentPaths(fileName string) []string {
|
||||
cwd, _ := os.Getwd()
|
||||
|
||||
paths := make([]string, 0)
|
||||
paths := []string{}
|
||||
|
||||
// special case if homedir is not in current path then check there anyway
|
||||
homedir := os.Getenv("HOME")
|
||||
if !strings.HasPrefix(cwd, homedir) {
|
||||
file := fmt.Sprintf("%s/%s", homedir, fileName)
|
||||
if _, err := os.Stat(file); err == nil {
|
||||
paths = append(paths, file)
|
||||
homedir := homedir()
|
||||
if !filepath.HasPrefix(cwd, homedir) {
|
||||
path := filepath.Join(homedir, fileName)
|
||||
if _, err := os.Stat(path); err == nil {
|
||||
paths = append(paths, path)
|
||||
}
|
||||
}
|
||||
|
||||
var dir string
|
||||
for _, part := range strings.Split(cwd, string(os.PathSeparator)) {
|
||||
if dir == "/" {
|
||||
dir = fmt.Sprintf("/%s", part)
|
||||
} else {
|
||||
dir = fmt.Sprintf("%s/%s", dir, part)
|
||||
path := filepath.Join(cwd, fileName)
|
||||
if _, err := os.Stat(path); err == nil {
|
||||
paths = append(paths, path)
|
||||
}
|
||||
for true {
|
||||
cwd = filepath.Dir(cwd)
|
||||
path := filepath.Join(cwd, fileName)
|
||||
if _, err := os.Stat(path); err == nil {
|
||||
paths = append(paths, path)
|
||||
}
|
||||
file := fmt.Sprintf("%s/%s", dir, fileName)
|
||||
if _, err := os.Stat(file); err == nil {
|
||||
paths = append(paths, file)
|
||||
if cwd[len(cwd)-1] == filepath.Separator {
|
||||
break
|
||||
}
|
||||
}
|
||||
return paths
|
||||
}
|
||||
|
||||
// FindClosestParentPath finds the path that matches the given fileName path that is
|
||||
// closest to the current working directory
|
||||
func FindClosestParentPath(fileName string) (string, error) {
|
||||
paths := FindParentPaths(fileName)
|
||||
if len(paths) > 0 {
|
||||
return paths[len(paths)-1], nil
|
||||
}
|
||||
return "", errors.New(fmt.Sprintf("%s not found in parent directory hierarchy", fileName))
|
||||
return "", fmt.Errorf("%s not found in parent directory hierarchy", fileName)
|
||||
}
|
||||
|
||||
func readFile(file string) string {
|
||||
var bytes []byte
|
||||
var err error
|
||||
log.Debugf("readFile: reading %q", file)
|
||||
if bytes, err = ioutil.ReadFile(file); err != nil {
|
||||
log.Error("Failed to read file %s: %s", file, err)
|
||||
log.Errorf("Failed to read file %s: %s", file, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
return string(bytes)
|
||||
@@ -80,35 +95,36 @@ func copyFile(src, dst string) (err error) {
|
||||
}
|
||||
|
||||
func fuzzyAge(start string) (string, error) {
|
||||
if t, err := time.Parse("2006-01-02T15:04:05.000-0700", start); err != nil {
|
||||
t, err := time.Parse("2006-01-02T15:04:05.000-0700", start)
|
||||
if err != nil {
|
||||
return "", err
|
||||
} else {
|
||||
delta := time.Now().Sub(t)
|
||||
if delta.Minutes() < 2 {
|
||||
return "a minute", nil
|
||||
} else if dm := delta.Minutes(); dm < 45 {
|
||||
return fmt.Sprintf("%d minutes", int(dm)), nil
|
||||
} else if dm := delta.Minutes(); dm < 90 {
|
||||
return "an hour", nil
|
||||
} else if dh := delta.Hours(); dh < 24 {
|
||||
return fmt.Sprintf("%d hours", int(dh)), nil
|
||||
} else if dh := delta.Hours(); dh < 48 {
|
||||
return "a day", nil
|
||||
} else {
|
||||
return fmt.Sprintf("%d days", int(delta.Hours()/24)), nil
|
||||
}
|
||||
}
|
||||
return "unknown", nil
|
||||
delta := time.Now().Sub(t)
|
||||
if delta.Minutes() < 2 {
|
||||
return "a minute", nil
|
||||
} else if dm := delta.Minutes(); dm < 45 {
|
||||
return fmt.Sprintf("%d minutes", int(dm)), nil
|
||||
} else if dm := delta.Minutes(); dm < 90 {
|
||||
return "an hour", nil
|
||||
} else if dh := delta.Hours(); dh < 24 {
|
||||
return fmt.Sprintf("%d hours", int(dh)), nil
|
||||
} else if dh := delta.Hours(); dh < 48 {
|
||||
return "a day", nil
|
||||
}
|
||||
return fmt.Sprintf("%d days", int(delta.Hours()/24)), nil
|
||||
}
|
||||
|
||||
func dateFormat(format string, content string) (string, error) {
|
||||
if t, err := time.Parse("2006-01-02T15:04:05.000-0700", content); err != nil {
|
||||
t, err := time.Parse("2006-01-02T15:04:05.000-0700", content)
|
||||
if err != nil {
|
||||
return "", err
|
||||
} else {
|
||||
return t.Format(format), nil
|
||||
}
|
||||
return t.Format(format), nil
|
||||
}
|
||||
|
||||
// RunTemplate will run the give templateContent as a golang text/template
|
||||
// and pass the provided data to the template execution. It will write
|
||||
// the output to the provided "out" writer.
|
||||
func RunTemplate(templateContent string, data interface{}, out io.Writer) error {
|
||||
return runTemplate(templateContent, data, out)
|
||||
}
|
||||
@@ -120,11 +136,11 @@ func runTemplate(templateContent string, data interface{}, out io.Writer) error
|
||||
|
||||
funcs := map[string]interface{}{
|
||||
"toJson": func(content interface{}) (string, error) {
|
||||
if bytes, err := json.MarshalIndent(content, "", " "); err != nil {
|
||||
bytes, err := json.MarshalIndent(content, "", " ")
|
||||
if err != nil {
|
||||
return "", err
|
||||
} else {
|
||||
return string(bytes), nil
|
||||
}
|
||||
return string(bytes), nil
|
||||
},
|
||||
"append": func(more string, content interface{}) (string, error) {
|
||||
switch value := content.(type) {
|
||||
@@ -133,13 +149,13 @@ func runTemplate(templateContent string, data interface{}, out io.Writer) error
|
||||
case []byte:
|
||||
return string(append(content.([]byte), []byte(more)...)), nil
|
||||
default:
|
||||
return "", errors.New(fmt.Sprintf("Unknown type: %s", value))
|
||||
return "", fmt.Errorf("Unknown type: %s", value)
|
||||
}
|
||||
},
|
||||
"indent": func(spaces int, content string) string {
|
||||
indent := make([]rune, spaces+1, spaces+1)
|
||||
indent[0] = '\n'
|
||||
for i := 1; i < spaces+1; i += 1 {
|
||||
for i := 1; i < spaces+1; i++ {
|
||||
indent[i] = ' '
|
||||
}
|
||||
|
||||
@@ -182,7 +198,7 @@ func runTemplate(templateContent string, data interface{}, out io.Writer) error
|
||||
},
|
||||
"rep": func(count int, content string) string {
|
||||
var buffer bytes.Buffer
|
||||
for i := 0; i < count; i += 1 {
|
||||
for i := 0; i < count; i++ {
|
||||
buffer.WriteString(content)
|
||||
}
|
||||
return buffer.String()
|
||||
@@ -194,19 +210,19 @@ func runTemplate(templateContent string, data interface{}, out io.Writer) error
|
||||
return dateFormat(format, content)
|
||||
},
|
||||
}
|
||||
if tmpl, err := template.New("template").Funcs(funcs).Parse(templateContent); err != nil {
|
||||
log.Error("Failed to parse template: %s", err)
|
||||
tmpl, err := template.New("template").Funcs(funcs).Parse(templateContent)
|
||||
if err != nil {
|
||||
log.Errorf("Failed to parse template: %s", err)
|
||||
return err
|
||||
}
|
||||
if err := tmpl.Execute(out, data); err != nil {
|
||||
log.Errorf("Failed to execute template: %s", err)
|
||||
return err
|
||||
} else {
|
||||
if err := tmpl.Execute(out, data); err != nil {
|
||||
log.Error("Failed to execute template: %s", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func responseToJson(resp *http.Response, err error) (interface{}, error) {
|
||||
func responseToJSON(resp *http.Response, err error) (interface{}, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -215,7 +231,7 @@ func responseToJson(resp *http.Response, err error) (interface{}, error) {
|
||||
if resp.StatusCode == 400 {
|
||||
if val, ok := data.(map[string]interface{})["errorMessages"]; ok {
|
||||
for _, errMsg := range val.([]interface{}) {
|
||||
log.Error("%s", errMsg)
|
||||
log.Errorf("%s", errMsg)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -228,7 +244,7 @@ func jsonDecode(io io.Reader) interface{} {
|
||||
var data interface{}
|
||||
err = json.Unmarshal(content, &data)
|
||||
if err != nil {
|
||||
log.Error("JSON Parse Error: %s from %s", err, content)
|
||||
log.Errorf("JSON Parse Error: %s from %s", err, content)
|
||||
}
|
||||
return data
|
||||
}
|
||||
@@ -239,7 +255,7 @@ func jsonEncode(data interface{}) (string, error) {
|
||||
|
||||
err := enc.Encode(data)
|
||||
if err != nil {
|
||||
log.Error("Failed to encode data %s: %s", data, err)
|
||||
log.Errorf("Failed to encode data %s: %s", data, err)
|
||||
return "", err
|
||||
}
|
||||
return buffer.String(), nil
|
||||
@@ -249,7 +265,7 @@ func jsonWrite(file string, data interface{}) {
|
||||
fh, err := os.OpenFile(file, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
|
||||
defer fh.Close()
|
||||
if err != nil {
|
||||
log.Error("Failed to open %s: %s", file, err)
|
||||
log.Errorf("Failed to open %s: %s", file, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
enc := json.NewEncoder(fh)
|
||||
@@ -260,11 +276,11 @@ func yamlWrite(file string, data interface{}) {
|
||||
fh, err := os.OpenFile(file, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
|
||||
defer fh.Close()
|
||||
if err != nil {
|
||||
log.Error("Failed to open %s: %s", file, err)
|
||||
log.Errorf("Failed to open %s: %s", file, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if out, err := yaml.Marshal(data); err != nil {
|
||||
log.Error("Failed to marshal yaml %v: %s", data, err)
|
||||
log.Errorf("Failed to marshal yaml %v: %s", data, err)
|
||||
os.Exit(1)
|
||||
} else {
|
||||
fh.Write(out)
|
||||
@@ -306,10 +322,13 @@ func yamlFixup(data interface{}) (interface{}, error) {
|
||||
}
|
||||
default:
|
||||
err := fmt.Errorf("YAML: key %s is type '%T', require 'string'", key, k)
|
||||
log.Error("%s", err)
|
||||
log.Errorf("%s", err)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if len(copy) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
return copy, nil
|
||||
case map[string]interface{}:
|
||||
copy := make(map[string]interface{})
|
||||
@@ -320,6 +339,9 @@ func yamlFixup(data interface{}) (interface{}, error) {
|
||||
copy[k] = fixed
|
||||
}
|
||||
}
|
||||
if len(copy) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
return copy, nil
|
||||
case []interface{}:
|
||||
copy := make([]interface{}, 0, len(d))
|
||||
@@ -330,6 +352,9 @@ func yamlFixup(data interface{}) (interface{}, error) {
|
||||
copy = append(copy, fixed)
|
||||
}
|
||||
}
|
||||
if len(copy) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
return copy, nil
|
||||
case string:
|
||||
if d == "" || d == "\n" {
|
||||
@@ -343,16 +368,16 @@ func yamlFixup(data interface{}) (interface{}, error) {
|
||||
|
||||
func mkdir(dir string) error {
|
||||
if stat, err := os.Stat(dir); err != nil && !os.IsNotExist(err) {
|
||||
log.Error("Failed to stat %s: %s", dir, err)
|
||||
log.Errorf("Failed to stat %s: %s", dir, err)
|
||||
return err
|
||||
} else if err == nil && !stat.IsDir() {
|
||||
err := fmt.Errorf("%s exists and is not a directory!", dir)
|
||||
log.Error("%s", err)
|
||||
err := fmt.Errorf("%s exists and is not a directory", dir)
|
||||
log.Errorf("%s", err)
|
||||
return err
|
||||
} else {
|
||||
// dir does not exist, so try to create it
|
||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||
log.Error("Failed to mkdir -p %s: %s", dir, err)
|
||||
log.Errorf("Failed to mkdir -p %s: %s", dir, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user