Compare commits

..

60 Commits

Author SHA1 Message Date
Mike Pountney c442e2c98d Add test for findClosestParentPath to check for figtree regression
I inadvertently 'upgraded' to coryb/figtree v1.0.0, and it caused an
error in findCLosestParentPath, due to figtree.FindParentPaths having
a different function signature in v1.0.0

No idea why a regular compile isn't picking this up - but when
explicitly testing like this, we get the error detailed in #277:

```
Mikes-MBP:jira mike$ go test ./...
?       github.com/go-jira/jira [no test files]
jiracli/util.go:29:34: too many arguments in call to figtree.FindParentPaths
        have (string, string, string)
        want (string)
jiracli/util.go:29:34: too many arguments in call to figtree.FindParentPaths
        have (string, string, string)
        want (string)
FAIL    github.com/go-jira/jira/jiracli [build failed]
ok      github.com/go-jira/jira/jiradata        (cached)
Mikes-MBP:jira mike$
```
2019-09-15 21:47:49 -07:00
Mike Pountney 27f57b2bbe Switch over to using github.com/go-jira/jira, from gopkg.in
There should be no reason to use gopkg.in versioned imports now that
we're using go modules. I think, IANAE.

gopkg.in kind of gets in the way of modules, as it only pulls over
tagged releases from github.com -- this then means that you need to use
go modules 'replace' syntax in the go.mod to use a non-versioned commit
or branch. This is feasible, but kind of ugly.

go modules defaults to pulling the latest version, so the default
behavior is the same as when pulling go-jira.v1 from gopkg.in.
2019-09-14 21:31:11 -07:00
coryb 71aaa88b13 Merge pull request #276 from go-jira/fix_228
Fixes #228: make ':' gpg files temporary to fix go mod
2019-09-13 22:13:01 -07:00
Mike Pountney 52a577ea48 Fixes #228: make ':' gpg files temporary to fix go mod 2019-09-13 21:38:25 -07:00
coryb b8e73a5cb0 Merge pull request #266 from mbethke/fix-multiline-worklog-comment
Fix worklog template for multiline comments
2019-07-05 11:43:48 -07:00
Matthias Bethke 43e07f1197 fix worklog template to allow multiline comments 2019-07-05 09:07:09 +02:00
coryb 957696bed8 Merge pull request #263 from kojustin/master
Allow reading password from stdin
2019-06-05 14:24:50 -07:00
Justin Ko 225e1dcc05 Allow reading password from stdin
Allow reading password from stdin in `jira login`. This is useful for
combining the jira tool with other command-line utilities.
2019-06-05 14:08:09 -07:00
Cory Bennett f1390760b4 add CODEOWNERS file 2019-05-28 17:31:23 -07:00
Daniel Martí 31c113d1ba all: unindent some code 2019-05-28 11:01:51 +01:00
Daniel Martí 9bcdcc128f don't use ReadAll when decoding JSON
An empty stream isn't valid JSON, so we shouldn't silently ignore it.
2019-05-27 13:09:31 +01:00
Daniel Martí 098d963881 README: trim down the content
This README is huge; the document shouldn't take more than two or three
screens. If we need more docs, they should be linked to and live
elsewhere.

For now, start by removing unnecessary sections. Gitter has been
completely unused since 2017, so remove it. The table of contents also
takes a lot of vertical space, when we don't want to have that much
content to begin with.

Tips on forking are also unnecessary, since that's the same for all Go
projects. Moreover, this is out of date now that the project is a Go
module.

Remove the help output, as that can be obtained extremely easily by just
running 'jira -h'.

Finally, remove the v0-v1 changes, as v0 was last released over three
years ago. It's extremely unlikely that anyone still has to upgrade from
the older version.
2019-05-27 13:08:36 +01:00
Daniel Martí 9b9186f7d4 start making staticcheck happier 2019-05-27 10:49:15 +01:00
Daniel Martí f125ef3fa9 all: convert to a Go module
And remove vendor/, as it's now unnecessary. go.sum ensures that
dependencies aren't tampered with, and proxy.golang.org keeps copies of
all the archives.
2019-05-26 23:40:16 +01:00
coryb 5cc009af4c Merge pull request #253 from mvdan/module
t: rename to _t to fix module support
2019-05-26 14:41:18 -07:00
Daniel Martí d237e86cda t: rename to _t to fix module support
The following two files contain characters which aren't valid in source
files within a Go module:

	t/.password-store/GoJira/api-token:gojira@corybennett.org.gpg
	t/.password-store/GoJira/api-token:mothra@corybennett.org.gpg

The directory only contains test scripts, so it doesn't need to be
included in the module. The simplest way to do that is to start the
directory with an underscore.

Fixes #228.
2019-05-26 22:34:24 +01:00
Daniel Martí 664c5cad24 CI: test on Go 1.12.x, cleanup
We can also use the apt addon to install packages. We also don't need
fast_finish, since we don't use allow_failures anywhere.

Finally, the 'go get' line was pointless, as all dependencies are
vendored, and 'go test' will catch build failures.
2019-05-26 21:34:53 +01:00
Cory Bennett 36c99ce040 make automatic pagination on search optional, fix tests 2019-05-25 17:06:03 -07:00
coryb 58a300422b Merge pull request #240 from jgraglia/patch-1
API Token auth requires "user" config
2019-05-25 15:34:25 -07:00
coryb eb90676bbc Merge pull request #219 from kerhac/master
docs: update pass documentation with password-name
2019-05-25 15:33:39 -07:00
coryb d54a549d24 Merge pull request #236 from CodeLingoBot/rewrite
Fix function comments based on best practices from Effective Go
2019-05-25 14:12:06 -07:00
Cory Bennett 3d00c294f4 add test for --limit 2019-05-25 14:10:18 -07:00
Cory Bennett 181bd74f1b prefer defer resp.Body.Close to avoid leaks on subsequent errors 2019-05-25 14:09:48 -07:00
Cory Bennett f6809e32f4 version bump 2019-05-25 14:09:12 -07:00
Cory Bennett cbcac1755a Updated Changelog 2019-05-25 14:09:12 -07:00
coryb 890b9aa724 Merge pull request #245 from justmiles/211
add pagination to search (closes #211)
2019-05-25 14:08:09 -07:00
Miles Maddox 76dd1d8982 add pagination to search 2019-04-21 07:33:17 -05:00
Julien Graglia 271289a3c9 API Token auth requires "user" config
API Token authentication requires the `user ` to be defined in the config
2019-03-26 09:49:29 +01:00
Julian Swagemakers d8189f0a01 docs: update pass documentation with password-name
This will update the README.md to include setting the configuration
option password-name when using pass.
2019-03-13 07:48:50 +01:00
CodeLingo Bot 23ac11872b Fix function comments based on best practices from Effective Go
Signed-off-by: CodeLingo Bot <bot@codelingo.io>
2019-02-28 21:20:03 +00:00
coryb 514c1491c7 Merge pull request #220 from ejsuncy/master
docs: Fix grammar typos (#1)
2018-11-29 19:52:02 -08:00
Dan Bunker 9258d4df15 Fix grammar typos (#1) 2018-11-29 11:20:55 -07:00
Cory Bennett ee69afadd0 [#201] update required library, failing to populate cookiejar from cookies file 2018-08-04 14:20:03 -07:00
Cory Bennett a87bdf4038 version bump 2018-08-02 23:07:00 -07:00
Cory Bennett 68c2588153 Updated Changelog 2018-08-02 23:07:00 -07:00
Cory Bennett 0cba806942 [#199] [#198] update http client library, fix issues with internal login retries 2018-08-02 23:05:01 -07:00
Cory Bennett df9dbe65b4 update amp in pom, prevents dumb prompt at jira startup 2018-08-02 23:05:01 -07:00
coryb 492754f059 Merge pull request #197 from kojiromike/spellcheck
Fix Spelling
2018-07-30 08:32:49 -07:00
Michael A. Smith 97d8c5f6e0 Fix Spelling 2018-07-30 09:20:04 -04:00
Cory Bennett 1e619ea690 version bump 2018-07-29 14:56:46 -07:00
Cory Bennett b7d8a9c324 Updated Changelog 2018-07-29 14:56:46 -07:00
Cory Bennett 8b717de870 Updated Usage 2018-07-29 14:56:46 -07:00
Cory Bennett f5921077ca [#196] add jira session command to show session information if user is authenticated 2018-07-29 14:54:21 -07:00
Cory Bennett c9a4e30606 [#192] fix template usage for 'fixVersions' in transition template 2018-07-29 13:24:19 -07:00
Cory Bennett ef9b731bac move HandleExit to the jiracli package 2018-07-28 17:21:18 -07:00
Cory Bennett 62303ed81b add missing vendored files 2018-07-28 16:55:36 -07:00
Cory Bennett 7191c7751b they broke golang.org/x/net: ERROR: vendor/golang.org/x/net/proxy/socks5.go:11:2: use of internal package not allowed 2018-07-28 16:41:16 -07:00
Cory Bennett d16bcc2f51 udpate deps 2018-07-28 16:29:51 -07:00
Cory Bennett 07ba74b34a udpate for figtree api changes 2018-07-28 15:50:13 -07:00
Cory Bennett 462ef1c485 udpate to use latest dep, update figtree 2018-07-28 15:49:57 -07:00
Cory Bennett eead13aef1 Merge branch 'pr/171' 2018-05-25 21:52:44 -07:00
Cory Bennett 213a7e04af [#171] change proposed PasswordPath to PasswordName 2018-05-25 21:50:28 -07:00
Cory Bennett 49e44670d9 fix usage generation after #178 2018-05-24 00:23:29 -07:00
Cory Bennett 5503e53168 Updated Changelog 2018-05-24 00:23:15 -07:00
Cory Bennett 9ebd2cd64e fix IsTerminal usage for windows 2018-05-24 00:23:15 -07:00
Cory Bennett 84b6155b0d version bump 2018-05-24 00:23:15 -07:00
Cory Bennett c23ad75957 Updated Changelog 2018-05-24 00:23:15 -07:00
coryb 3c478004d2 Merge pull request #178 from vergenzt/patch-1
Move usage info higher up in readme
2018-05-24 00:22:51 -07:00
Tim Vergenz ed42ef920a Move usage info higher up in readme
It was frustrating wading through the whole readme when all I was trying to figure out was what typical usage looks like to decide whether this project is what I'm looking for. :) Putting basic usage sooner would have helped.
2018-05-14 10:35:11 -07:00
dvogt23 fa01ff5c46 add pass path to config 2018-04-27 12:09:53 +02:00
804 changed files with 811 additions and 234620 deletions
+2
View File
@@ -0,0 +1,2 @@
# one of these users must approve the PR before merging
* @coryb @mvdan @vanniktech @mikepea
+10 -8
View File
@@ -1,10 +1,12 @@
jira
schemas/*.json
t/.gnupg/random_seed
t/issue.props
t/attach.props
t/garbage.bin
t/attach1.txt
t/binary.out
t/foobar.bin
dist
/_t/.gnupg/random_seed
/_t/issue.props
/_t/attach.props
/_t/garbage.bin
/_t/attach1.txt
/_t/binary.out
/_t/foobar.bin
/_t/.password-store/GoJira/api-token:gojira@corybennett.org.gpg
/_t/.password-store/GoJira/api-token:mothra@corybennett.org.gpg
dist
+11 -11
View File
@@ -1,19 +1,19 @@
sudo: true
before_install:
- sudo apt-get update && sudo apt-get install -y pass gnupg
addons:
apt:
packages:
- pass
- gnupg
language: go
go_import_path: gopkg.in/Netflix-Skunkworks/go-jira.v1
go:
- 1.9
matrix:
fast_finish: true
go:
- 1.12.x
env:
- GO111MODULE=on
script:
- go get -t -v ./...
- go test ./...
- go vet -composites=false ./...
- make
- make prove 2>&1
+20
View File
@@ -1,5 +1,25 @@
# Changelog
## 1.0.20 - 2018-08-04
* [[#201](https://github.com/Netflix-Skunkworks/go-jira/issues/201)] update required library, failing to populate cookiejar from cookies file [Cory Bennett] [[ee69afa](https://github.com/Netflix-Skunkworks/go-jira/commit/ee69afa)]
## 1.0.19 - 2018-08-02
* [[#199](https://github.com/Netflix-Skunkworks/go-jira/issues/199)] [[#198](https://github.com/Netflix-Skunkworks/go-jira/issues/198)] update http client library, fix issues with internal login retries [Cory Bennett] [[0cba806](https://github.com/Netflix-Skunkworks/go-jira/commit/0cba806)]
## 1.0.18 - 2018-07-29
* [[#196](https://github.com/Netflix-Skunkworks/go-jira/issues/196)] add `jira session` command to show session information if user is authenticated [Cory Bennett] [[f592107](https://github.com/Netflix-Skunkworks/go-jira/commit/f592107)]
* [[#192](https://github.com/Netflix-Skunkworks/go-jira/issues/192)] fix template usage for 'fixVersions' in transition template [Cory Bennett] [[c9a4e30](https://github.com/Netflix-Skunkworks/go-jira/commit/c9a4e30)]
* move HandleExit to the jiracli package [Cory Bennett] [[ef9b731](https://github.com/Netflix-Skunkworks/go-jira/commit/ef9b731)]
* they broke golang.org/x/net: ERROR: vendor/golang.org/x/net/proxy/socks5.go:11:2: use of internal package not allowed [Cory Bennett] [[7191c77](https://github.com/Netflix-Skunkworks/go-jira/commit/7191c77)]
* udpate deps [Cory Bennett] [[d16bcc2](https://github.com/Netflix-Skunkworks/go-jira/commit/d16bcc2)]
* udpate for figtree api changes [Cory Bennett] [[07ba74b](https://github.com/Netflix-Skunkworks/go-jira/commit/07ba74b)]
* udpate to use latest dep, update figtree [Cory Bennett] [[462ef1c](https://github.com/Netflix-Skunkworks/go-jira/commit/462ef1c)]
* [[#171](https://github.com/Netflix-Skunkworks/go-jira/issues/171)] change proposed PasswordPath to PasswordName [Cory Bennett] [[213a7e0](https://github.com/Netflix-Skunkworks/go-jira/commit/213a7e0)]
* add pass path to config [dvogt23] [[fa01ff5](https://github.com/Netflix-Skunkworks/go-jira/commit/fa01ff5)]
## 1.0.17 - 2018-04-15
* fix IsTerminal usage for windows [Cory Bennett] [[7f9595c](https://github.com/Netflix-Skunkworks/go-jira/commit/7f9595c)]
Generated
-189
View File
@@ -1,189 +0,0 @@
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
[[projects]]
branch = "master"
name = "github.com/alecthomas/template"
packages = [".","parse"]
revision = "a0175ee3bccc567396460bf5acd36800cb10c49c"
[[projects]]
branch = "master"
name = "github.com/alecthomas/units"
packages = ["."]
revision = "2efee857e7cfd4f3d0138cc3cbb1b4966962b93a"
[[projects]]
branch = "master"
name = "github.com/cheekybits/genny"
packages = ["generic"]
revision = "9127e812e1e9e501ce899a18121d316ecb52e4ba"
[[projects]]
branch = "master"
name = "github.com/coryb/figtree"
packages = ["."]
revision = "c7d8fbf1d7746b5864b8262fabffec813b5a43fa"
[[projects]]
branch = "master"
name = "github.com/coryb/kingpeon"
packages = ["."]
revision = "9a669f143f2e7454e80064c47365d139420a3fff"
[[projects]]
branch = "master"
name = "github.com/coryb/oreo"
packages = ["."]
revision = "95687d61c95ee1522c1140e2af59b0c1846abfc1"
[[projects]]
name = "github.com/davecgh/go-spew"
packages = ["spew"]
revision = "346938d642f2ec3594ed81d874461961cd0faa76"
version = "v1.1.0"
[[projects]]
branch = "master"
name = "github.com/fatih/camelcase"
packages = ["."]
revision = "f6a740d52f961c60348ebb109adde9f4635d7540"
[[projects]]
branch = "master"
name = "github.com/guelfey/go.dbus"
packages = ["."]
revision = "f6a3a2366cc39b8479cadc499d3c735fb10fbdda"
[[projects]]
branch = "master"
name = "github.com/jinzhu/copier"
packages = ["."]
revision = "32e0d0db1dcd4373fb9eb0f9d727b1fe1a723e54"
[[projects]]
branch = "master"
name = "github.com/kballard/go-shellquote"
packages = ["."]
revision = "cd60e84ee657ff3dc51de0b4f55dd299a3e136f2"
[[projects]]
branch = "master"
name = "github.com/mattn/go-colorable"
packages = ["."]
revision = "ad5389df28cdac544c99bd7b9161a0b5b6ca9d1b"
[[projects]]
name = "github.com/mattn/go-isatty"
packages = ["."]
revision = "fc9e8d8ef48496124e79ae0df75490096eccf6fe"
version = "v0.0.2"
[[projects]]
branch = "master"
name = "github.com/mgutz/ansi"
packages = ["."]
revision = "9520e82c474b0a04dd04f8a40959027271bab992"
[[projects]]
branch = "master"
name = "github.com/pkg/browser"
packages = ["."]
revision = "c90ca0c84f15f81c982e32665bffd8d7aac8f097"
[[projects]]
name = "github.com/pkg/errors"
packages = ["."]
revision = "645ef00459ed84a119197bfb8d8205042c6df63d"
version = "v0.8.0"
[[projects]]
name = "github.com/pmezard/go-difflib"
packages = ["difflib"]
revision = "792786c7400a136282c1664665ae0a8db921c6c2"
version = "v1.0.0"
[[projects]]
branch = "master"
name = "github.com/sethgrid/pester"
packages = ["."]
revision = "a86a2d88f4dc3c7dbf3a6a6bbbfb095690b834b6"
[[projects]]
name = "github.com/stretchr/testify"
packages = ["assert"]
revision = "69483b4bd14f5845b5a1e55bca19e954e827f1d0"
version = "v1.1.4"
[[projects]]
branch = "master"
name = "github.com/theckman/go-flock"
packages = ["."]
revision = "6de226b0d5f040ed85b88c82c381709b98277f3d"
[[projects]]
branch = "master"
name = "github.com/tidwall/gjson"
packages = ["."]
revision = "be96719f990978a867f52c48f29d43f6b591da28"
[[projects]]
branch = "master"
name = "github.com/tidwall/match"
packages = ["."]
revision = "173748da739a410c5b0b813b956f89ff94730b4c"
[[projects]]
branch = "master"
name = "github.com/tmc/keyring"
packages = ["."]
revision = "06e6283d50adc5f8fcdb3cdf33ee1244d4400ae1"
[[projects]]
branch = "master"
name = "golang.org/x/crypto"
packages = ["ssh/terminal"]
revision = "9ba3862cf6a5452ae579de98f9364dd2e544844c"
[[projects]]
branch = "master"
name = "golang.org/x/net"
packages = ["proxy"]
revision = "01c190206fbdffa42f334f4b2bf2220f50e64920"
[[projects]]
branch = "master"
name = "golang.org/x/sys"
packages = ["unix","windows"]
revision = "a5054c7c1385fd50d9394475365355a87a7873ec"
[[projects]]
name = "gopkg.in/AlecAivazis/survey.v1"
packages = [".","core","terminal"]
revision = "9d910423e24aa6d7c7950160658c295e0734c7e0"
version = "1.3.1"
[[projects]]
name = "gopkg.in/alecthomas/kingpin.v2"
packages = ["."]
revision = "1087e65c9441605df944fb12c33f0fe7072d18ca"
version = "v2.2.5"
[[projects]]
branch = "v2"
name = "gopkg.in/coryb/yaml.v2"
packages = ["."]
revision = "fb7cb9628c6e3bdd76c29fb91798d51a09832470"
[[projects]]
name = "gopkg.in/op/go-logging.v1"
packages = ["."]
revision = "b2cb9fa56473e98db8caba80237377e83fe44db5"
version = "v1"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "50016720a1e2509a915e4465a53ffa957f977d2145e831b81d946ef87f7a8f48"
solver-name = "gps-cdcl"
solver-version = 1
-75
View File
@@ -1,75 +0,0 @@
# Gopkg.toml example
#
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
# for detailed Gopkg.toml documentation.
#
# required = ["github.com/user/thing/cmd/thing"]
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
#
# [[constraint]]
# name = "github.com/user/project"
# version = "1.0.0"
#
# [[constraint]]
# name = "github.com/user/project2"
# branch = "dev"
# source = "github.com/myfork/project2"
#
# [[override]]
# name = "github.com/x/y"
# version = "2.4.0"
[[constraint]]
name = "github.com/coryb/figtree"
[[constraint]]
name = "github.com/coryb/kingpeon"
[[constraint]]
name = "github.com/coryb/oreo"
[[constraint]]
name = "github.com/jinzhu/copier"
[[constraint]]
name = "github.com/kballard/go-shellquote"
[[constraint]]
name = "github.com/mgutz/ansi"
[[constraint]]
name = "github.com/pkg/browser"
[[constraint]]
name = "github.com/pkg/errors"
version = "0.8.0"
[[constraint]]
name = "github.com/savaki/jq"
[[constraint]]
name = "github.com/tmc/keyring"
[[constraint]]
name = "golang.org/x/crypto"
[[constraint]]
name = "gopkg.in/AlecAivazis/survey.v1"
version = "1.3.1"
[[constraint]]
name = "gopkg.in/alecthomas/kingpin.v2"
version = "2.2.5"
[[constraint]]
name = "gopkg.in/coryb/yaml.v2"
[[constraint]]
name = "gopkg.in/op/go-logging.v1"
version = "1.0.0"
[[constraint]]
branch = "master"
name = "github.com/tidwall/gjson"
+16 -8
View File
@@ -66,11 +66,11 @@ update-changelog:
mv CHANGELOG.md.new CHANGELOG.md; \
$(NULL)
update-usage:
@perl -pi -e 'undef $$/; s|\n```\nusage.*?```|"\n```\n".qx{./jira --help}."```"|esg' README.md
release:
perl -pi -e 'undef $$/; s/\n```\nusage.*```//sg' README.md
echo '```' >> README.md
./jira --help >> README.md 2>&1 || true
echo '```' >> README.md
make update-usage
git diff --exit-code --quiet README.md || git commit -m "Updated Usage" README.md
git commit -m "Updated Changelog" CHANGELOG.md
git commit -m "version bump" jira.go
@@ -80,12 +80,20 @@ release:
version:
@echo $(CURVER)
clean:
clean: clean-password-store
rm -rf ./$(NAME)
prove:
chmod -R g-rwx,o-rwx $(CWD)/t/.gnupg
OSHT_VERBOSE=1 prove -v
clean-password-store:
rm -f "$(CWD)/_t/.password-store/GoJira/api-token:gojira@corybennett.org.gpg"
rm -f "$(CWD)/_t/.password-store/GoJira/api-token:mothra@corybennett.org.gpg"
test-password-store:
ln -s "$(CWD)/_t/.password-store/GoJira/api-token__gojira@corybennett.org.gpg" "$(CWD)/_t/.password-store/GoJira/api-token:gojira@corybennett.org.gpg"
ln -s "$(CWD)/_t/.password-store/GoJira/api-token__mothra@corybennett.org.gpg" "$(CWD)/_t/.password-store/GoJira/api-token:mothra@corybennett.org.gpg"
prove: test-password-store
chmod -R g-rwx,o-rwx $(CWD)/_t/.gnupg
OSHT_VERBOSE=1 prove -v _t/*.t
generate:
cd schemas && ./fetch-schemas.py
+46 -215
View File
@@ -1,140 +1,45 @@
[![Join the chat at https://gitter.im/go-jira-cli/help](https://badges.gitter.im/go-jira-cli/help.svg)](https://gitter.im/go-jira-cli/help?utm_source=badge&utm_medium=badge&utm_content=badge)
[![Build Status](https://travis-ci.org/Netflix-Skunkworks/go-jira.svg?branch=master)](https://travis-ci.org/Netflix-Skunkworks/go-jira)
[![GoDoc](https://godoc.org/gopkg.in/Netflix-Skunkworks/go-jira.v1?status.svg)](https://godoc.org/gopkg.in/Netflix-Skunkworks/go-jira.v1)
[![Build Status](https://travis-ci.org/go-jira/jira/go-jira.svg?branch=master)](https://travis-ci.org/go-jira/jira)
[![GoDoc](https://godoc.org/github.com/go-jira/jira?status.svg)](https://godoc.org/github.com/go-jira/jira)
[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
### Table of Contents
* [Summary](#go-jira)
* [Install](#install)
* [Download](#download)
* [Build](#build)
* [v1 vs v0 changes](#v1-vs-v0-changes)
* [<strong>Golang library import</strong>](#golang-library-import)
* [<strong>Configs per command</strong>](#configs-per-command)
* [<strong>Custom Commands</strong>](#custom-commands)
* [<strong>Incompatible command changes</strong>](#incompatible-command-changes)
* [<strong>Login process change</strong>](#login-process-change)
* [Configuration](#configuration)
* [Dynamic Configuration](#dynamic-configuration)
* [Custom Commands](#custom-commands-1)
* [Commands](#commands)
* [Options](#options)
* [Arguments](#arguments)
* [Script Template](#script-template)
* [Examples](#examples)
* [Editing](#editing)
* [Templates](#templates)
* [Writing/Editing Templates](#writingediting-templates)
* [Authentication](#authentication)
* [user vs login](#user-vs-login)
* [keyring password source](#keyring-password-source)
* [pass password source](#pass-password-source)
* [Usage](#usage)
* [TAB completion](#setting-up-tab-completion)
# go-jira
simple command line client for Atlassian's Jira service written in Go
Simple command line client for Atlassian's Jira service written in Go.
## Install
### Download
You can download one of the pre-built binaries for **go-jira** [here](https://github.com/Netflix-Skunkworks/go-jira/releases).
You can download one of the pre-built binaries for **go-jira** [here](https://github.com/go-jira/jira/releases).
### Build
You can build and install the official repository with [Go](https://golang.org/dl/):
```
go get gopkg.in/Netflix-Skunkworks/go-jira.v1/cmd/jira
```
This will checkout this repository into `$GOPATH/src/gopkg.in/Netflix-Skunkworks/go-jira.v1`, build, and install it.
go get github.com/go-jira/jira/cmd/jira
Because golang likes fully qualified import paths, forking and contributing can be a bit tricky.
This will checkout this repository into `$GOPATH/src/github.com/go-jira/jira/`, build, and install it.
If you want to tinker or hack on go-jira, the [easiest way to do so](http://code.openark.org/blog/development/forking-golang-repositories-on-github-and-managing-the-import-path) is to fork the repository and clone directly into the official path like this:
It should then be available in $GOPATH/bin/jira
`git clone https://github.com/YOUR_USER_NAME_HERE/go-jira $GOPATH/src/gopkg.in/Netflix-Skunkworks/go-jira.v1`
From within that source dir you can build and install modifications from within that directory like:
`go install ./...`
## Usage
## v1 vs v0 changes
#### Setting up TAB completion
###### **Golang library import**
For the new version of go-jira you should use:
```
import "gopkg.in/Netflix-Skunkworks/go-jira.v1"
```
Since go-jira is built with the "kingpin" golang command line library we support bash/zsh shell completion automatically:
If you have code that depends on the old apis, you can still use them with this import:
```
import "gopkg.in/Netflix-Skunkworks/go-jira.v0"
```
* <https://github.com/alecthomas/kingpin/tree/v2.2.5#bashzsh-shell-completion>
###### **Configs per command**
Instead of requiring a exectuable template to get configs for a given command now you can create a config to be applied to a command. So if you want to use `template: table` by default for yor `jira list` you can now do:
```
$ cat $HOME/.jira.d/list.yml
template: table
```
Where previously you needed something like:
```
# cat $HOME/.jira.d/config.yml
#!/bin/sh
case $JIRA_OPERATION in
list)
echo "template: table";;
esac
```
For example, in bash, adding something along the lines of:
###### **Custom Commands**
Now you can create your own custom commands to do common operations with jira. Please see the details **Custom Commands** section below for more details. If you want to create a command `jira mine` that lists all the issues assigned to you now you can modify your `.jira.d/config.yml` file to add a `custom-commands` section like this:
```yaml
custom-commands:
- name: mine
help: display issues assigned to me
script: |-
{{jira}} list --query "resolution = unresolved and assignee=currentuser() ORDER BY created"
```
Then the next time you run `jira help` you will see your usage:
```
$ jira mine --help
usage: jira mine
`eval "$(jira --completion-script-bash)"`
display issues assigned to me
Flags:
--help Show context-sensitive help (also try --help-long and --help-man).
-v, --verbose ... Increase verbosity for debugging
-e, --endpoint=ENDPOINT Base URI to use for Jira
-u, --user=USER Login name used for authentication with Jira service
--unixproxy=UNIXPROXY Path for a unix-socket proxy
-k, --insecure Disable TLS certificate verification
```
###### **Incompatible command changes**
Unfortunately during the rewrite between v0 and v1 there were some necessary changes that broke backwards compatibility with existing commands. Specifically the `dups`, `blocks`, `add worklog` and `add|remove|set labels` commands have had the command word swapped around:
* `jira DUPLICATE dups ISSUE` => `jira dup DUPLICATE ISSUE`
* `jira BLOCKER blocks ISSUE` => `jira block BLOCKER ISSUE`
* `jira add worklog` => `jira worklog add`
* `jira add labels` => `jira labels add`
* `jira remove labels` => `jira labels remove`
* `jira set labels` => `jira labels set`
###### **Login process change**
We have, once again, changed how login happens for Jira. When authenticating against Atlassian Cloud Jira [API Tokens are now required](https://developer.atlassian.com/cloud/jira/platform/deprecation-notice-basic-auth-and-cookie-based-auth/). Please read [the Authentication section](#authentication) below for more information.
If you use a privately hosted Jira service, you can chose to use the API Token method or continue using the session login api. Please read [the Authentication section](#authentication) below for more information.
Previously `jira` used attempt to get a `JSESSION` cookies by authenticating with the webservice standard GUI login process. This has been especially problematic as users need to authenticate with various credential providers (google auth, etc). We now attempt to authenticate via the [session login api](https://docs.atlassian.com/jira/REST/cloud/#auth/1/session-login). This may be problematic for users if admins have locked down the session-login api, so we might have to bring back the error-prone Basic-Auth approach. For users that are unable to authenticate via `jira` hopefully someone in your organization can provide me with details on a process for you to authenticate and we can try to update `jira`.
to your bashrc, or .profile (assuming go-jira binary is already in your path) will cause jira to offer tab completion behavior.
## Configuration
**go-jira** uses a configuration hierarchy. When loading the configuration from disk it will recursively look through all parent directories in your current path looking for a **.jira.d** directory. If your current directory is not a child directory of your homedir, then your homedir will also be inspected for a **.jira.d** directory. From all of **.jira.d** directories discovered **go-jira** will load a **&lt;command&gt;.yml** file (ie for `jira list` it will load `.jira.d/list.yml`) then it will merge in any properties from the **config.yml** if found. The configuration properties found in a file closests to your current working directory will have precedence. Properties overriden with command line options will have final precedence.
**go-jira** uses a configuration hierarchy. When loading the configuration from disk it will recursively look through all parent directories in your current path looking for a **.jira.d** directory. If your current directory is not a child directory of your homedir, then your homedir will also be inspected for a **.jira.d** directory. From all of **.jira.d** directories discovered **go-jira** will load a **&lt;command&gt;.yml** file (ie for `jira list` it will load `.jira.d/list.yml`) then it will merge in any properties from the **config.yml** if found. The configuration properties found in a file closest to your current working directory will have precedence. Properties overridden with command line options will have final precedence.
The complicated configuration hierarchy is used because **go-jira** attempts to be context aware. For example, if you are working on a "foo" project and you `cd` into your project workspace, wouldn't it be nice if `jira ls` automatically knew to list only issues related to the "foo" project? Likewise when you `cd` to the "bar" project then `jira ls` should only list issues related to "bar" project. You can do this with by creating a configuration under your project workspace at **./.jira.d/config.yml** that looks like:
@@ -164,7 +69,7 @@ If the **.jira.d/config.yml** file is executable, then **go-jira** will attempt
echo "endpoint: https://jira.mycompany.com"
echo "editor: emacs -nw"
case $JIRA_OPERATION in
case $JIRA_OPERATION in
list)
echo "template: table";;
esac
@@ -199,14 +104,14 @@ Where the individual commands are maps with these keys:
* `default: bool` Use this for compound command groups. If you wanted to have `jira foo bar` and `jira foo baz` you would have two commands with `name: foo bar` and `name: foo baz`. Then if you wanted `jira foo baz` to be called by default when you type `jira foo` you would set `default: true` for that custom command.
* `options: list` This is the list of possible option flags that the command will accept
* `args: list` This is the list of command arguments (like the ISSUE) that the command will accept.
* `aliases: string list`: This is a list of alternate names that the user can provide on the command line to run the same command. Typically used to shorten the command name or provide alternatives that users might expect.
* `aliases: string list`: This is a list of alternate names that the user can provide on the command line to run the same command. Typically used to shorten the command name or provide alternatives that users might expect.
* `script: string` [**required**] This is the script that will be executed as the action for this command. The value will be treated as a template and substitutions for options and arguments will be made before executing.
##### Options
These are possible keys under the command `options` property:
* `name: string` [**required**] Name of the option, so `name: foobar` will result in `--foobar` option.
* `help: string` The help messsage displayed in usage for the option.
* `type: string`: The type of the option, can be one of these values: `BOOL`, `COUNTER`, `ENUM`, `FLOAT32`, `FLOAT64`, `INT8`, `INT16`, `INT32`, `INT64`, `INT`, `STRING`, `STRINGMAP`, `UINT8`, `UINT16`, `UINT32`, `UINT64` and `UINT`. Most of these are primitive data types an should be self-explanitory. The default type is `STRING`. There are some special types:
* `help: string` The help message displayed in usage for the option.
* `type: string`: The type of the option, can be one of these values: `BOOL`, `COUNTER`, `ENUM`, `FLOAT32`, `FLOAT64`, `INT8`, `INT16`, `INT32`, `INT64`, `INT`, `STRING`, `STRINGMAP`, `UINT8`, `UINT16`, `UINT32`, `UINT64` and `UINT`. Most of these are primitive data types an should be self-explanatory. The default type is `STRING`. There are some special types:
* `COUNTER` will be an integer type that increments each time the option is used. So something like `--count --count` will results in `{{options.count}}` of `2`.
* `ENUM` type is used with the `enum` property. The raw type is a string and **must** be one of the values listed in the `enum` property.
* `STRINGMAP` is a `string => string` map with the format of `KEY=VALUE`. So `--override foo=bar --override bin=baz` will allow for `{{options.override.foo}}` to be `bar` and `{{options.override.bin}}` to be `baz`.
@@ -219,9 +124,9 @@ These are possible keys under the command `options` property:
##### Arguments
These are possible keys under the command `args` property:
* `name: string` [**required**] Name of the option, so `name: ISSUE` will show in the usasge as `jira <command> ISSUE`. This also represents the name of the argument to be used in the script template, so `{{args.ISSUE}}`.
* `help: string` The help messsage displayed in usage for the argument.
* `type: string`: The type of the argumemnt, can be one of these values: `BOOL`, `COUNTER`, `ENUM`, `FLOAT32`, `FLOAT64`, `INT8`, `INT16`, `INT32`, `INT64`, `INT`, `STRING`, `STRINGMAP`, `UINT8`, `UINT16`, `UINT32`, `UINT64` and `UINT`. Most of these are primitive data types an should be self-explanitory. The default type is `STRING`. There are some special types:
* `name: string` [**required**] Name of the option, so `name: ISSUE` will show in the usage as `jira <command> ISSUE`. This also represents the name of the argument to be used in the script template, so `{{args.ISSUE}}`.
* `help: string` The help message displayed in usage for the argument.
* `type: string`: The type of the argument, can be one of these values: `BOOL`, `COUNTER`, `ENUM`, `FLOAT32`, `FLOAT64`, `INT8`, `INT16`, `INT32`, `INT64`, `INT`, `STRING`, `STRINGMAP`, `UINT8`, `UINT16`, `UINT32`, `UINT64` and `UINT`. Most of these are primitive data types an should be self-explanatory. The default type is `STRING`. There are some special types:
* `COUNTER` will be an integer type that increments each the argument is provided So something like `jira <command> ISSUE-12 ISSUE-23` will results in `{{args.ISSUE}}` of `2`.
* `ENUM` type is used with the `enum` property. The raw type is a string and **must** be one of the values listed in the `enum` property.
* `STRINGMAP` is a `string => string` map with the format of `KEY=VALUE`. So `jira <command> foo=bar bin=baz` along with a `name: OVERRIDE` property will allow for `{{args.OVERRIDE.foo}}` to be `bar` and `{{args.OVERRIDE.bin}}` to be `baz`.
@@ -231,7 +136,7 @@ These are possible keys under the command `args` property:
* `enum: string list` Used with the `type: ENUM` property, it is a list of strings values that represent the set of possible values for the argument.
##### Script Template
The `script` property is a template that whould produce `/bin/sh` compatible syntax after the template has been processed. There are 2 key template functions `{{args}}` and `{{options}}` that return the parsed arguments and option flags as a map.
The `script` property is a template that would produce `/bin/sh` compatible syntax after the template has been processed. There are 2 key template functions `{{args}}` and `{{options}}` that return the parsed arguments and option flags as a map.
To demonstrate how you might use args and options here is a `custom-test` command:
```yaml
@@ -292,7 +197,7 @@ custom-commands:
script: |
env | grep JIRA
```
You could use the environment variables automatically, so if your `.jira.d/config.yml` looks something like this:
```yaml
project: PROJECT
@@ -336,7 +241,7 @@ custom-commands:
### Editing
When you run command like `jira edit` it will open up your favorite editor with the templatized output so you can quickly edit. When the editor
closes **go-jira** will submit the completed form. The order which **go-jira** attempts to determine your prefered editor is:
closes **go-jira** will submit the completed form. The order which **go-jira** attempts to determine your preferred editor is:
* **editor** property in any config.yml file
* **JIRA_EDITOR** environment variable
@@ -355,9 +260,9 @@ hard-coded templates with `jira export-templates` which will write them to **~/.
#### 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/jiracli/templates.go#L64).
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 easier to format the data, those functions are defined [here](https://github.com/go-jira/jira/blob/master/jiracli/templates.go#L64).
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:
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 example to find out what is available to the "view" templates, you can use:
```
jira view GOJIRA-321 -t debug
```
@@ -369,12 +274,14 @@ jira list -t debug
### Authentication
For Atlassian Cloud hosted Jira [API Tokens are now required](https://developer.atlassian.com/cloud/jira/platform/deprecation-notice-basic-auth-and-cookie-based-auth/). You will automatically be prompted for an API Token if your jira endoint ends in `.atlassian.net`. If you are using a private Jira service, you can force `jira` to use an api-token by setting the `authentication-method: api-token` property in your `$HOME/.jira.d/config.yml` file. The API Token needs to be presented to the Jira service on every request, so it is recommended to store this API Token security within your OS's keyring, or using the `pass` service as documented below so that it can be programatically accessed via `jira` and not prompt you every time. For a less-secure option you can also provide the API token via a `JIRA_API_TOKEN` environment variable. If you are unable to use an api-token for an Atlassian Cloud hosted Jira then you can still force `jira` to use the old session based authentication (until it the hosted system stops accepting it) by setting `authentication-method: session`.
For Atlassian Cloud hosted Jira [API Tokens are now required](https://developer.atlassian.com/cloud/jira/platform/deprecation-notice-basic-auth-and-cookie-based-auth/). You will automatically be prompted for an API Token if your jira endpoint ends in `.atlassian.net`. If you are using a private Jira service, you can force `jira` to use an api-token by setting the `authentication-method: api-token` property in your `$HOME/.jira.d/config.yml` file. The API Token needs to be presented to the Jira service on every request, so it is recommended to store this API Token security within your OS's keyring, or using the `pass` service as documented below so that it can be programmatically accessed via `jira` and not prompt you every time. For a less-secure option you can also provide the API token via a `JIRA_API_TOKEN` environment variable. If you are unable to use an api-token for an Atlassian Cloud hosted Jira then you can still force `jira` to use the old session based authentication (until it the hosted system stops accepting it) by setting `authentication-method: session`.
If your Jira service still allows you to use the Session based authention method then `jira` will prompt for a password automatically when get a response header from the Jira service that indicates you do not have an active session (ie the `X-Ausername` header is set to `anonymous`). Then after authentication we cache the `cloud.session.token` cookie returned by the service [session login api](https://docs.atlassian.com/jira/REST/cloud/#auth/1/session-login) and reuse that on subsequent requests. Typically this cookie will be valid for several hours (depending on the service configuration). To automatically securely store your password for easy reuse by jira You can enable a `password-source` via `.jira.d/config.yml` with possible values of `keyring` or `pass`.
The API Token authentication requires both the token and the email of the user. The email mut be set in the `user:` in your config.yml. Failure to provide the `user` will result in a 401 error.
If your Jira service still allows you to use the Session based authentication method then `jira` will prompt for a password automatically when get a response header from the Jira service that indicates you do not have an active session (ie the `X-Ausername` header is set to `anonymous`). Then after authentication we cache the `cloud.session.token` cookie returned by the service [session login api](https://docs.atlassian.com/jira/REST/cloud/#auth/1/session-login) and reuse that on subsequent requests. Typically this cookie will be valid for several hours (depending on the service configuration). To automatically securely store your password for easy reuse by jira You can enable a `password-source` via `.jira.d/config.yml` with possible values of `keyring` or `pass`.
#### User vs Login
The Jira service has sometimes differing opinions about how a user is identified. In other words the ID you login with might not be ID that the jira system recognized you as. This matters when trying to identify a user via various Jira REST APIs (like issue assignment). This is especially relevent when trying to authenticate with an API Token where the authentication user is usually an email address, but within the Jira system the user is identified by a user name. To accomodate this `jira` now supports two different properties in the config file. So when authentication using the API Tokens you will likely want something like this in your `$HOME/.jira.d/config.yml` file:
The Jira service has sometimes differing opinions about how a user is identified. In other words the ID you login with might not be ID that the jira system recognized you as. This matters when trying to identify a user via various Jira REST APIs (like issue assignment). This is especially relevant when trying to authenticate with an API Token where the authentication user is usually an email address, but within the Jira system the user is identified by a user name. To accommodate this `jira` now supports two different properties in the config file. So when authentication using the API Tokens you will likely want something like this in your `$HOME/.jira.d/config.yml` file:
```yaml
user: person
login: person@example.com
@@ -382,7 +289,7 @@ login: person@example.com
You can also override these values on the command line with `jira --user person --login person@example.com`. The `login` value will be used only for authentication purposes, the `user` value will be used when a user name is required for any Jira service API calls.
#### keyring password source
#### `keyring` password source
On OSX and Linux there are a few keyring providers that `go-jira` can use (via this [golang module](https://github.com/tmc/keyring)). To integrate `go-jira` with a supported keyring just add this configuration to `$HOME/.jira.d/config.yml`:
```yaml
password-source: keyring
@@ -393,6 +300,7 @@ After setting this and issuing a `jira login`, your credentials will be stored i
An alternative to the keyring password source is the `pass` tool (documentation [here](https://www.passwordstore.org/)). This uses gpg to encrypt/decrypt passwords on demand and by using `gpg-agent` you can cache the gpg credentials for a period of time so you will not be prompted repeatedly for decrypting the passwords. The advantage over the keyring integration is that `pass` can be used on more platforms than OSX and Linux, although it does require more setup. To use `pass` for password storage and retrieval via `go-jira` just add this configuration to `$HOME/.jira.d/config.yml`:
```yaml
password-source: pass
password-name: jira.example.com/myuser
```
This assumes you have already setup `pass` correctly on your system. Specifically you will need to have created a gpg key like this:
@@ -416,7 +324,13 @@ Then initialize the `pass` tool to use the correct key:
$ pass init "Go Jira <gojira@example.com>"
```
You probably want to setup gpg-agent so that you dont have to type in your gpg passphrase all the time. You can get `gpg-agent` to automatically start by adding something like this to your `$HOME/.bashrc`
Now insert your password with the name you configured.
```
$ pass insert jira.example.com/myuser
```
You probably want to setup gpg-agent so that you don't have to type in your gpg passphrase all the time. You can get `gpg-agent` to automatically start by adding something like this to your `$HOME/.bashrc`
```bash
if [ -f $HOME/.gpg-agent-info ]; then
. $HOME/.gpg-agent-info
@@ -442,93 +356,10 @@ fi
export GPG_TTY=$(tty)
```
## Usage
#### `stdin` password source
When `password-source` is set to `stdin`, the `jira login` command will read from stdin until EOF, and the bytes read will be the used as the password. This is useful if you have some other programmatic method for fetching passwords. For example, if `password-generator` creates a one-time password and prints it to stdout, you could use it like this.
#### Setting up TAB completion
Since go-jira is build with the "kingpin" golang command line library we supports bash/zsh shell completion automatically:
* <https://github.com/alecthomas/kingpin/tree/v2.2.5#bashzsh-shell-completion>
For example, in bash, adding something along the lines of:
`eval "$(jira --completion-script-bash)"`
to your bashrc, or .profile (assuming go-jira binary is already in your path) will cause jira to offer tab completion behavior.
```
usage: jira [<flags>] <command> [<args> ...]
Jira Command Line Interface
Global flags:
--help Show context-sensitive help (also try --help-long and --help-man).
-v, --verbose ... Increase verbosity for debugging
-e, --endpoint=ENDPOINT Base URI to use for Jira
-k, --insecure Disable TLS certificate verification
-Q, --quiet Suppress output to console
--unixproxy=UNIXPROXY Path for a unix-socket proxy
--socksproxy=SOCKSPROXY Address for a socks proxy
-u, --user=USER user name used within the Jira service
--login=LOGIN login name that corresponds to the user used for authentication
Commands:
help: Show help.
version: Prints version
acknowledge: Transition issue to acknowledge state
assign: Assign user to issue
attach create: Attach file to issue
attach get: Fetch attachment
attach list: Prints attachment details for issue
attach remove: Delete attachment
backlog: Transition issue to Backlog state
block: Mark issues as blocker
browse: Open issue in browser
close: Transition issue to close state
comment: Add comment to issue
component add: Add component
components: Show components for a project
create: Create issue
createmeta: View 'create' metadata
done: Transition issue to Done state
dup: Mark issues as duplicate
edit: Edit issue details
editmeta: View 'edit' metadata
epic add: Add issues to Epic
epic create: Create Epic
epic list: Prints list of issues for an epic with optional search criteria
epic remove: Remove issues from Epic
export-templates: Export templates for customizations
fields: Prints all fields, both System and Custom
in-progress: Transition issue to Progress state
issuelink: Link two issues
issuelinktypes: Show the issue link types
issuetypes: Show issue types for a project
labels add: Add labels to an issue
labels remove: Remove labels from an issue
labels set: Set labels on an issue
list: Prints list of issues for given search criteria
login: Attempt to login into jira server
logout: Deactivate session with Jira server
rank: Mark issues as blocker
reopen: Transition issue to reopen state
request: Open issue in requestr
resolve: Transition issue to resolve state
start: Transition issue to start state
stop: Transition issue to stop state
subtask: Subtask issue
take: Assign issue to yourself
todo: Transition issue to To Do state
transition: Transition issue to given state
transitions: List valid issue transitions
transmeta: List valid issue transitions
unassign: Unassign an issue
unexport-templates: Remove unmodified exported templates
view: Prints issue details
vote: Vote up/down an issue
watch: Add/Remove watcher to issue
worklog add: Add a worklog to an issue
worklog list: Prints the worklog data for given issue
```bash
$ ./password-generator | jira login --endpoint=https://my.jira.endpoint.com --user=USERNAME
```
View File
View File
View File
+13 -1
View File
@@ -4,7 +4,7 @@ cd $(dirname $0)
jira="../jira"
. env.sh
PLAN 94
PLAN 98
# reset login
RUNS $jira logout
@@ -567,3 +567,15 @@ labels: better-label, more-label
description: |
blocks
EOF
###############################################################################
## List 102 closed issues, should be more than 100 (max page size), verify pagination
###############################################################################
RUNS $jira ls -q "project = 'BASIC' AND status = 'Done'" --limit 102
IS $(wc -l <$OSHT_STDOUT) -eq 102
###############################################################################
## List 1 issue, verify we dont get full page
###############################################################################
RUNS $jira ls -q "project = 'BASIC' AND status = 'Done'" --limit 1
IS $(wc -l <$OSHT_STDOUT) -eq 1
+1 -1
View File
@@ -62,7 +62,7 @@ DIFF <<EOF
+------------+------------------------------+------------+--------------+--------------+
| id | filename | bytes | user | created |
+------------+------------------------------+------------+--------------+--------------+
| $(printf %10s $attach1) | README.md | 1238 | gojira | a minute |
| $(printf %10s $attach1) | README.md | 1239 | gojira | a minute |
| $(printf %10s $attach2) | garbage.bin | 1048576 | gojira | a minute |
| $(printf %10s $attach3) | foobar.bin | 1048576 | gojira | a minute |
+------------+------------------------------+------------+--------------+--------------+
View File
View File
View File
View File
View File
View File
+1 -1
View File
@@ -9,7 +9,7 @@ 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
# this runs the integration tests in the "_t" directory
prove
```
@@ -173,7 +173,7 @@
<properties>
<jira.version>7.2.0</jira.version>
<amps.version>6.2.6</amps.version>
<amps.version>6.3.15</amps.version>
<plugin.testrunner.version>1.2.3</plugin.testrunner.version>
<atlassian.spring.scanner.version>1.2.13</atlassian.spring.scanner.version>
<!-- This key is used to keep the consistency between the key in atlassian-plugin.xml and the key to generate bundle. -->
View File
+4 -2
View File
@@ -1,7 +1,9 @@
package jira
import (
"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiradata"
"encoding/json"
"github.com/go-jira/jira/jiradata"
)
// https://docs.atlassian.com/jira/REST/cloud/#api/2/attachment-getAttachment
@@ -19,7 +21,7 @@ func GetAttachment(ua HttpClient, endpoint string, id string) (*jiradata.Attachm
if resp.StatusCode == 200 {
results := &jiradata.Attachment{}
return results, readJSON(resp.Body, results)
return results, json.NewDecoder(resp.Body).Decode(results)
}
return nil, responseError(resp)
}
+18 -22
View File
@@ -1,49 +1,45 @@
package main
import (
"fmt"
"os"
"path/filepath"
"runtime/debug"
"github.com/coryb/figtree"
"github.com/coryb/oreo"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiracli"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiracmd"
"github.com/go-jira/jira/jiracli"
"github.com/go-jira/jira/jiracmd"
"gopkg.in/op/go-logging.v1"
)
var (
log = logging.MustGetLogger("jira")
)
type oreoLogger struct {
logger *logging.Logger
}
func handleExit() {
if e := recover(); e != nil {
if exit, ok := e.(jiracli.Exit); ok {
os.Exit(exit.Code)
} else {
fmt.Fprintf(os.Stderr, "%s\n%s", e, debug.Stack())
os.Exit(1)
}
}
var log = logging.MustGetLogger("jira")
func (ol *oreoLogger) Printf(format string, args ...interface{}) {
ol.logger.Debugf(format, args...)
}
func main() {
defer handleExit()
defer jiracli.HandleExit()
jiracli.InitLogging()
fig := figtree.NewFigTree()
fig.EnvPrefix = "JIRA"
fig.ConfigDir = ".jira.d"
configDir := ".jira.d"
fig := figtree.NewFigTree(
figtree.WithHome(jiracli.Homedir()),
figtree.WithEnvPrefix("JIRA"),
figtree.WithConfigDir(configDir),
)
if err := os.MkdirAll(filepath.Join(jiracli.Homedir(), fig.ConfigDir), 0755); err != nil {
if err := os.MkdirAll(filepath.Join(jiracli.Homedir(), configDir), 0755); err != nil {
log.Errorf("%s", err)
panic(jiracli.Exit{Code: 1})
}
o := oreo.New().WithCookieFile(filepath.Join(jiracli.Homedir(), fig.ConfigDir, "cookies.js"))
o := oreo.New().WithCookieFile(filepath.Join(jiracli.Homedir(), configDir, "cookies.js")).WithLogger(&oreoLogger{log})
jiracmd.RegisterAllCommands()
+2 -2
View File
@@ -4,7 +4,7 @@ import (
"bytes"
"encoding/json"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiradata"
"github.com/go-jira/jira/jiradata"
)
type ComponentProvider interface {
@@ -31,7 +31,7 @@ func CreateComponent(ua HttpClient, endpoint string, cp ComponentProvider) (*jir
if resp.StatusCode == 201 {
results := &jiradata.Component{}
return results, readJSON(resp.Body, results)
return results, json.NewDecoder(resp.Body).Decode(results)
}
return nil, responseError(resp)
}
+2 -2
View File
@@ -9,7 +9,7 @@ import (
"github.com/coryb/oreo"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiradata"
"github.com/go-jira/jira/jiradata"
)
// https://docs.atlassian.com/jira-software/REST/latest/#agile/1.0/epic-getIssuesForEpic
@@ -53,7 +53,7 @@ func EpicSearch(ua HttpClient, endpoint string, epic string, sp SearchProvider)
if resp.StatusCode == 200 {
results := &jiradata.SearchResults{}
return results, readJSON(resp.Body, results)
return results, json.NewDecoder(resp.Body).Decode(results)
}
return nil, responseError(resp)
}
+3 -2
View File
@@ -1,14 +1,15 @@
package jira
import (
"encoding/json"
"net/http"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiradata"
"github.com/go-jira/jira/jiradata"
)
func responseError(resp *http.Response) error {
results := &jiradata.ErrorCollection{}
if err := readJSON(resp.Body, results); err != nil {
if err := json.NewDecoder(resp.Body).Decode(results); err != nil {
results.Status = resp.StatusCode
results.ErrorMessages = append(results.ErrorMessages, err.Error())
}
+4 -2
View File
@@ -1,7 +1,9 @@
package jira
import (
"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiradata"
"encoding/json"
"github.com/go-jira/jira/jiradata"
)
// https://docs.atlassian.com/jira/REST/cloud/#api/2/field-getFields
@@ -18,7 +20,7 @@ func GetFields(ua HttpClient, endpoint string) ([]jiradata.Field, error) {
defer resp.Body.Close()
if resp.StatusCode == 200 {
results := []jiradata.Field{}
return results, readJSON(resp.Body, &results)
return results, json.NewDecoder(resp.Body).Decode(&results)
}
return nil, responseError(resp)
}
+41
View File
@@ -0,0 +1,41 @@
module github.com/go-jira/jira
go 1.12
require (
github.com/Netflix/go-expect v0.0.0-20180928190340-9d1f4485533b // indirect
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc // indirect
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf // indirect
github.com/cheekybits/genny v1.0.0 // indirect
github.com/coryb/figtree v0.0.0-20180728224503-071d1ef303df
github.com/coryb/kingpeon v0.0.0-20180107011214-9a669f143f2e
github.com/coryb/oreo v0.0.0-20180804211640-3e1b88fc08f1
github.com/davecgh/go-spew v1.1.0 // indirect
github.com/fatih/camelcase v1.0.0 // indirect
github.com/guelfey/go.dbus v0.0.0-20131113121618-f6a3a2366cc3 // indirect
github.com/hinshun/vt10x v0.0.0-20180809195222-d55458df857c // indirect
github.com/jinzhu/copier v0.0.0-20180308034124-7e38e58719c3
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
github.com/kr/pretty v0.1.0 // indirect
github.com/kr/pty v1.1.4 // indirect
github.com/mattn/go-colorable v0.0.9 // indirect
github.com/mattn/go-isatty v0.0.3 // indirect
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b
github.com/pkg/browser v0.0.0-20170505125900-c90ca0c84f15
github.com/pkg/errors v0.8.0
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/stretchr/testify v1.2.2
github.com/theckman/go-flock v0.4.0 // indirect
github.com/tidwall/gjson v0.0.0-20180711011033-ba784d767ac7
github.com/tidwall/match v1.0.0 // indirect
github.com/tmc/keyring v0.0.0-20171121202319-839169085ae1
golang.org/x/crypto v0.0.0-20180723164146-c126467f60eb
golang.org/x/net v0.0.0-20171102191033-01c190206fbd
golang.org/x/sys v0.0.0-20180727230415-bd9dbc187b6e // indirect
gopkg.in/AlecAivazis/survey.v1 v1.6.1
gopkg.in/alecthomas/kingpin.v2 v2.2.6
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
gopkg.in/coryb/yaml.v2 v2.0.0-20180616071044-0e40e46f7153
gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473
gopkg.in/yaml.v2 v2.2.2 // indirect
)
+74
View File
@@ -0,0 +1,74 @@
github.com/Netflix/go-expect v0.0.0-20180928190340-9d1f4485533b h1:sSQK05nvxs4UkgCJaxihteu+r+6ela3dNMm7NVmsS3c=
github.com/Netflix/go-expect v0.0.0-20180928190340-9d1f4485533b/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc h1:cAKDfWh5VpdgMhJosfJnn5/FoN2SRZ4p7fJNX58YPaU=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf h1:qet1QNfXsQxTZqLG4oE62mJzwPIB8+Tee4RNCL9ulrY=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE=
github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ=
github.com/coryb/figtree v0.0.0-20180728224503-071d1ef303df h1:cS4Z9Nlv8J4UqFbLp9ltZypgenm2p3Jeg0yqLfpH2pc=
github.com/coryb/figtree v0.0.0-20180728224503-071d1ef303df/go.mod h1:uAkZUEGm6dROpxfy+8vXLs7JrLCI4O+gQyKAuISxI/g=
github.com/coryb/kingpeon v0.0.0-20180107011214-9a669f143f2e h1:tGmk9Tuyz7fKuBq/d3nFJvVWRvc48MEBKQC4uYV3wb0=
github.com/coryb/kingpeon v0.0.0-20180107011214-9a669f143f2e/go.mod h1:gBc0uEH6swbOMoR7VkVuW7w5fGvZu/KHeSgxBR4Ta7Q=
github.com/coryb/oreo v0.0.0-20180804211640-3e1b88fc08f1 h1:Hh0qSvmvoAGL8VxvEoUv9UuUf9XlKcQtSxAMTz1kqfE=
github.com/coryb/oreo v0.0.0-20180804211640-3e1b88fc08f1/go.mod h1:l/wuS2rM8ostk0aApWje8tsZNWJPOc2TVr85B0n3e6M=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8=
github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc=
github.com/guelfey/go.dbus v0.0.0-20131113121618-f6a3a2366cc3 h1:fngCxKbvZdctIsWj2hYijhAt4iK0JXSSA78B36xP0yI=
github.com/guelfey/go.dbus v0.0.0-20131113121618-f6a3a2366cc3/go.mod h1:0CNX5Cvi77WEH8llpfZ/ieuqyceb1cnO5//b5zzsnF8=
github.com/hinshun/vt10x v0.0.0-20180809195222-d55458df857c h1:kp3AxgXgDOmIJFR7bIwqFhwJ2qWar8tEQSE5XXhCfVk=
github.com/hinshun/vt10x v0.0.0-20180809195222-d55458df857c/go.mod h1:DqJ97dSdRW1W22yXSB90986pcOyQ7r45iio1KN2ez1A=
github.com/jinzhu/copier v0.0.0-20180308034124-7e38e58719c3 h1:sHsPfNMAG70QAvKbddQ0uScZCHQoZsT5NykGRCeeeIs=
github.com/jinzhu/copier v0.0.0-20180308034124-7e38e58719c3/go.mod h1:yL958EeXv8Ylng6IfnvG4oflryUi3vgA3xPs9hmII1s=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.4 h1:5Myjjh3JY/NaAi4IsUbHADytDyl1VE1Y9PXDlL+P/VQ=
github.com/kr/pty v1.1.4/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-isatty v0.0.3 h1:ns/ykhmWi7G9O+8a448SecJU3nSMBXJfqQkl0upE1jI=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/pkg/browser v0.0.0-20170505125900-c90ca0c84f15 h1:mrI+6Ae64Wjt+uahGe5we/sPS1sXjvfT3YjtawAVgps=
github.com/pkg/browser v0.0.0-20170505125900-c90ca0c84f15/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA=
github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/theckman/go-flock v0.4.0 h1:bcqNkS4RTQBGWybG7IBimUMxnLz53Qes1+D4QaOhzJc=
github.com/theckman/go-flock v0.4.0/go.mod h1:kjuth3y9VJ2aNlkNEO99G/8lp9fMIKaGyBmh84IBheM=
github.com/tidwall/gjson v0.0.0-20180711011033-ba784d767ac7 h1:PW7TzL8BOpYMcUYSv4qWDoH1Y5iRzVABteynvfF7pwE=
github.com/tidwall/gjson v0.0.0-20180711011033-ba784d767ac7/go.mod h1:c/nTNbUr0E0OrXEhq1pwa8iEgc2DOt4ZZqAt1HtCkPA=
github.com/tidwall/match v1.0.0 h1:Ym1EcFkp+UQ4ptxfWlW+iMdq5cPH5nEuGzdf/Pb7VmI=
github.com/tidwall/match v1.0.0/go.mod h1:LujAq0jyVjBy028G1WhWfIzbpQfMO8bBZ6Tyb0+pL9E=
github.com/tmc/keyring v0.0.0-20171121202319-839169085ae1 h1:+gXfyhy0t28Guz+vFztBg45yIquB2bNtiFvbItzJtUc=
github.com/tmc/keyring v0.0.0-20171121202319-839169085ae1/go.mod h1:gsa3jftQ3xia55nzIN4lXLYzDcWdxjojdKoz+N0St2Y=
golang.org/x/crypto v0.0.0-20180723164146-c126467f60eb h1:Ah9YqXLj6fEgeKqcmBuLCbAsrF3ScD7dJ/bYM0C6tXI=
golang.org/x/crypto v0.0.0-20180723164146-c126467f60eb/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/net v0.0.0-20171102191033-01c190206fbd h1:CLQSRrSDQMOMkogMxky7XOkERftMegAnxjT2re4E66M=
golang.org/x/net v0.0.0-20171102191033-01c190206fbd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/sys v0.0.0-20180727230415-bd9dbc187b6e h1:3dQ4fR8k5KugjVKO0oqSd1odxuk2yaE2CIfxWP2WarQ=
golang.org/x/sys v0.0.0-20180727230415-bd9dbc187b6e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
gopkg.in/AlecAivazis/survey.v1 v1.6.1 h1:HyWkjKGBpzhNxrpaKRLDqoa4L1f4cMVBNU4bnVmU8Mw=
gopkg.in/AlecAivazis/survey.v1 v1.6.1/go.mod h1:2Ehl7OqkBl3Xb8VmC4oFW2bItAhnUfzIjrOzwRxCrOU=
gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/coryb/yaml.v2 v2.0.0-20180616071044-0e40e46f7153 h1:3KfEubBNUdXqlEXuMz13dXy4cYK2AvuPhp8fKTYuPdU=
gopkg.in/coryb/yaml.v2 v2.0.0-20180616071044-0e40e46f7153/go.mod h1:Vth2iKfSejHZ3p6akgWO0iSjuuiu6mNCEgzcYUCnumw=
gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473 h1:6D+BvnJ/j6e222UW8s2qTSe3wGBtvo0MbVQG/c5k8RE=
gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473/go.mod h1:N1eN2tsCx0Ydtgjl4cqmbRCsY4/+z4cYDeqwZTk6zog=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+30 -29
View File
@@ -11,7 +11,7 @@ import (
"github.com/coryb/oreo"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiradata"
"github.com/go-jira/jira/jiradata"
)
type IssueQueryProvider interface {
@@ -69,7 +69,7 @@ func GetIssue(ua HttpClient, endpoint string, issue string, iqg IssueQueryProvid
if resp.StatusCode == 200 {
results := &jiradata.Issue{}
return results, readJSON(resp.Body, results)
return results, json.NewDecoder(resp.Body).Decode(results)
}
return nil, responseError(resp)
}
@@ -95,15 +95,13 @@ func GetIssueWorklog(ua HttpClient, endpoint string, issue string) (*jiradata.Wo
if resp.StatusCode == 200 {
results := &jiradata.WorklogWithPagination{}
err := readJSON(resp.Body, results)
err := json.NewDecoder(resp.Body).Decode(results)
if err != nil {
return nil, err
}
startAt = startAt + maxResults
total = results.Total
for _, worklog := range results.Worklogs {
worklogs = append(worklogs, worklog)
}
worklogs = append(worklogs, results.Worklogs...)
} else {
return nil, responseError(resp)
}
@@ -135,7 +133,7 @@ func AddIssueWorklog(ua HttpClient, endpoint string, issue string, wp WorklogPro
if resp.StatusCode == 201 {
results := &jiradata.Worklog{}
return results, readJSON(resp.Body, results)
return results, json.NewDecoder(resp.Body).Decode(results)
}
return nil, responseError(resp)
}
@@ -155,7 +153,7 @@ func GetIssueEditMeta(ua HttpClient, endpoint string, issue string) (*jiradata.E
if resp.StatusCode == 200 {
results := &jiradata.EditMeta{}
return results, readJSON(resp.Body, results)
return results, json.NewDecoder(resp.Body).Decode(results)
}
return nil, responseError(resp)
}
@@ -208,7 +206,7 @@ func CreateIssue(ua HttpClient, endpoint string, iup IssueUpdateProvider) (*jira
if resp.StatusCode == 201 {
results := &jiradata.IssueCreateResponse{}
return results, readJSON(resp.Body, results)
return results, json.NewDecoder(resp.Body).Decode(results)
}
return nil, responseError(resp)
}
@@ -229,7 +227,7 @@ func GetIssueCreateMetaProject(ua HttpClient, endpoint string, projectKey string
if resp.StatusCode == 200 {
results := &jiradata.CreateMeta{}
err = readJSON(resp.Body, results)
err = json.NewDecoder(resp.Body).Decode(results)
if err != nil {
return nil, err
}
@@ -238,7 +236,7 @@ func GetIssueCreateMetaProject(ua HttpClient, endpoint string, projectKey string
return project, nil
}
}
return nil, fmt.Errorf("Project %s not found", projectKey)
return nil, fmt.Errorf("project %s not found", projectKey)
}
return nil, responseError(resp)
}
@@ -257,24 +255,24 @@ func GetIssueCreateMetaIssueType(ua HttpClient, endpoint string, projectKey, iss
}
defer resp.Body.Close()
if resp.StatusCode == 200 {
results := &jiradata.CreateMeta{}
err = readJSON(resp.Body, results)
if err != nil {
return nil, err
if resp.StatusCode != 200 {
return nil, responseError(resp)
}
results := &jiradata.CreateMeta{}
if err := json.NewDecoder(resp.Body).Decode(results); err != nil {
return nil, err
}
for _, project := range results.Projects {
if project.Key != projectKey {
continue
}
for _, project := range results.Projects {
if project.Key == projectKey {
for _, issueType := range project.IssueTypes {
if issueType.Name == issueTypeName {
return issueType, nil
}
}
for _, issueType := range project.IssueTypes {
if issueType.Name == issueTypeName {
return issueType, nil
}
}
return nil, fmt.Errorf("Project %s and IssueType %s not found", projectKey, issueTypeName)
}
return nil, responseError(resp)
return nil, fmt.Errorf("project %s and IssueType %s not found", projectKey, issueTypeName)
}
type LinkIssueProvider interface {
@@ -321,7 +319,7 @@ func GetIssueTransitions(ua HttpClient, endpoint string, issue string) (*jiradat
if resp.StatusCode == 200 {
results := &jiradata.TransitionsMeta{}
return results, readJSON(resp.Body, results)
return results, json.NewDecoder(resp.Body).Decode(results)
}
return nil, responseError(resp)
}
@@ -369,7 +367,7 @@ func GetIssueLinkTypes(ua HttpClient, endpoint string) (*jiradata.IssueLinkTypes
}{
IssueLinkTypes: jiradata.IssueLinkTypes{},
}
return &results.IssueLinkTypes, readJSON(resp.Body, &results)
return &results.IssueLinkTypes, json.NewDecoder(resp.Body).Decode(&results)
}
return nil, responseError(resp)
}
@@ -503,7 +501,7 @@ func IssueAddComment(ua HttpClient, endpoint string, issue string, cp CommentPro
if resp.StatusCode == 201 {
results := jiradata.Comment{}
return &results, readJSON(resp.Body, &results)
return &results, json.NewDecoder(resp.Body).Decode(&results)
}
return nil, responseError(resp)
}
@@ -563,6 +561,9 @@ func IssueAttachFile(ua HttpClient, endpoint string, issue, filename string, con
}
uri, err := url.Parse(URLJoin(endpoint, "rest/api/2/issue", issue, "attachments"))
if err != nil {
return nil, err
}
req := oreo.RequestBuilder(uri).WithMethod("POST").WithHeader(
"X-Atlassian-Token", "no-check",
).WithHeader(
@@ -578,7 +579,7 @@ func IssueAttachFile(ua HttpClient, endpoint string, issue, filename string, con
if resp.StatusCode == 200 {
results := jiradata.ListOfAttachment{}
return &results, readJSON(resp.Body, &results)
return &results, json.NewDecoder(resp.Body).Decode(&results)
}
return nil, responseError(resp)
}
+1 -4
View File
@@ -2,12 +2,9 @@ package jira
import (
"github.com/coryb/oreo"
logging "gopkg.in/op/go-logging.v1"
)
var log = logging.MustGetLogger("jira")
const VERSION = "1.0.17"
const VERSION = "1.0.20"
type Jira struct {
Endpoint string `json:"endpoint,omitempty" yaml:"endpoint,omitempty"`
+135 -74
View File
@@ -11,6 +11,7 @@ import (
"os"
"os/exec"
"reflect"
"runtime/debug"
"strings"
"github.com/coryb/figtree"
@@ -21,23 +22,79 @@ import (
"gopkg.in/AlecAivazis/survey.v1"
kingpin "gopkg.in/alecthomas/kingpin.v2"
yaml "gopkg.in/coryb/yaml.v2"
logging "gopkg.in/op/go-logging.v1"
)
type Exit struct {
Code int
}
// HandleExit will unwind any panics and check to see if they are jiracli.Exit
// and exit accordingly.
//
// Example:
// func main() {
// defer jiracli.HandleExit()
// ...
// }
func HandleExit() {
if e := recover(); e != nil {
if exit, ok := e.(Exit); ok {
os.Exit(exit.Code)
} else {
fmt.Fprintf(os.Stderr, "%s\n%s", e, debug.Stack())
os.Exit(1)
}
}
}
type GlobalOptions struct {
// AuthenticationMethod is the method we use to authenticate with the jira serivce. Possible values are "api-token" or "session".
// The default is "api-token" when the service endpoint ends with "atlassian.net", otherwise it "session". Session authentication
// will promt for user password and use the /auth/1/session-login endpoint.
AuthenticationMethod figtree.StringOption `yaml:"authentication-method,omitempty" json:"authentication-method,omitempty"`
Endpoint figtree.StringOption `yaml:"endpoint,omitempty" json:"endpoint,omitempty"`
Insecure figtree.BoolOption `yaml:"insecure,omitempty" json:"insecure,omitempty"`
Login figtree.StringOption `yaml:"login,omitempty" json:"login,omitempty"`
PasswordSource figtree.StringOption `yaml:"password-source,omitempty" json:"password-source,omitempty"`
PasswordDirectory figtree.StringOption `yaml:"password-directory,omitempty" json:"password-directory,omitempty"`
Quiet figtree.BoolOption `yaml:"quiet,omitempty" json:"quiet,omitempty"`
SocksProxy figtree.StringOption `yaml:"socksproxy,omitempty" json:"socksproxy,omitempty"`
UnixProxy figtree.StringOption `yaml:"unixproxy,omitempty" json:"unixproxy,omitempty"`
User figtree.StringOption `yaml:"user,omitempty" json:"user,omitempty"`
// Endpoint is the URL for the Jira service. Something like: https://go-jira.atlassian.net
Endpoint figtree.StringOption `yaml:"endpoint,omitempty" json:"endpoint,omitempty"`
// Insecure will allow you to connect to an https endpoint with a self-signed SSL certificate
Insecure figtree.BoolOption `yaml:"insecure,omitempty" json:"insecure,omitempty"`
// Login is the id used for authenticating with the Jira service. For "api-token" AuthenticationMethod this is usually a
// full email address, something like "user@example.com". For "session" AuthenticationMethod this will be something
// like "user", which by default will use the same value in the `User` field.
Login figtree.StringOption `yaml:"login,omitempty" json:"login,omitempty"`
// PasswordSource specificies the method that we fetch the password. Possible values are "keyring" or "pass".
// If this is unset we will just prompt the user. For "keyring" this will look in the OS keychain, if missing
// then prompt the user and store the password in the OS keychain. For "pass" this will look in the PasswordDirectory
// location using the `pass` tool, if missing prompt the user and store in the PasswordDirectory
PasswordSource figtree.StringOption `yaml:"password-source,omitempty" json:"password-source,omitempty"`
// PasswordDirectory is only used for the "pass" PasswordSource. It is the location for the encrypted password
// files used by `pass`. Effectively this overrides the "PASSWORD_STORE_DIR" environment variable
PasswordDirectory figtree.StringOption `yaml:"password-directory,omitempty" json:"password-directory,omitempty"`
// PasswordName is the the name of the password key entry stored used with PasswordSource `pass`.
PasswordName figtree.StringOption `yaml:"password-name,omitempty" json:"password-name,omitempty"`
// Quiet will lower the defalt log level to suppress the standard output for commands
Quiet figtree.BoolOption `yaml:"quiet,omitempty" json:"quiet,omitempty"`
// SocksProxy is used to configure the http client to access the Endpoint via a socks proxy. The value
// should be a ip address and port string, something like "127.0.0.1:1080"
SocksProxy figtree.StringOption `yaml:"socksproxy,omitempty" json:"socksproxy,omitempty"`
// UnixProxy is use to configure the http client to access the Endpoint via a local unix domain socket used
// to proxy requests
UnixProxy figtree.StringOption `yaml:"unixproxy,omitempty" json:"unixproxy,omitempty"`
// User is use to represent the user on the Jira service. This can be different from the username used to
// authenticate with the service. For example when using AuthenticationMethod `api-token` the Login is
// typically an email address like `username@example.com` and the User property would be someting like
// `username` The User property is used on Jira service API calls that require a user to associate with
// an Issue (like assigning a Issue to yourself)
User figtree.StringOption `yaml:"user,omitempty" json:"user,omitempty"`
}
type CommonOptions struct {
@@ -93,43 +150,39 @@ func register(app *kingpin.Application, o *oreo.Client, fig *figtree.FigTree) {
app.Flag("user", "user name used within the Jira service").Short('u').SetValue(&globals.User)
app.Flag("login", "login name that corresponds to the user used for authentication").SetValue(&globals.Login)
o = o.WithPreCallback(
func(req *http.Request) (*http.Request, error) {
if globals.AuthMethod() == "api-token" {
// need to set basic auth header with user@domain:api-token
token := globals.GetPass()
authHeader := fmt.Sprintf("Basic %s", base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", globals.Login.Value, token))))
req.Header.Add("Authorization", authHeader)
}
return req, nil
},
)
o = o.WithPreCallback(func(req *http.Request) (*http.Request, error) {
if globals.AuthMethod() == "api-token" {
// need to set basic auth header with user@domain:api-token
token := globals.GetPass()
authHeader := fmt.Sprintf("Basic %s", base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", globals.Login.Value, token))))
req.Header.Add("Authorization", authHeader)
}
return req, nil
})
o = o.WithPostCallback(
func(req *http.Request, resp *http.Response) (*http.Response, error) {
if globals.AuthMethod() == "session" {
authUser := resp.Header.Get("X-Ausername")
if authUser == "" || authUser == "anonymous" {
// preserve the --quiet value, we need to temporarily disable it so
// the normal login output is surpressed
defer func(quiet bool) {
globals.Quiet.Value = quiet
}(globals.Quiet.Value)
globals.Quiet.Value = true
o = o.WithPostCallback(func(req *http.Request, resp *http.Response) (*http.Response, error) {
if globals.AuthMethod() == "session" {
authUser := resp.Header.Get("X-Ausername")
if authUser == "" || authUser == "anonymous" {
// preserve the --quiet value, we need to temporarily disable it so
// the normal login output is surpressed
defer func(quiet bool) {
globals.Quiet.Value = quiet
}(globals.Quiet.Value)
globals.Quiet.Value = true
// we are not logged in, so force login now by running the "login" command
app.Parse([]string{"login"})
// we are not logged in, so force login now by running the "login" command
app.Parse([]string{"login"})
// rerun the original request
return o.Do(req)
}
} else if globals.AuthMethod() == "api-token" && resp.StatusCode == 401 {
globals.SetPass("")
// rerun the original request
return o.Do(req)
}
return resp, nil
},
)
} else if globals.AuthMethod() == "api-token" && resp.StatusCode == 401 {
globals.SetPass("")
return o.Do(req)
}
return resp, nil
})
for _, command := range globalCommandRegistry {
copy := command
@@ -181,11 +234,12 @@ func register(app *kingpin.Application, o *oreo.Client, fig *figtree.FigTree) {
copy.Entry.UsageFunc(fig, cmd)
}
cmd.Action(
func(_ *kingpin.ParseContext) error {
return copy.Entry.ExecuteFunc(o, &globals)
},
)
cmd.Action(func(_ *kingpin.ParseContext) error {
if logging.GetLevel("") > logging.DEBUG {
o = o.WithTrace(true)
}
return copy.Entry.ExecuteFunc(o, &globals)
})
}
}
@@ -261,38 +315,45 @@ func (o *CommonOptions) editFile(fileName string) (changes bool, err error) {
}
// now we just need to diff the files to see if there are any changes
var oldHandle, newHandle *os.File
var oldStat, newStat os.FileInfo
if oldHandle, err = os.Open(tmpFileNameOrig); err == nil {
if newHandle, err = os.Open(fileName); err == nil {
if oldStat, err = oldHandle.Stat(); err == nil {
if newStat, err = newHandle.Stat(); err == nil {
// different sizes, so must have changes
if oldStat.Size() != newStat.Size() {
return true, err
}
oldBuf, newBuf := make([]byte, 1024), make([]byte, 1024)
var oldCount, newCount int
// loop though 1024 bytes at a time comparing the buffers for changes
for err != io.EOF {
oldCount, _ = oldHandle.Read(oldBuf)
newCount, err = newHandle.Read(newBuf)
if oldCount != newCount {
return true, nil
}
if bytes.Compare(oldBuf[:oldCount], newBuf[:newCount]) != 0 {
return true, nil
}
}
return false, nil
}
}
f1, err := os.Open(tmpFileNameOrig)
if err != nil {
return false, err
}
f2, err := os.Open(fileName)
if err != nil {
return false, err
}
stat1, err := f1.Stat()
if err != nil {
return false, err
}
stat2, err := f2.Stat()
if err != nil {
return false, err
}
// different sizes, so must have changes
if stat1.Size() != stat2.Size() {
return true, nil
}
p1, p2 := make([]byte, 1024), make([]byte, 1024)
var n1, n2 int
// loop though 1024 bytes at a time comparing the buffers for changes
for err != io.EOF {
n1, _ = f1.Read(p1)
n2, err = f2.Read(p2)
if n1 != n2 {
return true, nil
}
if !bytes.Equal(p1[:n1], p2[:n2]) {
return true, nil
}
}
return false, err
return false, nil
}
var EditLoopAbort = fmt.Errorf("Edit Loop aborted by request")
var EditLoopAbort = fmt.Errorf("edit Loop aborted by request")
func EditLoop(opts *CommonOptions, input interface{}, output interface{}, submit func() error) error {
tmpFile, err := tmpTemplate(opts.Template.Value, input)
-5
View File
@@ -4,7 +4,6 @@ import (
"os"
"strconv"
"github.com/coryb/oreo"
logging "gopkg.in/op/go-logging.v1"
)
@@ -14,10 +13,6 @@ var (
func IncreaseLogLevel(verbosity int) {
logging.SetLevel(logging.GetLevel("")+logging.Level(verbosity), "")
if logging.GetLevel("") > logging.DEBUG {
oreo.TraceRequestBody = true
oreo.TraceResponseBody = true
}
}
func InitLogging() {
+11 -1
View File
@@ -3,12 +3,13 @@ package jiracli
import (
"bytes"
"fmt"
"io/ioutil"
"os"
"os/exec"
"strings"
"github.com/go-jira/jira/jiradata"
"gopkg.in/AlecAivazis/survey.v1"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiradata"
)
func (o *GlobalOptions) ProvideAuthParams() *jiradata.AuthParams {
@@ -25,6 +26,9 @@ func (o *GlobalOptions) keyName() string {
}
if o.PasswordSource.Value == "pass" {
if o.PasswordName.Value != "" {
return o.PasswordName.Value
}
return fmt.Sprintf("GoJira/%s", user)
}
return user
@@ -54,6 +58,12 @@ func (o *GlobalOptions) GetPass() string {
passwd = strings.TrimSpace(buf.String())
}
}
} else if o.PasswordSource.Value == "stdin" {
allBytes, err := ioutil.ReadAll(os.Stdin)
if err != nil {
panic(fmt.Sprintf("unable to read bytes from stdin: %s", err))
}
passwd = string(allBytes)
} else {
log.Warningf("Unknown password-source: %s", o.PasswordSource)
}
+5 -4
View File
@@ -44,7 +44,8 @@ func getTemplate(name string) (string, error) {
b, err := findTemplate(name)
if err != nil {
return "", err
} else if b != nil {
}
if b != nil {
return string(b), nil
}
if s, ok := AllTemplates[name]; ok {
@@ -119,7 +120,7 @@ func TemplateProcessor() *template.Template {
}
},
"indent": func(spaces int, content string) string {
indent := make([]rune, spaces+1, spaces+1)
indent := make([]rune, spaces+1)
indent[0] = '\n'
for i := 1; i < spaces+1; i++ {
indent[i] = ' '
@@ -504,7 +505,7 @@ fields:
{{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: {{.name}}{{end}}{{else}}{{range .fields.fixVersions}}
- name: {{.}}{{end}}{{else}}{{range .fields.fixVersions}}
- name: {{.name}}{{end}}{{end}}
{{- end -}}
{{- end -}}
@@ -546,7 +547,7 @@ transition:
const defaultWorklogTemplate = `{{/* worklog template */ -}}
# issue: {{ .issue }}
comment: |~
{{ or .comment "" }}
{{ or .comment "" | indent 2 }}
timeSpent: {{ or .timeSpent "" }}
started: {{ or .started "" }}
`
+7 -8
View File
@@ -11,7 +11,7 @@ import (
"github.com/coryb/figtree"
"github.com/coryb/kingpeon"
"github.com/coryb/oreo"
jira "gopkg.in/Netflix-Skunkworks/go-jira.v1"
jira "github.com/go-jira/jira"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
@@ -187,13 +187,12 @@ func ParseCommandLine(app *kingpin.Application, args []string) {
if _, ok := err.(*Error); ok {
log.Errorf("%s", err)
panic(Exit{Code: 1})
} else {
ctx, _ := app.ParseContext(os.Args[1:])
if ctx != nil {
app.UsageForContext(ctx)
}
log.Errorf("Invalid Usage: %s", err)
panic(Exit{Code: 1})
}
ctx, _ := app.ParseContext(os.Args[1:])
if ctx != nil {
app.UsageForContext(ctx)
}
log.Errorf("Invalid Usage: %s", err)
panic(Exit{Code: 1})
}
}
+7 -4
View File
@@ -1,7 +1,6 @@
package jiracli
import (
"errors"
"fmt"
"io"
"io/ioutil"
@@ -23,11 +22,15 @@ func Homedir() string {
}
func findClosestParentPath(fileName string) (string, error) {
paths := figtree.FindParentPaths(fileName)
cwd, err := os.Getwd()
if err != nil {
return "", err
}
paths := figtree.FindParentPaths(Homedir(), cwd, 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 tmpYml(tmpFilePrefix string) (*os.File, error) {
@@ -82,7 +85,7 @@ func fuzzyAge(start string) (string, error) {
if err != nil {
return "", err
}
delta := time.Now().Sub(t)
delta := time.Since(t)
if delta.Minutes() < 2 {
return "a minute", nil
} else if dm := delta.Minutes(); dm < 45 {
+71
View File
@@ -0,0 +1,71 @@
package jiracli
import (
"io/ioutil"
"os"
"path/filepath"
"testing"
)
func realPath(path string) string {
cpath, err := filepath.EvalSymlinks(path)
if err != nil {
log.Fatal(err)
}
return cpath
}
func comparePaths(p1 string, p2 string) bool {
if realPath(p1) == realPath(p2) {
return true
}
return false
}
func TestFindClosestParentPath(t *testing.T) {
dir, err := ioutil.TempDir("", "testFindParentPath")
if err != nil {
log.Fatal(err)
}
defer os.RemoveAll(dir)
origDir, err := os.Getwd()
if err != nil {
log.Fatal(err)
}
defer os.Chdir(origDir)
t1 := filepath.Join(dir, "/.test1")
t2 := filepath.Join(t1, "/.test2")
t3 := filepath.Join(t2, "/.test1")
err = os.MkdirAll(t3, os.ModePerm)
if err != nil {
log.Fatal(err)
}
os.Chdir(t3)
path1, err := findClosestParentPath(".test1")
if err != nil {
t.Errorf("findClosestParentPath should not have errored: %s", err)
}
if ok := comparePaths(path1, t3); !ok {
t.Errorf("%s != %s", path1, t3)
}
path2, err := findClosestParentPath(".test2")
if err != nil {
t.Errorf("findClosestParentPath should not have errored: %s", err)
}
if ok := comparePaths(path2, t2); !ok {
t.Errorf("%s != %s", path2, t2)
}
path3, err := findClosestParentPath(".test3")
if err.Error() != ".test3 not found in parent directory hierarchy" {
t.Errorf("incorrect error from findClosestParentPath: %s", err)
}
if path3 != "" {
t.Errorf("path3 should be empty, but is not: %s", path3)
}
}
+2 -2
View File
@@ -5,8 +5,8 @@ import (
"github.com/coryb/figtree"
"github.com/coryb/oreo"
"gopkg.in/Netflix-Skunkworks/go-jira.v1"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiracli"
"github.com/go-jira/jira"
"github.com/go-jira/jira/jiracli"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
+2 -2
View File
@@ -9,8 +9,8 @@ import (
"github.com/coryb/figtree"
"github.com/coryb/oreo"
jira "gopkg.in/Netflix-Skunkworks/go-jira.v1"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiracli"
jira "github.com/go-jira/jira"
"github.com/go-jira/jira/jiracli"
kingpin "gopkg.in/alecthomas/kingpin.v2"
yaml "gopkg.in/coryb/yaml.v2"
)
+2 -2
View File
@@ -7,8 +7,8 @@ import (
"github.com/coryb/figtree"
"github.com/coryb/oreo"
jira "gopkg.in/Netflix-Skunkworks/go-jira.v1"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiracli"
jira "github.com/go-jira/jira"
"github.com/go-jira/jira/jiracli"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
+3 -3
View File
@@ -5,9 +5,9 @@ import (
"github.com/coryb/figtree"
"github.com/coryb/oreo"
"gopkg.in/Netflix-Skunkworks/go-jira.v1"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiracli"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiradata"
"github.com/go-jira/jira"
"github.com/go-jira/jira/jiracli"
"github.com/go-jira/jira/jiradata"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
+2 -2
View File
@@ -5,8 +5,8 @@ import (
"github.com/coryb/figtree"
"github.com/coryb/oreo"
jira "gopkg.in/Netflix-Skunkworks/go-jira.v1"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiracli"
jira "github.com/go-jira/jira"
"github.com/go-jira/jira/jiracli"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
+3 -3
View File
@@ -6,9 +6,9 @@ import (
"github.com/coryb/figtree"
"github.com/coryb/oreo"
"gopkg.in/Netflix-Skunkworks/go-jira.v1"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiracli"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiradata"
"github.com/go-jira/jira"
"github.com/go-jira/jira/jiracli"
"github.com/go-jira/jira/jiradata"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
+2 -2
View File
@@ -3,9 +3,9 @@ package jiracmd
import (
"github.com/coryb/figtree"
"github.com/coryb/oreo"
jira "github.com/go-jira/jira"
"github.com/go-jira/jira/jiracli"
"github.com/pkg/browser"
jira "gopkg.in/Netflix-Skunkworks/go-jira.v1"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiracli"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
+3 -3
View File
@@ -6,9 +6,9 @@ import (
"github.com/coryb/figtree"
"github.com/coryb/oreo"
"gopkg.in/Netflix-Skunkworks/go-jira.v1"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiracli"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiradata"
"github.com/go-jira/jira"
"github.com/go-jira/jira/jiracli"
"github.com/go-jira/jira/jiradata"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
+4 -5
View File
@@ -6,9 +6,9 @@ import (
"github.com/coryb/figtree"
"github.com/coryb/oreo"
"gopkg.in/Netflix-Skunkworks/go-jira.v1"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiracli"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiradata"
"github.com/go-jira/jira"
"github.com/go-jira/jira/jiracli"
"github.com/go-jira/jira/jiradata"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
@@ -52,9 +52,8 @@ func CmdComponentAddUsage(cmd *kingpin.CmdClause, opts *ComponentAddOptions) err
func CmdComponentAdd(o *oreo.Client, globals *jiracli.GlobalOptions, opts *ComponentAddOptions) error {
var err error
component := &jiradata.Component{}
var resp *jiradata.Component
err = jiracli.EditLoop(&opts.CommonOptions, &opts.Component, component, func() error {
resp, err = jira.CreateComponent(o, globals.Endpoint.Value, component)
_, err = jira.CreateComponent(o, globals.Endpoint.Value, component)
return err
})
if err != nil {
+2 -2
View File
@@ -6,8 +6,8 @@ import (
"github.com/coryb/figtree"
"github.com/coryb/oreo"
"gopkg.in/Netflix-Skunkworks/go-jira.v1"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiracli"
"github.com/go-jira/jira"
"github.com/go-jira/jira/jiracli"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
+3 -3
View File
@@ -7,9 +7,9 @@ import (
"github.com/coryb/figtree"
"github.com/coryb/oreo"
"gopkg.in/Netflix-Skunkworks/go-jira.v1"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiracli"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiradata"
"github.com/go-jira/jira"
"github.com/go-jira/jira/jiracli"
"github.com/go-jira/jira/jiradata"
kingpin "gopkg.in/alecthomas/kingpin.v2"
yaml "gopkg.in/coryb/yaml.v2"
)
+2 -2
View File
@@ -3,8 +3,8 @@ package jiracmd
import (
"github.com/coryb/figtree"
"github.com/coryb/oreo"
"gopkg.in/Netflix-Skunkworks/go-jira.v1"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiracli"
"github.com/go-jira/jira"
"github.com/go-jira/jira/jiracli"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
+27 -26
View File
@@ -6,9 +6,9 @@ import (
"github.com/coryb/figtree"
"github.com/coryb/oreo"
"gopkg.in/Netflix-Skunkworks/go-jira.v1"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiracli"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiradata"
"github.com/go-jira/jira"
"github.com/go-jira/jira/jiracli"
"github.com/go-jira/jira/jiradata"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
@@ -60,7 +60,7 @@ func CmdDupUsage(cmd *kingpin.CmdClause, opts *DupOptions) error {
return nil
}
// CmdDups will update the given issue as being a duplicate by the given dup issue
// CmdDup will update the given issue as being a duplicate by the given dup issue
// and will attempt to resolve the dup issue
func CmdDup(o *oreo.Client, globals *jiracli.GlobalOptions, opts *DupOptions) error {
if err := jira.LinkIssues(o, globals.Endpoint.Value, &opts.LinkIssueRequest); err != nil {
@@ -76,30 +76,31 @@ func CmdDup(o *oreo.Client, globals *jiracli.GlobalOptions, opts *DupOptions) er
}
for _, trans := range []string{"close", "done", "cancel", "start", "stop"} {
transMeta := meta.Transitions.Find(trans)
if transMeta != nil {
issueUpdate := jiradata.IssueUpdate{
Transition: transMeta,
}
resolution := defaultResolution(transMeta)
if resolution != "" {
issueUpdate.Fields = map[string]interface{}{
"resolution": map[string]interface{}{
"name": resolution,
},
}
}
if err = jira.TransitionIssue(o, globals.Endpoint.Value, opts.InwardIssue.Key, &issueUpdate); err != nil {
return err
}
if trans != "start" {
break
}
// if we are here then we must be stopping, so need to reset the meta
meta, err = jira.GetIssueTransitions(o, globals.Endpoint.Value, opts.InwardIssue.Key)
if err != nil {
return err
if transMeta == nil {
continue
}
issueUpdate := jiradata.IssueUpdate{
Transition: transMeta,
}
resolution := defaultResolution(transMeta)
if resolution != "" {
issueUpdate.Fields = map[string]interface{}{
"resolution": map[string]interface{}{
"name": resolution,
},
}
}
if err = jira.TransitionIssue(o, globals.Endpoint.Value, opts.InwardIssue.Key, &issueUpdate); err != nil {
return err
}
if trans != "start" {
break
}
// if we are here then we must be stopping, so need to reset the meta
meta, err = jira.GetIssueTransitions(o, globals.Endpoint.Value, opts.InwardIssue.Key)
if err != nil {
return err
}
}
if !globals.Quiet.Value {
+16 -18
View File
@@ -5,10 +5,10 @@ import (
"github.com/coryb/figtree"
"github.com/coryb/oreo"
"github.com/go-jira/jira"
"github.com/go-jira/jira/jiracli"
"github.com/go-jira/jira/jiradata"
"gopkg.in/AlecAivazis/survey.v1"
"gopkg.in/Netflix-Skunkworks/go-jira.v1"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiracli"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiradata"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
@@ -124,22 +124,20 @@ func CmdEdit(o *oreo.Client, globals *jiracli.GlobalOptions, opts *EditOptions)
err = jiracli.EditLoop(&opts.CommonOptions, &input, &issueUpdate, func() error {
return jira.EditIssue(o, globals.Endpoint.Value, issueData.Key, &issueUpdate)
})
if err == jiracli.EditLoopAbort {
if len(results.Issues) > i+1 {
var answer bool
survey.AskOne(
&survey.Confirm{
Message: fmt.Sprintf("Continue to edit next issue %s?", results.Issues[i+1].Key),
Default: true,
},
&answer,
nil,
)
if answer {
continue
}
panic(jiracli.Exit{1})
if err == jiracli.EditLoopAbort && len(results.Issues) > i+1 {
var answer bool
survey.AskOne(
&survey.Confirm{
Message: fmt.Sprintf("Continue to edit next issue %s?", results.Issues[i+1].Key),
Default: true,
},
&answer,
nil,
)
if answer {
continue
}
panic(jiracli.Exit{1})
}
if err != nil {
return err
+3 -3
View File
@@ -3,8 +3,8 @@ package jiracmd
import (
"github.com/coryb/figtree"
"github.com/coryb/oreo"
"gopkg.in/Netflix-Skunkworks/go-jira.v1"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiracli"
"github.com/go-jira/jira"
"github.com/go-jira/jira/jiracli"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
@@ -41,7 +41,7 @@ func CmdEditMetaUsage(cmd *kingpin.CmdClause, opts *EditMetaOptions) error {
return nil
}
// EditMeta will get issue edit metadata and send to "editmeta" template
// CmdEditMeta will get issue edit metadata and send to "editmeta" template
func CmdEditMeta(o *oreo.Client, globals *jiracli.GlobalOptions, opts *EditMetaOptions) error {
editMeta, err := jira.GetIssueEditMeta(o, globals.Endpoint.Value, opts.Issue)
if err != nil {
+3 -3
View File
@@ -6,9 +6,9 @@ import (
"github.com/coryb/figtree"
"github.com/coryb/oreo"
"gopkg.in/Netflix-Skunkworks/go-jira.v1"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiracli"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiradata"
"github.com/go-jira/jira"
"github.com/go-jira/jira/jiracli"
"github.com/go-jira/jira/jiradata"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
+1 -1
View File
@@ -4,7 +4,7 @@ import (
"github.com/coryb/figtree"
"github.com/coryb/oreo"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiracli"
"github.com/go-jira/jira/jiracli"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
+2 -2
View File
@@ -3,8 +3,8 @@ package jiracmd
import (
"github.com/coryb/figtree"
"github.com/coryb/oreo"
"gopkg.in/Netflix-Skunkworks/go-jira.v1"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiracli"
"github.com/go-jira/jira"
"github.com/go-jira/jira/jiracli"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
+3 -3
View File
@@ -6,9 +6,9 @@ import (
"github.com/coryb/figtree"
"github.com/coryb/oreo"
"gopkg.in/Netflix-Skunkworks/go-jira.v1"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiracli"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiradata"
"github.com/go-jira/jira"
"github.com/go-jira/jira/jiracli"
"github.com/go-jira/jira/jiradata"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
+1 -1
View File
@@ -7,7 +7,7 @@ import (
"github.com/coryb/figtree"
"github.com/coryb/oreo"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiracli"
"github.com/go-jira/jira/jiracli"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
+2 -2
View File
@@ -3,8 +3,8 @@ package jiracmd
import (
"github.com/coryb/figtree"
"github.com/coryb/oreo"
"gopkg.in/Netflix-Skunkworks/go-jira.v1"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiracli"
"github.com/go-jira/jira"
"github.com/go-jira/jira/jiracli"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
+4 -4
View File
@@ -6,9 +6,9 @@ import (
"github.com/coryb/figtree"
"github.com/coryb/oreo"
"gopkg.in/Netflix-Skunkworks/go-jira.v1"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiracli"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiradata"
"github.com/go-jira/jira"
"github.com/go-jira/jira/jiracli"
"github.com/go-jira/jira/jiradata"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
@@ -54,7 +54,7 @@ func CmdIssueLinkUsage(cmd *kingpin.CmdClause, opts *IssueLinkOptions) error {
return nil
}
// CmdBlock will update the given issue as being a duplicate by the given dup issue
// CmdIssueLink will update the given issue as being a duplicate by the given dup issue
// and will attempt to resolve the dup issue
func CmdIssueLink(o *oreo.Client, globals *jiracli.GlobalOptions, opts *IssueLinkOptions) error {
if err := jira.LinkIssues(o, globals.Endpoint.Value, &opts.LinkIssueRequest); err != nil {
+2 -2
View File
@@ -3,8 +3,8 @@ package jiracmd
import (
"github.com/coryb/figtree"
"github.com/coryb/oreo"
"gopkg.in/Netflix-Skunkworks/go-jira.v1"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiracli"
"github.com/go-jira/jira"
"github.com/go-jira/jira/jiracli"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
+2 -2
View File
@@ -6,8 +6,8 @@ import (
"github.com/coryb/figtree"
"github.com/coryb/oreo"
"gopkg.in/Netflix-Skunkworks/go-jira.v1"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiracli"
"github.com/go-jira/jira"
"github.com/go-jira/jira/jiracli"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
+4 -4
View File
@@ -6,9 +6,9 @@ import (
"github.com/coryb/figtree"
"github.com/coryb/oreo"
"gopkg.in/Netflix-Skunkworks/go-jira.v1"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiracli"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiradata"
"github.com/go-jira/jira"
"github.com/go-jira/jira/jiracli"
"github.com/go-jira/jira/jiradata"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
@@ -39,7 +39,7 @@ func CmdLabelsAddUsage(cmd *kingpin.CmdClause, opts *LabelsAddOptions) error {
return nil
}
// CmdLabels will add labels on a given issue
// CmdLabelsAdd will add labels on a given issue
func CmdLabelsAdd(o *oreo.Client, globals *jiracli.GlobalOptions, opts *LabelsAddOptions) error {
ops := jiradata.FieldOperations{}
for _, label := range opts.Labels {
+4 -4
View File
@@ -6,9 +6,9 @@ import (
"github.com/coryb/figtree"
"github.com/coryb/oreo"
"gopkg.in/Netflix-Skunkworks/go-jira.v1"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiracli"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiradata"
"github.com/go-jira/jira"
"github.com/go-jira/jira/jiracli"
"github.com/go-jira/jira/jiradata"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
@@ -39,7 +39,7 @@ func CmdLabelsRemoveUsage(cmd *kingpin.CmdClause, opts *LabelsRemoveOptions) err
return nil
}
// CmdLabels will remove labels on a given issue
// CmdLabelsRemove will remove labels on a given issue
func CmdLabelsRemove(o *oreo.Client, globals *jiracli.GlobalOptions, opts *LabelsRemoveOptions) error {
ops := jiradata.FieldOperations{}
for _, label := range opts.Labels {
+4 -4
View File
@@ -6,9 +6,9 @@ import (
"github.com/coryb/figtree"
"github.com/coryb/oreo"
"gopkg.in/Netflix-Skunkworks/go-jira.v1"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiracli"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiradata"
"github.com/go-jira/jira"
"github.com/go-jira/jira/jiracli"
"github.com/go-jira/jira/jiradata"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
@@ -39,7 +39,7 @@ func CmdLabelsSetUsage(cmd *kingpin.CmdClause, opts *LabelsSetOptions) error {
return nil
}
// CmdLabels will set labels on a given issue
// CmdLabelsSet will set labels on a given issue
func CmdLabelsSet(o *oreo.Client, globals *jiracli.GlobalOptions, opts *LabelsSetOptions) error {
issueUpdate := jiradata.IssueUpdate{
Update: jiradata.FieldOperationsMap{
+3 -6
View File
@@ -5,8 +5,8 @@ import (
"github.com/coryb/figtree"
"github.com/coryb/oreo"
"gopkg.in/Netflix-Skunkworks/go-jira.v1"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiracli"
"github.com/go-jira/jira"
"github.com/go-jira/jira/jiracli"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
@@ -30,9 +30,6 @@ func CmdListRegistry() *jiracli.CommandRegistryEntry {
return CmdListUsage(cmd, &opts, fig)
},
func(o *oreo.Client, globals *jiracli.GlobalOptions) error {
if opts.MaxResults == 0 {
opts.MaxResults = 500
}
if opts.QueryFields == "" {
opts.QueryFields = "assignee,created,priority,reporter,status,summary,updated,issuetype"
}
@@ -72,7 +69,7 @@ func CmdListUsage(cmd *kingpin.CmdClause, opts *ListOptions, fig *figtree.FigTre
// List will query jira and send data to "list" template
func CmdList(o *oreo.Client, globals *jiracli.GlobalOptions, opts *ListOptions) error {
data, err := jira.Search(o, globals.Endpoint.Value, opts)
data, err := jira.Search(o, globals.Endpoint.Value, opts, jira.WithAutoPagination())
if err != nil {
return err
}
+2 -2
View File
@@ -6,9 +6,9 @@ import (
"github.com/coryb/figtree"
"github.com/coryb/oreo"
"github.com/go-jira/jira"
"github.com/go-jira/jira/jiracli"
"github.com/mgutz/ansi"
"gopkg.in/Netflix-Skunkworks/go-jira.v1"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiracli"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
+2 -2
View File
@@ -6,11 +6,11 @@ import (
"github.com/coryb/figtree"
"github.com/coryb/oreo"
"github.com/go-jira/jira"
"github.com/go-jira/jira/jiracli"
"github.com/mgutz/ansi"
"golang.org/x/crypto/ssh/terminal"
survey "gopkg.in/AlecAivazis/survey.v1"
"gopkg.in/Netflix-Skunkworks/go-jira.v1"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiracli"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
+3 -3
View File
@@ -6,9 +6,9 @@ import (
"github.com/coryb/figtree"
"github.com/coryb/oreo"
"gopkg.in/Netflix-Skunkworks/go-jira.v1"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiracli"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiradata"
"github.com/go-jira/jira"
"github.com/go-jira/jira/jiracli"
"github.com/go-jira/jira/jiradata"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
+2 -1
View File
@@ -1,6 +1,6 @@
package jiracmd
import "gopkg.in/Netflix-Skunkworks/go-jira.v1/jiracli"
import "github.com/go-jira/jira/jiracli"
func RegisterAllCommands() {
jiracli.RegisterCommand(jiracli.CommandRegistry{Command: "acknowledge", Entry: CmdTransitionRegistry("acknowledge"), Aliases: []string{"ack"}})
@@ -57,4 +57,5 @@ func RegisterAllCommands() {
jiracli.RegisterCommand(jiracli.CommandRegistry{Command: "watch", Entry: CmdWatchRegistry()})
jiracli.RegisterCommand(jiracli.CommandRegistry{Command: "worklog add", Entry: CmdWorklogAddRegistry()})
jiracli.RegisterCommand(jiracli.CommandRegistry{Command: "worklog list", Entry: CmdWorklogListRegistry(), Default: true})
jiracli.RegisterCommand(jiracli.CommandRegistry{Command: "session", Entry: CmdSessionRegistry()})
}
+3 -16
View File
@@ -3,14 +3,13 @@ package jiracmd
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/url"
"strings"
"github.com/coryb/figtree"
"github.com/coryb/oreo"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiracli"
"github.com/go-jira/jira/jiracli"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
@@ -75,21 +74,9 @@ func CmdRequest(o *oreo.Client, globals *jiracli.GlobalOptions, opts *RequestOpt
}
defer resp.Body.Close()
content, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
if len(content) == 0 {
if !globals.Quiet.Value {
fmt.Println("No content in response")
}
return nil
}
var data interface{}
err = json.Unmarshal(content, &data)
if err != nil {
return fmt.Errorf("JSON Parse Error: %s from %q", err, content)
if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
return fmt.Errorf("JSON Parse Error: %v", err)
}
return opts.PrintTemplate(&data)
}
+47
View File
@@ -0,0 +1,47 @@
package jiracmd
import (
"fmt"
"github.com/coryb/figtree"
"github.com/coryb/oreo"
"github.com/go-jira/jira"
"github.com/go-jira/jira/jiracli"
kingpin "gopkg.in/alecthomas/kingpin.v2"
yaml "gopkg.in/coryb/yaml.v2"
)
func CmdSessionRegistry() *jiracli.CommandRegistryEntry {
opts := jiracli.CommonOptions{}
return &jiracli.CommandRegistryEntry{
"Attempt to login into jira server",
func(fig *figtree.FigTree, cmd *kingpin.CmdClause) error {
jiracli.LoadConfigs(cmd, fig, &opts)
return nil
},
func(o *oreo.Client, globals *jiracli.GlobalOptions) error {
return CmdSession(o, globals, &opts)
},
}
}
// CmdSession will attempt to login into jira server
func CmdSession(o *oreo.Client, globals *jiracli.GlobalOptions, opts *jiracli.CommonOptions) error {
ua := o.WithoutRedirect().WithRetries(0).WithoutPostCallbacks()
session, err := jira.GetSession(ua, globals.Endpoint.Value)
var output []byte
if err != nil {
defer panic(jiracli.Exit{1})
output, err = yaml.Marshal(err)
if err != nil {
return err
}
} else {
output, err = yaml.Marshal(session)
if err != nil {
return err
}
}
fmt.Print(string(output))
return nil
}
+3 -3
View File
@@ -6,9 +6,9 @@ import (
"github.com/coryb/figtree"
"github.com/coryb/oreo"
"gopkg.in/Netflix-Skunkworks/go-jira.v1"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiracli"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiradata"
"github.com/go-jira/jira"
"github.com/go-jira/jira/jiracli"
"github.com/go-jira/jira/jiradata"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
+1 -1
View File
@@ -3,7 +3,7 @@ package jiracmd
import (
"github.com/coryb/figtree"
"github.com/coryb/oreo"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiracli"
"github.com/go-jira/jira/jiracli"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
+11 -13
View File
@@ -7,9 +7,9 @@ import (
"github.com/coryb/figtree"
"github.com/coryb/oreo"
"gopkg.in/Netflix-Skunkworks/go-jira.v1"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiracli"
"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiradata"
"github.com/go-jira/jira"
"github.com/go-jira/jira/jiracli"
"github.com/go-jira/jira/jiradata"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
@@ -110,15 +110,13 @@ func CmdTransition(o *oreo.Client, globals *jiracli.GlobalOptions, opts *Transit
}
// need to default the Resolution, usually Fixed works but sometime need Done
if opts.Resolution == "" {
if resField, ok := transMeta.Fields["resolution"]; ok {
for _, allowedValueRaw := range resField.AllowedValues {
if allowedValue, ok := allowedValueRaw.(map[string]interface{}); ok {
if allowedValue["name"] == "Fixed" {
opts.Resolution = "Fixed"
} else if allowedValue["name"] == "Done" {
opts.Resolution = "Done"
}
if resField, ok := transMeta.Fields["resolution"]; ok && opts.Resolution == "" {
for _, allowedValueRaw := range resField.AllowedValues {
if allowedValue, ok := allowedValueRaw.(map[string]interface{}); ok {
if allowedValue["name"] == "Fixed" {
opts.Resolution = "Fixed"
} else if allowedValue["name"] == "Done" {
opts.Resolution = "Done"
}
}
}
@@ -129,7 +127,7 @@ func CmdTransition(o *oreo.Client, globals *jiracli.GlobalOptions, opts *Transit
*jiradata.Issue `yaml:",inline"`
// Yes, Meta and Transition are redundant, but this is for backwards compatibility
// with old templates
Meta *jiradata.Transition `yaml:"meta,omitempty" json:"meta,omitemtpy"`
Meta *jiradata.Transition `yaml:"meta,omitempty" json:"meta,omitempty"`
Transition *jiradata.Transition `yaml:"transition,omitempty" json:"transition,omitempty"`
Overrides map[string]string `yaml:"overrides,omitempty" json:"overrides,omitempty"`
}

Some files were not shown because too many files have changed in this diff Show More